diff --git a/tlatools/.classpath b/tlatools/.classpath
index 5df2c0e31ace0682e9abd3d638adafa4534c24fa..af7fe93b8e8c46dccdd67e1df4ebf45458fdefc9 100644
--- a/tlatools/.classpath
+++ b/tlatools/.classpath
@@ -1,6 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+		<accessrules>
+			<accessrule kind="accessible" pattern="jdk/internal/**"/>
+		</accessrules>
+	</classpathentry>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry excluding="tlc2/tool/WorkerMonitorAspect.aj|tlc2/tool/distributed/TLCServerMonitorAspect.aj|tlc2/tool/distributed/RMIMethodMonitorAspect.aj|tlc2/tool/distributed/RMIMethodMonitorAspect.aj|tlc2/tool/distributed/TLCServerMonitorAspect.aj" kind="src" path="src-aj"/>
@@ -8,7 +15,15 @@
 	<classpathentry kind="src" path="test-long"/>
 	<classpathentry kind="src" path="test-concurrent"/>
 	<classpathentry kind="src" path="test-verify"/>
-	<classpathentry kind="lib" path="lib/javax.mail.jar"/>
+	<classpathentry kind="src" path="test-benchmark"/>
+	<classpathentry kind="lib" path="lib/javax.mail/mailapi-1.6.3.jar"/>
 	<classpathentry kind="lib" path="lib/jpf.jar"/>
+	<classpathentry kind="lib" path="lib/jmh/jmh-core-1.21.jar"/>
+	<classpathentry kind="lib" path="lib/jmh/commons-math3-3.2.jar"/>
+	<classpathentry kind="src" path=".apt_generated">
+		<attributes>
+			<attribute name="optional" value="true"/>
+		</attributes>
+	</classpathentry>
 	<classpathentry kind="output" path="class"/>
 </classpath>
diff --git a/tlatools/.factorypath b/tlatools/.factorypath
new file mode 100644
index 0000000000000000000000000000000000000000..82707435cb204e21ae2b472b2d0493bf1c86acd4
--- /dev/null
+++ b/tlatools/.factorypath
@@ -0,0 +1,4 @@
+<factorypath>
+    <factorypathentry kind="WKSPJAR" id="/tlatools/lib/jmh/jmh-core-1.21.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="WKSPJAR" id="/tlatools/lib/jmh/jmh-generator-annprocess-1.21.jar" enabled="true" runInBatchMode="false"/>
+</factorypath>
diff --git a/tlatools/.project b/tlatools/.project
index 7d018f8bd294b7baccfeb0e513d92bce4e08cc5a..71aaca256bda77b7ff4b51738798b65ed3343e0c 100644
--- a/tlatools/.project
+++ b/tlatools/.project
@@ -20,8 +20,14 @@
 			<arguments>
 			</arguments>
 		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 	</buildSpec>
 	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.pde.PluginNature</nature>
 	</natures>
diff --git a/tlatools/.settings/org.eclipse.jdt.apt.core.prefs b/tlatools/.settings/org.eclipse.jdt.apt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..fa6bcfb3fdb3a5ff5ccf658c79e656a02d8dbf61
--- /dev/null
+++ b/tlatools/.settings/org.eclipse.jdt.apt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.apt.aptEnabled=true
+org.eclipse.jdt.apt.genSrcDir=.apt_generated
+org.eclipse.jdt.apt.genTestSrcDir=.apt_generated_tests
+org.eclipse.jdt.apt.reconcileEnabled=true
diff --git a/tlatools/.settings/org.eclipse.jdt.core.prefs b/tlatools/.settings/org.eclipse.jdt.core.prefs
index 812fbdb54710711e928854c8b1a4473c53289b0c..b17a04a86a31280939bd38107722182912f58e5d 100644
--- a/tlatools/.settings/org.eclipse.jdt.core.prefs
+++ b/tlatools/.settings/org.eclipse.jdt.core.prefs
@@ -6,9 +6,10 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota
 org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
 org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=11
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -22,6 +23,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
@@ -65,6 +67,7 @@ org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=igno
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
 org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
 org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
 org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
@@ -96,4 +99,6 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
 org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.processAnnotations=enabled
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/tlatools/META-INF/MANIFEST.MF b/tlatools/META-INF/MANIFEST.MF
index 92192777b4ce1d8187176261d0266bb71ba4472b..74de04aeca0d4d8ec85c1c5faec1483f3941b356 100644
--- a/tlatools/META-INF/MANIFEST.MF
+++ b/tlatools/META-INF/MANIFEST.MF
@@ -17,16 +17,19 @@ Export-Package: pcal,
  tla2sany.utilities,
  tla2tex,
  tlc2,
+ tlc2.model,
  tlc2.module,
  tlc2.output,
  tlc2.pprint,
  tlc2.tool,
+ tlc2.tool.coverage,
  tlc2.tool.distributed,
  tlc2.tool.distributed.fp,
  tlc2.tool.distributed.management,
  tlc2.tool.distributed.selector,
  tlc2.tool.fp,
  tlc2.tool.fp.management,
+ tlc2.tool.impl,
  tlc2.tool.liveness,
  tlc2.tool.management,
  tlc2.tool.other,
@@ -35,11 +38,12 @@ Export-Package: pcal,
  tlc2.value,
  util
 Bundle-Vendor: Leslie Lamport, Markus Alexander Kuppe
-Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-RequiredExecutionEnvironment: JavaSE-11,
+ JavaSE-1.8
 Eclipse-BundleShape: dir
 Require-Bundle: org.aspectj.runtime;bundle-version="1.6.0";resolution:=optional;x-installation:=greedy,
- org.junit;bundle-version="3.8.0";resolution:=optional;x-installation:=greedy,
+ org.junit;bundle-version="4.12.0";resolution:=optional;x-installation:=greedy,
  javax.mail;bundle-version="1.4.0";resolution:=optional,
- javax.activation;bundle-version="1.1.0";resolution:=optional,
  org.easymock;bundle-version="2.4.0";resolution:=optional
 Bundle-ClassPath: .
+Automatic-Module-Name: org.lamport.tlatools
diff --git a/tlatools/build.properties b/tlatools/build.properties
index 40edd6e44e2d13cee0b67d4d03ec4eadef80c7d4..06d8afaaaa5f8d1144de1907d762b14f8892112c 100644
--- a/tlatools/build.properties
+++ b/tlatools/build.properties
@@ -1,9 +1,5 @@
 source.. = src/,\
-           src-aj/,\
-           test/
+           src-aj/
 bin.includes = META-INF/,\
                .
-output.. = bin/
-jre.compilation.profile = J2SE-1.5
-javacSource = 1.5
-javacTarget = 1.5
+output.. = class/
diff --git a/tlatools/customBuild.xml b/tlatools/customBuild.xml
index f062665e9d0c452b5a1d712264ce952e6f3f93f1..88a386dd0d204a2c4827416405d524f933acd138 100644
--- a/tlatools/customBuild.xml
+++ b/tlatools/customBuild.xml
@@ -24,26 +24,55 @@
 
 	<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
 		<classpath>
-			<pathelement location="lib/aspectjtools-1.8.5.jar" />
+			<pathelement location="lib/aspectjtools-1.9.2.jar" />
 		</classpath>
 	</taskdef>
-
+	
 	<condition property="test.skip">
 		<istrue value="${maven.test.skip}"/>
 	</condition>
 
-	<target name="info" description="Prints information">
-		<echo message="ANT_HOME     : ${env.ANT_HOME}" />
-		<echo message="Ant Version  : ${ant.version}" />
-		<echo message="Java Version : ${ant.java.version}" />
+	<condition property="test.halt">
+		<istrue value="${maven.test.halt}"/>
+	</condition>
+
+	<!-- https://github.com/alx3apps/jgit-buildnumber -->
+	<target name="git-revision">
+	    <taskdef name="jgit-buildnumber" classname="ru.concerteza.util.buildnumber.JGitBuildNumberAntTask">
+			<classpath>
+				<pathelement location="lib/org.eclipse.jgit-2.3.1.201302201838-r.jar"/>
+				<pathelement location="lib/jgit-buildnumber-ant-task-1.2.10.jar" />
+			</classpath>
+	    </taskdef>
+	    <jgit-buildnumber/>
+	</target>
+	
+	<target name="info" description="Prints information" depends="git-revision">
+		<echo message="ANT_HOME       : ${env.ANT_HOME}" />
+		<echo message="Ant Version    : ${ant.version}" />
+		<echo message="Java Version   : ${ant.java.version}" />
 		<echo message="Skipping Tests : ${test.skip}" />
-		<echo message="With AspectJ: ${withaj}" />
+		<echo message="With AspectJ   : ${withaj}" />
+		<echo message="Git BuildNumber: ${git.branch}_${git.tag}_${git.shortRevision}" />
 	</target>
 
 	<target name="default" depends="info" description="Default">
 		<antcall target="compile" inheritall="true" inheritrefs="true" />
 		<antcall target="compile-aj" inheritall="true" inheritrefs="true" />
+		<antcall target="compile-test" inheritall="true" inheritrefs="true" />
 		<antcall target="test" inheritall="true" inheritrefs="true" />
+		<antcall target="git-revision" inheritall="true" inheritrefs="true" />
+		<antcall target="dist" inheritall="true" inheritrefs="true" />
+		<antcall target="test-dist" inheritall="true" inheritrefs="true" />
+	</target>
+	
+	<!-- Similar to default except it skips test and only runs test-dist. This is called from pom.xml -->
+	<target name="default-maven" depends="info" description="Default">
+		<antcall target="compile" inheritall="true" inheritrefs="true" />
+		<antcall target="compile-aj" inheritall="true" inheritrefs="true" />
+		<antcall target="compile-test" inheritall="true" inheritrefs="true" />
+		<!-- <antcall target="test" inheritall="true" inheritrefs="true" /> -->
+		<antcall target="git-revision" inheritall="true" inheritrefs="true" />
 		<antcall target="dist" inheritall="true" inheritrefs="true" />
 		<antcall target="test-dist" inheritall="true" inheritrefs="true" />
 	</target>
@@ -53,16 +82,46 @@
 		<antcall target="dist-mixed-zip" inheritall="true" inheritrefs="true" />
 	</target>
 
+	<!-- Compiles and runs the short-running tests. -->
+	<target name="shorttests" depends="info" description="Run tests">
+		<antcall target="default" inheritall="true" inheritrefs="true" />
+		<antcall target="test-verify" inheritall="true" inheritrefs="true" />
+	</target>
 
-
-	<target name="clean" description="Cleans the compilation output directory" unless="noclean">
+	<!-- Compiles and runs *only* the long-running tests. Not the ones. -->
+	<target name="longtests" depends="info" description="Run long-running tests">
+		<antcall target="compile" inheritall="true" inheritrefs="true" />
+		<antcall target="compile-aj" inheritall="true" inheritrefs="true">
+			<!-- long tests require AspjectJ -->
+	        <param name="withaj" value="true"/>
+	    </antcall>
+		<antcall target="compile-test" inheritall="true" inheritrefs="true" />
+		<antcall target="dist" inheritall="true" inheritrefs="true" />
+		<antcall target="test-dist-long" inheritall="true" inheritrefs="true" />
+	</target>
+	
+	<!-- ===========================================================-->
+	<!-- Don't call any of the targets below, use the umbrella      -->
+	<!-- targets above which correctly call a target's dependencies -->
+	<!-- ===========================================================-->
+
+	<!-- Cleans the compilation output directory -->
+	<target name="clean" unless="noclean">
 		<delete includeemptydirs="true" failonerror="false">
 			<fileset dir="${class.dir}" includes="*/**" />
 			<fileset dir="${test.class.dir}" includes="*/**" />
 		</delete>
+		<!-- Delete leftovers from previous junit invocation -->
+		<delete includeemptydirs="true" failonerror="false">
+			<!-- Delete ALL states folders in the tlatools/ directory -->
+			<!-- A stale states/ folder in src/model/ once caused the -->
+			<!-- Maven build to fail with an OOM in the pack200 task. --> 
+			<fileset dir="${basedir}" includes="**/states/**" />
+		</delete>
 	</target>
 
-	<target name="compile" depends="clean" description="Compile">
+	<!-- Compiles the TLA+ tools code -->
+	<target name="compile" depends="clean">
 		<echo>
 			================================================================
 			= The following warnings about sun.misc.Unsafe can be ignored. = 
@@ -73,11 +132,11 @@
 		</echo>
 		<!-- compile -->
 		<mkdir dir="${class.dir}" />
-		<javac includeantruntime="false" srcdir="${src.dir}" destdir="${class.dir}" debug="true" verbose="false" source="1.5" target="1.5">
+		<javac includeantruntime="false" srcdir="${src.dir}" destdir="${class.dir}" debug="true" verbose="false" source="1.8" target="1.8">
 			<!-- compilerarg value="-Xlint:deprecation"/-->
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/javax.mail.jar" />
+				<pathelement location="lib/javax.mail/mailapi-1.6.3.jar" />
 			</classpath>
 		</javac>
 
@@ -88,10 +147,15 @@
 				<exclude name="**/*.java" />
 				<exclude name="**/*.~*" />
 				<exclude name="**/*##*" />
+				<exclude name="**/*.09-09-07" />
+				<exclude name="**/*.09-07-02" />
+				<exclude name="**/*.11-02-10" />
+				<exclude name="**/*.jpg" />
 			</fileset>
 		</copy>
 	</target>
 
+	<!-- Compiles AspectJ auxiliary code -->
 	<target name="compile-aj" if="withaj">
 		<echo>
 			====================================================================
@@ -101,11 +165,15 @@
 			====================================================================
 		</echo>
 		<!-- compile aspectj related class files -->
-		<iajc destdir="${class.dir}" sourceRoots="${src-aj.dir}" debug="true">
+		<iajc destdir="${class.dir}" debug="true" verbose="true" source="1.8" target="1.8">
+			<sourceroots>
+				<!-- Here we implicitly decide to use AspectJ Load-Time weaving by excludign ${src.dir} as a pathelement. Adding ${src.dir} results in aspects being woven at compile-time as part of the ant build. -->
+				<pathelement location="${src-aj.dir}"/>
+			</sourceroots>
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/aspectjrt-1.8.5.jar" />
-				<pathelement location="lib/aspectjtools-1.8.5.jar" />
+				<pathelement location="lib/aspectjrt-1.9.2.jar" />
+				<pathelement location="lib/aspectjtools-1.9.2.jar" />
 				<pathelement path="${class.dir}" />
 			</classpath>
 		</iajc>
@@ -122,32 +190,91 @@
 		<!-- These files are requried for load time weaving and during runtime -->
 		<copy todir="${class.dir}/lib">
 			<fileset dir="lib/">
-				<include name="aspectjrt-1.8.5.jar" />
-				<include name="aspectjweaver-1.8.5.jar" />
+				<include name="aspectjrt-1.9.2.jar" />
+				<include name="aspectjweaver-1.9.2.jar" />
 			</fileset>
 		</copy>
 	</target>
 
-	<target name="dist" description="Build a distribution">
+	<!-- Creates a distribution out of compiled code -->
+	<target name="dist">
 		<!-- Extract javax.mail jar into class directory to be packaged into tla2tools.jar -->
 		<!-- javax.mail is used by the MailSender functionality, that mails the output -->
 		<!-- of TLC to a given email address. -->
 		<!-- The javax.mail dependency as well as javax.activation is also depended upon -->
 		<!-- by the tla2tools OSGi bundle and is expected to be the same version. -->
-		<unzip src="lib/javax.mail.jar" dest="${class.dir}"/>
+		<unzip src="lib/javax.mail/mailapi-1.6.3.jar" dest="${class.dir}">
+			<patternset>
+			    <include name="**/*.class"/>
+			    <include name="META-INF/LICENSE.txt"/>
+			    <include name="META-INF/mailcap"/>
+			    <include name="META-INF/javamail.charset.map"/>
+				<exclude name="javax/mail/search/**"/>
+			</patternset>
+		</unzip>
+		<unzip src="lib/javax.mail/smtp-1.6.3.jar" dest="${class.dir}">
+			<patternset>
+		        <include name="**/*.class"/>
+			</patternset>
+		</unzip>
+		<unzip src="lib/javax.mail/javax.activation_1.1.0.v201211130549.jar" dest="${class.dir}">
+			<patternset>
+		        <include name="**/*.class"/>
+				<exclude name="org/**"/>
+			</patternset>
+		</unzip>
+		<touch file="${class.dir}/META-INF/javamail.default.address.map"/>
 
 		<!-- Extract javax.mail to maven classes folder. This script is triggered by -->
 		<!-- maven. However, maven compiles the code again to target/classes from -->
 		<!-- where it creates the plugin -->
-		<unzip src="lib/javax.mail.jar" dest="target/classes"/>
-
-		<!-- TODO: add javax.activation to lib/ and extract it here too. Otherwise we have a -->
-		<!-- runtime dependency on Java 6. -->
+		<unzip src="lib/javax.mail/mailapi-1.6.3.jar" dest="target/classes">
+			<patternset>
+			    <include name="**/*.class"/>
+			    <include name="META-INF/LICENSE.txt"/>
+			    <include name="META-INF/mailcap"/>
+			    <include name="META-INF/javamail.charset.map"/>
+				<exclude name="javax/mail/search/**"/>
+			</patternset>
+		</unzip>
+		<unzip src="lib/javax.mail/smtp-1.6.3.jar" dest="target/classes">
+			<patternset>
+		        <include name="**/*.class"/>
+			</patternset>
+		</unzip>
+		<unzip src="lib/javax.mail/javax.activation_1.1.0.v201211130549.jar" dest="target/classes">
+			<patternset>
+		        <include name="**/*.class"/>
+				<exclude name="org/**"/>
+			</patternset>
+		</unzip>
+		<touch file="target/classes/META-INF/javamail.default.address.map"/>
+		
 
 		<!-- create a JAR file for the users -->
 		<mkdir dir="${dist.dir}" />
-		<jar destfile="${dist.file}">
-			<fileset dir="${class.dir}" includes="**/*" />
+		<jar destfile="${dist.file}" level="9">
+			<fileset dir="${class.dir}" includes="**/*" excludes="
+					**/*.jpg,
+					README.txt,
+				    heapstats.jfc,
+				    jpf.properties,
+					tlc2/tool/fp/*.tla,
+					tlc2/value/*.tla,
+					pcal/*.tla,
+					META-INF/maven/,
+					src/tla2sany/parser/ParseException.09-09-07,
+					src/tla2sany/parser/TLAplusParser.09-07-02,
+					src/tla2sany/parser/TLAplusParser.11-02-10,
+					src/tla2sany/parser/TLAplusParserConstants.09-07-02,
+					src/tla2sany/parser/TLAplusParserConstants.09-09-07,
+					src/tla2sany/parser/TLAplusParserConstants.11-02-10,
+					src/tla2sany/parser/TLAplusParserTokenManager.09-07-02,
+					src/tla2sany/parser/TLAplusParserTokenManager.09-09-07,
+					src/tla2sany/parser/TLAplusParserTokenManager.11-02-10,
+					src/tla2sany/parser/Token.09-09-07,
+					src/tla2sany/parser/TokenMgrError.09-09-07"/>
+			<fileset dir="${doc.dir}" includes="License.txt"/>
 			<manifest>
 				<attribute name="Built-By" value="${user.name}" />
 				<attribute name="Build-Tag" value="${env.BUILD_TAG}" />
@@ -155,26 +282,45 @@
 				<attribute name="Implementation-Title" value="TLA+ Tools" />
 				<attribute name="Implementation-Version" value="${version} ${TODAY}" />
 				<attribute name="Implementation-Vendor" value="Microsoft Corp." />
+				<!-- The jar files contains many main classes (SANY, TEX, pcal, ...) --> 
+                <!-- but lets consider TLC the one users primarily use. --> 
+				<attribute name="Main-class" value="tlc2.TLC" />
+				<attribute name="Class-Path" value="CommunityModules.jar" />
+				<!-- Git revision -->
+				<attribute name="X-Git-Branch" value="${git.branch}" />
+				<attribute name="X-Git-Tag" value="${git.tag}" />
+				<attribute name="X-Git-Revision" value="${git.revision}" />
+				<attribute name="X-Git-ShortRevision" value="${git.shortRevision}" />
+				<attribute name="X-Git-BuildNumber" value="${git.branch}_${git.tag}_${git.shortRevision}" />
+				<attribute name="X-Git-Commits-Count" value="${git.commitsCount}" />
 				<!-- App-Name and Permissions is required by Java Webstart used by distributed TLC -->
 				<!-- Depending on security level, the user will see a warning otherwise. -->
 				<!-- http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/security/manifest.html -->
 				<attribute name="Application-Name" value="TLC" />
-				<attribute name="permissions" value="all-permissions" /> <!-- won't work with 'sandbox' as it writes files -->
+				<attribute name="permissions" value="all-permissions" />
+				<!-- won't work with 'sandbox' as it writes files -->
 			</manifest>
 		</jar>
 	</target>
 
-	<target name="test" description="Executes accompining unit tests" unless="test.skip">
+	<!-- Compiles the TLA+ tools *test* code -->
+	<target name="compile-test" unless="test.skip">
 		<!-- compile unit tests -->
 		<mkdir dir="${test.class.dir}" />
-		<javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false">
+		<javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false" source="1.8" target="1.8">
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/junit-4.8.2.jar" />
+				<pathelement location="lib/junit-4.12.jar" />
+				<pathelement location="lib/hamcrest-core-1.3.jar" />
 				<pathelement location="lib/easymock-3.3.1.jar" />
 				<pathelement path="${class.dir}" />
 			</classpath>
 		</javac>
+		<!-- Include auxilliary files when compiling classes. -->
+		<copy todir="${test.class.dir}">
+		   <fileset dir="${test.dir}" includes="**/*.dot"/>
+		   <fileset dir="${test.dir}" includes="**/*.dump"/>
+		</copy>		
 		<!-- copy class.dir to path with whitespace -->
 		<!-- this is required by some tests to make sense -->
 		<!-- even throw a non digit in -->
@@ -182,107 +328,303 @@
 		<copy todir="${ws.class.dir}">
 			<fileset dir="${class.dir}" />
 		</copy>
-		<!-- run junit tests -->
-		<mkdir dir="${test.reports}" />
-		<jacoco:coverage destfile="target/code-coverage.exec">
-			<junit printsummary="yes" haltonfailure="no" haltonerror="no"  forkmode="perTest" fork="yes">
-				<!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044.
-	            <jvmarg value="-Xdebug" />
-		        <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" />
-				-->
-				<classpath refid="project.classpath" />
-				<classpath>
-					<pathelement location="lib/junit-4.8.2.jar" />
-					<pathelement location="lib/cglib-nodep-3.1.jar" />
-					<pathelement location="lib/objenesis-2.1.jar" />
-					<pathelement location="lib/easymock-3.3.1.jar" />
-					<pathelement path="${ws.class.dir}" />
-					<pathelement path="${test.class.dir}" />
-				</classpath>
-				<formatter type="xml" />
-
-				<!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs -->
-				<sysproperty key="basedir" value="${basedir}/"/>
-				<batchtest fork="yes" todir="${test.reports}">
-					<fileset dir="${test.dir}">
-						<include name="**/*Test*.java" />
-						<exclude name="**/ModelCheckerTestCase.java" />
-						<exclude name="**/SuccessfulSimulationTestCase.java" />
-						<exclude name="**/AbstractExampleTestCase.java" />
-						<exclude name="**/TestMPRecorder.java" />
-						<exclude name="**/Abstract*Test.java" />
-						<exclude name="**/TestDriver.java" />
-						<exclude name="**/TestDriver2.java" />
-						<exclude name="**/AllTests.java" />
-					</fileset>
-				</batchtest>
-			</junit>
-		</jacoco:coverage>
-
-		<!-- remove copied class.dir -->
-		<delete dir="${ws.class.dir}" deleteonexit="true"/>
 	</target>
 
-	<target name="test-dist" description="Executes accompining unit tests on jar file" unless="test.skip">
-		<!-- compile unit tests -->
-		<mkdir dir="${test.class.dir}" />
-		<javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false">
-			<classpath refid="project.classpath" />
-			<classpath>
-				<pathelement location="lib/junit-4.8.2.jar" />
-				<pathelement location="lib/easymock-3.3.1.jar" />
-				<pathelement path="${class.dir}" />
-			</classpath>
-		</javac>
-		<!-- copy class.dir to path with whitespace -->
-		<!-- this is required by some tests to make sense -->
-		<!-- even throw a "+" and whitespace into the mix -->
-		<property name="ws.class.dir" value="TLA+ Tools" />
-		<copy todir="${ws.class.dir}">
-			<fileset dir="${class.dir}" />
-		</copy>
-		<!-- run junit tests on tlatools.jar -->
-		<mkdir dir="${test.reports}/onJar" />
-		<junit printsummary="yes" haltonfailure="no" haltonerror="no" forkmode="perTest" fork="yes">
+	<!-- Executes accompining unit tests -->
+	<target name="test" unless="test.skip">
+		<!-- run junit tests -->
+		<mkdir dir="${test.reports}" />
+		<!-- forkmode used to be "perBatch" on Java 1.8 using the util.IsolatedTestCaseRunner.  
+		     This concept broken with Java11 for unknown reasons which is why forkmode has been
+		     changed to perTest to run each test in a separate VM.  This is slower compared to
+		     running all tests in a single VM. -->
+		<junit printsummary="yes" haltonfailure="${test.halt}" showoutput="no" haltonerror="${test.halt}"  forkmode="perTest" fork="yes">
+			<!-- enable all assertions -->
+			<jvmarg value="-ea"/>
+			<jvmarg value="-XX:MaxDirectMemorySize=512k"/>
+			<jvmarg value="-XX:+UseParallelGC"/>
+			<!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044.
+            <jvmarg value="-Xdebug" />
+	        <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" />
+			-->
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/junit-4.8.2.jar" />
+				<pathelement location="lib/junit-4.12.jar" />
+				<pathelement location="lib/hamcrest-core-1.3.jar" />
 				<pathelement location="lib/cglib-nodep-3.1.jar" />
 				<pathelement location="lib/objenesis-2.1.jar" />
 				<pathelement location="lib/easymock-3.3.1.jar" />
-				<pathelement location="${dist.file}" />
+				<pathelement location="lib/javax.mail/mailapi-1.6.3.jar" />
+				<pathelement location="test-model/UserModuleOverrideFromJar.jar" />
+				<pathelement path="${class.dir}" />
 				<pathelement path="${test.class.dir}" />
+				<pathelement path="test-model/" />
 			</classpath>
-
 			<formatter type="xml" />
-			<sysproperty key="basedir" value="${basedir}/"/>
 
-			<batchtest fork="yes" todir="${test.reports}/onJar">
+			<!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs -->
+			<sysproperty key="basedir" value="${basedir}/"/>
+			<sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/>
+			<sysproperty key="util.FileUtil.milliseconds" value="true"/>
+			<sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/>
+			<!-- The tests below can be tricked into running in a single VM by fiddling with the classloader 
+			     to reload and thus initialize all classes for each tests. -->
+			<batchtest fork="yes" todir="${test.reports}">
 				<fileset dir="${test.dir}">
 					<include name="**/*Test*.java" />
-					<exclude name="**/ModelCheckerTestCase.java" />
+					
+					<!-- Produce bogus crash with Quarantine runner. -->
+					<exclude name="tlc2/tool/distributed/TLCSetTest.java" />
+					<exclude name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" />
+					<!-- Just fail because of Quarantine runner. -->
+					<exclude name="tlc2/TLCTest.java"/>
+					<exclude name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/>
+					<exclude name="tlc2/tool/EvaluatingValueTest.java" />
+					<exclude name="tlc2/tool/UserModuleOverrideTest.java" />
+					<exclude name="tlc2/tool/UserModuleOverrideFromJarTest.java" />
+					<exclude name="tlc2/tool/UserModuleOverrideAnnotationTest.java" />
+					
+					<exclude name="**/PCalTest.java" />
+					<exclude name="**/CommonTestCase.java" />
+					<exclude name="**/ModelCheckerTestCase.java" />
+					<exclude name="**/PCalModelCheckerTestCase.java" />
+					<exclude name="**/SuiteTestCase.java" />
+					<exclude name="**/SuiteETestCase.java" />
+					<exclude name="**/DistributedTLCTestCase.java" />
+					<exclude name="**/TLCServerTestCase.java" />
 					<exclude name="**/SuccessfulSimulationTestCase.java" />
 					<exclude name="**/AbstractExampleTestCase.java" />
-					<exclude name="**/TestMPRecorder.java" />
+					<exclude name="**/AbstractCoverageTest.java" />
+					<exclude name="**/InitEvalOrderTest.java" />
+					<exclude name="**/TestMPRecorder.java" />
+					<exclude name="**/TestPrintStream.java" />
+					<exclude name="**/BidirectionalTransitions1BTest.java" />
+					<exclude name="**/BidirectionalTransitions2CTest.java" />
 					<exclude name="**/Abstract*Test.java" />
 					<exclude name="**/TestDriver.java" />
 					<exclude name="**/TestDriver2.java" />
 					<exclude name="**/AllTests.java" />
+					<exclude name="util/IsolatedTestCaseRunner.java"/>
+					<!-- Known test failures -->
+					<exclude name="**/pcal/StackTestTest.java" />
+					<exclude name="**/pcal/TestPCandStackTest.java" />
+					<!-- These take too long -->
+					<exclude name="**/MacroQuicksortTest.java" />
+					<exclude name="**/MacroRealQuicksortTest.java" />
+					<exclude name="**/PcalPaxosTest.java" />
+					<exclude name="**/DetlefsTest.java" />
+					<exclude name="**/StarkMutexTest.java" />
+					<exclude name="**/SimpleMultiProcTest.java" />
+				</fileset>
+			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/tool/EvaluatingValueTest.java" />
+				</fileset>
+			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/tool/UserModuleOverrideAnnotationTest.java" />
+				</fileset>
+			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/tool/UserModuleOverrideFromJarTest.java" />
+				</fileset>
+			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/tool/UserModuleOverrideTest.java" />
+				</fileset>
+			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/tool/distributed/TLCSetTest.java" />
 				</fileset>
 			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" />
+				</fileset>
+			</batchtest>
+			<batchtest fork="yes" todir="${test.reports}">
+				<fileset dir="${test.dir}">
+					<include name="tlc2/TLCTest.java"/>
+					<include name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/>
+				</fileset>
+			</batchtest>
+		</junit>
+
+		<!-- remove copied class.dir -->
+		<delete dir="${ws.class.dir}" deleteonexit="true"/>
+	</target>
+
+	<!-- Executes a single unit test. -->
+	<target name="test-single" unless="test.skip">
+		<!-- run junit test -->
+		<junit printsummary="yes" haltonfailure="true" showoutput="no" haltonerror="true" forkmode="perTest" fork="yes">
+			<!-- enable all assertions -->
+			<jvmarg value="-ea"/>
+			<jvmarg value="-XX:MaxDirectMemorySize=512k"/>
+			<jvmarg value="-XX:+UseParallelGC"/>
+			<!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044.
+            <jvmarg value="-Xdebug" />
+	        <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" />
+			-->
+			<classpath refid="project.classpath" />
+			<classpath>
+				<pathelement location="lib/junit-4.12.jar" />
+				<pathelement location="lib/hamcrest-core-1.3.jar" />
+				<pathelement location="lib/cglib-nodep-3.1.jar" />
+				<pathelement location="lib/objenesis-2.1.jar" />
+				<pathelement location="lib/easymock-3.3.1.jar" />
+				<pathelement location="lib/javax.mail/mailapi-1.6.3.jar" />
+				<pathelement location="test-model/UserModuleOverrideFromJar.jar" />
+				<pathelement path="${class.dir}" />
+				<pathelement path="${test.class.dir}" />
+				<pathelement path="${test.class.dir}" />
+				<pathelement path="test-model/" />
+			</classpath>
+
+			<!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs -->
+			<sysproperty key="basedir" value="${basedir}/"/>
+			<sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/>
+			<sysproperty key="util.FileUtil.milliseconds" value="true"/>
+			<sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/>
+
+			<!-- Print all output of the test to the screen. -->
+			<formatter type="plain" usefile="false" />
+			<!-- Run a single test case specified by its class name, by setting the property 'test.testcase'. -->
+			<test name="${test.testcase}" skipNonTests="true"/>
 		</junit>
 
 		<!-- remove copied class.dir -->
 		<delete dir="${ws.class.dir}" deleteonexit="true"/>
 	</target>
 
-	<target name="test-dist-long" description="Executes accompining long-running unit tests on jar file" unless="test.skip">
+	<!-- Executes accompining unit tests on jar file -->
+	<target name="test-dist" unless="test.skip">
+		<!-- run junit tests on tlatools.jar -->
+		<mkdir dir="${test.reports}/onJar" />
+		<jacoco:coverage destfile="target/code-coverage.exec" includes="pcal.*:tla2sany.*:tla2tex.*:tlc2.*:util.*:org.lamport.*" excludes="com.sun.*:javax.*:**/Tests.*:**/*Test*">
+			<junit printsummary="yes" haltonfailure="${test.halt}" haltonerror="${test.halt}" forkmode="perTest" fork="yes">
+				<!-- enable all assertions -->
+				<jvmarg value="-ea"/>
+				<jvmarg value="-XX:MaxDirectMemorySize=512k"/>
+				<jvmarg value="-XX:+UseParallelGC"/>
+				<classpath refid="project.classpath" />
+				<classpath>
+					<pathelement location="lib/junit-4.12.jar" />
+					<pathelement location="lib/hamcrest-core-1.3.jar" />
+					<pathelement location="lib/cglib-nodep-3.1.jar" />
+					<pathelement location="lib/objenesis-2.1.jar" />
+					<pathelement location="lib/easymock-3.3.1.jar" />
+					<pathelement location="${dist.file}" />
+					<pathelement path="${test.class.dir}" />
+					<pathelement path="test-model/" />
+					<pathelement location="test-model/UserModuleOverrideFromJar.jar" />
+				</classpath>
+	
+				<formatter type="xml" />
+				<sysproperty key="basedir" value="${basedir}/"/>
+				<sysproperty key="util.FileUtil.milliseconds" value="true"/>
+				<sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/>
+				<sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/>
+	
+				<batchtest fork="yes" todir="${test.reports}/onJar">
+					<fileset dir="${test.dir}">
+						<include name="**/*Test*.java" />
+						
+						<!-- Produce bogus crash with Quarantine runner. -->
+						<exclude name="tlc2/tool/distributed/TLCSetTest.java" />
+						<exclude name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" />
+						<!-- Just fail because of Quarantine runner. -->
+						<exclude name="tlc2/TLCTest.java"/>
+						<exclude name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/>
+						<exclude name="tlc2/tool/UserModuleOverrideTest.java" />
+						<exclude name="tlc2/tool/UserModuleOverrideFromJarTest.java" />
+						<exclude name="tlc2/tool/UserModuleOverrideAnnotationTest.java" />
+												
+						<exclude name="**/PCalTest.java" />
+						<exclude name="**/CommonTestCase.java" />
+						<exclude name="**/ModelCheckerTestCase.java" />
+						<exclude name="**/PCalModelCheckerTestCase.java" />
+						<exclude name="**/SuiteTestCase.java" />
+						<exclude name="**/SuiteETestCase.java" />
+						<exclude name="**/DistributedTLCTestCase.java" />
+						<exclude name="**/TLCServerTestCase.java" />
+						<exclude name="**/SuccessfulSimulationTestCase.java" />
+						<exclude name="**/AbstractExampleTestCase.java" />
+						<exclude name="**/AbstractCoverageTest.java" />
+						<exclude name="**/InitEvalOrderTest.java" />
+						<exclude name="**/BidirectionalTransitions1BTest.java" />
+						<exclude name="**/BidirectionalTransitions2CTest.java" />
+						<exclude name="**/TestMPRecorder.java" />
+						<exclude name="**/TestPrintStream.java" />
+						<exclude name="**/Abstract*Test.java" />
+						<exclude name="**/TestDriver.java" />
+						<exclude name="**/TestDriver2.java" />
+						<exclude name="**/AllTests.java" />
+						<exclude name="util/IsolatedTestCaseRunner.java"/>
+						<!-- Known test failures -->
+						<exclude name="**/pcal/StackTestTest.java" />
+						<exclude name="**/pcal/TestPCandStackTest.java" />
+						<!-- These take too long -->
+						<exclude name="**/MacroQuicksortTest.java" />
+						<exclude name="**/MacroRealQuicksortTest.java" />
+						<exclude name="**/PcalPaxosTest.java" />
+						<exclude name="**/DetlefsTest.java" />
+						<exclude name="**/StarkMutexTest.java" />
+						<exclude name="**/SimpleMultiProcTest.java" />
+					</fileset>
+				</batchtest>
+				<batchtest fork="yes" todir="${test.reports}">
+					<fileset dir="${test.dir}">
+						<include name="tlc2/tool/UserModuleOverrideAnnotationTest.java" />
+					</fileset>
+				</batchtest>
+				<batchtest fork="yes" todir="${test.reports}">
+					<fileset dir="${test.dir}">
+						<include name="tlc2/tool/UserModuleOverrideFromJarTest.java" />
+					</fileset>
+				</batchtest>
+				<batchtest fork="yes" todir="${test.reports}/onJar">
+					<fileset dir="${test.dir}">
+						<include name="tlc2/tool/UserModuleOverrideTest.java" />
+					</fileset>
+				</batchtest>
+				<batchtest fork="yes" todir="${test.reports}/onJar">
+					<fileset dir="${test.dir}">
+						<include name="tlc2/tool/distributed/TLCSetTest.java" />
+					</fileset>
+				</batchtest>
+				<batchtest fork="yes" todir="${test.reports}/onJar">
+					<fileset dir="${test.dir}">
+						<include name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" />
+					</fileset>
+				</batchtest>
+				<batchtest fork="yes" todir="${test.reports}/onJar">
+					<fileset dir="${test.dir}">
+						<include name="tlc2/TLCTest.java"/>
+						<include name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/>
+					</fileset>
+				</batchtest>
+			</junit>
+		</jacoco:coverage>
+
+		<!-- remove copied class.dir -->
+		<delete dir="${ws.class.dir}" deleteonexit="true"/>
+	</target>
+
+	<!-- Executes accompining long-running unit tests on jar file -->
+	<target name="test-dist-long" unless="test.skip">
 		<!-- compile unit tests -->
 		<mkdir dir="${test.class.dir}" />
 		<javac includeantruntime="false" srcdir="${test.dir}-long" destdir="${test.class.dir}" debug="true" verbose="false">
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/junit-4.8.2.jar" />
+				<pathelement location="lib/junit-4.12.jar" />
+				<pathelement location="lib/hamcrest-core-1.3.jar" />
 				<pathelement path="${class.dir}" />
 			</classpath>
 		</javac>
@@ -296,14 +638,19 @@
 		<!-- run junit tests on tlatools.jar -->
 		<mkdir dir="${test.reports}/onJarLong" />
 		<junit printsummary="yes" haltonfailure="no" haltonerror="no" maxmemory="4096m" forkmode="perTest" fork="yes">
-			<jvmarg value="-javaagent:lib/aspectjweaver-1.8.5.jar" />
+			<!-- enable all assertions -->
+			<jvmarg value="-ea"/>
+			<jvmarg value="-javaagent:lib/aspectjweaver-1.9.2.jar" />
 			<sysproperty key="org.aspectj.weaver.showWeaveInfo" value="false"/>
 			<sysproperty key="aj.weaving.verbose" value="false"/>
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/junit-4.8.2.jar" />
+				<pathelement location="lib/junit-4.12.jar" />
+				<pathelement location="lib/hamcrest-core-1.3.jar" />
 				<pathelement location="${dist.file}" />
 				<pathelement path="${test.class.dir}" />
+				<!-- Need class.dir on path to find AspectJ related classes which don't get packaged into dist -->
+				<pathelement path="${class.dir}" />
 			</classpath>
 
 			<formatter type="xml" />
@@ -329,8 +676,9 @@
 		<delete dir="${ws.class.dir}" deleteonexit="true"/>
 	</target>
 
-	<!-- -->
-	<target name="test-verify" description="Verifies TLC parts with NASA's pathfinder model checker">
+	<!-- Verifies TLC parts with NASA's pathfinder model checker -->
+	<target name="test-verify">
+		<mkdir dir="${test.class.dir}"/>
 		<!-- Compile our boilerplate code needed to verify TLC. E.g. the StateQueueVerify creates -->
 		<!-- examplary produces & consumers and a dummy implementation of StateQueue. -->
 		<javac includeantruntime="false" srcdir="${test.dir}-verify" destdir="${test.class.dir}" debug="true" verbose="false">
@@ -346,7 +694,8 @@
 			dir="${basedir}/test-verify" >
 			<classpath refid="project.classpath" />
 			<classpath>
-				<pathelement location="lib/junit-4.8.2.jar" />
+				<pathelement location="lib/junit-4.12.jar" />
+				<pathelement location="lib/hamcrest-core-1.3.jar" />
 				<pathelement location="lib/jpf.jar" />
 				<pathelement location="lib/jpf-classes.jar" />
 				<pathelement path="${class.dir}" />
@@ -363,7 +712,38 @@
 		</junit>
 	</target>
 
-	<target name="dist-mixed-jar" description="Build a distribution" depends="default">
+	<!-- http://hg.openjdk.java.net/code-tools/jmh/file/3769055ad883/jmh-ant-sample/build.xml -->
+	<target name="benchmark" description="Generate the self-contained JMH benchmark JAR (run with java -jar target/benchmarks.jar)">
+		<!-- Compile benchmark class files in test-benchmark against jmh. -->
+		<delete dir="target/benchmark/" />
+		<mkdir dir="target/benchmark/" />
+		<javac includeantruntime="false" srcdir="test-benchmark/" destdir="target/benchmark/">
+			<classpath refid="project.classpath" />
+			<classpath>
+				<pathelement path="${class.dir}" />
+				<pathelement path="${test.class.dir}" />
+				<pathelement location="lib/jmh/jmh-core-1.21.jar" />
+				<pathelement location="lib/jmh/jmh-generator-annprocess-1.21.jar" />
+			</classpath>
+		</javac>
+
+		<!-- Build benchmark jar which includes third-party deps, TLC proper (including tests bc of tlc2.tool.TLCStates.createDummyState) and the benchmark files compiled in the previous step. -->
+		<delete file="target/benchmarks.jar" />
+		<jar jarfile="target/benchmarks.jar" basedir="target/benchmark/">
+			<manifest>
+				<attribute name="Main-Class" value="org.openjdk.jmh.Main" />
+			</manifest>
+<!--			<zipfileset dir="${src.dir}" includes="**/*.java" />-->
+			<fileset dir="${class.dir}" includes="**/*" />
+			<fileset dir="${test.class.dir}" includes="**/*" />
+			<zipfileset src="lib/jmh/jmh-core-1.21.jar" excludes="**/META-INF/services/**" />
+			<zipfileset src="lib/jmh/jopt-simple-4.6.jar" />
+			<zipfileset src="lib/jmh/commons-math3-3.2.jar" />
+		</jar>
+	</target>
+
+	<!-- Build a distribution -->
+	<target name="dist-mixed-jar" depends="default">
 		<!-- create a JAR file for the users -->
 		<jar destfile="${dist-mixed.file.jar}">
 			<fileset dir="${class.dir}" includes="**/*" />
@@ -389,7 +769,8 @@
 	</target>
 
 
-	<target name="dist-mixed-zip" description="Build a distribution" depends="default">
+	<!-- Build a distribution -->
+	<target name="dist-mixed-zip" depends="default">
 		<!-- create a zip file for the users and developers-->
 		<zip destfile="${dist-mixed.file}">
 			<zipfileset dir="${src.dir}" includes="**/*.java" prefix="tla" />
diff --git a/tlatools/doc/License.txt b/tlatools/doc/License.txt
index b7894bc47219e78b13a9923649f067bbb09d2cde..c8f71de5d088dc159f4b482904901e938538598c 100644
--- a/tlatools/doc/License.txt
+++ b/tlatools/doc/License.txt
@@ -1,109 +1,21 @@
-
-1)  Where the following copyright notice appears:
-
-Copyright (c) 2003 Compaq Corporation.  All rights reserved.  
-
-Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.  or
-
-Copyright (c) 2003 Microsoft Corporation. All rights reserved.
-
-that code is licensed as follows:   
-
-This Microsoft Research end user license agreement ("MSR-EULA") is a
-legal agreement between you and Microsoft Corporation
-("Microsoft" or "we") for the software identified above,
-which includes source code, and any associated materials and "online"
-or electronic documentation (together, the "Software").
-
-By installing, copying, or otherwise using the Software, found at
-http://research.microsoft.com/downloads, you agree to be bound by the
-terms of this MSR-EULA. If you do not agree, do not install, copy or
-use the Software. The Software is protected by copyright and other
-intellectual property laws and is licensed, not sold.
-
-Upon your agreement to the terms below, you may do anything you want
-with the Software source code for research, teaching purposes, or
-internal use, free of charge, provided that you agree to the
-following:
-
-(a) To leave in place all copyright notices and licensing information
-that you might find in the Software.
-
-(b) That you will not use the Software in a live operating environment
-where it may be relied upon to perform in the same manner as a
-commercially released product, or with data that has not been
-sufficiently backed up.
-
-(c) That any feedback about the Software provided by you to us is
-voluntarily given, and Microsoft shall be free to use the feedback as
-it sees fit without obligation or restriction of any kind, even if the
-feedback is designated by you as confidential.
-
-(d) To make freely available to others the source code or data of any
-modifications or additions you make to the Software, and any related
-documentation, solely and exclusively under the same terms as this
-License.
-
-(e) That Microsoft is granted back, without limitations, the rights to
-reproduce, install, use, modify, distribute and transfer your
-modifications to the Software source code or data.
-
-(f) NO WARRANTIES WHATSOEVER: That the Software comes "AS IS",
-with all faults and with no warranties, conditions or
-representations. The implied warranties of merchantability and fitness
-for a particular purpose, and any warranty against interference with
-your enjoyment of the Software or against infringement, do not apply
-to the Software.  The entire risk as to satisfactory quality,
-performance, accuracy, and effort concerning the Software is assumed
-by you.  There is no warranty that this Software will fulfill any of
-your particular purposes or needs.
-
-(g) That we have no duty of reasonable care or lack of negligence, and
-we are not obligated to (and will not) provide technical support for
-the Software.
-
-(h) That we will not be liable for any damages, including those known
-as direct, indirect, special, consequential, or incidental damages
-related to the Software, this MSR-EULA, or under any legal theory
-(such as negligence), to the maximum extent that overriding applicable
-law permits.
-
-(i) That if you sue or threaten to sue anyone over patents that you
-think may apply to the Software, or if you breach this MSR-EULA in any
-way, your license to the Software ends automatically.
-
-(j) That this MSR-EULA shall be construed and controlled by the laws
-of the State of Washington, USA, without regard to conflicts of law.
-
-
-2) Where the following copyright notice appears: Copyright (c) 2003
-Compaq Corporation.  All rights reserved.
-
-that code is licensed as follows:
-Compaq Computer Corporation, a Delaware corporation with offices at
-20555 SH 249, Houston, TX, (COMPAQ) and you the CUSTOMER agree as
-follows:
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL COMPAQ BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
-THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name of COMPAQ shall not be
-used in advertising or otherwise to promote the sale, use or other
-dealings in this Software without prior written authorization from
-COMPAQ.
+Copyright (c) 2019 Microsoft Research. All rights reserved.
+
+The MIT License (MIT)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tlatools/doc/TraceExplorer_README.txt b/tlatools/doc/TraceExplorer_README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..41afc32e81f6bd94f7e2b0523f259a449c3d6a46
--- /dev/null
+++ b/tlatools/doc/TraceExplorer_README.txt
@@ -0,0 +1,78 @@
+As of December 2019, the tlatools ship with the TraceExplorer command line application.
+
+The current usage of this is as follows; this usage text can also be seen by invoking the application (tla2.TraceExplorer) with no arguments:
+
+	To generate a SpecTE file pair:
+			java tlc2.TraceExplorer -generateSpecTE \
+				[-source=_directory_containing_prior_run_output_] \
+				[-overwrite] \
+				SpecName
+		o source defaults to CWD if not specified.
+		o if a SpecTE.tla already exists and overwrite is not specified, execution will halt.
+		o if no SpecName is specified, output will be expected to arrive via stdin; -source will be ignored in this case.
+
+	To pretty print the error states of a previous run:
+			java tlc2.TraceExplorer -prettyPrint \
+				[-source=_directory_containing_prior_run_output_] \
+				SpecName
+		o source defaults to CWD if not specified.
+		o if no SpecName is specified, output will be expected to arrive via stdin; -source will be ignored in this case.
+
+
+------------------------------------------------------------------------------------
+
+An example of the pretty print output is:
+
+algebraic:Model_1_SnapShot_1576778796288 loki$ java ... tlc2.TraceExplorer -prettyPrint -source=/Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/
+ <Initial predicate>
+	/\  todo = {<<>>}
+	/\  sols = {}
+
+ <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens>
+	/\  todo = {<<1>>, <<2>>, <<3>>}
+	/\  sols = {}
+
+ <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens>
+	/\  todo = {<<2>>, <<3>>, <<1, 3>>}
+	/\  sols = {}
+
+ <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens>
+	/\  todo = {<<3>>, <<1, 3>>}
+	/\  sols = {}
+
+ <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens>
+	/\  todo = {<<1, 3>>, <<3, 1>>}
+	/\  sols = {}
+
+ <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens>
+	/\  todo = {<<3, 1>>}
+	/\  sols = {}
+
+ <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens>
+	/\  todo = {}
+	/\  sols = {}
+
+algebraic:Model_1_SnapShot_1576778796288 loki$ 
+
+------------------------------------------------------------------------------------
+
+An example of a piped run from TLC:
+
+algebraic:FourQueens loki$ /Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk/Contents/Home/bin/java -XX:MaxDirectMemorySize=5460m -Xmx2732m -Dtlc2.tool.fp.FPSet.impl=tlc2.tool.fp.OffHeapDiskFPSet -XX:+UseParallelGC -Dfile.encoding=UTF-8 -classpath /Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/javax.mail/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/class tlc2.TLC -fpbits 1 -fp 4 -config MC.cfg -coverage 3 -workers 1 -tool -metadir /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens MC | /Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk/Contents/Home/bin/java -XX:+UseParallelGC -Dfile.encoding=UTF-8 -classpath /Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/javax.mail/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/class tlc2.TraceExplorer -generateSpecTE
+Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/MC.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/Queens.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/TLC.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Naturals.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Sequences.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/FiniteSets.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module Queens
+Semantic processing of module FiniteSets
+Semantic processing of module TLC
+Semantic processing of module MC
+Starting... (2019-12-22 12:56:00)
+The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/SpecTE.tla has been created.
+The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/SpecTE.cfg has been created.
+algebraic:FourQueens loki$ 
+
diff --git a/tlatools/doc/environment.txt b/tlatools/doc/environment.txt
index c486f4f5075548a5599e2e0c9964183b362b3d6e..8dcd3e63f73bebbd632cc60b75e86a32c3975e62 100644
--- a/tlatools/doc/environment.txt
+++ b/tlatools/doc/environment.txt
@@ -1,6 +1,13 @@
 Introduction
 ===============================================================================
- This document describes the environment setup for development of TLA+ Tools. 
+THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION.
+THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION.
+THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION.
+THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION.
+THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION.
+THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION.
+===============================================================================
+This document describes the environment setup for development of TLA+ Tools. 
  It consists of the following sections:
  - Used versions
  - Environment setup 
diff --git a/tlatools/github.xml b/tlatools/github.xml
new file mode 100644
index 0000000000000000000000000000000000000000..141044f3086b7f14a12392cb2746a6cab4c90349
--- /dev/null
+++ b/tlatools/github.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+
+	<!-- The settings below have been duplicated from ../pom.xml because the 
+		Toolbox - among other things - uses a different groupId for historical reasons. -->
+
+	<groupId>org.lamport</groupId>
+	<artifactId>tla2tools</artifactId>
+	<name>TLA+ Tools</name>
+	<version>1.7.3-SNAPSHOT</version>
+	<description>The TLC model checker, the syntax and semantic checker SANY, the PlusCal translator, and the LaTeX pretty printer.</description>
+	<packaging>jar</packaging>
+
+	<organization>
+		<name>Microsoft Research Inria Joint Centre</name>
+		<url>http://msr-inria.inria.fr/</url>
+	</organization>
+
+	<licenses>
+		<license>
+			<name>MIT License</name>
+			<url>http://www.opensource.org/licenses/mit-license.php</url>
+		</license>
+	</licenses>
+
+	<issueManagement>
+		<system>GitHub</system>
+		<url>https://github.com/tlaplus/tlaplus/issues</url>
+	</issueManagement>
+
+	<scm>
+		<connection>scm:git:https://github.com/tlaplus/tlaplus</connection>
+		<developerConnection>scm:git:https://github.com/tlaplus/tlaplus</developerConnection>
+		<tag>HEAD</tag>
+	</scm>
+
+	<distributionManagement>
+	<repository>
+	    <id>github</id>
+	    <name>GitHub TLAPlus Apache Maven Packages</name>
+	    <url>https://maven.pkg.github.com/tlaplus/tlaplus</url>
+	</repository>
+	</distributionManagement>
+
+	<dependencies>
+		<dependency>
+			<groupId>com.sun.mail</groupId>
+			<artifactId>mailapi</artifactId>
+			<version>1.6.3</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymock</artifactId>
+			<version>2.4</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-gpg-plugin</artifactId>
+				<version>1.6</version>
+				<executions>
+					<execution>
+						<id>sign-artifacts</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>sign</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>
+
diff --git a/tlatools/ide/RunAllTLCTests.launch b/tlatools/ide/RunAllTLCTests.launch
new file mode 100644
index 0000000000000000000000000000000000000000..03463076c018d99aaab0f1a51edeb8aa21a44304
--- /dev/null
+++ b/tlatools/ide/RunAllTLCTests.launch
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.ant.AntLaunchConfigurationType">
+<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
+<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${project}"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/tlatools/customBuild.xml"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.ant.internal.launching.remote.InternalAntRunner"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tlatools"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_ANT_TARGETS" value="test,"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LAUNCH_CONFIGURATION_BUILD_SCOPE" value="${none}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/tlatools/customBuild.xml}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/tlatools}"/>
+<stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/>
+</launchConfiguration>
diff --git a/tlatools/javacc/README/README.txt b/tlatools/javacc/README/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..92b6b2cecb5506f25e0bee7d71be50260e926d01
--- /dev/null
+++ b/tlatools/javacc/README/README.txt
@@ -0,0 +1,13 @@
+The lexer code for SANY is produced by running JavaCC version 4.0 on the file tla+.jj 
+and then making some hand modifications of the output. 
+
+The file javacc-tutorial.pdf contains documentation for JavaCC, which I believe was
+written for version 4.0.
+
+The files method.txt and methods-sany1.txt contain instructions for making the
+necessary modifications, along with miscellaneous documentation of the SANY code.
+
+The last modifications of those files seem to have been made on:
+
+   methods.txt       - 5 October 2009
+   methods-sany1.txt - 15 March 2007
diff --git a/tlatools/javacc/README/javacc-4.0.zip b/tlatools/javacc/README/javacc-4.0.zip
new file mode 100644
index 0000000000000000000000000000000000000000..f0afe4f59bbbeeccc1200b23a41397aa02176fe1
Binary files /dev/null and b/tlatools/javacc/README/javacc-4.0.zip differ
diff --git a/tlatools/javacc/README/javacc-tutorial.pdf b/tlatools/javacc/README/javacc-tutorial.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..2bd0fe0e6c9c97108f09b986d0b3b64e5b5fa9ff
Binary files /dev/null and b/tlatools/javacc/README/javacc-tutorial.pdf differ
diff --git a/tlatools/javacc/README/methods-sany1.txt b/tlatools/javacc/README/methods-sany1.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3aef1cf5933af22b450f2dd0a605c37aa9d1cf55
--- /dev/null
+++ b/tlatools/javacc/README/methods-sany1.txt
@@ -0,0 +1,428 @@
+Note: There are almost identical copies of the files Token.java and
+TokenMgrError.java in both the parser/ and configuration/ directories.
+Both need to be there to compile correctly.  Clearly, something is
+wrong here.
+
+tlasany/drivers directory
+-----------------
+FrontEndException.java
+  Simple extension to Exception to create an exception specific to the FrontEnd
+
+InitException.java
+  Simple extension to Exception to create an exception specific to the FrontEnd
+
+SemanticException.java
+  Simple extension to Exception to create an exception specific to the FrontEnd
+
+SANY.java
+   static SANYmain
+     The main driver, called by tlasany/SANY.java.  It handles
+     the command line and calles frontEndMain.
+   static frontEndMain
+     The real main driver, called by SANYmain.
+   static frontEndInitialize
+     [called from] frontEndMain
+     initializes Configuration, Context, level data.
+     throws InitException in case of problems.
+   static frontEndParse
+     [called from] frontEndMain
+     loads and parse the spec file.
+     uses SpecObj.loadSpec
+   static frontEndSemanticAnalysis
+     [called from] frontEndMain
+     Coming out of parsing, we have all the modules required to analyse 
+     the file from the SpecObj. They are organized in a vector, ordered 
+     from modules with no dependencies to modules with most dependencies.
+     Semantic analysis is done incrementally, with the context of each 
+     module stored in a gobal table for easy retrieval.
+
+==================================================================
+tlasany/parser directory
+-----------------------
+
+This directory contains classes implementing the parser.  There are
+different group of classes:
+   1. implementing support for identifying matching alignments
+   2. implementing the expression parser
+   3. implementing the parser
+   4. support
+
+1. Implementing support for identifying matching alignments
+-----------------------------------------------------------
+BracketStack.java
+   class to support the enforcement of alignment as delimiter in TLA+, e.g.
+     /\
+     /\
+       /\
+       /\
+     /\
+   we keep track of the kind of "bracket", e.g.  AND, OR, but
+   identified from the type of STN. It is organized as a stack as
+   operators are embedded in expressions.  Stacks are created,
+   pushed or popped.  With the top reference, we can compare whether
+   an operator will be aligned, to the right or the left.
+
+StackElement.java
+   An element in BracketStack
+
+2. Implementing the expression parser
+-------------------------------------
+Expression parser:
+   The "high level" parser simply pushed expression elements on a stack,
+   on which well formed expressions are reduced, according to precedence
+   + associativity rules.
+
+OSelement.java
+   Defines the objects that go on the operator stack: the operator and
+   the syntax node.  An OSelement object can be viewed as an
+   information cache, really.
+
+Operator.java
+   The Operator object associated with an operator defines the symbol
+   and its properties--e.g.  "+" is infix.  An operator will also have
+   low and high precedence levels, to direct the order in which
+   expressions are evaluated.  e.g.  a*b+c is (a*b)+c
+
+   Note that there is a lack of modularity between Operator and
+   Operators.  An interface should be defined to define in one place
+   constant values (nofix, infix, ...)  and string representations
+   thereof.
+
+OperatorStack.java
+   This class name is a misnommer.  Or rather a short for Operator
+   Expression Reduction Stack.  It is also really a stack of stacks.
+   See comments in file.
+
+   Operators are pushed onto the stack, and reduction operations called
+   periodically.  As well as a final operation once we have captured
+   all elements of the expression.
+
+Operators.java
+   Creates and manages all symbols defined in the grammar.  Initialized
+   by Configuration.
+
+3. implementing the parser
+--------------------------
+SyntaxTreeNode.java
+
+The following are automatically generated from tla+.jj, the
+implementation of the parser.
+   ParseException.java
+   SimpleCharStream.java
+   TLAplusParser.java
+   TLAplusParserConstants.java
+   TLAplusParserTokenManager.java
+   TokenMgrError.java 
+   Token.java
+
+The ParseException.java file produced by javacc must be modified for
+use by the other parser files.  The modification consists of adding the
+method getShortMessage(), whose current code is given at the end of
+this file.  That method is called by code in TLAplusParser.java that is
+obtained from the tla+.jj grammar file.
+
+4. Support
+----------
+ParseError.java
+   An exception.
+ParseErrors.java
+   An array of ParseError objects.
+
+
+Note:
+The current directory also contains ASCII_CharStream.java, which is
+obsolete.  It was constructed by an earlier version of javacc, which
+now constructs SimpleCharStream.java instead.
+
+==================================================================
+tlasany/st directory
+-----------------------
+
+st (short for Syntax Tree) creates an abstract view of the parse tree.
+It was created for decoupling from other operations, but its use is
+certainly not strictly necessary.
+
+Only Location and SyntaxTreeNode are required.
+
+Location.java
+  A location specifies the position of a syntactic unit in the source
+  - beginLine() start line of a syntactic unit
+  - beginColumn() start line of a syntactic unit
+  - endLine() start column of a syntactic unit
+  - endColumn() end column of a syntactic unit
+  - source() returns source file
+  - toString() returns a pprintable version of content
+
+SyntaxTreeConstants.java
+   Interface defining all constants for SyntaxTreeNodes.  This extends
+   the numbering of tokens, which is done by javacc and put in
+   TLAplusParserConstants.java, to SyntaxTreeNode objects' `kind'
+   field.  Both tokens and nodes are in the syntax tree.  This class
+   also contains an array of strings which give a printable version of
+   the tree node kinds.
+
+The ParseError and ParseErrors interfaces in this directory (st) are
+implemented by the classes of the same name in the parser/ directory.
+These interfaces are also implemented by classes in modanalyzer/.
+------
+ParseError.java
+  interface for a parse error
+  prescribes
+  - String reportedError()
+  - String defaultError()
+
+ParseErrors.java
+  interface to an Array of Parse Errors.
+
+
+ParseTree.java
+  Interface to parse tree. Used in modanalyzer/ classes.
+
+TreeNode.java
+  interface for TreeNode. Used in semantic/ classes
+
+==================================================================
+tlasany/utilities directory
+---------------------------
+Assert.java
+   Assertion, obsolete in java 1.5
+IntWrapper.java
+   Abstraction of int: value can be set, read, or incremented.
+Stack.java
+   Stack abstraction, pre-java Generics
+Strings.java
+   Add an indentation method to basic (java) strings.
+Vector.java
+   A generic (Object-based) java abstraction for dynamic vectors.  Can be
+   resized.  supported methods:
+  - int size ()
+  - String toString()
+  - void addElement( Obj)
+  - void setElementAt( Obj, int)
+  - bool contains (Obj)
+  - void append( Vector)
+  - void appendNoRepeats ( Vector) 
+  - Enumeration elements ()
+  - void removeAllElements ()
+  - void removeElementAt (int)
+  - void insertElementAt (Obj, int)
+
+VectorEnumeration.java
+  Support class for Vector. Creates an enumeration.
+  - implements nextElement, hasMoreElements for an Enumeration on vectors
+  - create a local copy of references to obj content.
+
+==================================================================
+tlasany/explorer directory
+-----------------------
+   This is an interface to the semantic Tree, for exploration, that is
+   mainly to display the semantic tree.  It interacts with an inputStream
+   for commands.  The operations are really straightforward: identifying
+   a symbol and printing relevant information.
+
+ExploreNode.java
+  interface to retrieve information fron node
+Explorer.java
+  getLine 
+  printNode
+  lookUpAndPrintSyntaxTree( String)
+  lookUpAndPrintDef( String)
+  levelDataPrint( String )
+  executeCommand()
+  parseAndExecuteCommand()
+  printSyntaxTree()
+  main()
+
+ExplorerQuitException.java
+  Exception in case of Explorer problem.
+
+==================================================================
+tlasany/configuration directory
+-------------------------------
+
+ConfigConstants.java
+   This file was originally generated by javacc for parsing
+   defaultConfig, and then J-Ch added the definition of defaultConfig
+   to it.
+
+The following files were originally generated by javacc from a
+grammar file that has vanished.  They parse the string
+ConfigConstants.defaultConfig to build Operators.BuiltinTable.
+--------------------
+ASCII_CharStream.java
+Configuration.java
+ConfigurationTokenManager.java
+ParseException.java
+Token.java
+TokenMgrError.java
+
+==================================================================
+tlasany/error directory
+-----------------------
+
+As noted below, this entire directory can be eliminated.
+
+ErrorRegistry.java
+   Doesn't appear to be used.
+
+Log.java
+   Used by BracketStack for no good purpose.  It can be removed and
+   references to it eliminated.
+
+LogCategories.java
+   Used only by Log.java
+
+Timing.java
+   Appears to be unused.
+
+ToolkitError.java
+   Appears to be unused.
+==================================================================
+tlasany/modanalyzer directory
+-----------------------------
+The files in this directory are used as a interface between the driver
+and the single-module parser.  It contains the mechanics to extract
+which modules have been referenced, to map module names to files, and
+to keep track of which modules have already been parsed.
+
+ModuleContext.java
+ModulePointer.java
+ModuleRelationships.java
+ModuleRelatives.java
+NameToFileIStream.java
+NamedInputStream.java
+ParseUnit.java
+ParseUnitRelatives.java
+ParseUnitsTable.java
+SpecObj.java
+StringToNamedInputStream.java
+SyntaxTreePrinter.java
+
+==================================================================
+tlasany/semantic directory
+-----------------------
+ASTConstants.java
+   Defines the values of the semantic nodes' `kind' field and their
+   printed names.
+
+AbortException.java
+   Random exception.
+SemanticsException.java
+   Random exception.
+
+ArgLevelParam.java
+   Seems to be the object that contains the level information for an
+   operator.
+
+BuiltInLevel.java
+   Seems to define the levels of the built-in operators.
+
+Context.java
+   A hashtable of definition and declaration nodes.
+
+Errors.java
+   Probably used to keep track of the errors found during semantic
+   analysis.
+
+ExternalModuleTable.java
+   Seems to keep track of contexts and level (constant or non-constant)
+   of modules that have been semantically analyzed.
+
+FrontEnd.java
+   This class contains the methods by which a tool calls the Front End
+   to parse input and to create a semantic tree, and for various other
+   interactions.
+
+Generator.java
+   Generates a semantic graph from a parse tree.  It also uses the list
+   of modules to access contexts to instantiate or extend.
+
+LevelConstants.java
+   Trivial constants.
+
+LevelException.java
+   A random exception.
+
+Subst.java
+   This class represents a single substitution of the form
+   op <- expr such as appears in module instantiations
+
+SetOfArgLevelConstraints.java
+   Implements a map mapping arg-level parameters (ParamAndPosition) to
+   levels (Integer).  An entry <pap,x> means that the argument
+   pap.position of the operator pap.param must have a level >= x.
+
+SetOfLevelConstraints.java
+   Implements a map mapping parameters to levels. An entry <p,x> in
+   the set means that p must have a level <= x.
+
+SymbolTable.java
+   The Symbol Table builds the stack of context tables.
+
+SemanticNode.java
+   The abstract class that is the superclass of all individual node
+   classes.  These node classes are:
+      AssumeNode.java
+      AssumeProveNode.java
+      ParamAndPosition.java
+      ProofNode.java
+      TheoremNode.java
+      LevelNode.java
+         This is an abstract class that extends SemanticNode
+         and is extended by:
+            InstanceNode.java
+            ExprOrOpArgNode.java
+               This is abstract and is extended by 
+                  ExprNode.java
+                     This is an abstract node that is extended by:
+                        LetInNode.java
+                        AtNode.java
+                        DecimalNode.java
+                        NumeralNode.java
+                        StringNode.java
+                        SubstInNode.java
+                        OpApplNode.java
+                 OpArgNode.java
+            SymbolNode.java
+               This is an abstract class that is extended by 
+                  FormalParamNode.java ModuleNode.java
+                  OpDefOrDeclNode.java
+                     Is extended by:
+                        OpDeclNode.java
+                        OpDefNode.java
+
+
+==================================================================
+
+The method getShortMessage() to be inserted into ParseException.java.
+
+  /**
+   *  * shorter variation on the following
+   *
+   */
+  public String getShortMessage() {
+    if (!specialConstructor) {
+      return super.getMessage();
+    }
+    int maxSize = 0;
+    for (int i = 0; i < expectedTokenSequences.length; i++) {
+      if (maxSize < expectedTokenSequences[i].length) {
+        maxSize = expectedTokenSequences[i].length;
+      }
+    }
+    String retval = "Encountered \"";
+    Token tok = currentToken.next;
+
+    for (int i = 0; i < maxSize; i++) {
+      if (i != 0) retval += " ";
+      if (tok.kind == 0) {
+        retval += tokenImage[0];
+        break;
+      }
+      retval += tok.image;
+      //      retval += add_escapes(tok.image);
+      tok = tok.next;
+    }
+    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    return retval;
+  }
diff --git a/tlatools/javacc/README/methods.txt b/tlatools/javacc/README/methods.txt
new file mode 100644
index 0000000000000000000000000000000000000000..aaeabd2812cab8ffc722870dfef6957207df9b9f
--- /dev/null
+++ b/tlatools/javacc/README/methods.txt
@@ -0,0 +1,491 @@
+Note: The following files produced by javacc have been edited.
+
+   parser/ParseException.java
+     The method at the end of this file was added--I believe by J-Ch
+      The method in question seems to be getShortMessage().
+
+   configuration/Configuration.java 
+   configuration/ASCII_CharStream.java 
+   configuration/ConfigurationTokenManager
+     These have small bug fixes introduced by David Jefferson. Search for 
+     "DRJ" to find them.  These files were originally produced by running
+     javacc on config.jj, a grammar file that specifies the 
+     parsing of the string ConfigConstants.defaultConfig.
+     I suspect that current the version of javacc would not produce
+     the ASCI_CharStream.java file, but I'm not sure.  I just know that 
+     running javacc on tla+.jj doesn't produce such a file.
+
+tlasany/drivers directory
+-----------------
+FrontEndException.java
+  Simple extension to Exception to create an exception specific to the FrontEnd
+
+InitException.java
+  Simple extension to Exception to create an exception specific to the FrontEnd
+
+SemanticException.java
+  Simple extension to Exception to create an exception specific to the FrontEnd
+
+SANY.java
+   static SANYmain
+     The main driver, called by tlasany/SANY.java.  It handles
+     the command line and calles frontEndMain.
+   static frontEndMain
+     The real main driver, called by SANYmain.
+   static frontEndInitialize
+     [called from] frontEndMain
+     initializes Configuration, Context, level data.
+     throws InitException in case of problems.
+   static frontEndParse
+     [called from] frontEndMain
+     loads and parse the spec file.
+     uses SpecObj.loadSpec
+   static frontEndSemanticAnalysis
+     [called from] frontEndMain
+     Coming out of parsing, we have all the modules required to analyse 
+     the file from the SpecObj. They are organized in a vector, ordered 
+     from modules with no dependencies to modules with most dependencies.
+     Semantic analysis is done incrementally, with the context of each 
+     module stored in a gobal table for easy retrieval.
+
+==================================================================
+tlasany/parser directory
+-----------------------
+
+This directory contains classes implementing the parser.  There are
+different group of classes:
+   1. implementing support for identifying matching alignments
+   2. implementing the expression parser
+   3. implementing the parser
+   4. support
+
+1. Implementing support for identifying matching alignments
+-----------------------------------------------------------
+BracketStack.java
+   class to support the enforcement of alignment as delimiter in TLA+, e.g.
+     /\
+     /\
+       /\
+       /\
+     /\
+   we keep track of the kind of "bracket", e.g.  AND, OR, but
+   identified from the type of STN. It is organized as a stack as
+   operators are embedded in expressions.  Stacks are created,
+   pushed or popped.  With the top reference, we can compare whether
+   an operator will be aligned, to the right or the left.
+
+StackElement.java
+   An element in BracketStack
+
+2. Implementing the expression parser
+-------------------------------------
+Expression parser:
+   The "high level" parser simply pushed expression elements on a stack,
+   on which well formed expressions are reduced, according to precedence
+   + associativity rules.
+
+OSelement.java
+   Defines the objects that go on the operator stack: the operator and
+   the syntax node.  An OSelement object can be viewed as an
+   information cache, really.
+
+Operator.java
+   The Operator object associated with an operator defines the symbol
+   and its properties--e.g.  "+" is infix.  An operator will also have
+   low and high precedence levels, to direct the order in which
+   expressions are evaluated.  e.g.  a*b+c is (a*b)+c
+
+   Note that there is a lack of modularity between Operator and
+   Operators.  An interface should be defined to define in one place
+   constant values (nofix, infix, ...)  and string representations
+   thereof.
+
+OperatorStack.java
+   This class name is a misnommer.  Or rather a short for Operator
+   Expression Reduction Stack.  It is also really a stack of stacks.
+   See comments in file.
+
+   Operators are pushed onto the stack, and reduction operations called
+   periodically.  As well as a final operation once we have captured
+   all elements of the expression.
+
+Operators.java
+   Creates and manages all symbols defined in the grammar.  Initialized
+   by Configuration.
+
+3. implementing the parser
+--------------------------
+SyntaxTreeNode.java
+
+The following are automatically generated from tla+.jj, the
+implementation of the parser.
+   ParseException.java
+   SimpleCharStream.java
+   TLAplusParser.java
+   TLAplusParserConstants.java
+   TLAplusParserTokenManager.java
+   TokenMgrError.java 
+   Token.java
+
+The ParseException.java file produced by javacc must be modified for
+use by the other parser files.  The modification consists of adding the
+method getShortMessage(), whose current code is given at the end of
+this file.  That method is called by code in TLAplusParser.java that is
+obtained from the tla+.jj grammar file.
+
+4. Support
+----------
+ParseError.java
+   An exception.
+ParseErrors.java
+   An array of ParseError objects.
+
+
+Note:
+The current directory also contains ASCII_CharStream.java, which is
+obsolete.  It was constructed by an earlier version of javacc, which
+now constructs SimpleCharStream.java instead.
+
+==================================================================
+tlasany/st directory
+-----------------------
+
+st (short for Syntax Tree) creates an abstract view of the parse tree.
+It was created for decoupling from other operations, but its use is
+certainly not strictly necessary.
+
+Only Location and SyntaxTreeNode are required.
+
+Location.java
+  A location specifies the position of a syntactic unit in the source
+  - beginLine() start line of a syntactic unit
+  - beginColumn() start line of a syntactic unit
+  - endLine() start column of a syntactic unit
+  - endColumn() end column of a syntactic unit
+  - source() returns source file
+  - toString() returns a pprintable version of content
+
+SyntaxTreeConstants.java
+   Interface defining all constants for SyntaxTreeNodes.  This extends
+   the numbering of tokens, which is done by javacc and put in
+   TLAplusParserConstants.java, to SyntaxTreeNode objects' `kind'
+   field.  Both tokens and nodes are in the syntax tree.  This class
+   also contains an array of strings which gives a printable version of
+   the tree node kinds.
+
+The ParseError and ParseErrors interfaces in this directory (st) are
+implemented by the classes of the same name in the parser/ directory.
+These interfaces are also implemented by classes in modanalyzer/.
+------
+ParseError.java
+  interface for a parse error
+  prescribes
+  - String reportedError()
+  - String defaultError()
+
+ParseErrors.java
+  interface to an Array of Parse Errors.
+
+
+ParseTree.java
+  Interface to parse tree. Used in modanalyzer/ classes.
+
+TreeNode.java
+  Interface for TreeNode. Imported by a lot of classes
+  in semantic/ and modanalyzer/, and by 
+  parser/{SyntaxTreeNode,TLAplusParser, TLAplusParserTokenManager}
+  It seems to be implemented only by SyntaxTreeNode.
+
+==================================================================
+tlasany/utilities directory
+---------------------------
+Assert.java
+   Assertion, obsolete in java 1.5
+IntWrapper.java
+   Abstraction of int: value can be set, read, or incremented.
+Stack.java
+   Stack abstraction, pre-java Generics
+Strings.java
+   Add an indentation method to basic (java) strings.
+Vector.java
+   A generic (Object-based) java abstraction for dynamic vectors.  Can be
+   resized.  supported methods:
+  - int size ()
+  - String toString()
+  - void addElement( Obj)
+  - void setElementAt( Obj, int)
+  - bool contains (Obj)
+  - void append( Vector)
+  - void appendNoRepeats ( Vector) 
+  - Enumeration elements ()
+  - void removeAllElements ()
+  - void removeElementAt (int)
+  - void insertElementAt (Obj, int)
+
+VectorEnumeration.java
+  Support class for Vector. Creates an enumeration.
+  - implements nextElement, hasMoreElements for an Enumeration on vectors
+  - create a local copy of references to obj content.
+
+==================================================================
+tlasany/explorer directory
+-----------------------
+   This is an interface to the semantic Tree, for exploration, that is
+   mainly to display the semantic tree.  It interacts with an inputStream
+   for commands.  The operations are really straightforward: identifying
+   a symbol and printing relevant information.
+
+ExploreNode.java
+  interface to retrieve information fron node
+    methods:  String toString(int depth);
+              String levelDataToString();
+              void   walkGraph(Hashtable semNodesTable);
+
+
+Explorer.java
+  getLine 
+  printNode
+  lookUpAndPrintSyntaxTree( String)
+  lookUpAndPrintDef( String)
+  levelDataPrint( String )
+  executeCommand()
+  parseAndExecuteCommand()
+  printSyntaxTree()
+  main()
+
+ExplorerQuitException.java
+  Exception in case of Explorer problem.
+
+==================================================================
+tlasany/configuration directory
+-------------------------------
+
+ConfigConstants.java
+   This file was originally generated by javacc for parsing
+   defaultConfig, and then J-Ch added the definition of defaultConfig
+   to it.
+
+The following files were originally generated by javacc from a
+grammar file that has vanished.  They parse the string
+ConfigConstants.defaultConfig to build Operators.BuiltinTable.
+--------------------
+ASCII_CharStream.java
+Configuration.java
+ConfigurationTokenManager.java
+ParseException.java
+Token.java
+TokenMgrError.java
+
+==================================================================
+tlasany/error directory
+-----------------------
+
+As noted below, this entire directory can be eliminated.
+
+ErrorRegistry.java
+   Doesn't appear to be used.
+
+Log.java
+   Used by BracketStack for no good purpose.  It can be removed and
+   references to it eliminated.
+
+LogCategories.java
+   Used only by Log.java
+
+Timing.java
+   Appears to be unused.
+
+ToolkitError.java
+   Appears to be unused.
+==================================================================
+tlasany/modanalyzer directory
+-----------------------------
+The files in this directory are used as a interface between the driver
+and the single-module parser.  It contains the mechanics to extract
+which modules have been referenced, to map module names to files, and
+to keep track of which modules have already been parsed.
+
+ModuleContext.java
+ModulePointer.java
+ModuleRelationships.java
+ModuleRelatives.java
+NameToFileIStream.java
+NamedInputStream.java
+ParseUnit.java
+  This class contains the code that walks through a module's parse 
+  tree to find the names of EXTENDed or INSTANCEd modules. 
+ParseUnitRelatives.java
+ParseUnitsTable.java
+SpecObj.java
+StringToNamedInputStream.java
+SyntaxTreePrinter.java
+
+==================================================================
+tlasany/semantic directory
+-----------------------
+ASTConstants.java
+   Defines the values of the semantic nodes' `kind' field and their
+   printed names.
+
+AbortException.java
+   Random exception.
+SemanticsException.java
+   Random exception.
+
+ArgLevelParam.java
+   Seems to be the object that contains the level information for an
+   operator.
+
+BuiltInLevel.java
+   Seems to define the levels of the built-in operators.
+
+Context.java
+   A hashtable of definition and declaration nodes.
+
+Errors.java
+   Probably used to keep track of the errors found during semantic
+   analysis.
+
+ExternalModuleTable.java
+   Seems to keep track of contexts and level (constant or non-constant)
+   of modules that have been semantically analyzed.
+
+FrontEnd.java
+   This class contains the methods by which a tool calls the Front End
+   to parse input and to create a semantic tree, and for various other
+   interactions.
+
+Generator.java
+   Generates a semantic graph from a parse tree.  It also uses the list
+   of modules to access contexts to instantiate or extend.
+
+LevelConstants.java
+   Trivial constants.
+
+LevelException.java
+   A random exception.
+
+OpDefOrLabelNode.java
+   An interface implemented by OpDefNode, ThmOrAssumpDefNode, and
+   LabelNode.  It contains methods for accessing the set of labels
+   "immediately within" this node.  [Added by LL on 23 Apr 2007]
+
+Subst.java
+   This class represents a single substitution of the form
+   op <- expr such as appears in module instantiations
+
+SetOfArgLevelConstraints.java
+   Implements a map mapping arg-level parameters (ParamAndPosition) to
+   levels (Integer).  An entry <pap,x> means that the argument
+   pap.position of the operator pap.param must have a level >= x.
+
+SetOfLevelConstraints.java
+   Implements a map mapping parameters to levels. An entry <p,x> in
+   the set means that p must have a level <= x.
+
+SymbolTable.java
+   The Symbol Table builds the stack of context tables.
+
+SemanticNode.java
+   The abstract class that is the superclass of all individual node
+   classes.  These node classes are:
+      ParamAndPosition.java
+      LevelNode.java
+         This is an abstract class that extends SemanticNode
+         and is extended by:
+            AssumeNode.java  
+            TheoremNode.java 
+               [AssumeNode and TheoremNode moved here by LL 22 Jul 2007.
+                I don't know why they weren't already here.]
+            ProofNode.java 
+              This is abstract and is extended by 
+                LeafProofNode.java
+                NonLeafProofNode.java
+            InstanceNode.java
+            obsolete:  QEDStepNode.java  
+            ExprOrOpArgNode.java
+               This is abstract and is extended by 
+                  ExprNode.java
+                     This is an abstract node that is extended by:
+                        LabelNode.java  [Added by LL 23 Apr 2007]
+                        LetInNode.java
+                        AtNode.java
+                        DecimalNode.java
+                        NumeralNode.java
+                        StringNode.java
+                        OpApplNode.java
+                        SubstInNode.java     
+                 OpArgNode.java
+            AssumeProveNode.java [Moved here by LL 15 Mar 2007]
+            NewSymbNode          [Added by LL 15 Mar 2007 ]
+            UseOrHideNode.java   [Added by LL 29 Jul 2007]
+            DefStepNode.java     [Added by LL 16 Aug 2007]
+            APSubstInNode.java   [Added by LL  6 Aug 2007]
+            SymbolNode.java
+               This is an abstract class that extends LevelNode by
+               adding the following concrete methods:
+                    getName(), occur(), isParam()
+               and the following abstract methods:
+                   getArity(), isLocal(), and match() [for testing arity].
+               It is extended by 
+                  FormalParamNode.java 
+                  ModuleNode.java
+                  OpDefOrDeclNode.java
+                     Abstract class that adds the fields
+                        ModuleNode  originallyDefinedInModule 
+                        SymbolTable st
+                        int         arity
+                     Is extended by:
+                        OpDeclNode.java
+                        OpDefNode.java
+                  ThmOrAssumpDefNode.java [Added by LL 17 Mar 2007]
+
+==================================================================
+
+To add a new BuiltIn operator, search for $Nop and OP_nop in the files
+
+  semantic/ASTConstants.java 
+  semantic/BuiltInLevel.java
+  configuration/ConfigConstants.java
+
+and copy what's done there.  If the model checker needs to be able to
+evaluate the operator, search for Op_nop and OPCODE_nop in
+
+  tool/BuiltInOPs.java
+  tool/Tool.java
+  tool/ToolGlobals.java
+
+==================================================================
+
+The method getShortMessage() to be inserted into ParseException.java.
+
+  /**
+   *  * shorter variation on the following
+   *
+   */
+  public String getShortMessage() {
+    if (!specialConstructor) {
+      return super.getMessage();
+    }
+    int maxSize = 0;
+    for (int i = 0; i < expectedTokenSequences.length; i++) {
+      if (maxSize < expectedTokenSequences[i].length) {
+        maxSize = expectedTokenSequences[i].length;
+      }
+    }
+    String retval = "Encountered \"";
+    Token tok = currentToken.next;
+
+    for (int i = 0; i < maxSize; i++) {
+      if (i != 0) retval += " ";
+      if (tok.kind == 0) {
+        retval += tokenImage[0];
+        break;
+      }
+      retval += tok.image;
+      //      retval += add_escapes(tok.image);
+      tok = tok.next;
+    }
+    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    return retval;
+  }
diff --git a/tlatools/javacc/TLAplusParser.java b/tlatools/javacc/TLAplusParser.java
index eec70becf90a22bf8545358dd1bc857d1d34d025..dde171b77bd0bcc5c7119fd33d24f256342c93ed 100644
--- a/tlatools/javacc/TLAplusParser.java
+++ b/tlatools/javacc/TLAplusParser.java
@@ -8,6 +8,7 @@ import tlc2.output.EC;
 import tla2sany.utilities.Vector;
 import tla2sany.utilities.Stack;
 import util.Assert;
+import util.TLAConstants;
 import util.UniqueString;
 import util.ToolIO;
 
@@ -1645,7 +1646,7 @@ Token t;
   SyntaxTreeNode tn, sn[];
   Token t;
   bpa("Parameter declaration");
-  expecting = "CONSTANT";
+  expecting = TLAConstants.KeyWords.CONSTANT;
     tn = ParamSubDecl();
                          addHeir(tn);
  expecting = "Identifier, operator or _";
diff --git a/tlatools/jfr/Overview.jpg b/tlatools/jfr/Overview.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8ecb56cfa28f403492d40d8d72c17aa5b0551f72
Binary files /dev/null and b/tlatools/jfr/Overview.jpg differ
diff --git a/tlatools/jfr/README.md b/tlatools/jfr/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c1789a8627567d245ea3c9aea9654cacaa482633
--- /dev/null
+++ b/tlatools/jfr/README.md
@@ -0,0 +1,42 @@
+This folder contains all libraries and config files to profile TLC with Java Flight Recorder (JFR).
+
+A default JFR is created with:
+
+-XX:StartFlightRecording=settings=default
+-XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/tmp,dumponexit=true,dumponexitpath=dump.jfr,maxage=1h,settings=${project_loc:tlatools}/jfr/tlc.jfc
+
+To additionally record JMX data in the flight recording, add:
+
+-javaagent:${project_loc:tlatools}/jfr/jmx2jfr.jar=${project_loc:tlatools}/jfr/jmxprobes.xml
+
+and replace the "default" settings with ${project_loc:tlatools}/jfr/tlc.jfc
+
+Afterwards, the resulting flight recording can be opened with Java Mission Control (JMC).	
+
+
+Further processing (charting...) is best done by converting the .jfr to xml:
+
+java oracle.jrockit.jfr.parser.Parser -xml dump.jfr > dump.xml
+
+To select all TLC related elements, do:
+
+xmlstarlet sel 
+  -N jfr=http://www.oracle.com/hotspot/jvm/
+  -N jvm=http://www.oracle.com/hotspot/jvm/ 
+  -N p4108=http://www.hirt.se/jfr/jmx2jfr/
+  -t -c "//p4108:MBeans_tlc2_tool_ModelChecker" dump.xml
+
+Do print all the attributes of "StatesGeneratedPerMinute do:
+
+xmlstarlet sel 
+  -N jfr=http://www.oracle.com/hotspot/jvm/ 
+  -N jvm=http://www.oracle.com/hotspot/jvm/ 
+  -N p4108=http://www.hirt.se/jfr/jmx2jfr/ 
+  -t -v "//p4108:MBeans_tlc2_tool_ModelChecker/p4108:StatesGeneratedPerMinute" dump.xml
+
+---
+http://www.slideshare.net/marcushirt/java-mission-control-java-flight-recorder-deep-dive
+https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr004.html#BABHCDEA
+https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/run.htm#JFRUH179
+http://hirt.se/blog/?p=689
+http://hirt.se/blog/?p=446
\ No newline at end of file
diff --git a/tlatools/jfr/jmxprobes.xml b/tlatools/jfr/jmxprobes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0a17520e52dfab302b1060d737a1801b76915b31
--- /dev/null
+++ b/tlatools/jfr/jmxprobes.xml
@@ -0,0 +1,241 @@
+<!--
+	This is an example configuration file. It should be edited to capture the MBean 
+	attributes you are interested in.
+	
+	Use it with the agent in the following manner:
+	java -javaagent:<path to jmx2jfr.jar>=<path to this file> <your normal vmargs>	
+-->
+<jmx2jfr>
+    <!-- Time to wait before retrying getting an MBean attribute in ms -->
+	<retrytime>60000</retrytime>
+	
+	<!-- Time to wait until starting in ms --> 
+	<delay>10000</delay>
+	
+	<!-- How often to get the attributes and emit events for them, in ms -->
+	<period>30000</period>
+	
+	<!-- clean: Use name or type keys for event name. Leads to cleaner event 
+	     type names, but can result in name collisions if you have MBeans with 
+	     the same name in the same domain.
+	     canonical: ugly event type names, but guaranteed not to collide -->
+	<namingmode>clean</namingmode>
+	
+	<!-- objectname:    MBean name - look it up in JMC
+	     attributename: Attribute name - look it up in JMC
+	     contenttype:   [None | Bytes | Timestamp | Millis | Nanos | Ticks | Address] -->
+	<attributes>
+		<attribute>
+			<objectname>java.lang:type=OperatingSystem</objectname>
+			<attributename>FreeSwapSpaceSize</attributename>
+			<contenttype>Bytes</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=OperatingSystem</objectname>
+			<attributename>TotalSwapSpaceSize</attributename>
+			<contenttype>Bytes</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=OperatingSystem</objectname>
+			<attributename>AvailableProcessors</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=OperatingSystem</objectname>
+			<attributename>AvailableProcessors</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=OperatingSystem</objectname>
+			<attributename>Name</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=Threading</objectname>
+			<attributename>PeakThreadCount</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=Threading</objectname>
+			<attributename>CurrentThreadCpuTime</attributename>
+			<contenttype>Nanos</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=Runtime</objectname>
+			<attributename>StartTime</attributename>
+			<contenttype>Timestamp</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=Runtime</objectname>
+			<attributename>StartTime</attributename>
+			<contenttype>Timestamp</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>java.lang:type=Runtime</objectname>
+			<attributename>Uptime</attributename>
+			<contenttype>Millis</contenttype>
+		</attribute>
+		<!-- tlc2.tool:type=ModelChecker -->
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>StatesGenerated</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>StatesGeneratedPerMinute</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>DistinctStatesGenerated</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>DistinctStatesGeneratedPerMinute</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>StateQueueSize</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>Progress</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>WorkerCount</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>AverageBlockCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>RuntimeRatio</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>SpecName</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>ModelName</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool:type=ModelChecker</objectname>
+			<attributename>CommandLine</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<!-- tlc2.tool.fp:type=DiskFPSet0 -->
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>BucketCapacity</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>TblCapacity</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>IndexCapacity</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>OverallCapacity</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>TblLoad</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>TblCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>MaxTblCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>FileCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>DiskLookupCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>MemHitCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>DiskHitCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>DiskWriteCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>DiskSeekCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>DiskSeekCache</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>GrowDiskMark</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>CheckPointMark</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>FlushTime</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>ReaderWriterCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>LoadFactor</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+		<attribute>
+			<objectname>tlc2.tool.fp:type=DiskFPSet0</objectname>
+			<attributename>LockCnt</attributename>
+			<contenttype>None</contenttype>
+		</attribute>
+	</attributes>
+</jmx2jfr>
\ No newline at end of file
diff --git a/tlatools/jfr/tlc.jfc b/tlatools/jfr/tlc.jfc
new file mode 100644
index 0000000000000000000000000000000000000000..89b444acaca862cd7bc7aed983ad94278344b6eb
--- /dev/null
+++ b/tlatools/jfr/tlc.jfc
@@ -0,0 +1,456 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration version="1.0" name="Settings for &apos;My Recording&apos;" description="These settings were used to start the recording &apos;flight_recording_18091tlc2TLC18748.jfr&apos;." provider="Oracle">
+
+  <producer uri="http://www.oracle.com/hotspot/jvm/" label="Oracle JDK">
+
+    <event path="java/statistics/thread_allocation">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="java/statistics/class_loading">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="java/statistics/threads">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="java/thread_start">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="java/thread_end">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="java/thread_sleep">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/thread_park">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/monitor_enter">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/monitor_wait">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="vm/class/load">
+      <setting name="enabled">false</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/class/unload">
+      <setting name="enabled">false</setting>
+    </event>
+
+    <event path="vm/info">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/initial_system_property">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/prof/execution_sample">
+      <setting name="enabled">true</setting>
+      <setting name="period">10 ms</setting>
+    </event>
+
+    <event path="vm/prof/execution_sampling_info">
+      <setting name="enabled">false</setting>
+      <setting name="threshold">1 ms</setting>
+    </event>
+
+    <event path="vm/runtime/execute_vm_operation">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="vm/runtime/thread_dump">
+      <setting name="enabled">true</setting>
+      <setting name="period">60 s</setting>
+    </event>
+
+    <event path="vm/flag/long">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/ulong">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/double">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/boolean">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/string">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/long_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/ulong_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/double_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/boolean_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/string_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/object_count">
+      <setting name="enabled">false</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/gc">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/heap">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/young_generation">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/tlab">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/survivor">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/detailed/object_count_after_gc">
+      <setting name="enabled">false</setting>
+    </event>
+
+    <event path="vm/gc/heap/summary">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/heap/ps_summary">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/heap/metaspace_summary">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/gc_threshold">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/allocation_failure">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/out_of_memory">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/chunk_free_list_summary">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/collector/garbage_collection">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/parold_garbage_collection">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/young_garbage_collection">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/old_garbage_collection">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/g1_garbage_collection">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause_level_1">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause_level_2">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause_level_3">
+      <setting name="enabled">false</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/reference/statistics">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/promotion_failed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/evacuation_failed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/evacuation_info">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/concurrent_mode_failure">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/allocation_requiring_gc">
+      <setting name="enabled">false</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="vm/compiler/config">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/compiler/stats">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="vm/compiler/compilation">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">100 ms</setting>
+    </event>
+
+    <event path="vm/compiler/phase">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">10 s</setting>
+    </event>
+
+    <event path="vm/compiler/failure">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/code_sweeper/config">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_sweeper/stats">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_sweeper/sweep">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">100 ms</setting>
+    </event>
+
+    <event path="vm/code_cache/config">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_cache/stats">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_cache/full">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="os/information">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/processor/cpu_information">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/processor/context_switch_rate">
+      <setting name="enabled">true</setting>
+      <setting name="period">10 s</setting>
+    </event>
+
+    <event path="os/processor/cpu_load">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="os/processor/cpu_tsc">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/system_process">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/initial_environment_variable">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/memory/physical_memory">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="java/object_alloc_in_new_TLAB">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="java/object_alloc_outside_TLAB">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+  </producer>
+
+  <producer uri="http://www.oracle.com/hotspot/jdk/" label="Oracle JDK">
+
+    <event path="java/file_read">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/file_write">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/socket_read">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/socket_write">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="java/exception_throw">
+      <setting name="enabled">false</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="java/error_throw">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="java/statistics/throwables">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+  </producer>
+
+  <producer uri="http://www.oracle.com/hotspot/jfr-info/" label="Oracle JDK">
+
+    <event path="recordings/recording">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="recordings/recording_setting">
+      <setting name="enabled">true</setting>
+    </event>
+
+  </producer>
+
+  <producer uri="http://www.hirt.se/jfr/jmx2jfr/">
+
+    <event path="MBeans/java_lang/Runtime">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="MBeans/java_lang/OperatingSystem">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="MBeans/java_lang/Threading">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="MBeans/tlc2_tool/ModelChecker">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="MBeans/tlc2_tool_fp/DiskFPSet0">
+      <setting name="enabled">true</setting>
+    </event>
+    
+  </producer>
+
+</configuration>
diff --git a/tlatools/lib/aspectjrt-1.8.5.jar b/tlatools/lib/aspectjrt-1.8.5.jar
deleted file mode 100644
index b8241c820410996cc4a13dc942c27c6b0d9d6050..0000000000000000000000000000000000000000
Binary files a/tlatools/lib/aspectjrt-1.8.5.jar and /dev/null differ
diff --git a/tlatools/lib/aspectjtools-1.8.5.jar b/tlatools/lib/aspectjtools-1.8.5.jar
deleted file mode 100644
index 31f045d54150a074202fafac517f9be9243d0519..0000000000000000000000000000000000000000
Binary files a/tlatools/lib/aspectjtools-1.8.5.jar and /dev/null differ
diff --git a/tlatools/lib/aspectjweaver-1.8.5.jar b/tlatools/lib/aspectjweaver-1.8.5.jar
deleted file mode 100644
index 952a8e4c7cae616b1d20da5dde99a820911df527..0000000000000000000000000000000000000000
Binary files a/tlatools/lib/aspectjweaver-1.8.5.jar and /dev/null differ
diff --git a/tlatools/lib/jacocoant.jar b/tlatools/lib/jacocoant.jar
index 43340ba1cc3fecd7345a296fcaa72ef5b86fc55e..09ac4a806f595e54eeb4623d20e134ebf2706b89 100644
Binary files a/tlatools/lib/jacocoant.jar and b/tlatools/lib/jacocoant.jar differ
diff --git a/tlatools/lib/javax.mail.jar b/tlatools/lib/javax.mail.jar
deleted file mode 100644
index 0c5ac3967e2fb98b74f86b41de47666f9bd2758d..0000000000000000000000000000000000000000
Binary files a/tlatools/lib/javax.mail.jar and /dev/null differ
diff --git a/tlatools/lib/junit-4.8.2.jar b/tlatools/lib/junit-4.8.2.jar
deleted file mode 100644
index 5b4bb849af9583fec1ea0a0ccc0d571ef49aa8ba..0000000000000000000000000000000000000000
Binary files a/tlatools/lib/junit-4.8.2.jar and /dev/null differ
diff --git a/tlatools/logging.properties b/tlatools/logging.properties
index 68d1db468dcd42188bf41335ef66080e2f324e21..f4fb74c8d53ff25375ece6a261db66300aa69a1f 100644
--- a/tlatools/logging.properties
+++ b/tlatools/logging.properties
@@ -18,10 +18,8 @@
 # "handlers" specifies a comma separated list of log Handler 
 # classes.  These handlers will be installed during VM startup.
 # Note that these classes must be on the system classpath.
-# By default we only configure a FileHandler, which will only
-# show messages at the INFO and above levels.
-handlers= java.util.logging.FileHandler
-
+# By default we only configure a ConsoleHandler.
+handlers= java.util.logging.ConsoleHandler
 # To also add the ConsoleHandler, use the following line instead.
 #handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
 
@@ -38,6 +36,9 @@ handlers= java.util.logging.FileHandler
 # Describes specific configuration info for Handlers.
 ############################################################
 
+# Omit the date line (requires Java >=1.7)
+java.util.logging.SimpleFormatter.format=%5$s%6$s%n
+
 # default file output is in user's home directory.
 # %u stands for a simple counter
 java.util.logging.FileHandler.pattern = %h/java%u.log
@@ -45,11 +46,13 @@ java.util.logging.FileHandler.limit = 50000
 java.util.logging.FileHandler.count = 1
 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
 
+java.util.logging.ConsoleHandler.level = ALL
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
 ############################################################
 # Facility specific properties.
 # Provides extra control for each logger.
 ############################################################
 
-# For example, set the com.xyz.foo logger to only log SEVERE
-# messages:
-com.xyz.foo.level = SEVERE
+#tlc2.tool.level = ALL
+#tlc2.tool.fp.level = ALL
+#tlc2.tool.liveness.level = ALL
diff --git a/tlatools/ossrh.xml b/tlatools/ossrh.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b4cc5f211b66247c9af45228d363b41ee17b0624
--- /dev/null
+++ b/tlatools/ossrh.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.sonatype.oss</groupId>
+		<artifactId>oss-parent</artifactId>
+		<version>9</version>
+		<relativePath>../do-not-reference-our-parent-pom</relativePath>
+	</parent>
+
+	<!-- The settings below have been duplicated from ../pom.xml because the 
+		Toolbox - among other things - uses a different groupId for historical reasons. -->
+
+	<groupId>org.lamport</groupId>
+	<artifactId>tla2tools</artifactId>
+	<name>TLA+ Tools</name>
+	<version>1.7.3-SNAPSHOT</version>
+	<description>The TLC model checker, the syntax and semantic checker SANY, the PlusCal translator, and the LaTeX pretty printer.</description>
+	<packaging>jar</packaging>
+
+	<organization>
+		<name>Microsoft Research Inria Joint Centre</name>
+		<url>http://msr-inria.inria.fr/</url>
+	</organization>
+
+	<licenses>
+		<license>
+			<name>MIT License</name>
+			<url>http://www.opensource.org/licenses/mit-license.php</url>
+		</license>
+	</licenses>
+
+	<issueManagement>
+		<system>GitHub</system>
+		<url>https://github.com/tlaplus/tlaplus/issues</url>
+	</issueManagement>
+
+	<scm>
+		<connection>scm:git:https://github.com/tlaplus/tlaplus</connection>
+		<developerConnection>scm:git:https://github.com/tlaplus/tlaplus</developerConnection>
+		<tag>HEAD</tag>
+	</scm>
+
+	<distributionManagement>
+		<snapshotRepository>
+			<id>ossrh</id>
+			<url>https://oss.sonatype.org/content/repositories/snapshots</url>
+		</snapshotRepository>
+		<repository>
+			<id>ossrh</id>
+			<url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
+		</repository>
+	</distributionManagement>
+
+	<dependencies>
+		<dependency>
+			<groupId>com.sun.mail</groupId>
+			<artifactId>mailapi</artifactId>
+			<version>1.6.3</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymock</artifactId>
+			<version>2.4</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-gpg-plugin</artifactId>
+				<version>1.6</version>
+				<executions>
+					<execution>
+						<id>sign-artifacts</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>sign</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>
+
diff --git a/tlatools/ossrh.xml.README b/tlatools/ossrh.xml.README
new file mode 100644
index 0000000000000000000000000000000000000000..9e7f08f253b96c6a38d5ace59571857f77a1fae0
--- /dev/null
+++ b/tlatools/ossrh.xml.README
@@ -0,0 +1,47 @@
+Maven Central is the canonical repository for Java libraries.  To allow
+users to build software on top of tla2tools.jar, we publish tla2tools.jar 
+to Maven Central.  Users have asked us to do this [1].  Moreover, our 
+own Apalache [2] will find this useful [3].
+
+We initially had to claim [4] the org.lamport namespace to be allowed to 
+publish tla2tools.jar into it.  The artifacts are available online [5]
+and so far we have only pushed snapshots.
+
+[1] https://groups.google.com/forum/#!msg/tlaplus/xzmsu6qRMyo/YGgRNFE1CAAJ
+[2] https://github.com/konnov/apalache
+[3] https://github.com/konnov/apalache/issues/48
+[4] https://issues.sonatype.org/browse/OSSRH-50021
+[5] https://oss.sonatype.org/content/repositories/snapshots/org/lamport/
+
+Manually deploy tla2tools.jar
+-----------------------------
+
+1) Strip javax/ directory from tla2tools.jar built with regular customBuild.xml.
+   The version of tla2tools.jar - that we will upload to MC - will declare its
+   dependencies to javax packages instead of bundling them.
+
+zip -d dist/tla2tools.jar \
+  javax/\* \
+  com/\* \
+  META-INF/mailcap \
+  META-INF/javamail*
+   
+2) Upload to Maven Central (credentials have to be set in ~/.m2/settings.xml).
+
+mvn gpg:sign-and-deploy-file \
+ -Durl=https://oss.sonatype.org/content/repositories/snapshots \
+ -DrepositoryId=ossrh \
+ -DpomFile=ossrh.xml \
+ -Dfile=dist/tla2tools.jar
+
+
+Background material:
+* <https://central.sonatype.org/pages/manual-staging-bundle-creation-and-deployment.html>
+  * Credentials are the login details for Maven Central (OSSRH)
+* What is wrong when deploy returns a 400 bad request response: <https://stackoverflow.com/a/18702353/6291195>
+
+Delete existing/old artifacts
+-----------------------------
+
+1) Login to the OSSRH web front-end <https://oss.sonatype.org/index.html>
+2) Follow steps outlined in: <https://issues.sonatype.org/browse/OSSRH-776?focusedCommentId=117817&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-117817>
diff --git a/tlatools/pom.xml b/tlatools/pom.xml
index 147cd9c783d0dc531cd1cb83915fc5965992da1a..924f664036cebd2b05aa49e27faf9aa9ab1af4b8 100644
--- a/tlatools/pom.xml
+++ b/tlatools/pom.xml
@@ -8,18 +8,60 @@
     <artifactId>parent</artifactId>
     <groupId>tlatoolbox</groupId>
     <version>0.0.1-SNAPSHOT</version>
-    <relativePath>../pom.xml</relativePath>
+    <relativePath>../../pom.xml</relativePath>
   </parent>
   <groupId>tlatoolbox</groupId>
   <artifactId>org.lamport.tlatools</artifactId>
   <version>1.0.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 
+  <properties>
+    <!-- tlatools is a non-standard maven project. Its heart is customBuild.xml written for ant          -->
+    <!-- which gets invoked from maven. Unfortunately, it means that the tasks which are executed        -->
+    <!-- by ant are not visible to maven. In this case it results in sonar not finding necessary         -->
+    <!-- data to include coverage information. The properties below make sure that sonar finds the data. -->
+	<sonar.sources>src,src-aj</sonar.sources>
+	<sonar.tests>test,test-verify,test-long,test-concurrent</sonar.tests>
+
+	<sonar.java.binaries>class</sonar.java.binaries>
+	<sonar.java.libraries>lib/*.jar</sonar.java.libraries>
+	
+	<sonar.java.test.binaries>class,test-class</sonar.java.test.binaries>
+	<sonar.java.test.libraries>lib/*.jar</sonar.java.test.libraries>
+	 
+	<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
+	<sonar.surefire.reportsPath>target/surefire-reports</sonar.surefire.reportsPath>
+	<sonar.junit.reportsPath>target/surefire-reports</sonar.junit.reportsPath>
+	<sonar.jacoco.reportPaths>target/code-coverage.exec</sonar.jacoco.reportPaths>
+  </properties>
+
+
     <build>
     <plugins>
-	   <plugin>
+		<!-- Delete the ../states/ folder created by the Ant JUnit tests as part 
+			of the maven-antrun-plugin of customBuild.xml. On the CI-machine, the ../states/ 
+			folder gradually grows to eventually fill-up the entire disk. -->
+        <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-clean-plugin</artifactId>
+            <version>3.0.0</version>
+            <configuration>
+                <filesets>
+                    <fileset>
+                        <directory>../states/</directory>
+                        <includes>
+                            <include>**/*</include>
+                        </includes>
+                        <followSymlinks>false</followSymlinks>
+                    </fileset>
+                </filesets>
+            </configuration>
+        </plugin>
+        
+        <plugin>
 	    <groupId>org.apache.maven.plugins</groupId>
 	    <artifactId>maven-antrun-plugin</artifactId>
+	    <version>1.8</version>
 	    <executions>
 	    	<execution>
 	    		<id>compile</id>
@@ -33,9 +75,32 @@
 
 		    			<property name="build.compiler" value="extJavac"/>
 		    			<property name="maven.test.skip" value="${maven.test.skip}"/>
+		    			<property name="maven.test.halt" value="${maven.test.halt}"/>
 	    				<ant antfile="customBuild.xml" inheritRefs="true">
-	    					<target name="default" />
-	    					<target name="dist-mixed-zip" />
+	    					<target name="default-maven" />
+	    				</ant>
+	    			</tasks>
+	    		</configuration>
+	    		<goals>
+	    			<goal>run</goal>
+	    		</goals>
+	    	</execution>
+	    	<!-- Run customBuild.xml's clean target -->
+	    	<execution>
+	    		<id>clean</id>
+	    		<phase>clean</phase>
+	    		<configuration>
+	    			<tasks>
+
+						<taskdef name="junit"
+							classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask"
+							classpathref="maven.test.classpath" />
+
+		    			<property name="build.compiler" value="extJavac"/>
+		    			<property name="maven.test.skip" value="${maven.test.skip}"/>
+		    			<property name="maven.test.halt" value="${maven.test.halt}"/>
+	    				<ant antfile="customBuild.xml" inheritRefs="true">
+	    					<target name="clean" />
 	    				</ant>
 	    			</tasks>
 	    		</configuration>
@@ -60,11 +125,52 @@
 	        </configuration>
 	      </plugin>
 
+			<!-- Update the Manifest.mf packaged into the OSGi variant of tla2tools.jar 
+				which is embedded in the Toolbox. The standalone tla2tools.jar (see customBuild.xml) 
+				defines various properties in its manifest among which are the git revision 
+				and the implementation title. These two properties are read by TLCGlobals 
+				to determine TLCs revision. This is turn is reported as part of execution statistics.
+				To correctly report the TLC revision when it is launched from inside the Toolbox, the
+				embedded tla2tools.jar created here has to also include the two properties. -->
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>buildnumber-maven-plugin</artifactId>
+				<version>1.4</version>
+				<executions>
+					<execution>
+						<phase>validate</phase>
+						<goals>
+							<goal>create</goal>
+						</goals>
+					</execution>
+				</executions>
+				<configuration>
+					<getRevisionOnlyOnce>true</getRevisionOnlyOnce>
+				    <shortRevisionLength>7</shortRevisionLength>
+					<doCheck>false</doCheck>
+					<doUpdate>false</doUpdate>
+				</configuration>
+			</plugin>
+      		<plugin>
+				<groupId>org.eclipse.tycho</groupId>
+				<artifactId>tycho-packaging-plugin</artifactId>
+				<version>${tycho-version}</version>
+				<configuration>
+					<archive>
+						<manifestEntries>
+							<Implementation-Title>TLA+ Tools</Implementation-Title>
+							<X-Git-ShortRevision>${buildNumber}</X-Git-ShortRevision>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+
 			<!-- Compile aspects -->
             <plugin>
-                <groupId>org.codehaus.mojo</groupId>
+                <!-- https://github.com/nickwongdev/aspectj-maven-plugin -->
+                <groupId>com.nickwongdev</groupId>
                 <artifactId>aspectj-maven-plugin</artifactId>
-                <version>1.7</version>
+                <version>1.12.1</version>
                 <configuration>
                     <ajdtBuildDefFile>build.ajproperties</ajdtBuildDefFile>
                 </configuration>
@@ -73,6 +179,11 @@
                         <goals>
                             <goal>compile</goal>
                         </goals>
+                        <configuration>
+                            <complianceLevel>1.8</complianceLevel>
+                            <source>1.8</source>
+                            <target>1.8</target>
+                        </configuration>
                     </execution>
                </executions>
            </plugin>
@@ -81,15 +192,15 @@
 
   <dependencies>
 	<dependency>
-		<groupId>ant</groupId>
+		<groupId>org.apache.ant</groupId>
 		<artifactId>ant-junit</artifactId>
-		<version>1.6.2</version>
+		<version>1.7.3</version>
 		<scope>compile</scope>
 	</dependency>
   		<dependency>
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjrt</artifactId>
-            <version>1.8.2</version>
+            <version>1.9.1</version>
         </dependency>
       </dependencies>
 
diff --git a/tlatools/project.properties b/tlatools/project.properties
index b98226fd30c7696b0ab55dfc66744510f9069373..170b370d0f59900dbfb579a684b26451d5db4556 100644
--- a/tlatools/project.properties
+++ b/tlatools/project.properties
@@ -12,8 +12,6 @@ dist.file	=${dist.dir}/tla2tools.jar
 dist-mixed.file	=${dist.dir}/tla.zip
 dist-mixed.file.jar	=${dist.dir}/tla2tools-mixed.jar
 
-srcgen.dir	=src-gen
-
 test.reports =target/surefire-reports/
 
 examples.dir	=../examples
diff --git a/tlatools/src-aj/META-INF/aop-ajc.xml b/tlatools/src-aj/META-INF/aop-ajc.xml
index c425d69c2e85a51b25349b374bea97f310a8b088..71712cc740e474162e42029adff926e78fb8f5c1 100644
--- a/tlatools/src-aj/META-INF/aop-ajc.xml
+++ b/tlatools/src-aj/META-INF/aop-ajc.xml
@@ -5,6 +5,18 @@
 		<aspect name="tlc2.tool.WorkerMonitorAspect"/>
 		<aspect name="tlc2.tool.distributed.RMIMethodMonitorAspect"/>
 		<aspect name="tlc2.tool.distributed.TLCServerMonitorAspect"/>
+		<!-- 
+		<aspect name="tlc2.tool.CostModelAspect"/>
+		 -->
 	</aspects>
-
+	
+	<!-- 
+	Dump load-time weaving classes to disk.
+	https://www.eclipse.org/aspectj/doc/released/pdguide/ltwdump.html#ltwdump-examples 
+	-->
+    <weaver options="-verbose -debug">
+    		<!-- dump before and after they are woven. -->
+	        <dump within="tlc2..*" beforeandafter="true"/> 
+    	    <include within="tlc2..*"/>
+	</weaver>
 </aspectj>
diff --git a/tlatools/src-aj/META-INF/aop.xml b/tlatools/src-aj/META-INF/aop.xml
index c425d69c2e85a51b25349b374bea97f310a8b088..102e0e21286937a8ac5e7838e17aa15eb1b6a47d 100644
--- a/tlatools/src-aj/META-INF/aop.xml
+++ b/tlatools/src-aj/META-INF/aop.xml
@@ -5,6 +5,18 @@
 		<aspect name="tlc2.tool.WorkerMonitorAspect"/>
 		<aspect name="tlc2.tool.distributed.RMIMethodMonitorAspect"/>
 		<aspect name="tlc2.tool.distributed.TLCServerMonitorAspect"/>
+		<!-- 
+		<aspect name="tlc2.tool.CostModelAspect"/>
+		 -->
 	</aspects>
 
+	<!-- 
+	Dump load-time weaving classes to disk.
+	https://www.eclipse.org/aspectj/doc/released/pdguide/ltwdump.html#ltwdump-examples 
+	-->
+    <weaver options="-verbose -debug">
+    		<!-- dump before and after they are woven. -->
+	        <dump within="tlc2..*" beforeandafter="true"/> 
+    	    <include within="tlc2..*"/>
+	</weaver>
 </aspectj>
diff --git a/tlatools/src-aj/tlc2/tool/CostModelAspect.aj b/tlatools/src-aj/tlc2/tool/CostModelAspect.aj
new file mode 100644
index 0000000000000000000000000000000000000000..a050d7549097664583a61ae31ca309e6ded2a7e3
--- /dev/null
+++ b/tlatools/src-aj/tlc2/tool/CostModelAspect.aj
@@ -0,0 +1,244 @@
+///*******************************************************************************
+// * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+// *
+// * The MIT License (MIT)
+// * 
+// * Permission is hereby granted, free of charge, to any person obtaining a copy 
+// * of this software and associated documentation files (the "Software"), to deal
+// * in the Software without restriction, including without limitation the rights
+// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// * of the Software, and to permit persons to whom the Software is furnished to do
+// * so, subject to the following conditions:
+// *
+// * The above copyright notice and this permission notice shall be included in all
+// * copies or substantial portions of the Software. 
+// * 
+// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// *
+// * Contributors:
+// *   Markus Alexander Kuppe - initial API and implementation
+// ******************************************************************************/
+//package tlc2.tool;
+//
+//import tlc2.tool.Action;
+//import tlc2.tool.ActionItemList;
+//import tlc2.tool.IStateFunctor;
+//import tlc2.tool.StateVec;
+//import tlc2.tool.TLCState;
+//import tlc2.tool.coverage.CostModel;
+//import tlc2.util.Context;
+//import tlc2.TLCGlobals;
+//import tla2sany.semantic.OpApplNode;
+//import tla2sany.semantic.SemanticNode;
+//
+//public final aspect CostModelAspect {
+//	
+//	// -------------------------------- //
+//	
+///*
+//  private final void getInitStates(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm) {
+// */
+//	
+//	final pointcut getInitStates(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm): 
+//		execution(private void tlc2.tool.Tool.getInitStates(ActionItemList, TLCState, IStateFunctor, CostModel))
+//		&& args(acts, ps, states, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()) ;
+//
+//	void around(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm): (getInitStates(acts, ps, states, cm)) {
+//		if (acts.isEmpty() || ps.allAssigned()) {
+//			cm.incInvocations();
+//			cm.getRoot().incInvocations();
+//		}
+//		proceed(acts, ps, states, cm);
+//	}
+//
+//	// -------------------------------- //
+//	
+///*
+//  public StateVec getNextStates(Action action, TLCState state) {
+//    action.cm.incInvocations(nss.size());
+//  }
+//*/
+//
+//	final pointcut getNextStatesAction(Action action, TLCState state): 
+//		execution(public tlc2.tool.StateVec tlc2.tool.Tool.getNextStates(Action, TLCState))
+//		&& args(action, state) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	//TODO Rewrite to after returning?
+//	StateVec around(final Action action, final TLCState state): (getNextStatesAction(action, state)) {
+//		final StateVec nss = (StateVec) proceed(action, state);
+//		action.cm.incInvocations(nss.size());
+//		
+//		return nss;
+//	}
+//
+//	// -------------------------------- //
+//	
+///*
+//  private final TLCState getNextStates(ActionItemList acts, final TLCState s0, final TLCState s1, final StateVec nss, CostModel cm) {
+//	  final TLCState copy = getNextStates0(acts, s0, s1, nss, cm);
+//	  if (copy != s1) {
+//		  cm.incInvocations();
+//	  }
+//	  return copy;
+//  }
+//*/
+//
+//	final pointcut getNextStates(ActionItemList acts, TLCState s0, TLCState s1, StateVec nss, CostModel cm): 
+//		execution(private tlc2.tool.TLCState tlc2.tool.Tool.getNextStates(ActionItemList, TLCState, TLCState, StateVec, CostModel))
+//		&& args(acts, s0, s1, nss, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	TLCState around(ActionItemList acts, TLCState s0, TLCState s1, StateVec nss, CostModel cm): (getNextStates(acts, s0, s1, nss, cm)) {
+//		final TLCState copy = (TLCState) proceed(acts, s0, s1, nss, cm);
+//		if (copy != s1) {
+//			cm.incInvocations();
+//		}
+//		return copy;
+//	}
+//
+//	// -------------------------------- //
+//	
+///*
+//  private final Value setSource(final SemanticNode expr, final Value value, CostModel cm) {
+//    value.setCostModel(cm.get(expr));
+//*/
+//
+//	final pointcut setSource(SemanticNode expr, final Value value, CostModel cm): 
+//		execution(private tlc2.value.Value tlc2.tool.Tool.setSource(SemanticNode, Value, CostModel))
+//		&& args(expr, value, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	Value around(SemanticNode expr, final Value value, CostModel cm): (setSource(expr, value, cm)) {
+//		value.setCostModel(cm.get(expr));
+//		return proceed(expr, value, cm);
+//	}
+//
+//	// -------------------------------- //
+//	
+///*
+//	private final Value evalAppl(final OpApplNode expr, Context c, TLCState s0, TLCState s1, final int control, CostModel cm) {
+//		cm = cm.get(expr);
+//		cm.incInvocations();
+//*/
+//
+//	final pointcut evalsAppl(OpApplNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): 
+//		execution(private tlc2.value.Value tlc2.tool.Tool.evalAppl(tla2sany.semantic.OpApplNode, Context, TLCState, TLCState, int, CostModel))
+//		&& args(expr, c, s0, s1, control, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	Value around(OpApplNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): (evalsAppl(expr, c, s0, s1, control, cm)) {
+//		return proceed(expr, c, s0, s1, control, cm.get(expr).incInvocations());
+//	}
+//	
+//	// -------------------------------- //
+//	
+///*
+//    
+//  private final TLCState getNextStates(SemanticNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, CostModel cm) {
+//    cm = cm.get(pred);
+//    
+//  private final TLCState processUnchanged(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, CostModel cm) {
+//    cm = cm.get(expr);
+//*/
+//
+//	final pointcut evals6(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss,
+//			CostModel cm): 
+//		(execution(private tlc2.tool.TLCState tlc2.tool.Tool.getNextStates(SemanticNode, ActionItemList, Context, TLCState, TLCState, StateVec, CostModel)) ||
+//				execution(private tlc2.tool.TLCState tlc2.tool.Tool.processUnchanged(SemanticNode, ActionItemList, Context, TLCState, TLCState, StateVec, CostModel)))
+//				&& args(expr, acts, c, s0, s1, nss, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	TLCState around(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, CostModel cm): (evals6(expr, acts, c, s0, s1, nss, cm)) {
+//		return proceed(expr, acts, c, s0, s1, nss, cm.get(expr));
+//	}
+//	
+//	// -------------------------------- //
+//	
+///*
+//  private final void getInitStatesAppl(OpApplNode init, ActionItemList acts, Context c, TLCState ps, IStateFunctor states, CostModel cm) {
+//    cm = cm.get(init);
+//*/
+//	
+//	final pointcut getInitStatesAppl(OpApplNode expr, ActionItemList acts, Context c, TLCState ps, IStateFunctor states, CostModel cm): 
+//				execution(private void tlc2.tool.Tool.getInitStatesAppl(OpApplNode, ActionItemList, Context, TLCState, IStateFunctor, CostModel))
+//				&& args(expr, acts, c, ps, states, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	void around(OpApplNode expr, ActionItemList acts, Context c, TLCState ps, IStateFunctor states, CostModel cm): (getInitStatesAppl(expr, acts, c, ps, states, cm)) {
+//		proceed(expr, acts, c, ps, states, cm.get(expr));
+//	}
+//	
+//	// -------------------------------- //
+//	
+///*
+//  private final TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm) {
+//    cm = cm.get(pred);
+//*/
+//	
+//	final pointcut enableAppl(OpApplNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm): 
+//				execution(private tlc2.tool.TLCState tlc2.tool.Tool.enabledAppl(OpApplNode, ActionItemList, Context, TLCState, TLCState, CostModel))
+//		&& args(expr, acts, c, s0, s1, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	TLCState around(OpApplNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm): (enableAppl(expr, acts, c, s0, s1, cm)) {
+//		return proceed(expr, acts, c, s0, s1, cm.get(expr));
+//	}
+//	
+//	// -------------------------------- //
+//	
+///*
+//  public final Value eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, final int control, CostModel cm) {
+//	 cm = cm.get(expr)
+//*/
+//	
+//	final pointcut eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): 
+//		execution(public tlc2.value.Value tlc2.tool.Tool.eval(SemanticNode, Context, TLCState, TLCState, int, CostModel))
+//			&& args(expr, c, s0, s1, control, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	Value around(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): (eval(expr, c, s0, s1, control, cm)) {
+//		return proceed(expr, c, s0, s1, control, cm.get(expr));
+//	}
+//
+//	// ---------------- Pass on CostModel instances from existing Value to newly instantiated ones. ---------------- //
+//	
+//	final pointcut newValueCtor() : call(tlc2.value.Value+.new(..)) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//	
+//	after() returning(final Value newValue) : newValueCtor() {
+//		if (thisJoinPoint.getThis() instanceof Value) {
+//			// Get CostModel instance from existing Value and attach to new one.
+//			final Value existingValue = (Value) thisJoinPoint.getThis();
+//			newValue.setCostModel(existingValue.getCostModel());
+//		}
+//	}
+//
+//	// ---------------- (Finally) count invocations to ValueEnumerator#elements to measure costs. ---------------- //
+//
+//	final pointcut elementsExec(Enumerable en): 
+//		execution(public tlc2.value.ValueEnumeration tlc2.value.Enumerable+.elements(..))
+//		&& target(en) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled());
+//
+//	ValueEnumeration around(Enumerable en): (elementsExec(en)) {
+//		return new WrappingValueEnumeration(((Enumerable) en).getCostModel(), (ValueEnumeration) proceed(en));
+//	}
+//
+//	private static class WrappingValueEnumeration implements ValueEnumeration {
+//
+//		private final CostModel cm;
+//		private final ValueEnumeration ve;
+//		
+//		public WrappingValueEnumeration(CostModel costModel, ValueEnumeration ve) {
+//			this.cm = costModel.incInvocations(1);
+//			this.ve = ve;
+//		}
+//
+//		@Override
+//		public void reset() {
+//			ve.reset();
+//		}
+//
+//		@Override
+//		public Value nextElement() {
+//			cm.incSecondary();
+//			return ve.nextElement();
+//		}
+//	}
+//}
diff --git a/tlatools/src/model/InJarFilenameToStream.java b/tlatools/src/model/InJarFilenameToStream.java
index 39f07a222b12497ee02896bf683a98b692b25bc1..e3ff7afc4e65530e2ed1cea9bc1eab948177ac2f 100644
--- a/tlatools/src/model/InJarFilenameToStream.java
+++ b/tlatools/src/model/InJarFilenameToStream.java
@@ -23,7 +23,7 @@ public class InJarFilenameToStream extends SimpleFilenameToStream implements
 		InputStream is = InJarFilenameToStream.class.getResourceAsStream(prefix + name);
 		if(is != null) {
 			try {
-				File sourceFile = new File(TMPDIR + File.separator + name);
+				File sourceFile = new TLAFile(TMPDIR + File.separator + name, this);
 				sourceFile.deleteOnExit();
 				
 				FileOutputStream fos = new FileOutputStream(sourceFile);
diff --git a/tlatools/src/model/ModelInJar.java b/tlatools/src/model/ModelInJar.java
index d823fda2e6e6aac46ce612d1ab8161be0aae3aa1..1d51d4ecbc5708bb68e2dea136140045f2c9056a 100644
--- a/tlatools/src/model/ModelInJar.java
+++ b/tlatools/src/model/ModelInJar.java
@@ -1,20 +1,40 @@
 package model;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.Enumeration;
 import java.util.Properties;
 
+import util.TLAConstants;
+
 public abstract class ModelInJar {
 	public static final String PATH = "/model/";
 	
 	public static boolean hasModel() {
-		return ModelInJar.class.getResource("/model/MC.tla") != null;
+		return ModelInJar.class.getResource(PATH + TLAConstants.Files.MODEL_CHECK_TLA_FILE) != null;
+	}
+	
+	public static boolean hasCfg() {
+		return ModelInJar.class.getResource(PATH + TLAConstants.Files.MODEL_CHECK_CONFIG_FILE) != null;
+	}
+
+	public static File getCfg() {
+		try {
+			final InputStream source = ModelInJar.class.getResourceAsStream(PATH + TLAConstants.Files.MODEL_CHECK_CONFIG_FILE);
+			Path target = Files.createTempFile(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, TLAConstants.Files.CONFIG_EXTENSION);
+			Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+			return target.toFile();
+		} catch (IOException notExpectedToHappen) {
+			return new File("");
+		}
 	}
 	
 	public static boolean loadProperties() {
-		InputStream in = ModelInJar.class
-				.getResourceAsStream("/model/generated.properties");
+		InputStream in = ModelInJar.class.getResourceAsStream(PATH + "generated.properties");
 		if (in != null) {
 			Properties prop = new Properties();
 			try {
diff --git a/tlatools/src/pcal/AST.java b/tlatools/src/pcal/AST.java
index c9d203d445fc1f2ab05d16a1ccbd03a6536a25d6..5107add92b89272ed1baf35682471a01f441a08e 100644
--- a/tlatools/src/pcal/AST.java
+++ b/tlatools/src/pcal/AST.java
@@ -17,7 +17,7 @@
 * However, there are the following classes that do not represent explicit  *
 * non-terminals of the +CAL grammar.                                       *
 *                                                                          *
-*     Uniprocess   MultiProcess   SingleAssign   CallReturn                *
+* Uniprocess   MultiProcess   SingleAssign   CallReturn   CallGoto         *
 *                                                                          *
 * Every AST has col and line fields that contain the position of the       *
 * first character of the corresponding portion of the algorithm text (as   *
@@ -100,6 +100,7 @@ public class AST
     public static AST.Call         CallObj         ;
     public static AST.Return       ReturnObj       ;
     public static AST.CallReturn   CallReturnObj   ;
+    public static AST.CallGoto     CallGotoObj     ;
     public static AST.Goto         GotoObj         ;
     public static AST.Macro        MacroObj        ;
     public static AST.MacroCall    MacroCallObj    ;
@@ -214,6 +215,7 @@ public class AST
         CallObj         = new AST.Call() ;
         ReturnObj       = new AST.Return() ;
         CallReturnObj   = new AST.CallReturn() ;
+        CallGotoObj     = new AST.CallGoto() ;
         GotoObj         = new AST.Goto() ;
         MacroObj        = new AST.Macro() ;
         MacroCallObj    = new AST.MacroCall() ;
@@ -845,6 +847,24 @@ public class AST
           }
       }
 
+    public static class CallGoto extends AST
+      { public String    after = "" ;
+        public String    to       = "" ;
+        public Vector    args     = null ; // of TLAExpr
+        public CallGoto() { };
+        public String toString()
+          { return 
+             Indent(lineCol()) + 
+               "[type     |-> \"callGoto\"," + NewLine() +
+               " after    |-> \"" + after + "\"," + NewLine() +
+               " to       |-> \"" + to + "\"," + NewLine() +
+               Indent("args     |-> ") + VectorToSeqString(args) 
+                                             + "]" + NewLine() +
+               EndIndent() +
+             EndIndent() ;
+          }
+      }
+
     public static class Goto extends AST
       { public String    to       = "" ;
         public Goto() {};
diff --git a/tlatools/src/pcal/PCalTLAGenerator.java b/tlatools/src/pcal/PCalTLAGenerator.java
index 6887de50c617c3649772b2dc713da5d5878b1810..75054b0aaacbd36e6f00ce6075277bc32a41ae40 100644
--- a/tlatools/src/pcal/PCalTLAGenerator.java
+++ b/tlatools/src/pcal/PCalTLAGenerator.java
@@ -64,13 +64,13 @@ public class PCalTLAGenerator
      * Note that this requires RemoveNameConflicts to be called first   *
      * because of the grotty use of the class variable st.              *
      ********************************************************************/
-    public Vector translate() throws RemoveNameConflictsException
+    public Vector<String> translate() throws RemoveNameConflictsException
     {
-        Vector result = new Vector();
+        Vector<String> result = new Vector<String>();
         AST xast = null;  // Set to the exploded AST
 
         for (int i = 0; i < st.disambiguateReport.size(); i++)
-            result.addElement(st.disambiguateReport.elementAt(i));
+            result.addElement((String) st.disambiguateReport.elementAt(i));
         // System.out.println("Before: " + ast.toString());
         // System.out.println("After renaming: " + ast.toString());
         try
diff --git a/tlatools/src/pcal/ParseAlgorithm.java b/tlatools/src/pcal/ParseAlgorithm.java
index 753f5d5be77df98a8f05b8b430e93443e93a5efe..913b348c61815a1d355ef0154f634ed2fbb94a6d 100644
--- a/tlatools/src/pcal/ParseAlgorithm.java
+++ b/tlatools/src/pcal/ParseAlgorithm.java
@@ -49,9 +49,10 @@
 *    holds the label (the absence of a label represented by                *
 *    a lbl field equal to the empty string "").  The only difference       *
 *    from the simple grammar is that a <Call> followed by an unlabeled     *
-*    <Return> is converted into a single AST.CallReturn object.  This      *
-*    pass does not produce any AST.If or AST.Either objects, and any       *
-*    AST.While object it produces has an empty labDo field.                *
+*    <Return> or <Goto> is converted into a single AST.CallReturn or       *
+*    AST.CallGoto object, respectively.  This pass does not produce any    *
+*    AST.If or AST.Either objects, and any AST.While object it produces    *
+*    has an empty labDo field.                                             *
 *                                                                          *
 *  - It calls the procedure AddLabelsToStmtSeq to check for missing        *
 *    labels and either add them or report an error if it finds one,        *
@@ -107,11 +108,11 @@ package pcal;
 
 import java.util.Hashtable;
 import java.util.Vector;
+
 import pcal.exception.ParseAlgorithmException;
 import pcal.exception.TLAExprException;
 import pcal.exception.TokenizerException;
 import pcal.exception.UnrecoverableException;
-import tla2sany.parser.ParseError;
 import tla2tex.Debug;
 
 
@@ -292,6 +293,12 @@ public class ParseAlgorithm
       * Performs the initialization method of the AST to create default  *
       * objects for each subclass.                                       *
       *******************************************************************/
+    
+    /*
+     * Following added by LL on 17 Aug 2015 to fix bug apparently caused by
+     * incomplete initialization 
+     */
+    LATsize = 0 ;
    }
 
    
@@ -307,7 +314,8 @@ public class ParseAlgorithm
      * should be set to the position of the PcalParams.BeginAlg string by  *
      * whatever method finds that string and calls GetAlgorithm.           *
      **********************************************************************/
-     { Init(charR) ;
+     { try {
+	   Init(charR) ;
        if (fairAlgorithm) {
     	   String nextToken = GetAlgToken() ;
     	   if (!nextToken.equals(PcalParams.BeginFairAlg2)) {
@@ -538,6 +546,14 @@ public class ParseAlgorithm
                              GetLastLocationEnd())) ;
            return uniproc ;
          }
+       } catch (final RuntimeException e) {
+			// Catch generic/unhandled errors (ArrayIndexOutOfbounds/NullPointers/...) that
+			// the nested code might throw at us. Not converting them into a
+			// ParseAlgorithmException means the Toolbox silently fails to translate an
+			// algorithm (see Github issue at https://github.com/tlaplus/tlaplus/issues/313)
+	    	ParsingError("Unknown error at or before");
+    	    return null;
+       }
      }
 
    /**
@@ -947,6 +963,8 @@ public class ParseAlgorithm
          }
        else
          { result.unlabDo = GetCStmt() ; } ;
+       if (result.unlabDo.size() == 0)
+         { ParsingError("Missing body of while statement at"); }
        result.labDo = new Vector() ;
          /******************************************************************
          * For historical reasons, some methods expect a labDo field to    *
@@ -1071,7 +1089,7 @@ public class ParseAlgorithm
        if (nextTok.equals("print"))  { return GetPrintS() ; } ;
        if (nextTok.equals("assert")) { return GetAssert() ; } ;
        if (nextTok.equals("skip"))   { return GetSkip() ; }   ;
-       if (nextTok.equals("call"))   { return GetCallOrCallReturn() ; }   ;
+       if (nextTok.equals("call"))   { return GetCallOrCallReturnOrCallGoto() ; }   ;
        if (nextTok.equals("return")) { return GetReturn() ; }   ;
        if (nextTok.equals("goto"))   { return GetGoto() ; }   ;
        if (nextTok.equals("while"))  { return GetWhile() ; } ;
@@ -1378,9 +1396,13 @@ public class ParseAlgorithm
          { result.Do = new Vector() ;
            result.Do.addElement(InnerGetWith(depth+1, begLoc)) ;
          };
-       result.setOrigin(new Region(begLoc, 
-           ((AST) result.Do.elementAt(result.Do.size()-1)).getOrigin().getEnd())) ;
-       return result ;
+		try {
+		       result.setOrigin(new Region(begLoc, 
+		               ((AST) result.Do.elementAt(result.Do.size()-1)).getOrigin().getEnd())) ;
+		} catch (ArrayIndexOutOfBoundsException e) {
+			throw new ParseAlgorithmException("Missing body of with statement", result);
+		}
+	       return result ;
      } 
 
    public static AST.Assign GetAssign() throws ParseAlgorithmException
@@ -1575,7 +1597,7 @@ public class ParseAlgorithm
        return result ;
      }
 
-   public static AST GetCallOrCallReturn() throws ParseAlgorithmException 
+   public static AST GetCallOrCallReturnOrCallGoto() throws ParseAlgorithmException 
      /**********************************************************************
      * Note: should not complain if it finds a return that is not inside   *
      * a procedure because it could be in a macro that is called only      *
@@ -1595,6 +1617,23 @@ public class ParseAlgorithm
            result.setOrigin(new Region(theCall.getOrigin().getBegin(), end)) ;
            return result ;
          }
+       else if (PeekAtAlgToken(1).equals("goto"))
+         { MustGobbleThis("goto") ;
+           AST.CallGoto result = new AST.CallGoto();
+           result.col   = theCall.col ;
+           result.line  = theCall.line ;
+           result.to    = theCall.to ;
+           result.after = GetAlgToken() ;
+           result.args  = theCall.args ;
+           PCalLocation end = new PCalLocation(lastTokLine-1, lastTokCol-1+result.to.length());
+           result.setOrigin(new Region(theCall.getOrigin().getBegin(), end)) ;
+           gotoUsed = true ;
+           if (result.to.equals("Done") || result.to.equals("\"Done\"")) {
+               gotoDoneUsed = true;
+           }
+           GobbleThis(";") ;
+           return result ;
+         }
        else 
          { return theCall; }
      }
@@ -2010,15 +2049,16 @@ public class ParseAlgorithm
                * set assignedVbls to the set of variables being assigned   *
                * by this statement.  Should set nextStepNeedsLabel to      *
                * true and set assignedVbls to a non-empty set iff this is  *
-               * a call, return, or callReturn.                            *
+               * a call, return, callReturn, or callGoto.                  *
                ************************************************************/
                nextStepNeedsLabel = false ;
                Vector assignedVbls = new Vector() ;
 
                /************************************************************
-               * Set isCallOrReturn true iff this is a call, return, or    *
-               * callReturn.  Will set setsPrcdVbls true iff this is a     *
-               * return or callReturn or a call of currentProcedure.       *
+               * Set isCallOrReturn true iff this is a call, return,       *
+               * callReturn, or callGoto.  Will set setsPrcdVbls true iff  *
+               * this is a return or callReturn or a call of               *
+               * currentProcedure.                                         *
                ************************************************************/
                boolean isCallOrReturn = false ;
                boolean setsPrcdVbls   = false ;
@@ -2031,6 +2071,12 @@ public class ParseAlgorithm
                    if (obj.to.equals(currentProcedure))
                      { setsPrcdVbls = true ; } ;
                  }
+               else if (stmt.getClass().equals(AST.CallGotoObj.getClass()))
+                 { AST.CallGoto obj = (AST.CallGoto) stmt ;
+                   isCallOrReturn = true ;
+                   if (obj.to.equals(currentProcedure))
+                     { setsPrcdVbls = true ; } ;
+                 }
                else if (   stmt.getClass().equals(AST.ReturnObj.getClass())
                         || stmt.getClass().equals(AST.CallReturnObj.getClass())
                        )
@@ -2483,8 +2529,10 @@ public class ParseAlgorithm
                    } ;
                  result = 1 ;
                }
-             else if (node.getClass().equals(
-                           AST.GotoObj.getClass())  
+             else if (   node.getClass().equals(
+                           AST.GotoObj.getClass())
+                      || node.getClass().equals(
+                           AST.CallGotoObj.getClass())
                      )
                { result = 1 ;
                }
@@ -3151,6 +3199,25 @@ public class ParseAlgorithm
             return result;
           } ;
 
+        if (stmt.getClass().equals( AST.CallGotoObj.getClass()))
+          { AST.CallGoto tstmt = (AST.CallGoto) stmt ;
+            AST.CallGoto result = new AST.CallGoto() ;
+            result.col  = tstmt.col ;
+            result.line = tstmt.line ;
+            result.macroCol  = tstmt.macroCol ;
+            result.macroLine = tstmt.macroLine ;
+            result.setOrigin(tstmt.getOrigin()) ;
+            if (macroLine > 0)
+              { result.macroLine = macroLine ;
+                result.macroCol  = macroCol ;
+              } ; 
+            result.to    = tstmt.to ;
+            result.after = tstmt.after ;
+            result.args  = 
+               TLAExpr.SeqSubstituteForAll(tstmt.args, args, params) ;
+            return result;
+          } ;
+
         if (stmt.getClass().equals( AST.GotoObj.getClass()))
           { AST.Goto tstmt = (AST.Goto) stmt ;
             AST.Goto result = new AST.Goto() ;
@@ -3424,20 +3491,17 @@ public class ParseAlgorithm
                             + tok + "\"") ; } ;
      }
 
-   public static void MustGobbleThis(String str) throws ParseAlgorithmException
-     { 
-       String tok;
-    try
-    {
-        tok = GetAlgToken();
-    } catch (ParseAlgorithmException e)
-    {
-        throw new ParseAlgorithmException(e.getMessage());
-    }
-       if (! tok.equals(str) )
-         { PcalDebug.ReportBug("Expected \"" + str + "\" but found \""
-                                 + tok + "\"") ; } ;
-     }
+	public static void MustGobbleThis(final String str) throws ParseAlgorithmException {
+		final String tok;
+		try {
+			tok = GetAlgToken();
+		} catch (ParseAlgorithmException e) {
+			throw new ParseAlgorithmException(e.getMessage());
+		}
+		if (!tok.equals(str)) {
+			ParsingError("Expected \"" + str + "\" but found \"" + tok + "\"");
+		}
+	}
 
    public static boolean GobbleEqualOrIf() throws ParseAlgorithmException
      /**********************************************************************
diff --git a/tlatools/src/pcal/PcalBuiltInSymbols.java b/tlatools/src/pcal/PcalBuiltInSymbols.java
index 0f27eb7d85dc9cf6bd50e9bd7a7c3bdc6d8e8e5f..b3de7693e788b11d5ead668d9178c91c4ccda2cd 100644
--- a/tlatools/src/pcal/PcalBuiltInSymbols.java
+++ b/tlatools/src/pcal/PcalBuiltInSymbols.java
@@ -37,25 +37,26 @@ import java.util.Hashtable;
 
 import tla2tex.Misc;
 import tla2tex.Symbol;
+import util.TLAConstants;
 
 public final class PcalBuiltInSymbols
   { 
     /***********************************************************************
     * The following three hash tables are built by the Initialize method.  *
     ***********************************************************************/
-    private static Hashtable builtInHashTable = new Hashtable(200);
+    private static Hashtable<String, Symbol> builtInHashTable = new Hashtable<>(200);
       /*********************************************************************
       * Maps built-in symbols (which are strings) to their Symbol          *
       * objects.                                                           *
       *********************************************************************/
 
-    private static Hashtable prefixHashTable  = new Hashtable(700);
+    private static Hashtable<String, String> prefixHashTable  = new Hashtable<>(700);
       /*********************************************************************
       * A table containing the prefixes of all built-in symbols.  (It      *
       * holds only their keys.)                                            *
       *********************************************************************/
 
-    private static Hashtable stringCharTable  = new Hashtable(100);
+    private static Hashtable<String, String> stringCharTable  = new Hashtable<>(100);
       /*********************************************************************
       * A table of all the characters that may appear in a TLA+ string     *
       * token.                                                             *
@@ -79,7 +80,7 @@ public final class PcalBuiltInSymbols
       } ;
 
     public static Symbol GetBuiltInSymbol(String str)
-      { return (Symbol) builtInHashTable.get(str);
+      { return builtInHashTable.get(str);
       } ;
 
     public static boolean IsBuiltInPrefix(String str)
@@ -134,20 +135,20 @@ public final class PcalBuiltInSymbols
         add("AXIOM",      "{\\AXIOM}",       Symbol.KEYWORD, 0);
         add("BOOLEAN",    "{\\BOOLEAN}",     Symbol.KEYWORD, 0);
         add("CASE",       "{\\CASE}",        Symbol.KEYWORD, 0);
-        add("CONSTANT",   "{\\CONSTANT}",    Symbol.KEYWORD, 0);
-        add("CONSTANTS",  "{\\CONSTANTS}",   Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.CONSTANT,   "{\\CONSTANT}",    Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.CONSTANTS,  "{\\CONSTANTS}",   Symbol.KEYWORD, 0);
         add("EXCEPT",     "{\\EXCEPT}",      Symbol.KEYWORD, 0);
-        add("EXTENDS",    "{\\EXTENDS}",     Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.EXTENDS,    "{\\EXTENDS}",     Symbol.KEYWORD, 0);
         add("FALSE",      "{\\FALSE}",       Symbol.KEYWORD, 0);
         add("IF",         "{\\IF}",          Symbol.KEYWORD, 0);
         add("INSTANCE",   "{\\INSTANCE}",    Symbol.KEYWORD, 0);
         add("LOCAL",      "{\\LOCAL}",       Symbol.KEYWORD, 0);
-        add("MODULE",     "{\\MODULE}",      Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.MODULE,     "{\\MODULE}",      Symbol.KEYWORD, 0);
         add("OTHER",      "{\\OTHER}",       Symbol.KEYWORD, 0);
         add("STRING",     "{\\STRING}",      Symbol.KEYWORD, 0);
         add("THEOREM",    "{\\THEOREM}",     Symbol.KEYWORD, 0);
         add("TRUE",       "{\\TRUE}",        Symbol.KEYWORD, 0);
-        add("VARIABLE",   "{\\VARIABLE}",    Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.VARIABLE,   "{\\VARIABLE}",    Symbol.KEYWORD, 0);
         add("VARIABLES",  "{\\VARIABLES}",   Symbol.KEYWORD, 0);
         add("WITH",       "{\\WITH}",        Symbol.KEYWORD, 0);
 
@@ -340,9 +341,9 @@ public final class PcalBuiltInSymbols
       * Initializes prefixHashTable, assuming that builtInHashTable is     *
       * already initialized.                                               *
       *********************************************************************/
-      { Enumeration builtInEnum = builtInHashTable.keys();
+      { Enumeration<String> builtInEnum = builtInHashTable.keys();
         while (builtInEnum.hasMoreElements())
-          { String symbol = (String) builtInEnum.nextElement();
+          { String symbol = builtInEnum.nextElement();
             if (    Misc.IsLetter(symbol.charAt(0))
                  ||    (symbol.length() > 1)
                     && (symbol.charAt(0) == '\\')
diff --git a/tlatools/src/pcal/PcalDebug.java b/tlatools/src/pcal/PcalDebug.java
index 87e4933479a4b26ebd7641d140bd1a84a753d906..896e00bb6eda7148da865cd7a2da256d53467f57 100644
--- a/tlatools/src/pcal/PcalDebug.java
+++ b/tlatools/src/pcal/PcalDebug.java
@@ -105,14 +105,15 @@ public class PcalDebug
     * This method is called to report a bug in the program and abort.    *
     *********************************************************************/
     {
+    	final StringBuilder sb = new StringBuilder("You have discovered a bug in pcal.trans.\n");
+        sb.append("Send the following information and the\ninput file to the current maintainer(s).\n\n -- ");
+        sb.append(msg).append(".");
+    	
         ToolIO.out.println("");
-        ToolIO.out.println("You have discovered a bug in pcal.trans.");
-        ToolIO.out.println("Send the following information and the");
-        ToolIO.out.println("input file to the current maintainer(s).");
+        ToolIO.out.println(sb.toString());
         ToolIO.out.println("");
-        ToolIO.out.println(" -- " + msg + ".");
-        ToolIO.out.println("");
-        throw new Error();
+        
+        throw new Error(sb.toString());
     };
 
     public static void printObjectArray(Object[] array, String name)
diff --git a/tlatools/src/pcal/PcalFixIDs.java b/tlatools/src/pcal/PcalFixIDs.java
index 282c77cdc81921944749280c5ec865155137d016..38a43533e1ec81819e09e56d2d76c2e5e395e6ed 100644
--- a/tlatools/src/pcal/PcalFixIDs.java
+++ b/tlatools/src/pcal/PcalFixIDs.java
@@ -73,6 +73,8 @@ public class PcalFixIDs {
             FixReturn((AST.Return) ast, context);
         else if (ast.getClass().equals(AST.CallReturnObj.getClass()))
             FixCallReturn((AST.CallReturn) ast, context);
+        else if (ast.getClass().equals(AST.CallGotoObj.getClass()))
+            FixCallGoto((AST.CallGoto) ast, context);
         else if (ast.getClass().equals(AST.GotoObj.getClass()))
             FixGoto((AST.Goto) ast, context);
 
@@ -181,10 +183,25 @@ public class PcalFixIDs {
                 	// For some reason, this originally called PcalDebug.ReportBug.
                 	// Since it can occur just because there is no procedure by that
                 	// name in the code, it occurs on an error.  Fixed 31 May 2013 by LL.
+                	// This fix caused the for loop that follows to throw an ArrayIndexOutOfBounds
+                	// exception.  When run from the Toolbox, this caused the error message
+                	// to be lost and the Translate command to fail with no message.
+                	// It appears that it's not necessary to report the error here, because
+                	// it will be caught later.  Moreover, when caught later, the location
+                	// of the bad procedure name is indicated.  That location doesn't seem to
+                	// available here.  However, I'm hesitant to remove the error report in case
+                	// the error isn't caught later in all cases.  So I have added the return
+                	// to avoid the exception.  Hopefully, no further exceptions can be caused
+                	// by the error.  However, I don't know how to make sure that's the
+                	// case without figuring out how the code works.  
+                	// I also removed an unhelpful part of the message.  LL 30 Aug 2015
                     PcalDebug.reportError(
-                     "Could not find procedure name `" + pName +
-                     "' in method FixMultiprocess");
-                }
+                     "Could not find procedure name `" + pName +"'"
+                     // + "' in method FixMultiprocess"
+                     ) ;
+                    return ; // Added 30 Aug 2015 
+
+               }
                 // For each k such that path[pNum][k] is true
                 // (meaning that procedure number pNum calls
                 // procedure number k), add 
@@ -380,6 +397,13 @@ public class PcalFixIDs {
             FixExpr((TLAExpr) ast.args.elementAt(i), context);
     }
 
+    private static void FixCallGoto(AST.CallGoto ast, String context) throws PcalFixIDException {
+        ast.after = st.UseThis(PcalSymTab.PROCEDURE, ast.after, context);
+        ast.to = st.UseThis(PcalSymTab.PROCEDURE, ast.to, context);
+        for (int i = 0; i < ast.args.size(); i++)
+            FixExpr((TLAExpr) ast.args.elementAt(i), context);
+    }
+
     private static void FixGoto(AST.Goto ast, String context) throws PcalFixIDException {
         /*
          * Report an error if the goto destination is not a label.  This check
diff --git a/tlatools/src/pcal/PcalParams.java b/tlatools/src/pcal/PcalParams.java
index c7315d531780237fcaa76c6237110856c3213658..51be055c814bf01e5d2a60033e494c3e902295e3 100644
--- a/tlatools/src/pcal/PcalParams.java
+++ b/tlatools/src/pcal/PcalParams.java
@@ -1,318 +1,328 @@
-/***************************************************************************
-* CLASS PcalParams                                                         *
-*                                                                          *
-* The fields of this class consist of all the parameters used by           *
-* pcal.trans.  Some of them are set by program options.                    *
-***************************************************************************/
-package pcal ;
-import java.util.Vector;
-
-/**
- * Holds parameters for the PCal Translator
- * @author Keith Marzullo
- * @author Simon Zambrovski
- * @version $Id$
- */
-public final class PcalParams
-{ 
-    /**
-     * Parameters to be updated on each new release.
-     */
-    public static final String modDate = "2 Apr 2013";
-    public static final String version = "1.8";
-    /**
-     * SZ Mar 9, 2009:
-     * Added re-initialization method. Since PcalParams class
-     * is used instead of PcalParams instance, it is required to
-     * take care of parameter initialization and de-initialization
-     * by explicit method. This is required in order to make PCal
-     * instance reentrant. 
-     * 
-     * Maybe at some point in time this should be converted to an ordinary
-     * configuration object, from the collection of public static variables.
-     */
-    public static void resetParams()
-    {
-        Debug = false;
-        SpecOption = false;
-        MyspecOption = false;
-        Spec2Option = false;
-        Myspec2Option = false;
-        SpecFile = "";
-        WriteASTFlag = false;
-        LabelFlag = false;
-        ReportLabelsFlag = false;
-        LabelRoot = "Lbl_";
-        FairnessOption = "";
-        FairAlgorithm = false;
-        CheckTermination = false;
-        Nocfg = false;
-        NoDoneDisjunct = false;
-        optionsInFile = false;
-        versionOption = null;
-        inputVersionNumber = VersionToNumber(PcalParams.version);
-        PcalTLAGen.wrapColumn = 78;
-        PcalTLAGen.ssWrapColumn = 45;
-        tlaPcalMapping = null ;
-        
-    }
-    
-    
-  /*************************************************************************
-  * Parameters related to non-file Options                                 *
-  *************************************************************************/
-    public static boolean Debug = false ;
-    /***********************************************************************
-    * True if the -debug option is chosen.                                 *
-    ***********************************************************************/
-
-    public static boolean SpecOption = false ;
-    /***********************************************************************
-    * True if the -spec option is chosen.                                  *
-    ***********************************************************************/
-
-    public static boolean MyspecOption = false ;
-    /***********************************************************************
-    * True if the -myspec option is chosen.                                *
-    ***********************************************************************/
-
-    public static boolean Spec2Option = false ;
-    /***********************************************************************
-    * True if the -spec2 option is chosen.                                 *
-    ***********************************************************************/
-
-    public static boolean Myspec2Option = false ;
-    /***********************************************************************
-    * True if the -myspec2 option is chosen.                               *
-    ***********************************************************************/
-
-    public static String SpecFile = "" ;
-    /***********************************************************************
-    * The file name if the -spec option is chosen.                         *
-    ***********************************************************************/
-
-    public static boolean WriteASTFlag = false ;
-    /***********************************************************************
-    * True if the -writeAST option is chosen.                              *
-    ***********************************************************************/
-
-    public static boolean LabelFlag = false ;
-      /*********************************************************************
-      * True iff the -label option is chosen.                              *
-      *********************************************************************/
-
-    public static boolean ReportLabelsFlag = false ;
-      /*********************************************************************
-      * True iff the -reportLabels option is chosen.                       *
-      *********************************************************************/
-
-    public static String LabelRoot = "Lbl_" ;
-      /*********************************************************************
-      * The root of translator-generated labels, set to its non-default    *
-      * value by the -labelRoot option.                                    *
-      *********************************************************************/
-      
-  /*************************************************************************
-  * Parameters for Spec and .cfg file options.                             *
-  *************************************************************************/
-    public static String FairnessOption = "" ;
-      /*********************************************************************
-      * Should be "", "wf", "sf", "wfNext", or "sfNext".                   *
-      *********************************************************************/
-      
-    public static boolean CheckTermination = false ;
-      /*********************************************************************
-      * True iff there is a -termination option.                           *
-      *********************************************************************/
-      
-    public static boolean Nocfg = false ;
-      /*********************************************************************
-      * True iff there is a -nocfg option.                                 *
-      *********************************************************************/
-      
-    public static boolean NoDoneDisjunct = false ;
-     /*********************************************************************
-     * True iff there is a -noDoneDisjunct option.                        *
-     *********************************************************************/
-    
-    /**********************************************************************
-     * The following parameter is set true if --fair algorithm is used.   *
-     *********************************************************************/
-    public static boolean FairAlgorithm = false ; 
-    
-  /*************************************************************************
-  * Parameters related to language definition.                             *
-  *************************************************************************/
-    public static TLAExpr DefaultVarInit()
-      /*********************************************************************
-      * Returns the default initial value of a variable declared with no   *
-      * value specified, which is herein defined to be "{}".               *
-      *                                                                    *
-      * Default initial value changed to "defaultInitValue"                *
-      * by LL on 22 Aug 2007                                               *
-      *********************************************************************/
-      { Vector line = new Vector() ;
-//        line.addElement(new TLAToken("{", 0, 0)) ;
-//        line.addElement(new TLAToken("}", 0, 0)) ;
-        line.addElement(new TLAToken("defaultInitValue", 0, 0));
-        Vector vec = new Vector() ;
-        vec.addElement(line) ;
-        TLAExpr exp = new TLAExpr(vec) ;
-        exp.normalize() ;
-        return exp ;
-      }
-
-    /***********************************************************************
-    * The strings identifying the beginning of the algorithm in the .tla   *
-    * file.                                                                *
-    * The strings "--fair" and "algorithm" added by LL on 6 July 2011.     *
-    ***********************************************************************/
-    public static final String BeginAlg = "--algorithm" ;
-    public static final String BeginFairAlg = "--fair" ;
-    public static final String BeginFairAlg2 = "algorithm" ;
-     
-    
-
-    /***********************************************************************
-    * The strings marking the beginning and end of the translated          *
-    * algorithm in a .tla input file.  The translation is put immediately  *
-    * after any line containing                                            *
-    *                                                                      *
-    *    BeginXLation1 [one or more spaces] BeginXlation2                  *
-    *                                                                      *
-    * It is followed by a line containing                                  *
-    *                                                                      *
-    *    EndXLation1 [one or more spaces] EndXlation2.                     *
-    ***********************************************************************/
-    public static final String BeginXlation1 = "BEGIN" ;
-    public static final String BeginXlation2 = "TRANSLATION" ;
-
-    public static final String EndXlation1 = "END" ;
-    public static final String EndXlation2 = "TRANSLATION" ;
-
-  /*************************************************************************
-  * The string identifying the end of the automatically generated part of  *
-  * the .cfg file and the beginning of the user-added part.                *
-  *************************************************************************/
-  public static String CfgFileDelimiter = 
-            "\\* Add statements after this line." ;
-
-/************ Stuff for .pcal file ***************************************/  
-  
-//  /***********************************************************************
-//  * The string identifying the beginning of the algorithm in the .pcal   *
-//  * file.                                                                *
-//  ***********************************************************************/
-//  public static final String PcalBeginAlg = "algorithm";
-//   
-//  /**
-//   * The <row, col> (in Java coordinates) of the beginning of the
-//   * "algorithm" token in the .pcal file.
-//   */
-//  public static IntPair endOfPreamble = null;
-//
-//  /**
-//   * The <row, col> (in Java coordinates) of the beginning of the TLA+ 
-//   * "code" that follows the algorithm in the input (.pcal) and output 
-//   * (.tla) files.  For outputSuffixLoc, the column always equals 0.
-//   */
-//  public static IntPair inputSuffixLoc = null;
-//  public static IntPair outputSuffixLoc = null;
-//  
-  /*************************************************************************
-  * File parameters.                                                       *
-  *************************************************************************/
-  public static String TLAInputFile = "" ;
-    /***********************************************************************
-    * The name of the input file, with no extension.  It is set to equal   *
-    * the argument with which the program is called, minus the extension.  *
-    * With the introduction of pcal files, the name no longer makes sense. *
-    ***********************************************************************/
-
-  /**
-   * Pcal-File Parameters
-   *    The following parameters were introduced when .pcal files were 
-   *    (briefly) added.  However, most of them still seem to be used.
-   */
-
-  public static boolean optionsInFile = false ;
-     // Set true when an options statement has been found in the
-     // module.  It is a kludgy way to pass an argument to 
-     // trans.parseAndProcessStringArguments; things are done this
-     // way because of the way the code evolved, and no intelligent
-     // design has stepped in to fix it.
-  public static String versionOption = null;
-  public static int inputVersionNumber = VersionToNumber(PcalParams.version);
-     // The input file's version number * 1000
-//  public static boolean readOnly = false; 
-     // True iff this is a .pcal input file and the .tla file should 
-     // not be writable.  This is obsolete and is not used.
-
-  /**
-   * The following parameter is used to hold the TLAtoPCalMapping object
-   * that is computed by the translation.  Some of that object's fields are
-   * used in creating the actual mapping.  It's easier to have the methods
-   * that need to use those fields access them via this parameter than to do
-   * the more politically correct thing and pass the fields or the object
-   * as a parameter in the method calls.
-   */
-  public static TLAtoPCalMapping  tlaPcalMapping ;
-  
-  /**
-   * If str is a version number like 3.17, then this returns 1000 times
-   * its numeric value--e.g., 3170.  Otherwise, it returns -1.
-   * 
-   */
-  public static int VersionToNumber(String str) {
-    boolean error = false ;
-    int i = 0;
-    int result = 0;
-    int digitsAfterDecimal = 0;
-    boolean afterDecimal = false;
-    while ((!error) && i < str.length()) {
-        char c = str.charAt(i);
-        if (('0' <= c) && (c <= '9')) {
-            result = 10 * result  +  (c - '0');
-            if (afterDecimal) {
-                digitsAfterDecimal++;
-                if (digitsAfterDecimal > 3) {
-                    error = true;
-                }
-            }
-        } else if (c == '.') {
-                afterDecimal = true;
-        } else {
-            error = true;
-        }
-        i++;
-    }
-    if (error) {
-        return -1;
-    }
-    for (int j = 0; j < 3 - digitsAfterDecimal; j++) {
-        result = 10 * result;
-    }
-    return result;
-  }
-  /**
-   * Processes the version argument ver.  It sets versionNumber
-   * and returns true if it is a legal version number; otherwise,
-   * it reports the error with PcalDebug.reportError and returns false.
-   */
-  static boolean ProcessVersion(String ver) {
-      int vnum = VersionToNumber(ver);
-      if (vnum < 0) {
-          PcalDebug.reportError("Illegal version " + ver + " specified."); 
-          return false;
-      }
-      if (vnum > VersionToNumber(PcalParams.version)) {
-          PcalDebug.reportError("Specified version " + ver + 
-                  " later than current version " + PcalParams.version);
-          return false;
-      }
-      inputVersionNumber = vnum;
-      return true ;
-  }
- }  
-
-/* last modified on Thu 23 Aug 2007 at 10:40:25 PST by lamport */
+/***************************************************************************
+* CLASS PcalParams                                                         *
+*                                                                          *
+* The fields of this class consist of all the parameters used by           *
+* pcal.trans.  Some of them are set by program options.                    *
+***************************************************************************/
+package pcal ;
+import java.util.Vector;
+
+/**
+ * Holds parameters for the PCal Translator
+ * @author Keith Marzullo
+ * @author Simon Zambrovski
+ * @version $Id$
+ */
+public final class PcalParams
+{ 
+    /**
+     * Parameters to be updated on each new release.
+     */
+    public static final String modDate = "31 December 2020";
+	// Can't increment 1.9 to 1.10 because VersionToNumber(str) calculates a lower
+	// numerical value for 1.10 than it does for 1.9. This breaks the FairSeq?Test
+    // tests. Until we switch from 1.xx to 2.0, increment versionWeight along with
+    // version.
+    public static final int versionWeight = 1902;
+    public static final String version = "1.11";
+    /**
+     * SZ Mar 9, 2009:
+     * Added re-initialization method. Since PcalParams class
+     * is used instead of PcalParams instance, it is required to
+     * take care of parameter initialization and de-initialization
+     * by explicit method. This is required in order to make PCal
+     * instance reentrant. 
+     * 
+     * Maybe at some point in time this should be converted to an ordinary
+     * configuration object, from the collection of public static variables.
+     */
+    public static void resetParams()
+    {
+        Debug = false;
+        SpecOption = false;
+        MyspecOption = false;
+        Spec2Option = false;
+        Myspec2Option = false;
+        SpecFile = "";
+        WriteASTFlag = false;
+        LabelFlag = false;
+        ReportLabelsFlag = false;
+        LabelRoot = "Lbl_";
+        FairnessOption = "";
+        FairAlgorithm = false;
+        CheckTermination = false;
+        Nocfg = false;
+        NoDoneDisjunct = false;
+        optionsInFile = false;
+        versionOption = null;
+        inputVersionNumber = PcalParams.versionWeight;
+        PcalTLAGen.wrapColumn = 78;
+        PcalTLAGen.ssWrapColumn = 45;
+        tlaPcalMapping = null ;
+        
+    }
+    
+    
+  /*************************************************************************
+  * Parameters related to non-file Options                                 *
+  *************************************************************************/
+    public static boolean Debug = false ;
+    /***********************************************************************
+    * True if the -debug option is chosen.                                 *
+    ***********************************************************************/
+
+    public static boolean SpecOption = false ;
+    /***********************************************************************
+    * True if the -spec option is chosen.                                  *
+    ***********************************************************************/
+
+    public static boolean MyspecOption = false ;
+    /***********************************************************************
+    * True if the -myspec option is chosen.                                *
+    ***********************************************************************/
+
+    public static boolean Spec2Option = false ;
+    /***********************************************************************
+    * True if the -spec2 option is chosen.                                 *
+    ***********************************************************************/
+
+    public static boolean Myspec2Option = false ;
+    /***********************************************************************
+    * True if the -myspec2 option is chosen.                               *
+    ***********************************************************************/
+
+    public static String SpecFile = "" ;
+    /***********************************************************************
+    * The file name if the -spec option is chosen.                         *
+    ***********************************************************************/
+
+    public static boolean tlcTranslation() {
+    	return PcalParams.SpecOption || PcalParams.MyspecOption || PcalParams.Spec2Option
+                || PcalParams.Myspec2Option;
+    }
+    
+    public static boolean WriteASTFlag = false ;
+    /***********************************************************************
+    * True if the -writeAST option is chosen.                              *
+    ***********************************************************************/
+
+    public static boolean LabelFlag = false ;
+      /*********************************************************************
+      * True iff the -label option is chosen.                              *
+      *********************************************************************/
+
+    public static boolean ReportLabelsFlag = false ;
+      /*********************************************************************
+      * True iff the -reportLabels option is chosen.                       *
+      *********************************************************************/
+
+    public static String LabelRoot = "Lbl_" ;
+      /*********************************************************************
+      * The root of translator-generated labels, set to its non-default    *
+      * value by the -labelRoot option.                                    *
+      *********************************************************************/
+      
+  /*************************************************************************
+  * Parameters for Spec and .cfg file options.                             *
+  *************************************************************************/
+    public static String FairnessOption = "" ;
+      /*********************************************************************
+      * Should be "", "wf", "sf", "wfNext", or "sfNext".                   *
+      *********************************************************************/
+      
+    public static boolean CheckTermination = false ;
+      /*********************************************************************
+      * True iff there is a -termination option.                           *
+      *********************************************************************/
+      
+    public static boolean Nocfg = false ;
+      /*********************************************************************
+      * True iff there is a -nocfg option.                                 *
+      *********************************************************************/
+      
+    public static boolean NoDoneDisjunct = false ;
+     /*********************************************************************
+     * True iff there is a -noDoneDisjunct option.                        *
+     *********************************************************************/
+    
+    /**********************************************************************
+     * The following parameter is set true if --fair algorithm is used.   *
+     *********************************************************************/
+    public static boolean FairAlgorithm = false ; 
+    
+  /*************************************************************************
+  * Parameters related to language definition.                             *
+  *************************************************************************/
+    public static TLAExpr DefaultVarInit()
+      /*********************************************************************
+      * Returns the default initial value of a variable declared with no   *
+      * value specified, which is herein defined to be "{}".               *
+      *                                                                    *
+      * Default initial value changed to "defaultInitValue"                *
+      * by LL on 22 Aug 2007                                               *
+      *********************************************************************/
+      { Vector<TLAToken> line = new Vector<TLAToken>() ;
+//        line.addElement(new TLAToken("{", 0, 0)) ;
+//        line.addElement(new TLAToken("}", 0, 0)) ;
+        line.addElement(new TLAToken("defaultInitValue", 0, 0));
+        Vector<Vector<TLAToken>> vec = new Vector<Vector<TLAToken>>() ;
+        vec.addElement(line) ;
+        TLAExpr exp = new TLAExpr(vec) ;
+        exp.normalize() ;
+        return exp ;
+      }
+
+    /***********************************************************************
+    * The strings identifying the beginning of the algorithm in the .tla   *
+    * file.                                                                *
+    * The strings "--fair" and "algorithm" added by LL on 6 July 2011.     *
+    ***********************************************************************/
+    public static final String BeginAlg = "--algorithm" ;
+    public static final String BeginFairAlg = "--fair" ;
+    public static final String BeginFairAlg2 = "algorithm" ;
+     
+    
+
+    /***********************************************************************
+    * The strings marking the beginning and end of the translated          *
+    * algorithm in a .tla input file.  The translation is put immediately  *
+    * after any line containing                                            *
+    *                                                                      *
+    *    BeginXLation1 [one space] BeginXlation2 [one space] BeginXlation3 *
+    *                                                                      *
+    * It is followed by a line containing                                  *
+    *                                                                      *
+    *    EndXLation1 [one space] EndXlation2 [one space] EndXlation3       *
+    ***********************************************************************/
+    public static final String BeginXlation1 = "BEGIN" ;
+    public static final String BeginXlation2 = "TRANSLATION" ;
+
+    public static final String EndXlation1 = "END" ;
+    public static final String EndXlation2 = "TRANSLATION" ;
+    
+  /*************************************************************************
+  * The string identifying the end of the automatically generated part of  *
+  * the .cfg file and the beginning of the user-added part.                *
+  *************************************************************************/
+  public static String CfgFileDelimiter = 
+            "\\* Add statements after this line." ;
+
+/************ Stuff for .pcal file ***************************************/  
+  
+//  /***********************************************************************
+//  * The string identifying the beginning of the algorithm in the .pcal   *
+//  * file.                                                                *
+//  ***********************************************************************/
+//  public static final String PcalBeginAlg = "algorithm";
+//   
+//  /**
+//   * The <row, col> (in Java coordinates) of the beginning of the
+//   * "algorithm" token in the .pcal file.
+//   */
+//  public static IntPair endOfPreamble = null;
+//
+//  /**
+//   * The <row, col> (in Java coordinates) of the beginning of the TLA+ 
+//   * "code" that follows the algorithm in the input (.pcal) and output 
+//   * (.tla) files.  For outputSuffixLoc, the column always equals 0.
+//   */
+//  public static IntPair inputSuffixLoc = null;
+//  public static IntPair outputSuffixLoc = null;
+//  
+  /*************************************************************************
+  * File parameters.                                                       *
+  *************************************************************************/
+  public static String TLAInputFile = "" ;
+    /***********************************************************************
+    * The name of the input file, with no extension.  It is set to equal   *
+    * the argument with which the program is called, minus the extension.  *
+    * With the introduction of pcal files, the name no longer makes sense. *
+    ***********************************************************************/
+
+  /**
+   * Pcal-File Parameters
+   *    The following parameters were introduced when .pcal files were 
+   *    (briefly) added.  However, most of them still seem to be used.
+   */
+
+  public static boolean optionsInFile = false ;
+     // Set true when an options statement has been found in the
+     // module.  It is a kludgy way to pass an argument to 
+     // trans.parseAndProcessStringArguments; things are done this
+     // way because of the way the code evolved, and no intelligent
+     // design has stepped in to fix it.
+  public static String versionOption = null;
+  public static int inputVersionNumber = PcalParams.versionWeight;
+     // The input file's version number * 1000
+//  public static boolean readOnly = false; 
+     // True iff this is a .pcal input file and the .tla file should 
+     // not be writable.  This is obsolete and is not used.
+
+  /**
+   * The following parameter is used to hold the TLAtoPCalMapping object
+   * that is computed by the translation.  Some of that object's fields are
+   * used in creating the actual mapping.  It's easier to have the methods
+   * that need to use those fields access them via this parameter than to do
+   * the more politically correct thing and pass the fields or the object
+   * as a parameter in the method calls.
+   */
+  public static TLAtoPCalMapping  tlaPcalMapping ;
+  
+  /**
+   * If str is a version number like 3.17, then this returns 1000 times
+   * its numeric value--e.g., 3170.  Otherwise, it returns -1.
+   * 
+   */
+  public static int VersionToNumber(String str) {
+    boolean error = false ;
+    int i = 0;
+    int result = 0;
+    int digitsAfterDecimal = 0;
+    boolean afterDecimal = false;
+    while ((!error) && i < str.length()) {
+        char c = str.charAt(i);
+        if (('0' <= c) && (c <= '9')) {
+            result = 10 * result  +  (c - '0');
+            if (afterDecimal) {
+                digitsAfterDecimal++;
+                if (digitsAfterDecimal > 3) {
+                    error = true;
+                }
+            }
+        } else if (c == '.') {
+                afterDecimal = true;
+        } else {
+            error = true;
+        }
+        i++;
+    }
+    if (error) {
+        return -1;
+    }
+    for (int j = 0; j < 3 - digitsAfterDecimal; j++) {
+        result = 10 * result;
+    }
+    return result;
+  }
+  /**
+   * Processes the version argument ver.  It sets versionNumber
+   * and returns true if it is a legal version number; otherwise,
+   * it reports the error with PcalDebug.reportError and returns false.
+   */
+  static boolean ProcessVersion(String ver) {
+      int vnum = VersionToNumber(ver);
+      if (vnum < 0) {
+          PcalDebug.reportError("Illegal version " + ver + " specified."); 
+          return false;
+      }
+      if (vnum > PcalParams.versionWeight) {
+          PcalDebug.reportError("Specified version " + ver + 
+                  " later than current version " + PcalParams.version);
+          return false;
+      }
+      inputVersionNumber = vnum;
+      return true ;
+  }
+ }  
+
+/* last modified on Thu 23 Aug 2007 at 10:40:25 PST by lamport */
diff --git a/tlatools/src/pcal/PcalResourceFileReader.java b/tlatools/src/pcal/PcalResourceFileReader.java
index db66736b0597694a3d92a9fc38bb2ee1ff6dd82e..ebfb9f307dfb34bbbd9a9b12421be1c537824fe1 100644
--- a/tlatools/src/pcal/PcalResourceFileReader.java
+++ b/tlatools/src/pcal/PcalResourceFileReader.java
@@ -47,12 +47,12 @@ public class PcalResourceFileReader
        inputReader = new BufferedReader(new InputStreamReader(input)) ;
       };
 
-  public static Vector ResourceFileToStringVector(String fileName) throws PcalResourceFileReaderException
+  public static Vector<String> ResourceFileToStringVector(String fileName) throws PcalResourceFileReaderException
     /***********************************************************************
     * Reads file fileName into a StringVector, a vector in which each      *
     * element is a line of the file.                                       *
     ***********************************************************************/
-    { Vector inputVec = new Vector(100) ;
+    { Vector<String> inputVec = new Vector<String>(100) ;
        PcalResourceFileReader wordFileReader
                      = new PcalResourceFileReader(fileName);
 
diff --git a/tlatools/src/pcal/PcalSymTab.java b/tlatools/src/pcal/PcalSymTab.java
index f3ce69d66be24b78589a9e17d842060c6c9dc65e..e31fffa8d5b55978eb776c14c66e3a2b49ce99d9 100644
--- a/tlatools/src/pcal/PcalSymTab.java
+++ b/tlatools/src/pcal/PcalSymTab.java
@@ -484,6 +484,8 @@ public class PcalSymTab {
             ExtractReturn((AST.Return) ast, context, cType);
         else if (ast.getClass().equals(AST.CallReturnObj.getClass()))
             ExtractCallReturn((AST.CallReturn) ast, context, cType);
+        else if (ast.getClass().equals(AST.CallGotoObj.getClass()))
+            ExtractCallGoto((AST.CallGoto) ast, context, cType);
         else if (ast.getClass().equals(AST.GotoObj.getClass()))
             ExtractGoto((AST.Goto) ast, context, cType);
 
@@ -669,6 +671,11 @@ public class PcalSymTab {
                                    String cType) {
     }
 
+    private void ExtractCallGoto(AST.CallGoto ast,
+                                 String context,
+                                 String cType) {
+    }
+
     private void ExtractGoto(AST.Goto ast, String context, String cType) {
     }
 
diff --git a/tlatools/src/pcal/PcalTLAGen.java b/tlatools/src/pcal/PcalTLAGen.java
index 5d040ea3caf6c40945d040497ef12ff4f1dea73d..45c84dd6b9000818e2fc1d1677776b0b20a042e2 100644
--- a/tlatools/src/pcal/PcalTLAGen.java
+++ b/tlatools/src/pcal/PcalTLAGen.java
@@ -1,4327 +1,4372 @@
-package pcal;
-
-import java.util.Vector;
-
-import pcal.AST.VarDecl;
-import pcal.exception.PcalTLAGenException;
-import pcal.exception.TLAExprException;
-import tla2tex.Debug;
-
-/****************************************************************************
- * Given an exploded and disambiguated AST, generate the equivalent TLA+.
- * <br>
- * {@link PcalTLAGen#generate(AST, PcalSymTab)} returns a vector of Strings, one entry per line of generated TLA+.
- * 
- * @version $Id$ 
- * @author Leslie Lamport (modified on Thu  6 March 2008 at 10:16:22 PST)
- *                        (minor change on 9 December 2009)            
- * @author keith (modified on Mon  3 Oct 2005 at 21:43:09 UT)                  
- *                                                                          
- ****************************************************************************/
-public class PcalTLAGen
-{
-    // Constants that control formatting
-    public final static boolean boxUnderCASE = true; /* else [] at end of line  */
-
-    // The following two variables made non-final on 9 Dec 2009 so they can
-    // be set by options.  They are initialized in PcalParams.resetParams().
-    public static int wrapColumn ; 
-       /* If the line width will be greater than this, then try to wrap */
-    public static int ssWrapColumn ; 
-       // I think that this is used as follows: 
-       //    when translating an assignment statement (or multiassignment?)
-       //    to  var' = [var EXCEPT ...],  it begins the ... on a new line
-       //    iff  the ... would begin in a column > ssWrapColumn.
-       // For the time being, it is set to wrapColumn - 33.  We may want
-       // to do something cleverer or else make it a user option.
-
-    // Private class variables
-    /** The tlacode field accumulates the translation as it is constructed.  It 
-     * should be a vector of separate lines.  Keiths original implementation put
-     * multiple lines in a single element of tlacode in:
-     *   
-     *   GenVarsAndDefs
-     *   GenVarDecl
-     */
-    private Vector tlacode = new Vector(); /* of lines */
-    
-    /**
-     * The tlacodeNextLine field accumulates characters for the next
-     * line of tlacode.  It is always a string.  It is assumed that
-     * when it equals "", then a new line can be started without
-     * adding the current string in tlacodeNextLine as a new line.
-     */
-    private String tlacodeNextLine = "" ;
-    
-    /**
-     * mappingVector is a local pointer to {@link TLAtoPCalMapping#mappingVector},
-     * which is used to accumulate the TLA+ to PlusCal mapping.  It approximately
-     * reflects the TLA+ that has been inserted in the {@link PcalTLAGen#tlacode}  
-     * vector.  It is set in the {@link TLAtoPCalMapping#generate} method.
-     */
-    private Vector mappingVector;
-    
-    /**
-     * mappingVectorNextLine contains the sequence of MappingObject objects
-     * that correspond to the strings added to tlacodeNextLine.
-     */
-    private Vector mappingVectorNextLine = new Vector() ;
-    
-    /**
-     * The self field is set to "self" by GenProcess when called for a single process
-     * (rather than a process set) and by GenProcedure for a multiprocess algorithm. 
-     * It is set to the process id by GenProcess when called for a single process.
-     * selfIsSelf is set to true when self is set to "self", and to false when self is
-     * set to a process id.  The self field never seems to be reset to null.
-     */
-    private TLAExpr self = null; // changed by LL on 22 jan 2011 from: private String self = null; /* for current process */
-    private boolean selfIsSelf = false; 
-    
-    private Vector vars = new Vector(); /* list of all disambiguated vars */
-    private Vector pcV = new Vector(); /* sublist of vars of variables representing 
-                                          procedure parameters and procedure variables */
-    private Vector psV = new Vector(); /* sublist of vars local to a process set */
-    private PcalSymTab st = null; /* symbol table */
-    private boolean mp = false; /* true if multiprocess, else unip */
-    private Vector nextStep = new Vector(); /* unparam actions */ // For multiprocess alg, these are the individual (=) processes
-    private Vector nextStepSelf = new Vector(); /* param actions */ // These are process sets (\in processes) and procedures
-    // Following added to keep track of the length of the "lbl... == /\ "
-    // that precedes all the statements in the definition of a label's action
-    // because Keith screwed up and handled the assignment to the pc different 
-    // from that of all other variables, forgetting that the subscript exp
-    // in pc[exp] := ... can be multi-line.
-    private int kludgeToFixPCHandlingBug ;
-    /**
-     * The public method: generate TLA+ as a vector of strings. 
-     * @param ast the AST of the PCal
-     * @param symtab the symbol table
-     * @return the vector of strings with TLA+ code
-     * @throws PcalTLAGenException on any unrecoverable methods
-     */
-    
-    /*
-     * The  string currentProcName is the name of the current process (for a multiprocess algorithm)
-     * or "Next" for a uniprocess algorithm.  When ParseAlgorithm.omitPC is true, this is used
-     * instead of the label when generating the process's or the entire algorithm's next-state
-     * action.  Thus, with a single label lbl, this generates
-     *    Next == Xlation
-     * instead of
-     *    lbl == Xlation
-     *    Next == lbl
-     */
-    private String currentProcName ;
-    
-    /**
-     * Generates the translation.
-     * 
-     * @param ast  The AST produced by parsing and exploding.
-     * @param symtab The symbol table.
-     * @param report  A vector of strings, containing the reports of renaming.
-     * @return A vector of strings.
-     * @throws PcalTLAGenException
-     */
-    public Vector generate(AST ast, PcalSymTab symtab, Vector report) throws PcalTLAGenException
-    {
-        TLAtoPCalMapping map = PcalParams.tlaPcalMapping;
-        mappingVector = new Vector(50);
-        /*
-         * Add the reports of renaming to the output.
-         */
-        for (int i = 0; i < report.size(); i++) {
-            addOneLineOfTLA((String) report.elementAt(i));
-        }
-        
-        st = symtab;
-        GenSym(ast, "");
-        
-        /*
-         * We put at the beginning and end of mappingVector a LeftParen 
-         * and RightParen with location (0, 0), so that location will
-         * be found by the TLA+ to PCal translation algorithm if the
-         * user selects the entire algorithm, in which case it will
-         * return the null region to GotoPCalSourceHandler.execute.
-         */
-        PCalLocation ZeroLocation = new PCalLocation(0, 0);
-        ((Vector) mappingVector.elementAt(0)).
-             add(0, new MappingObject.LeftParen(ZeroLocation));
-        Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1);
-        lastLine.add(lastLine.size(), new MappingObject.RightParen(ZeroLocation));
-
-        /*
-         * For testing, throw a null pointer exception if the parentheses are not
-         * properly matching in mappingVector.
-         */
-        //int[] depths = new int[10000];
-        
-        int parenDepth = 0;
-        for (int i = 0; i < mappingVector.size(); i++) {
-            Vector line = (Vector) mappingVector.elementAt(i);
-            for (int j = 0; j < line.size(); j++) {
-                MappingObject obj = (MappingObject) line.elementAt(j);
-                if (obj.getType() == MappingObject.LEFT_PAREN) {
-                    parenDepth++;
-                }
-                else if (obj.getType() == MappingObject.RIGHT_PAREN) {
-                    parenDepth--;
-                    if (parenDepth < 0) {
-                        throw new NullPointerException("paren depth < 0");
-                    }
-                }
-            }
-        // depths[i] = parenDepth;
-        }
-        if (parenDepth != 0) {
-            throw new NullPointerException("Unmatched Left Paren");
-        }
-        /*   ------------------ end testing --------------------------*/
-        Vector nonredundantMappingVector = 
-            TLAtoPCalMapping.RemoveRedundantParens(mappingVector);
-        map.makeMapping(nonredundantMappingVector);
-        
-//System.out.println("Original mappingvector:");
-//MappingObject.printMappingVector(mappingVector);    
-//System.out.println("RemoveRedundantParens(mappingVector)");
-//MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(mappingVector));
-//System.out.println("Should be original mappingvector:");
-//MappingObject.printMappingVector(mappingVector); 
-// Debugging
-//for (int i = 0; i < tlacode.size(); i++) {
-//System.out.println("\nline " + i);
-//System.out.println((String) tlacode.elementAt(i)) ;
-//}
-//MappingObject.printMappingVector(mappingVector);
-
-        return tlacode;
-    }
-
-    /****************************************************************/
-    /* Returns whether the string is present in a vector of string. */
-    /****************************************************************/
-    private static boolean InVector(String var, Vector v)
-    {
-        for (int i = 0; i < v.size(); i++)
-            if (var.equals((String) v.elementAt(i)))
-                return true;
-        return false;
-    }
-
-    /******************************************************/
-    /* True if var is in the list of procedure variables. */
-    /******************************************************/
-    private boolean IsProcedureVar(String var)
-    {
-        return InVector(var, pcV);
-    }
-
-    /****************************************************/
-    /* True if var is in the list of process variables. */
-    /****************************************************/
-    private boolean IsProcessSetVar(String var)
-    {
-        return InVector(var, psV);
-    }
-
-    /**********************************************/
-    /* Returns a string of length n of all spaces */
-    /**********************************************/
-    private static String NSpaces(int n)
-    {
-        StringBuffer sb = new StringBuffer();
-        AddSpaces(sb, n);
-        return sb.toString();
-    }
-
-    /*********************************************/
-    /* Appends n spaces to the string buffer sb. */
-    /*********************************************/
-    private static void AddSpaces(StringBuffer sb, int num)
-    {
-        for (int i = 0; i < num; i++)
-            sb.append(" ");
-    }
-
-    /****************************************/
-    /* True if expr is an empty expression. */
-    /****************************************/
-    private static boolean EmptyExpr(TLAExpr expr)
-    {
-        if (expr == null)
-            return true;
-        if (expr.tokens == null || expr.tokens.size() == 0)
-            return true;
-        return false;
-    }
-
-    /*****************************************************************/
-    /* Top level routines. Context is "", "procedure", or "process". */
-    /**
-     ****************************************************************/
-    private void GenSym(AST ast, String context) throws PcalTLAGenException
-    {
-        if (ast.getClass().equals(AST.UniprocessObj.getClass()))
-            GenUniprocess((AST.Uniprocess) ast, context);
-        else if (ast.getClass().equals(AST.MultiprocessObj.getClass()))
-            GenMultiprocess((AST.Multiprocess) ast, context);
-        else if (ast.getClass().equals(AST.ProcedureObj.getClass()))
-            GenProcedure((AST.Procedure) ast, context);
-        else if (ast.getClass().equals(AST.ProcessObj.getClass()))
-            GenProcess((AST.Process) ast, context);
-        else if (ast.getClass().equals(AST.LabeledStmtObj.getClass()))
-            GenLabeledStmt((AST.LabeledStmt) ast, context);
-    }
-
-    private void GenUniprocess(AST.Uniprocess ast, String context) throws PcalTLAGenException
-    {
-        mp = false;
-        currentProcName = "Next";
-        GenVarsAndDefs(ast.decls, ast.prcds, null, ast.defs);
-        GenInit(ast.decls, ast.prcds, null);
-        for (int i = 0; i < ast.prcds.size(); i++)
-            GenProcedure((AST.Procedure) ast.prcds.elementAt(i), "");
-        for (int i = 0; i < ast.body.size(); i++)
-        {
-            AST.LabeledStmt ls = (AST.LabeledStmt) ast.body.elementAt(i);
-            /* Add this step to the disjunct of steps */
-            nextStep.addElement(ls.label);
-            GenLabeledStmt(ls, "");
-        }
-        GenNext();
-        GenSpec();
-        GenTermination();
-    }
-
-    private void GenMultiprocess(AST.Multiprocess ast, String context) throws PcalTLAGenException
-    {
-        mp = true;
-        GenVarsAndDefs(ast.decls, ast.prcds, ast.procs, ast.defs);
-        GenProcSet();
-        GenInit(ast.decls, ast.prcds, ast.procs);
-        for (int i = 0; i < ast.prcds.size(); i++)
-            GenProcedure((AST.Procedure) ast.prcds.elementAt(i), "");
-        for (int i = 0; i < ast.procs.size(); i++)
-            GenProcess((AST.Process) ast.procs.elementAt(i), "");
-        GenNext();
-        GenSpec();
-        GenTermination();
-    }
-
-    private void GenProcedure(AST.Procedure ast, String context) throws PcalTLAGenException {
-        /*
-         * First, generate the body's actions.  Must set self and selfIsSelf (?) for
-         * use by GenLabeledStmt.
-         */
-        if (mp) {
-      
-            self = selfAsExpr(); // subscript for variables is "self"
-            selfIsSelf = true;
-//            /* Add this step to the disjunct of steps with (self) */
-            nextStepSelf.addElement(ast.name + "(self)");
-        } else
-        {
-            /* Add this step to the disjunct of steps */
-            nextStep.addElement(ast.name);
-        }
-        for (int i = 0; i < ast.body.size(); i++) {
-            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
-            GenLabeledStmt(stmt, "procedure");
-        }
-        
-        /*
-         * Next add the definition of the procedure--e.g.,
-         * 
-         *    procedureName(self) == label_1(self) \/ ... \/ label_k(self)
-         *    
-         * We put Left/RightParens for the entire procedure around the entire 
-         * definition, and Left/RightParens around each disjunction for
-         * the labeled statement.
-         */
-        addLeftParen(ast.getOrigin());
-        String argument = (mp) ? "(self)" : "";
-        StringBuffer buf = new StringBuffer(ast.name + argument + " == ");
-        addOneTokenToTLA(buf.toString());
-        String indentSpaces = NSpaces(buf.length() + 2);        
-        for (int i = 0; i < ast.body.size(); i++) {
-            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
-            String disjunct = stmt.label + argument;
-            if (   i != 0 
-                && tlacodeNextLine.length() +  7 /* the 7 was obtained empirically */
-                    + disjunct.length() > wrapColumn) {
-                endCurrentLineOfTLA();
-            }
-            if (i != 0) {
-               addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); 
-            }
-            addLeftParen(stmt.getOrigin());
-            addOneTokenToTLA(disjunct);
-            addRightParen(stmt.getOrigin());
-        }
-        addRightParen(ast.getOrigin());
-        addOneLineOfTLA("");
-        
-// The previous version was very convoluted just to avoid having to go through the
-// list of labeled statements twice.  It seemed easier to just reimplement from
-// scratch.
-//        /* ns and nsV accumulate the disjunct of the steps of the procedure, where
-//         * ns contains the contents of the current line and nsV is the vector of
-//         * already accumulated lines.
-//         * 
-//         */
-//        StringBuffer ns = new StringBuffer();
-//        Vector nsV = new Vector();
-//        
-//       int nsC = ast.name.length() + ((mp) ? "(self)".length() : 0) + " == ".length();
-//        if (mp)
-//        {
-//            self = selfAsExpr(); // subscript for variables is "self"
-//            selfIsSelf = true;
-//            /* Add this step to the disjunct of steps with (self) */
-//            nextStepSelf.addElement(ast.name + "(self)");
-//        } else
-//        {
-//            /* Add this step to the disjunct of steps */
-//            nextStep.addElement(ast.name);
-//        }
-//        for (int i = 0; i < ast.body.size(); i++)
-//        {
-//            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
-//            if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((mp) ? "(self)".length() : 0)) > wrapColumn
-//                    - nsC - " \\/ ".length())
-//            {
-//                nsV.addElement(ns.toString());
-//                ns = new StringBuffer();
-//            }
-//            if (ns.length() > 0)
-//                ns.append(" \\/ ");
-//            ns.append(stmt.label);
-//            if (mp)
-//                ns.append("(self)");
-//            GenLabeledStmt(stmt, "procedure");
-//        }
-//        nsV.addElement(ns.toString());
-//        // Generate definition of procedure steps
-//        ns = new StringBuffer();
-//        ns.append(ast.name);
-//        if (mp)
-//            ns.append("(self)");
-//        ns.append(" == ");
-//        ns.append((String) nsV.elementAt(0));
-//        tlacode.addElement(ns.toString());
-//        for (int i = 1; i < nsV.size(); i++)
-//        {
-//            ns = new StringBuffer(NSpaces(nsC + 2));
-//            ns.append(" \\/ ");
-//            ns.append((String) nsV.elementAt(i));
-//            tlacode.addElement(ns.toString());
-//        }
-//        tlacode.addElement("");
-    }
-
-    private void GenProcess(AST.Process ast, String context) throws PcalTLAGenException
-    {
-        currentProcName = ast.name; 
-        
-        /*
-         * Generate the body's actions.   Must set self and selfIsSelf (?) for
-         * use by GenLabeledStmt.
-         */
-        boolean isSet = true;
-        /************************************************************/
-        /* Decide if it is a process set or not. If so, set self to */
-        /* the string "self"; otherwise set self to the process id. */
-        /************************************************************/
-        if (ast.isEq)
-        {
-            self = ast.id ;
-            selfIsSelf = false;
-            isSet = false;
-        } else {
-            self = selfAsExpr();
-            selfIsSelf = true;
-        }
-
-        if (isSet)
-        {
-            nextStepSelf.addElement(ast.name + "(self)");
-        } else
-            nextStep.addElement(ast.name);
-        
-        for (int i = 0; i < ast.body.size(); i++) {
-            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
-            GenLabeledStmt(stmt, "process");
-        }
-        
-        /*
-         * Next add the definition of the process--e.g.,
-         * 
-         *    processName(self) == label_1(self) \/ ... \/ label_k(self)
-         *    
-         * We put Left/RightParens for the entire procedure around the entire 
-         * definition, and Left/RightParens around each disjunction for
-         * the labeled statement.  
-         * 
-         * However, we don't add this definition if we are omitting the pc,
-         * because we have already defined the process name to equal the 
-         * only label action.
-         */
-
-      if (! ParseAlgorithm.omitPC) {
-        addLeftParen(ast.getOrigin());
-        String argument = (isSet) ? "(self)" : "";
-        StringBuffer buf = new StringBuffer(ast.name + argument + " == ");
-        addOneTokenToTLA(buf.toString());
-        String indentSpaces = NSpaces(buf.length() + 2);        
-        for (int i = 0; i < ast.body.size(); i++) {
-            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
-            String disjunct = stmt.label + argument;
-            if (   i != 0 
-                && tlacodeNextLine.length() + 7 /* the 7 was obtained empirically */
-                  + disjunct.length() > wrapColumn) {
-                endCurrentLineOfTLA();
-            }
-            if (i != 0) {
-               addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); 
-            }
-            addLeftParen(stmt.getOrigin());
-            addOneTokenToTLA(disjunct);
-            addRightParen(stmt.getOrigin());
-        }
-        addRightParen(ast.getOrigin());
-        addOneLineOfTLA("");
-      }
-
-        
-// As with GenProcedure, the original implementation was quite convoluted, so
-// it was rewritten.  The code above was copied with modifications from
-// the rewritten GenProcedure code.
-//        /* ns accumulates the disjunt of the steps of the process */
-//        StringBuffer ns = new StringBuffer();
-//        Vector nsV = new Vector();
-//        boolean isSet = true;
-//        /************************************************************/
-//        /* Decide if it is a process set or not. If so, set self to */
-//        /* the string "self"; otherwise set self to the process id. */
-//        /************************************************************/
-//        if (ast.isEq)
-//        {
-//            self = ast.id ;
-//            selfIsSelf = false;
-//            isSet = false;
-//        } else {
-//            self = selfAsExpr();
-//            selfIsSelf = true;
-//        }
-//        
-//        int nsC = ast.name.length() + ((isSet) ? "(self)".length() : 0) + " == ".length();
-//        if (isSet)
-//        {
-//            nextStepSelf.addElement(ast.name + "(self)");
-//        } else
-//            nextStep.addElement(ast.name);
-//        for (int i = 0; i < ast.body.size(); i++)
-//        {
-//            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
-//            if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((isSet) ? "(self)".length() : 0)) > wrapColumn
-//                    - nsC - " \\/ ".length())
-//            {
-//                nsV.addElement(ns.toString());
-//                ns = new StringBuffer();
-//            }
-//            if (ns.length() > 0)
-//                ns.append(" \\/ ");
-//            ns.append(stmt.label);
-//            if (isSet)
-//                ns.append("(self)");
-//            GenLabeledStmt(stmt, "process");
-//        }
-//        nsV.addElement(ns.toString());
-//        // Generate definition of process steps
-//        // This apparently defines the process name
-//        // to equal the disjunction of all the individual
-//        // label-named actions.  If we are omitting
-//        // the pc, we have already defined the process name
-//        // to equal the only label action, so we skip
-//        // this.
-//        if (! ParseAlgorithm.omitPC) {
-//          ns = new StringBuffer();
-//          ns.append(ast.name);
-//          if (isSet)
-//              ns.append("(self)");
-//          ns.append(" == ");
-//          ns.append((String) nsV.elementAt(0));
-//          tlacode.addElement(ns.toString());
-//          for (int i = 1; i < nsV.size(); i++)
-//          {
-//              ns = new StringBuffer(NSpaces(nsC + 2));
-//              ns.append(" \\/ ");
-//              ns.append((String) nsV.elementAt(i));
-//              tlacode.addElement(ns.toString());
-//          }
-//        }
-//        tlacode.addElement("");
-    }
-
-    /*****************************************************/
-    /* Generates an action with name equal to the label. */
-    /**
-     ****************************************************/
-    private void GenLabeledStmt(AST.LabeledStmt ast, String context) throws PcalTLAGenException
-    {
-        // Set actionName to the name of the action being defined.
-        // This is the label, except when we are omitting the PC,
-        // in which case it is "Next" for a uniprocess algorithm
-        // and the process name for a multiprocess algorithm.
-        String actionName = ast.label;
-        if (ParseAlgorithm.omitPC) {
-            actionName = currentProcName;
-        }
-        StringBuffer sb = new StringBuffer(actionName);
-        /* c is used to determine which vars are in UNCHANGED. */
-        Changed c = new Changed(vars);
-        if (mp && (context.equals("procedure") || selfIsSelf)) { // self.equals("self")))
-            sb.append("(self)");
-        }   
-        sb.append(" == ");
-        int col = sb.length();
-        kludgeToFixPCHandlingBug = col;
-        // There's a problem here.  If ast.stmts.size() = 1, then we don't preface
-        // the statement's translation with "/\".  However, that means that if we 
-        // then add an UNCHANGED conjunct, we wind up with  
-        //   A
-        //    /\ UNCHANGED ...
-        // where A is the statement's translation.  This looks bad and could wind up
-        // putting the UNCHANGED inside prefix operator--e.g.,
-        //   x' = CHOOSE t : ...
-        //     /\ UNCHANGED ...
-        // This is seems to be a problem only when omitPC = true, since otherwise the
-        // testing and setting of pc ensures that there is more than one element in ast.stmts.
-        // What we do is always add the "/\ ", but remove it afterwards if there's only
-        // a single statement in ast.stmts and there is no UNCHANGED clause.
-        // The code for doing this is based on the observation that the contents of
-        // StringBuffer sb begin the next line added to tlacode.
-        //
-        /* if (ast.stmts.size() > 1) {  */
-            sb.append("/\\ ");
-            kludgeToFixPCHandlingBug = kludgeToFixPCHandlingBug + 3;
-        /* } */
-            
-        // We set defStartLine to the index of the next line added to tlacode and
-        // colAfterAnd to the column position in that line immediately following
-        // the added "/\ ".  This gives us the information needed to remove the
-        // "/\ " later from tlacode.
-        int defStartLine = tlacode.size();
-        int colAfterAnd = sb.length();
-        
-        /*
-         * Note: it would make sense for this method to insert sb into the tlacode
-         * output, but it seems safer to maintain the current structure in which
-         * each GenX for each statement type X does that.
-         */
-        
-        /*
-         * We set macroBeginLeft to the macroOriginBegin field of the first statement
-         * in ast.stmts with a non-null origin, and macroEndRight to the macroOriginEnd
-         * field of the last statement in ast.stmts with a non-null origin.
-         */
-        PCalLocation macroBeginLeft = null;
-        PCalLocation macroEndRight = null;
-        boolean nonNullNotFound = true;
-        for (int i = 0 ; i < ast.stmts.size(); i++) {
-           AST stmt = (AST) ast.stmts.elementAt(i);
-           if (stmt.getOrigin() != null) {
-               if (nonNullNotFound) {
-                   nonNullNotFound = false;
-                   macroBeginLeft = stmt.macroOriginBegin;
-               }
-               macroEndRight = stmt.macroOriginEnd;
-           }
-        }
-        
-        /*
-         * addLeftParenV used instead of addLeftParen because if the first statement
-         * that came from PlusCal code arose from a macro call, then we want the 
-         * location of the macro call rather than that of the macro's code.
-         */
-        addLeftParenV(ast, macroBeginLeft);
-        for (int i = 0; i < ast.stmts.size(); i++)
-        {
-            GenStmt((AST) ast.stmts.elementAt(i), c, context, sb.toString(), sb.length());
-            sb = new StringBuffer(NSpaces(col));
-            sb.append("/\\ ");
-        }
-        
-        /*
-         * Since the UNCHANGED conjunct just consists of TLATokens, with no
-         * SourceTokens, we can just use the old code, simply replacing each
-         * tlacode.addElement call with a call of addOneLineOfTLA--except that
-         * the last one is replaced with a call of addOneTokenToTLA so we can
-         * put the RightParen object in mappingVector.
-         */
-        Vector unc = c.Unchanged(wrapColumn - col - "/\\ UNCHANGED << ".length());
-        if (c.NumUnchanged() > 1)
-        {
-            sb = new StringBuffer(NSpaces(col));
-            sb.append("/\\ UNCHANGED << ");
-            int here = sb.length();
-            sb.append((String) unc.elementAt(0));
-            for (int i = 1; i < unc.size(); i++)
-            {
-//                tlacode.addElement(sb.toString());
-                addOneLineOfTLA(sb.toString());
-                sb = new StringBuffer(NSpaces(here));
-                sb.append((String) unc.elementAt(i));
-            }
-            sb.append(" >>");
-//            tlacode.addElement(sb.toString());
-            addOneTokenToTLA(sb.toString());
-        } else if (c.NumUnchanged() == 1) {
-        	// Change made by LL on 16 Mar 2011 so that, if there is a single
-        	// unchanged variable v, it produces v' = v if v is a short variable,
-        	// otherwise it produces UNCHANGED v
-        	if (c.Unchanged().length() > 5) {
-//              tlacode.addElement(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged());
-        	    addOneTokenToTLA(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged());
-        	} else {
-//        		tlacode.addElement(NSpaces(col) + "/\\ " + c.Unchanged() + "' = "
-//        				+ c.Unchanged());
-        		 addOneTokenToTLA(NSpaces(col) + "/\\ " + c.Unchanged() + "' = "
-                         + c.Unchanged());
-        	}
-        } else {
-           // No unchanged.  If there was only one conjunction, remove it.
-           // To do that, we must remove the "/\ " and then remove three spaces
-           // from all other lines that were added.  We must also modify the
-           // TLAToken objects appropriately in the corresponding lines of
-           // mappingVector
-           if (ast.stmts.size() == 1) {
-               for (int i = defStartLine; i < tlacode.size(); i++) {
-                  String line = (String) tlacode.elementAt(i);
-                  if (i == defStartLine) {
-                     // remove the "/\ " added                      
-                     tlacode.setElementAt(line.substring(0, colAfterAnd-3) +
-                                          line.substring(colAfterAnd, line.length()) , i);
-                     shiftMappingVectorTokensLeft(i, colAfterAnd, 3);
-                    
-                  } else {
-                     // Remove three blanks from any following lines.  We test the length 
-                     // of the line just in case one or more short (hopefully blank) lines 
-                     // have been added.
-                      if (line.length() > 3) {
-                          tlacode.setElementAt(line.substring(3, line.length()) , i);
-                          shiftMappingVectorTokensLeft(i, colAfterAnd, 3);
-                      }
-                  }
-               }
-           }
-        }
-        /*
-         * We call addRightParenV rather than addRightParen because it the last statement 
-         * that came from the PCal code arose from the expansion of a macro call, then we 
-         * want the RightParen's location to be the end of the call, not the end of the
-         * macro's code.
-         */
-        addRightParenV(ast, macroEndRight);
-        addOneLineOfTLA("");
-//        tlacode.addElement("");
-    }
-    
-    /**
-     * Adjusts the objects in line lineNum of mappingVector so all column
-     * numbers starting with startCol are decreased by `shift'.  If any Begin/EndTLAToken
-     * pairs are changed to have a non-positive width, a bug is reported.
-     * 
-     * Note: It is assumed that there is no aliasing of MappingTokens in mappingVector.
-     * That is, other than in transient local variables, the only pointer to a
-     * MappingToken in mappingVector is the single one in its line of mappingVector.
-     * I can't see how any aliasing of MappingTokens could arise.
-     * 
-     * This method is called only by GenLabeledStmts.
-     * 
-     * @param lineNum
-     * @param startCol
-     * @param shift
-     */
-    private void shiftMappingVectorTokensLeft(int lineNum, int startCol, int shift) {
-        boolean lastWasBeginTLAToken = false;
-        int lastBeginTLATokCol = -777; // to keep the compiler happy.
-        Vector line = (Vector) mappingVector.elementAt(lineNum);
-        for (int i = 0; i < line.size(); i++) {
-            MappingObject obj = (MappingObject) line.elementAt(i);
-            if (obj.getType() == MappingObject.BEGIN_TLATOKEN) {
-               MappingObject.BeginTLAToken tobj = (MappingObject.BeginTLAToken) obj;
-               int col = tobj.getColumn();
-               if (col >= startCol) {
-                   tobj.setColumn(col - shift);
-               }
-               lastWasBeginTLAToken = true;
-               lastBeginTLATokCol = tobj.getColumn();
-            }
-            else {
-                if (obj.getType() == MappingObject.END_TLATOKEN) {
-                    MappingObject.EndTLAToken tobj = (MappingObject.EndTLAToken) obj;
-                    int col = tobj.getColumn();
-                    if (col >= startCol) {
-                        tobj.setColumn(col - shift);
-                    }
-                    if (lastWasBeginTLAToken && tobj.getColumn() <= lastBeginTLATokCol) {
-                        PcalDebug.ReportBug(
-                         "PcalTLAGen.shiftMappingVectorTokensLeft created a null TLA Token");
-                    }
-                }
-                else if (obj.getType() == MappingObject.SOURCE_TOKEN) {
-                    MappingObject.SourceToken tobj = (MappingObject.SourceToken) obj;
-                    int col = tobj.getBeginColumn();
-                    if (col >= startCol) {
-                        tobj.setBeginColumn(col - shift);
-                    }
-                    col = tobj.getEndColumn();
-                    if (col >= startCol) {
-                        tobj.setEndColumn(col - shift);
-                    }
-                    
-                lastWasBeginTLAToken = false;
-            }
-            }
-        }
-        
-        
-    }
-
-    /***************************************************************************
-    * LL Comment added 27 Jan 2006:                                            *
-    *                                                                          *
-    * There is a basic flaw in the way GenStmt works.  It now generates the    *
-    * output on the fly.  This means that                                      *
-    *                                                                          *
-    * - There is no way to avoid the prefix /\ on a one-element conjunct       *
-    *   because GenStmt has no way of knowing if there's another conjunct      *
-    *   coming.                                                                *
-    *                                                                          *
-    * - The handling of the UNCHANGEDs of the THEN and ELSE clauses of         *
-    *   an IF is a kludge, because the UNCHANGED of the THEN clause is         *
-    *   output before it can be known.                                         *
-    *                                                                          *
-    * The correct way of doing things is to define GenStmt so it returns a     *
-    * sequence (vector) of string vectors, each string vector being a          *
-    * conjunction expression (without a leading /\ or any leading spaces) and  *
-    * the new Changed object (which it can do as it now does by modifying its  *
-    * Changed object argument).  It would also be useful to define a           *
-    * GenStmtSeq that simply calls GenStmt iteratively on a sequence of        *
-    * simple statements.  The method that calls GenStmtSeq would then add the  *
-    * Unchanged conjunct and call a method that returns a sequence of          *
-    * conjuncts and a prefix into a string vector containing the prefix and    *
-    * the necessary /\s.                                                       *
-    ***************************************************************************/
-
-    /*****************************************************************/
-    /* General entry for generating the TLA+ for a simple statement. */
-    /* Prefix is the prefix of the first line. Col is where to start */
-    /* subsequent lines (I think we could replace it with the length */
-    /* of prefix).                                                   */
-    /*                                                               */
-    /* And what on earth are `c' and `context'? LL                   */
-    /**
-     ****************************************************************/
-    private void GenStmt(AST ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
-    {
-        if (ast.getClass().equals(AST.AssignObj.getClass()))
-            GenAssign((AST.Assign) ast, c, context, prefix, col);
-        else if (ast.getClass().equals(AST.IfObj.getClass()))
-            GenIf((AST.If) ast, c, context, prefix, col);
-        // Either case added by LL on 27 Jan 2006
-        else if (ast.getClass().equals(AST.EitherObj.getClass()))
-            GenEither((AST.Either) ast, c, context, prefix, col);
-        else if (ast.getClass().equals(AST.WithObj.getClass()))
-            GenWith((AST.With) ast, c, context, prefix, col);
-        else if (ast.getClass().equals(AST.WhenObj.getClass()))
-            GenWhen((AST.When) ast, c, context, prefix, col);
-        else if (ast.getClass().equals(AST.PrintSObj.getClass()))
-            GenPrintS((AST.PrintS) ast, c, context, prefix, col);
-        else if (ast.getClass().equals(AST.AssertObj.getClass()))
-            GenAssert((AST.Assert) ast, c, context, prefix, col);
-        else if (ast.getClass().equals(AST.SkipObj.getClass()))
-            GenSkip((AST.Skip) ast, c, context, prefix, col);
-        else
-            PcalDebug.ReportBug("Unexpected AST type " + ast.toString());
-    }
-
-    /*****************************************************************/
-    /* Generates a sequence of single assigns. Since all of them are */
-    /* executed "at the same time", we accumulate the changes in a   */
-    /* separate Changed cThis, and use c to determine which vars in  */
-    /* the right hand side are primed.                               */
-    /**
-     * ***************************************************************/
-    
-    /**
-     * @param ast
-     * @param c
-     * @param context
-     * @param prefix
-     * @param col
-     * @throws PcalTLAGenException
-     */
-    private void GenAssign(AST.Assign ast, Changed c, String context, String prefix, int col)
-            throws PcalTLAGenException
-    {
-        Changed cThis = new Changed(c);
-        StringBuffer sb = new StringBuffer();
-//        Vector vlines = new Vector();
-        /*
-         * Sort the vector ast.ass so that assignments to the same variable
-         * follow one another.
-         */
-        ast.ass = SortSass(ast.ass);
-        
-        addOneTokenToTLA(prefix);
-        addLeftParen(ast.getOrigin());
-        
-        int i = 0;
-        int numAssigns = 0;
-        /*
-         * hasMultipleVars set true iff the assignment assigns values to
-         * more than one variable, and hence the statement's translation
-         * has multiple conjuncts.
-         */
-        boolean hasMultipleVars = false;
-        while (i < ast.ass.size())
-        {
-            int iFirst = i;
-            AST.SingleAssign sF = (AST.SingleAssign) ast.ass.elementAt(i);
-            int iLast = i;
-            boolean hasAssignmentWithNoSubscript = false;
-            boolean lastAssignmentHasNoSubscript = EmptyExpr(sF.lhs.sub);
-            AST.SingleAssign sL = (AST.SingleAssign) ast.ass.elementAt(i);
-            while (iLast < ast.ass.size() && sF.lhs.var.equals(sL.lhs.var))
-            {
-                if (lastAssignmentHasNoSubscript) {
-                    hasAssignmentWithNoSubscript = true;
-                }
-                iLast = iLast + 1;
-                if (iLast < ast.ass.size()) {
-                    sL = (AST.SingleAssign) ast.ass.elementAt(iLast);
-                    if (EmptyExpr(sL.lhs.sub)) {
-                        lastAssignmentHasNoSubscript = true;
-                    }
-                }
-            }
-            
-            /*
-             * If there are assignments to multiple variables, then this sets
-             * hasMultiplevars true on the first execution of the outer while loop. 
-             */
-            if (iLast != ast.ass.size()) {
-                hasMultipleVars = true;
-            }
-            
-            iLast = iLast - 1;
-            // All statements from iFirst to iLast are to the same variable
-            
-            /*
-             * Throws an error if there are multiple assignments to the variable
-             * in different statements, or if there are multiple assignments to
-             * the variable in this statement and at least one of them has no 
-             * subscript.
-             */
-            if (cThis.Set(sF.lhs.var) > 1 || 
-                 (iLast - iFirst > 0 && hasAssignmentWithNoSubscript)) {
-                /***********************************************************
-                * The following was changed by LL on 3 Mar 06 to use       *
-                * AST.location to properly report the location of an       *
-                * error in a line created by expanding a macro.            *
-                * However, it doesn't work very well otherwise.  This      *
-                * should be fixed.                                         *
-                ***********************************************************/
-                throw new PcalTLAGenException("Multiple assignment to " + sF.lhs.var, ast /* sF */);
-            }
-            numAssigns = numAssigns + 1;
-            Vector lines = new Vector(); // For collecting generated lines
-
-            if (hasMultipleVars) {
-                sb.append("/\\ ");
-            }
-            if (iFirst == iLast)
-            {
-                /*
-                 * This is a single assignment to the variable.
-                 */
-                AST.SingleAssign sass = sF;
-
-                addLeftParen(sass.getOrigin());
-                TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c);
-                TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c);
-                if (mp
-                        && (sass.lhs.var.equals("pc") || IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var) || sass.lhs.var
-                                .equals("stack")))
-                {
-                    /* Generate single assignment to variable with self subscript */
-                    sb.append(sass.lhs.var);
-                    sb.append("' = [");
-                    int wrapCol = sb.length() + 2;
-                    sb.append(sass.lhs.var);
-                    sb.append(" EXCEPT ");
-                    
-                    Vector selfAsSV = self.toStringVector();
-                    
-                    // The test for selfAsSV size added by LL on 22 Jan 2011
-                    // because wrapping screws up the kludgeToFixPCHandlingBug
-                    // hack.
-                    if ( (sb.length() + prefix.length() > ssWrapColumn)
-                         && (selfAsSV.size() == 0))
-                    {
-//                        lines.addElement(sb.toString());
-                        addOneLineOfTLA(sb.toString());
-                        sb = new StringBuffer(NSpaces(wrapCol));
-                    }
-                    sb.append("![");
-                    addOneTokenToTLA(sb.toString());
-                    addLeftParen(self.getOrigin());
-                    addExprToTLA(self);
-                    addRightParen(self.getOrigin());
-                    
-//                    
-//                    // following code was modified by LL on 22 Jan 2011 as part of
-//                    // fixing bug 11_01_13, which required modifications to handle
-//                    // the case where self is a multi-line formula, which can happen
-//                    // for a "process (P = exp)" when exp is multi-line.
-//                    int here = sb.length();
-//                    for (int idx = 0; idx < selfAsSV.size(); idx++) {
-//                        if (idx > 0) {
-//                            sb.append("\n");
-//                            sb.append(NSpaces(here + kludgeToFixPCHandlingBug));
-//                        }
-//                        sb.append((String) selfAsSV.elementAt(idx)) ;
-//                    }
-////                    sb.append(self);
-//                    sb.append("]");
-//                    here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 1;
-                      addOneTokenToTLA("]");
-                    Vector sv = sub.toStringVector();
-                    /*****************************************************
-                    * Was                                                *
-                    *                                                    *
-                    *       Vector sv = sass.lhs.sub.toStringVector();   *
-                    *                                                    *
-                    * Changed by Chi Ho on 3 Aug 2006 to add             *
-                    * subscript.  See bug_06_08_03.                      *
-                    *****************************************************/
-                    if (sv.size() > 0)
-                    {
-                        addLeftParen(sub.getOrigin());
-                        addExprToTLA(sub);
-                        addRightParen(sub.getOrigin());
-                        
-//                        sb.append((String) sv.elementAt(0));
-//                        for (int v = 1; v < sv.size(); v++)
-//                        {
-//                            lines.addElement(sb.toString());
-//                            sb = new StringBuffer(NSpaces(here));
-//                            sb.append((String) sv.elementAt(v));
-//                        }
-                    }
-                    addOneTokenToTLA(" = ");;
-                    addLeftParen(rhs.getOrigin());
-                    addExprToTLA(rhs);
-                    addRightParen(rhs.getOrigin());
-                    addOneTokenToTLA("]");
-//                    sb.append(" = ");
-//                    here = sb.length();
-//                    sv = rhs.toStringVector();
-//                    sb.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        lines.addElement(sb.toString());
-//                        sb = new StringBuffer(NSpaces(here));
-//                        sb.append((String) sv.elementAt(v));
-//                    }
-//                    sb.append("]");
-//                    lines.addElement(sb.toString());
-                    sb = new StringBuffer();
-                } else if (!EmptyExpr(sass.lhs.sub))
-                {
-                    /* 
-                     * Generate single assignment to variable with no [self] subscript
-                     * but with an explicit subscript. 
-                     */
-                    sb.append(sass.lhs.var);
-                    sb.append("' = [");
-                    sb.append(sass.lhs.var);
-                    sb.append(" EXCEPT !");
-                    addOneTokenToTLA(sb.toString());
-                    addLeftParen(sub.getOrigin());
-                    addExprToTLA(sub);
-                    addRightParen(sub.getOrigin());
-                    addOneTokenToTLA(" = ");
-                    addLeftParen(rhs.getOrigin());
-                    addExprToTLA(rhs);
-                    addRightParen(rhs.getOrigin());
-                    addOneTokenToTLA("]");
-//                    
-//                    int here = sb.length();
-//                    Vector sv = sub.toStringVector();
-//                    sb.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        lines.addElement(sb.toString());
-//                        sb = new StringBuffer(NSpaces(here));
-//                        sb.append((String) sv.elementAt(v));
-//                    }
-//                    sb.append(" = ");
-//                    here = sb.length();
-//                    sv = rhs.toStringVector();
-//                    sb.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        lines.addElement(sb.toString());
-//                        sb = new StringBuffer(NSpaces(here));
-//                        sb.append((String) sv.elementAt(v));
-//                    }
-//                    sb.append("]");
-//                    lines.addElement(sb.toString());
-                    sb = new StringBuffer();
-                } else
-                {
-                    /* 
-                     * Generate assignment to a variable with no subscript at all.
-                     */
-                    sb.append(sass.lhs.var);
-                    sb.append("' = ");
-//                    int here = sb.length();
-                    boolean needsParens = NeedsParentheses(rhs.toStringVector());
-                    if (needsParens) {
-                        sb.append("(");
-                    }
-                    addOneTokenToTLA(sb.toString());
-                    addLeftParen(rhs.getOrigin());
-                    addExprToTLA(rhs);
-                    addRightParen(rhs.getOrigin());
-                    if (needsParens) {
-                        addOneTokenToTLA(")");
-                    }
-                    
-//                    Vector sv = Parenthesize(rhs.toStringVector());
-//                    /*******************************************************
-//                    * Call of Parenthesize added by LL on 27 Feb 2008.     *
-//                    * See bug_08-02-18.                                    *
-//                    *******************************************************/
-//                    for (int v = 0; v < sv.size(); v++)
-//                    {
-//                        sb.append((String) sv.elementAt(v));
-//                        lines.addElement(sb.toString());
-//                        sb = new StringBuffer(NSpaces(here));
-//                    }
-// Debugging
-//Vector exprVec = rhs.toMappingVector();
-//MappingObject.shiftMappingVector(exprVec, here);
-//MappingObject.printMappingVector(exprVec);
-//System.out.println("origin: " + rhs.getOrigin().toString() );
-                    
-                    sb = new StringBuffer();
-                }
-                addRightParen(sass.getOrigin());
-            } else
-            {
-                /*
-                 * Multiple assignments to the same variable, which must therefore
-                 * each have a user-specified subscript.
-                 */
-                AST.SingleAssign sass = sF;
-                sb.append(sass.lhs.var);
-                sb.append("' = [");
-                sb.append(sass.lhs.var);
-                sb.append(" EXCEPT ");
-                int cc = sb.length();
-                /*
-                 * If this  the first variable, so i = 0, then sb does not contain
-                 * any spaces to compensate for the missing prefix; otherwise it
-                 * does.
-                 */
-                if (i == 0) {
-                    cc = cc + prefix.length();
-                }
-                boolean subscript = (mp && (IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var)));
-                while (iFirst <= iLast)
-                {
-                    sass = (AST.SingleAssign) ast.ass.elementAt(iFirst);
-                    TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c);
-                    TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c);
-                    addLeftParen(sass.getOrigin());
-                    sb.append("!");
-                    
-                    // On 21 Jan 2011, LL moved the following statement to below the if
-                    // to correct part 3 of bug_11_01_13.
-                    //
-//                    int here = sb.length();
-                    if (subscript) {
-                        /*
-                         * This variable has a "self" subscript in addition to its user-specified
-                         * subscript.
-                         */
-                        sb.append("[");
-                        addOneTokenToTLA(sb.toString());
-                        TLAExpr self = Self(context);
-                        addLeftParen(self.getOrigin());
-                        addExprToTLA(self);
-                        addOneTokenToTLA("]");
-                        
-//                        Vector selfAsSV = Self(context).toStringVector();
-//                        for (int idx = 0; idx < selfAsSV.size(); idx++) {
-//                          String start = " ";
-//                          if (idx == 0) {
-//                              sb.append("[");
-//                          } else {
-//                              sb.append("\n");
-//                              sb.append(NSpaces(here + 1));
-//                          }
-//                          sb.append((String) selfAsSV.elementAt(idx));
-//                        }
-//                        sb.append("]");
-//                        here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 2;
-                    }
-                    else {
-                        addOneTokenToTLA(sb.toString());
-                    }
-                    
-                    addLeftParen(sub.getOrigin());
-                    addExprToTLA(sub);
-                    addRightParen(sub.getOrigin());
-                    addOneTokenToTLA(" = ");
-                    addLeftParen(rhs.getOrigin());
-                    addExprToTLA(rhs);
-                    addRightParen(rhs.getOrigin());
-                    addRightParen(sass.getOrigin());
-                    addOneTokenToTLA((iFirst == iLast) ? "]" : ",");
-//                    Vector sv = sub.toStringVector();
-//                    if (sv.size() > 0)
-//                    {
-//                        sb.append((String) sv.elementAt(0));
-//                        for (int v = 1; v < sv.size(); v++)
-//                        {
-//                            lines.addElement(sb.toString());
-//                            sb = new StringBuffer(NSpaces(here));
-//                            sb.append((String) sv.elementAt(v));
-//                        }
-//                    }
-//                    sb.append(" = ");
-//                    here = sb.length();
-//                    sv = rhs.toStringVector();
-//                    sb.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        lines.addElement(sb.toString());
-//                        sb = new StringBuffer(NSpaces(here));
-//                        sb.append((String) sv.elementAt(v));
-//                    }
-//                    sb.append(((iFirst == iLast) ? "]" : ","));
-//                    lines.addElement(sb.toString());
-                    sb = new StringBuffer();
-                    if (iFirst < iLast) {
-                        endCurrentLineOfTLA();
-                        AddSpaces(sb, cc);
-                    }
-                    iFirst = iFirst + 1;
-                }
-            }
-
-//            vlines.addElement(lines);
-            i = iLast + 1;
-            if (i <  ast.ass.size()) {
-                endCurrentLineOfTLA();
-                AddSpaces(sb, prefix.length());
-            }
-        }
-        addRightParen(ast.getOrigin());
-        endCurrentLineOfTLA();
-        
-        c.Merge(cThis);
-        // Append generated code to tlacode
-//        sb = new StringBuffer(prefix);
-//        col = sb.length();
-//        if (numAssigns > 1)
-//            sb.append("/\\ ");
-//        if (vlines.size() > 0)
-//        {
-//            for (int v1 = 0; v1 < vlines.size(); v1++)
-//            {
-//                Vector vl = (Vector) vlines.elementAt(v1);
-//                for (int v2 = 0; v2 < vl.size(); v2++)
-//                {
-//                    sb.append((String) vl.elementAt(v2));
-//                    tlacode.addElement(sb.toString());
-//                    sb = new StringBuffer(NSpaces(col));
-//                    if ((v1 > 0 || numAssigns > 1) && (v2 != vl.size() - 1))
-//                        sb.append("   ");
-//                }
-//                sb.append("/\\ ");
-//            }
-//        }
-    }
-
-    /**
-     * Generate TLA+ for if statement. Each branch has its own 
-     * UNCHANGED that lists variables that were changed in the 
-     * other branch. This is a little difficult since we don't 
-     * know the UNCHANGED for the Then branch until the code   
-     * for the Else branch is generated. So, we fix the        
-     * line in the Then branch after the Else branch is done.  
-     * The corresponding mappingVector line also has to be changed,
-     * but that's not a problem because the UNCHANGED is a TLA+
-     * token with no corresponding source.
-     */
-    private void GenIf(AST.If ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
-    {
-        Changed cThen = new Changed(c);
-        Changed cElse = new Changed(c);
-        int lineUncThen;
-        StringBuffer sb = new StringBuffer(prefix);
-        TLAExpr test = null;
-        test = AddSubscriptsToExpr(ast.test, SubExpr(Self(context)), c);
-//        Vector sv = test.toStringVector();
-        sb.append("IF ");
-        int here = sb.length();
-        /*************************************************************
-        * LL removed a bogus "- 1" here on 31 Jan 2006.              *
-        *************************************************************/
-        addLeftParen(ast.getOrigin());
-        addOneTokenToTLA(sb.toString());
-        addExprToTLA(test);
-        endCurrentLineOfTLA();
-        
-//        sb.append((String) sv.elementAt(0));
-//        for (int v = 1; v < sv.size(); v++)
-//        {
-//            tlacode.addElement(sb.toString());
-//            sb = new StringBuffer(NSpaces(here));
-//            sb.append((String) sv.elementAt(v));
-//        }
-//        tlacode.addElement(sb.toString());
-        
-        sb = new StringBuffer(NSpaces(here));
-        sb.append("THEN ");
-        here = sb.length();
-
-        sb.append("/\\ ");
-        for (int i = 0; i < ast.Then.size(); i++)
-        {
-            GenStmt((AST) ast.Then.elementAt(i), cThen, context, sb.toString(),
-            /*******************************************************************
-            * LL added the +3 on 18 Feb 2006 to take account of the            *
-            * indentation of the "IF ".                                        *
-            *******************************************************************/
-            here + 3);
-            sb = new StringBuffer(NSpaces(here) + "/\\ ");
-        }
-        lineUncThen = tlacode.size();
-//        tlacode.addElement(sb.toString());
-        addOneLineOfTLA(sb.toString());
-        sb = new StringBuffer(NSpaces(here - "THEN ".length()) + "ELSE ");
-        here = sb.length();
-        if (ast.Else.size() == 0)
-        {
-            sb.append("/\\ TRUE");
-//            tlacode.addElement(sb.toString());
-            addOneLineOfTLA(sb.toString());
-            
-            sb = new StringBuffer(NSpaces(here) + "/\\ ");
-        } else
-        {
-            sb.append("/\\ ");
-            for (int i = 0; i < ast.Else.size(); i++)
-            {
-                GenStmt((AST) ast.Else.elementAt(i), cElse, context, sb.toString(),
-                /*******************************************************************
-                * LL added the +3 on 18 Feb 2006 to take account of the            *
-                * indentation of the "IF ".                                        *
-                *******************************************************************/
-                here + 3);
-                sb = new StringBuffer(NSpaces(here) + "/\\ ");
-            }
-        }
-        // Generate UNCHANGED for the ELSE branch
-        if (cElse.NumUnchanged(cThen) > 1)
-        {
-            Vector uncElse = cElse.Unchanged(cThen, wrapColumn - sb.length() - "UNCHANGED << ".length());
-            sb.append("UNCHANGED << ");
-            int cc = sb.length();
-            sb.append((String) uncElse.elementAt(0));
-            for (int i = 1; i < uncElse.size(); i++)
-            {
-//                tlacode.addElement(sb.toString());
-                addOneLineOfTLA(sb.toString());
-                sb = new StringBuffer(NSpaces(cc));
-                sb.append((String) uncElse.elementAt(i));
-            }
-            sb.append(" >>");
-//            tlacode.addElement(sb.toString());
-            addOneTokenToTLA(sb.toString());
-            addRightParen(ast.getOrigin());
-            endCurrentLineOfTLA();
-        } else if (cElse.NumUnchanged(cThen) == 1)
-        {   // Change made by LL on 16 Mar 2011 so that, if there is a single
-        	// unchanged variable v, it produces v' = v if v is a short variable,
-        	// otherwise it produces UNCHANGED v
-        	//
-            // sb.append("UNCHANGED " + cElse.Unchanged(cThen));
-        	String uc = cElse.Unchanged(cThen);
-        	if (uc.length() > 5) {
-        		sb.append("UNCHANGED " + uc);
-        	} else {
-        		sb.append(uc + "' = " + uc);
-        	}
-//            tlacode.addElement(sb.toString());
-            addOneTokenToTLA(sb.toString());
-            addRightParen(ast.getOrigin());
-            endCurrentLineOfTLA();
-        } else 
-        {
-            /*
-             * There is no UNCHANGED after the ELSE, so we have to put
-             * the RightParen for the whole if statement at the end of
-             * the last line already generated
-             */
-            ((Vector) mappingVector.elementAt(mappingVector.size()-1))
-               .add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
-        }
-
-        // Patch up the UNCHANGED for the THEN branch
-        sb = new StringBuffer((String) tlacode.elementAt(lineUncThen));
-        tlacode.removeElementAt(lineUncThen);
-        mappingVector.removeElementAt(lineUncThen);
-        if (cThen.NumUnchanged(cElse) > 1)
-        {
-            Vector uncThen = cThen.Unchanged(cElse, wrapColumn - sb.length() - "UNCHANGED << ".length());
-            sb.append("UNCHANGED << ");
-            int cc = sb.length();
-            sb.append((String) uncThen.elementAt(0));
-            for (int i = 1; i < uncThen.size(); i++)
-            {
-                tlacode.insertElementAt(sb.toString(), lineUncThen);
-
-                 //set the mappingVector entry
-                mappingVector.insertElementAt(stringToTLATokens(sb.toString()), lineUncThen);
-
-                lineUncThen = lineUncThen + 1;
-                sb = new StringBuffer(NSpaces(cc));
-                sb.append((String) uncThen.elementAt(i));
-            }
-            sb.append(" >>");
-            tlacode.insertElementAt(sb.toString(), lineUncThen);
-            Vector vec =  stringToTLATokens(sb.toString());
-            
-            // The following is bogus because the RightParen for the
-            // entire procedure is inserted after (or instead of) the
-            // ELSE's UNCHANGED
-            // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
-            
-            mappingVector.insertElementAt(vec, lineUncThen);
-                    
-        } else if (cThen.NumUnchanged(cElse) == 1)
-        {   // Change made by LL on 16 Mar 2011 so that, if there is a single
-        	// unchanged variable v, it produces v' = v if v is a short variable,
-        	// otherwise it produces UNCHANGED v
-        	//
-            // sb.append("UNCHANGED ");
-            // sb.append(cThen.Unchanged(cElse));
-        	String uc = cThen.Unchanged(cElse);
-        	if (uc.length() > 5) {
-        		sb.append("UNCHANGED " + uc);
-        	} else {
-        		sb.append(uc + "' = " + uc);
-        	}
-            tlacode.insertElementAt(sb.toString(), lineUncThen);
-            Vector vec =  stringToTLATokens(sb.toString());
-            // The following is bogus because the RightParen for the
-            // entire procedure is inserted after (or instead of) the
-            // ELSE's UNCHANGED
-            // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
-            mappingVector.insertElementAt(vec, lineUncThen);
-        }
-
-        // Merge the change lists together
-        c.Merge(cThen);
-        c.Merge(cElse);
-    }
-    
-    /**
-     * Returns the vector of MappingObjects containing the BeginTLAToken and
-     * EndTLAToken that are put in the mappingVector by a call of addOneLineOfTLA.
-     * The code was essentially copied from addOneTokenToTLA.
-     * 
-     * @param token
-     * @return
-     */
-    private Vector stringToTLATokens(String token) {
-        Vector result = new Vector(3);
-        
-        String trimmedToken = token.trim() ;
-
-        int numberOfLeftTrimmedTokens = 
-                (trimmedToken.length() == 0) ? -1 :                   
-                  token.indexOf(trimmedToken.charAt(0));
-             
-             /**
-              * Handle a token of only space characters. 
-              */
-             if (numberOfLeftTrimmedTokens == -1) {
-                 numberOfLeftTrimmedTokens = 0 ;
-                 trimmedToken = token ;
-             }
-             
-             int objBegin = numberOfLeftTrimmedTokens;
-             result.addElement(new MappingObject.BeginTLAToken(objBegin));
-             result.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length()));
-             return result;
-    }
-
-    /***********************************************************************
-    * Added by LL on 30 Jan 2006.                                          *
-    *                                                                      *
-    * Generate TLA+ for the `either' statement.  This performs the same    *
-    * sort of hackery as for the `if' statement, necessitated by the       *
-    * design flaw commented on above.                                      
-     **
-    ***********************************************************************/
-    private void GenEither(AST.Either ast, Changed c, String context, String prefix, int col)
-            throws PcalTLAGenException
-    {
-        Changed allC = new Changed(c);
-        /*******************************************************************
-        * Accumulates the variable changes of all the clauses.             *
-        *******************************************************************/
-        Changed[] cOrs = new Changed[ast.ors.size()];
-        /*******************************************************************
-        * cOrs[i] is the Changed vector for the i-th `or' clause.          *
-        *******************************************************************/
-        int[] ucLocs = new int[ast.ors.size()]; // location of unchangeds.
-        /******************************************************************
-        * tlaout.elementAt(ucLocs[i]) is the UNCHANGED clause for the     *
-        * i-th `or' clause.                                               *
-        ******************************************************************/
-        StringBuffer sb = new StringBuffer(prefix);
-        int prefixIndent = sb.length();
-        sb.append("\\/ ");
-        int here = sb.length();
-        /*******************************************************************
-        * The number of columns to the left of the code generated for      *
-        * each `or' clause.                                                *
-        *******************************************************************/
-
-        /*
-         * Add the left paren for the statement.
-         */
-        addLeftParen(ast.getOrigin());
-        /*********************************************************************
-        * Produce the output for the clauses, but with a dummy line in       *
-        * place of the UNCHANGED clause, and compute allC, cOrs, and         *
-        * ucLocs.                                                            *
-        *********************************************************************/
-        for (int i = 0; i < ast.ors.size(); i++)
-        {
-            if (i != 0)
-            {
-                sb = new StringBuffer(NSpaces(prefixIndent) + "\\/ ");
-            }
-            ;
-            sb.append("/\\ ");
-            Vector orClause = (Vector) ast.ors.elementAt(i);
-            Changed cC = new Changed(c);
-            for (int j = 0; j < orClause.size(); j++)
-            {
-                /***********************************************************
-                * On 6 Jun 2010, LL added the "+3" in the following call   *
-                * of GenStmt.  This seems to fix a bug which caused        *
-                *                                                          *
-                *    either when \/ A                                      *
-                *                \/ B                                      *
-                *        or ...                                            *
-                *                                                          *
-                * to produce                                               *
-                *    \/ /\ \/ A                                            *
-                *       \/ B                                               *
-                *    \/ ...                                                *
-                ***********************************************************/
-                GenStmt((AST) orClause.elementAt(j), cC, context, sb.toString(), here + 3); 
-                sb = new StringBuffer(NSpaces(here) + "/\\ ");
-            }
-            ;
-            cOrs[i] = cC;
-            allC.Merge(cC);
-            ucLocs[i] = tlacode.size();
-//            tlacode.addElement("Replace by UNCHANGED"); // 
-            addOneLineOfTLA("Replace by UNCHANGED");
-        }
-        ; // End of for i
-
-        /**********************************************************************
-        * Insert real UNCHANGED clauses.  Note that we have to go through     *
-        * loop backwards since we will remove a line of output for each `or'  *
-        * clause that doesn't get an UNCHANGED.                               *
-        **********************************************************************/
-        int i = ast.ors.size();
-        while (i > 0)
-        {
-            i = i - 1;
-            tlacode.removeElementAt(ucLocs[i]);
-            mappingVector.removeElementAt(ucLocs[i]);
-            int numUnchanged = cOrs[i].NumUnchanged(allC);
-            String NotChanged = cOrs[i].Unchanged(allC);
-            if (numUnchanged > 1)
-            {
-                /*
-                 * The line should be wrapped if it's too long.
-                 */
-                String line = NSpaces(here) + "/\\ UNCHANGED <<" + NotChanged + ">>";
-                tlacode.insertElementAt(line, ucLocs[i]);
-                mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]);
-            } else if (numUnchanged == 1)
-            {   // Change made by LL on 16 Mar 2011 so that, if there is a single
-            	// unchanged variable v, it produces v' = v if v is a short variable,
-            	// otherwise it produces UNCHANGED v
-            	//
-                // tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]);
-            	if (NotChanged.length() > 5) {
-            	    String line = NSpaces(here) + "/\\ UNCHANGED " + NotChanged;
-                    tlacode.insertElementAt(line, ucLocs[i]);
-                    mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]);
-//                    tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]);
-            	} else {
-            	    String line = NSpaces(here) + "/\\ " + NotChanged + "' = " + NotChanged;
-            	    tlacode.insertElementAt(line, ucLocs[i]);
-                    mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]);
-//            		tlacode.insertElementAt(NSpaces(here) + "/\\ " + NotChanged + "' = "
-//            				+ NotChanged, ucLocs[i]);
-            	}
-            } 
-        }
-        ;
-        /*
-         * Add the right paren for the entire statement.
-         */
-        ((Vector) mappingVector.elementAt(mappingVector.size()-1))
-        .add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
-        /**********************************************************************
-        * Add the statement's unchangeds to c.                                *
-        **********************************************************************/
-        c.Merge(allC);
-    }
-
-    private void GenWith(AST.With ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
-    {
-        addLeftParen(ast.getOrigin());
-        StringBuffer sb = new StringBuffer(prefix);
-        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
-//        Vector sv = exp.toStringVector();
-        if (ast.isEq)
-        {
-            /* generate LET statement */
-            sb.append("LET ");
-            sb.append(ast.var);
-            sb.append(" == ");
-            addOneTokenToTLA(sb.toString());
-            addLeftParen(exp.getOrigin());
-            addExprToTLA(exp);
-            addRightParen(exp.getOrigin());
-//            int here = sb.length();
-//            sb.append((String) sv.elementAt(0));
-//            for (int v = 1; v < sv.size(); v++)
-//            {
-//                tlacode.addElement(sb.toString());
-//                sb = new StringBuffer(NSpaces(here));
-//                sb.append((String) sv.elementAt(v));
-//            }
-            addOneTokenToTLA(" IN");
-            endCurrentLineOfTLA();
-//            sb.append(" IN");
-//            tlacode.addElement(sb.toString());
-            sb = new StringBuffer(NSpaces(col + 2));
-            /*************************************************************
-            * LL changed "col + 4" to "col + 2" here to correct an       *
-            * alignment problem on 31 Jan 2006.                          *
-            *************************************************************/
-            if (ast.Do.size() > 1)
-                sb.append("/\\ ");
-        } else
-        {
-            /* generate \E statement */
-            sb.append("\\E ");
-            sb.append(ast.var);
-            sb.append(" \\in ");
-            addOneTokenToTLA(sb.toString());
-            addLeftParen(exp.getOrigin());
-            addExprToTLA(exp);
-            addRightParen(exp.getOrigin());
-//            int here = sb.le
-            addOneTokenToTLA(":");
-            endCurrentLineOfTLA();
-//            sb.append(":");
-//            tlacode.addElement(sb.toString());
-            sb = new StringBuffer(NSpaces(col + 2));
-            if (ast.Do.size() > 1)
-                sb.append("/\\ ");
-        }
-        for (int i = 0; i < ast.Do.size(); i++)
-        {
-            GenStmt((AST) ast.Do.elementAt(i), c, context, sb.toString(), sb.length());
-            sb = new StringBuffer(NSpaces(col + 2) + "/\\ ");
-        }
-        // tlacode.addElement(NSpaces(col) + ")");
-        
-        /*
-         * Add the right paren for the entire statement.
-         */
-        ((Vector) mappingVector.elementAt(mappingVector.size()-1))
-        .add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
-    }
-
-    private void GenWhen(AST.When ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
-    {
-        addOneTokenToTLA(prefix);
-        
-//        StringBuffer sb = new StringBuffer(prefix);
-        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
-        addLeftParen(exp.getOrigin());
-        addExprToTLA(exp);
-        addRightParen(exp.getOrigin());
-        endCurrentLineOfTLA();
-//        Vector sv = exp.toStringVector();
-//        
-//// Debugging
-////Vector vec = exp.toMappingVector();
-////System.out.println("Original vec:");
-////MappingObject.printMappingVector(vec);    
-////System.out.println("RemoveRedundantParens(vec)");
-////MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(vec));
-////System.out.println("Should be original mappingvector:");
-////MappingObject.printMappingVector(vec); 
-//
-//        sb.append((String) sv.elementAt(0));
-//        for (int v = 1; v < sv.size(); v++)
-//        {
-//            tlacode.addElement(sb.toString());
-//            sb = new StringBuffer(NSpaces(col));
-//            sb.append((String) sv.elementAt(v));
-//        }
-//        tlacode.addElement(sb.toString());
-    }
-
-    private void GenPrintS(AST.PrintS ast, Changed c, String context, String prefix, int col)
-            throws PcalTLAGenException
-    {
-        StringBuffer sb = new StringBuffer(prefix);
-        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
-        addLeftParen(ast.getOrigin());
-        addOneTokenToTLA(prefix + "PrintT(");
-        addExprToTLA(exp);
-        addOneTokenToTLA(")");
-        addRightParen(ast.getOrigin());
-        endCurrentLineOfTLA();
-        
-//        Vector sv = exp.toStringVector();
-//        // The following modified 19 Nov 05 by LL to use PrintT instead of Print
-//        sb.append("PrintT(");
-//        sb.append((String) sv.elementAt(0));
-//        for (int v = 1; v < sv.size(); v++)
-//        {
-//            tlacode.addElement(sb.toString());
-//            sb = new StringBuffer(NSpaces(col + "PrintT(".length()));
-//            sb.append((String) sv.elementAt(v));
-//        }
-//        sb.append(")");
-//        tlacode.addElement(sb.toString());
-    }
-
-    /********************************************************/
-    /* Assert(ast.expr, "Failure of assertion at... ")      */
-    /**
-     *******************************************************/
-    private void GenAssert(AST.Assert ast, Changed c, String context, String prefix, int col)
-            throws PcalTLAGenException
-    {
-        addLeftParen(ast.getOrigin());
-        StringBuffer sb = new StringBuffer(prefix);
-        StringBuffer sc = new StringBuffer();
-        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
-//        Vector sv = exp.toStringVector();
-        sb.append("Assert(");
-        addOneTokenToTLA(sb.toString());
-        addLeftParen(exp.getOrigin());
-        addExprToTLA(exp);
-        addRightParen(exp.getOrigin());
-        int here = sb.length();
-//        sb.append((String) sv.elementAt(0));
-//        for (int v = 1; v < sv.size(); v++)
-//        {
-//            tlacode.addElement(sb.toString());
-//            sb = new StringBuffer(NSpaces(col + "Assert(".length()));
-//            sb.append((String) sv.elementAt(v));
-//        }
-//        sb.append(", ");
-        sb = new StringBuffer(", ");
-        sc.append("\"Failure of assertion at ");
-        sc.append(ast.location());
-        // modified on 23 Mar 2006 by LL to use location() instead of
-        // ast.line and ast.col
-        sc.append(".\")");
-        if (tlacodeNextLine.length() + sb.length() + sc.length() < wrapColumn) {
-            addOneTokenToTLA(sb.toString() + sc.toString());
-        }
-//        if (sb.length() + sc.length() < wrapColumn)
-//            tlacode.addElement(sb.toString() + sc.toString());
-        else
-        {
-            addOneTokenToTLA(sb.toString());
-            endCurrentLineOfTLA();
-            addOneTokenToTLA(NSpaces(here) + sc.toString());
-//            tlacode.addElement(sb.toString());
-//            tlacode.addElement(NSpaces(here) + sc.toString());
-        }
-        addRightParen(ast.getOrigin());
-        endCurrentLineOfTLA();
-    }
-
-    /********************************************************/
-    /* I generate a TRUE conjunct, which is useless, but so */
-    /* is a skip statement.                                 */
-    /********************************************************/
-    private void GenSkip(AST.Skip ast, Changed c, String context, String prefix, int col)
-    {
-//        tlacode.addElement(prefix + "TRUE");
-        addOneTokenToTLA(prefix);
-        addLeftParen(ast.getOrigin());
-        addOneTokenToTLA("TRUE");
-        addRightParen(ast.getOrigin());
-        endCurrentLineOfTLA();
-    }
-
-    /***********************************************************************
-    * Generate the VARIABLES declaration(s), output the TLA+ "code" from   *
-    * a `define' statement, if any, and generate the definition of         *
-    * `vars'.                                                              *
-    *                                                                      *
-    * Method renamed from GenVars and given the defs argument by LL on     *
-    * 25 Jan 2006 to handle the `define' statement.                        *
-    ***********************************************************************/
-    private void GenVarsAndDefs(Vector globals, Vector procs, Vector processes, TLAExpr defs)
-      throws PcalTLAGenException
-    {
-        /*******************************************************************
-        * lVars and gVars are vectors of strings, each element being a     *
-        * variable name.  They hold the local and global variables,        *
-        * respectively.                                                    *
-        *******************************************************************/
-        Vector lVars = new Vector();
-        Vector gVars = new Vector();
-        
-        /**
-         * lVarsSource and gVarsSource are vectors of AST.VarDecl objects that 
-         * generated the elements of lVars and gVars, where PVarDecl objects
-         * are converted to VarDecl objects.
-         */
-        Vector lVarsSource = new Vector();
-        Vector gVarsSource = new Vector();
-
-        /*******************************************************************
-        * Set gVars to the global variables, including pc and `stack' if   *
-        * there are procedures, and add these variables to vars.           *
-        *******************************************************************/
-        if (globals != null)
-            for (int i = 0; i < globals.size(); i++)
-            {
-                AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i);
-                gVars.addElement(decl.var);
-                gVarsSource.addElement(decl);
-                vars.addElement(decl.var);
-            }
-        if (! ParseAlgorithm.omitPC) {
-          gVars.addElement("pc");
-          /**
-           * For added variables, create a VarDecl with null origin.
-           */
-          AST.VarDecl pcVarDecl = new AST.VarDecl();
-          pcVarDecl.var = "pc";
-          gVarsSource.addElement(pcVarDecl);
-          vars.addElement("pc");
-        }
-        if (procs != null && procs.size() > 0)
-        {
-            gVars.addElement("stack");
-            /**
-             * For added variables, create a VarDecl with null origin.
-             */
-            AST.VarDecl pcVarDecl = new AST.VarDecl();
-            pcVarDecl.var = "stack";
-            gVarsSource.addElement(pcVarDecl);
-            vars.addElement("stack");
-        }
-        /*******************************************************************
-        * Add local procedure variables to lVars, vars, and pcV.           *
-        *******************************************************************/
-        if (procs != null)
-            for (int i = 0; i < procs.size(); i++)
-            {
-                AST.Procedure proc = (AST.Procedure) procs.elementAt(i);
-                if (proc.params != null)
-                    for (int p = 0; p < proc.params.size(); p++)
-                    {
-                        AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p);
-                        lVars.addElement(decl.var);
-                        lVarsSource.addElement(decl.toVarDecl()) ;
-                        vars.addElement(decl.var);
-                        pcV.addElement(decl.var);
-                    }
-                if (proc.decls != null)
-                    for (int p = 0; p < proc.decls.size(); p++)
-                    {
-                        AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p);
-                        lVars.addElement(decl.var);
-                        lVarsSource.addElement(decl.toVarDecl()) ;
-                        vars.addElement(decl.var);
-                        pcV.addElement(decl.var);
-                    }
-            }
-
-        /*******************************************************************
-        * Add local process variables to lVars, vars, and psV for          *
-        * variables local to process sets.                                 *
-        *******************************************************************/
-        if (processes != null)
-            for (int i = 0; i < processes.size(); i++)
-            {
-                AST.Process proc = (AST.Process) processes.elementAt(i);
-                if (proc.decls != null)
-                    for (int p = 0; p < proc.decls.size(); p++)
-                    {
-                        AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p);
-                        lVars.addElement(decl.var);
-                        lVarsSource.addElement(decl);
-                        vars.addElement(decl.var);
-                        if (!proc.isEq)
-                            psV.addElement(decl.var);
-                    }
-            }
-
-        /********************************************************************
-        * Add a declaration of the constant defaultInitValue if it is       *
-        * used.  (Added by LL on 22 Aug 2007.)                              *
-        ********************************************************************/
-        if (ParseAlgorithm.hasDefaultInitialization)
-        {
-            addOneLineOfTLA("CONSTANT defaultInitValue");
-        }
-        ;
-
-        if (EmptyExpr(defs))
-        {
-            /******************************************************************
-            * There is no `define' statement.  In this case, generate a       *
-            * single VARIABLES statement and set gVars to vector of all       *
-            * variables.                                                      *
-            ******************************************************************/
-            gVars.addAll(lVars);
-            gVarsSource.addAll(lVarsSource) ;
-            GenVarDecl(gVars, gVarsSource); 
-        } else
-        {
-            /******************************************************************
-            * There is a `define' statement.  In this case, must declare      *
-            * global and local variables separately.  Also, set gVars to      *
-            * vector of all variables.                                        *
-            ******************************************************************/
-            GenVarDecl(gVars, gVarsSource);
-            addOneLineOfTLA("");
-            addOneLineOfTLA("(* define statement *)");
-            addExprToTLA(defs);
-//            Vector sv = defs.toStringVector();
-//            for (int i = 0; i < sv.size(); i++)
-//            {
-//                tlacode.addElement((String) sv.elementAt(i));
-//            }
-            ;
-            addOneLineOfTLA("");
-            GenVarDecl(lVars, lVarsSource); // to be fixed
-            gVars.addAll(lVars);
-            gVarsSource.addAll(lVarsSource);
-        }
-        ;
-        addOneLineOfTLA("");
-
-        /*
-         * We check for the unlikely case in which there are no variables.  
-         * Without this check, the Init is not generated but appears in
-         * the definition of Spec.
-         */
-        if (gVars.size() == 0) {
-            throw new PcalTLAGenException("The algorithm has no variables.");
-        }
-        /*******************************************************************
-        * Generate definition of var.                                     *
-        *******************************************************************/
-//        StringBuffer var = new StringBuffer("vars == << ");
-//        StringBuffer curLine = new StringBuffer("vars == << ");
-        addOneTokenToTLA("vars == << ") ;
-        int indent = tlacodeNextLine.length();
-        for (int i = 0; i < gVars.size(); i++)
-        {
-            if (i > 0)
-            {
-//                var.append(", ");
-//                curLine.append(", ");
-//                tlacodeNextLine = tlacodeNextLine + ", ";
-                addOneTokenToTLA(", ");
-            }
-            ;
-            String vbl = (String) gVars.elementAt(i);
-            AST.VarDecl vblDecl = (AST.VarDecl) gVarsSource.elementAt(i);
-            Region vblOrigin = vblDecl.getOrigin();
-//            if (curLine.length() + vbl.length() + 1 > wrapColumn)
-            if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn)
-            {
-//                curLine = new StringBuffer("vars == << ");
-//                var.append("\n" + NSpaces("vars == << ".length()));
-                endCurrentLineOfTLA();
-                tlacodeNextLine = NSpaces(indent);
-            }
-//            var.append(vbl);
-//            curLine.append(vbl);
-            addOneSourceTokenToTLA(vbl, vblOrigin);
-        }
-//        if (curLine.length() + " >>".length() + 1 > wrapColumn)
-        if (tlacodeNextLine.length() + " >>".length() + 1 > wrapColumn)
-        {
-//            var.append("\n" + NSpaces("vars ==".length()));
-            endCurrentLineOfTLA() ;
-            tlacodeNextLine = NSpaces("vars ==".length());
-        }
-        ;
-//        var.append(" >>");
-//        tlacodeNextLine = tlacodeNextLine + " >>";
-        addOneTokenToTLA(" >>");
-//        tlacode.addElement(var.toString());
-        addOneLineOfTLA("");
-    }
-
-    /**
-     * Generate a VARIABLE(S) declarations.  The varVec argument is a vector of
-     * strings that are the variables to be declared.  It does nothing if
-     * the vector has length 0.  The varVecSource argument is a vector
-     * of the same size as varVec that contains the AST.VarDecl objects.
-     * <p>
-     * Method added by LL on 25 Jan 2006.  
-     * 
-     * Modified 16 Dec 2011 to add varVecSource argument and generate TLA to 
-     * PCal mapping.
-     * 
-     * @param varVec A vector of strings.
-     * 
-     * @param varVecSource A vector of AST.VarDecl objects.
-     */
-    public void GenVarDecl(Vector varVec, Vector varVecSource)
-    {
-//        StringBuffer res = new StringBuffer();
-//        StringBuffer curLine = new StringBuffer("VARIABLES ");
-        // for measuring length
-        if (varVec.size() == 0)
-        {
-            return;
-        }
-        ;
-        if (varVec.size() > 1)
-        {
-//            res.append("VARIABLES ");
-            addOneTokenToTLA("VARIABLES ");
-        } else
-        {
-//            res.append("VARIABLE ");
-            addOneTokenToTLA("VARIABLE ");
-        }
-        ;
-        for (int i = 0; i < varVec.size(); i++)
-        {
-            if (i > 0)
-            {
-//                res.append(", ");
-//                curLine.append(", ");
-                addOneTokenToTLA(", ");
-            }
-            ;
-            String vbl = (String) varVec.elementAt(i);
-            AST vblsource = (AST) varVecSource.elementAt(i);
-//            if (curLine.length() + vbl.length() + 1 > wrapColumn)
-            if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn)
-            {
-//                curLine = new String
-                endCurrentLineOfTLA();
-                if (varVec.size() > 1)
-                {
-//                    res.append(NSpaces("VARIABLES ".length()));
-                    tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLES ".length());
-                } else
-                {
-//                    res.append(NSpaces("VARIABLE ".length()));
-                    tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLE ".length());
-                }
-                ;
-            }
-            ;
-//            res.append(vbl);
-//            curLine.append(vbl);
-            addOneSourceTokenToTLA(vbl, vblsource.getOrigin());
-        }
-        ;
-//        tlacode.addElement(res.toString());
-        endCurrentLineOfTLA();
-    }
-
-     /**
-     * Generates the "ProcSet == ..." output.  It is just a union of all the
-     * process sets, all on one line (except if a process set is a multi-line
-     * expression).  It wouldn't be too hard to break long lines, but that
-     * should be done later, if desired, after the TLA to PCal translation
-     * is finished.  
-     */
-    public void GenProcSet()
-    {
-        StringBuffer ps = new StringBuffer();
-        if (st.processes == null || st.processes.size() == 0)
-            return;
-//        ps.append("ProcSet == ");
-        addOneTokenToTLA("ProcSet == ");
-        for (int i = 0; i < st.processes.size(); i++)
-        {
-            PcalSymTab.ProcessEntry proc = (PcalSymTab.ProcessEntry) st.processes.elementAt(i);
-//            Vector sv = proc.id.toStringVector();
-            if (i > 0) {
-//                ps.append(" \\cup ");
-                addOneTokenToTLA(" \\cup ");
-            }
-            addLeftParen(proc.id.getOrigin());
-            if (proc.isEq) {
-//                ps.append("{");
-                addOneTokenToTLA("{");
-            }
-            else {
-//                ps.append("(");
-                addOneTokenToTLA("(");
-            }
-            int col = ps.length();
-//            ps.append((String) sv.elementAt(0));
-//            for (int v = 1; v < sv.size(); v++)
-//            {
-//                tlacode.addElement(ps.toString());
-//                ps = new StringBuffer(NSpaces(col));
-//                ps.append((String) sv.elementAt(v));
-//            }
-            addExprToTLA(proc.id);
-            if (proc.isEq) {
-//                ps.append("}");
-                addOneTokenToTLA("}");
-            }
-            else {
-//                ps.append(")");
-                addOneTokenToTLA(")");
-            }
-            addRightParen(proc.id.getOrigin());
-        }
-//        tlacode.addElement(ps.toString());
-//        tlacode.addElement("");
-        endCurrentLineOfTLA();
-        addOneLineOfTLA("");
-    }
-
-    /***********************************/
-    /* Generate the Init == statement. */
-    /**
-     **********************************/
-    private void GenInit(Vector globals, Vector procs, Vector processes) throws PcalTLAGenException
-    {
-        int col = "Init == ".length();
-        StringBuffer is = new StringBuffer();
-        is.append("Init == ");
-
-        /* Global variables */
-        if (globals != null && globals.size() > 0)
-        {
-            is.append("(* Global variables *)");
-//            tlacode.addElement(is.toString());
-            addOneLineOfTLA(is.toString()) ;
-            is = new StringBuffer(NSpaces(col));
-            for (int i = 0; i < globals.size(); i++)
-            {
-                AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i);
-                addVarDeclToTLA(decl, is);
-                is = new StringBuffer(NSpaces(col));
-            }
-        }
-        if (procs != null && procs.size() > 0)
-        {
-            /* Procedure variables and parameters */
-            for (int i = 0; i < procs.size(); i++)
-            {
-                AST.Procedure proc = (AST.Procedure) procs.elementAt(i);
-                if (proc.params.size() == 0 && proc.decls.size() == 0)
-                    // No parameters or procedure variables in this procedure
-                    continue;
-                is.append("(* Procedure ");
-                is.append(proc.name);
-                is.append(" *)");
-//                tlacode.addElement(is.toString());
-                addOneLineOfTLA(is.toString());
-                is = new StringBuffer(NSpaces(col));
-                for (int p = 0; p < proc.params.size(); p++)
-                {
-                    AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p);
-                    if (!mp) {
-                        addVarDeclToTLA(decl.toVarDecl(), is);
-                    }
-                    else {
-                        is.append("/\\ ");
-                        addOneTokenToTLA(is.toString());
-                        addLeftParen(decl.getOrigin());
-//                    is.append(decl.var);
-                        is = new StringBuffer(decl.var);
-                    /*******************************************************
-                    * Modified on 31 Jan 2006 by LL to add subscripts to   *
-                    * initialization expression if needed.  Also replaced  *
-                    * test for "\\in" with assertion that it can't occur,  *
-                    * since it's forbidden by the grammar.                 *
-                    *******************************************************/
-                        PcalDebug.Assert(decl.isEq);
-                        is.append(" = ");
-                        
-//                    Vector sv;
-//                    if (mp)
-//                    {
-//                        sv = AddSubscriptsToExpr(decl.val, 
-//                              SubExpr(Self("procedure")), new Changed(new Vector()))
-//                                .toStringVector();
-//                    } else
-//                    {
-//                        sv = Parenthesize(decl.val.toStringVector());
-//                        /*************************************************
-//                        * Call to Parenthesize added by LL on 27 Feb 2008. *
-//                        * See bug_08-02-18.                              *
-//                        *************************************************/
-//                    }
-//                    ;
-//                    if (mp)
-//                    {
-                        is.append("[ self \\in ProcSet |-> ");
-//                    }
-                        addOneTokenToTLA(is.toString());
-                        addLeftParen(decl.val.getOrigin());
-                        addExprToTLA(
-                          AddSubscriptsToExpr(decl.val, 
-                                              SubExpr(Self("procedure")), 
-                                              new Changed(new Vector())));
-                        addRightParen(decl.val.getOrigin());
-                        addOneTokenToTLA("]");
-                        addRightParen(decl.getOrigin());
-                        endCurrentLineOfTLA();
-                        
-//                    int col2 = is.length();
-//                    is.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        tlacode.addElement(is.toString());
-//                        is = new StringBuffer(NSpaces(col2));
-//                        is.append((String) sv.elementAt(v));
-//                    }
-//                    if (mp)
-//                        is.append("]");
-//                    tlacode.addElement(is.toString());
-                    }
-                    is = new StringBuffer(NSpaces(col));
-                }
-                for (int p = 0; p < proc.decls.size(); p++)
-                {
-                    /*
-                     * Note: the following code is identical to the for loop
-                     * code above for procedure variables.  (Well done, Keith!)
-                     * I realized this too late to feel like procedurizing it.
-                     */
-                    AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p);
-                    if (!mp) {
-                        addVarDeclToTLA(decl.toVarDecl(), is);
-                    }
-                    else {
-                        is.append("/\\ ");
-                        addOneTokenToTLA(is.toString());
-                        addLeftParen(decl.getOrigin());
-//                    is.append(decl.var);
-                        is = new StringBuffer(decl.var);
-//                    is.append("/\\ ");
-//                    is.append(decl.var);
-
-                    /*******************************************************
-                    * Modified on 31 Jan 2006 by LL to add subscripts to   *
-                    * initialization expression if needed.  Also replaced  *
-                    * test for "\\in" with assertion that it can't occur,  *
-                    * since it's forbidden by the grammar.                 *
-                    *******************************************************/
-                        PcalDebug.Assert(decl.isEq);
-                        is.append(" = ");
-//                    Vector sv;
-//                    if (mp)
-//                    {
-//                        sv = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector()))
-//                                .toStringVector();
-//                    } else
-//                    {
-//                        sv = Parenthesize(decl.val.toStringVector());
-//                        /*************************************************
-//                        * Call to Parenthesize added by LL on            *
-//                        * 27 Feb 2008.  See bug_08-02-18.                *
-//                        *************************************************/
-//                    }
-//                    ;
-//                    if (mp)
-//                    {
-                        is.append("[ self \\in ProcSet |-> ");
-//                    }
-                        addOneTokenToTLA(is.toString());
-                        addLeftParen(decl.val.getOrigin());
-                        addExprToTLA(AddSubscriptsToExpr(
-                                        decl.val, 
-                                        SubExpr(Self("procedure")), 
-                                        new Changed(new Vector())));
-                        addRightParen(decl.val.getOrigin());
-                        addOneTokenToTLA("]");
-                        addRightParen(decl.getOrigin());
-                        endCurrentLineOfTLA();
-
-//                    int col2 = is.length();
-//                    is.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        tlacode.addElement(is.toString());
-//                        is = new StringBuffer(NSpaces(col2));
-//                        is.append((String) sv.elementAt(v));
-//                    }
-//                    if (mp)
-//                        is.append("]");
-//                    tlacode.addElement(is.toString());
-                    }
-                    is = new StringBuffer(NSpaces(col));
-                }
-            }
-        }
-        if (processes != null && processes.size() > 0)
-        {
-            /* Process variables */
-            for (int i = 0; i < processes.size(); i++)
-            {
-                AST.Process proc = (AST.Process) processes.elementAt(i);
-                if (proc.decls.size() == 0) // No variables in this procedure
-                    continue;
-                is.append("(* Process ");
-                is.append(proc.name);
-                is.append(" *)");
-//                tlacode.addElement(is.toString());
-                addOneLineOfTLA(is.toString());
-                is = new StringBuffer(NSpaces(col));
-                for (int p = 0; p < proc.decls.size(); p++)
-                {
-                    /*
-                     * In the comments below, (( and )) represent
-                     * MappingObject.LeftParen and MappingObject.RightParen
-                     * objects.
-                     */
-                    AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p);
-                    is.append("/\\ ");
-                    /*
-                     * The following adds  /\ ((  to the TLA+ output.
-                     */
-                    addOneTokenToTLA(is.toString());
-                    addLeftParen(decl.getOrigin());
-                    
-                    
-                    if (proc.isEq) {
-                        /*
-                         * The source is
-                         * 
-                         *     process (P = S) variables ... v  @@  Val
-                         *     
-                         * where @@ is either "=" or "\in".  The TLA+ output is
-                         * 
-                         *    /\ (( v @@ (( Val )) ))
-                         */
-                        is = new StringBuffer(decl.var);
-                        if (decl.isEq) {
-                            is.append(" = ");
-                        }
-                        else {
-                            is.append(" \\in ");
-                        }
-                        addOneTokenToTLA(is.toString()); 
-                        addLeftParen(decl.val.getOrigin());
-                        addExprToTLA(decl.val);
-                        addRightParen(decl.val.getOrigin());
-                    }
-                    else {
-                        if (decl.isEq) {
-                            /*
-                             * The source is
-                             *    
-                             *     process (P \in S) variables ... v = Val
-                             *     
-                             * The TLA+ output is
-                             * 
-                             *    /\ (( v = [self \in (( S )) |-> (( ValBar )) ] ))
-                             *    
-                             * where ValBar obtained from Val by replacing each 
-                             * variable w of the process with w[self].   
-                             */
-                            is = new StringBuffer(decl.var);
-                            is.append(" = [self \\in ");
-                            addOneTokenToTLA(is.toString());
-                            addLeftParen(proc.id.getOrigin());
-                            addExprToTLA(proc.id);
-                            addRightParen(proc.id.getOrigin());
-                            addOneTokenToTLA(" |-> " );
-                            addLeftParen(decl.val.getOrigin());
-                            addExprToTLA(AddSubscriptsToExpr(
-                                           decl.val,
-                                           SubExpr(Self("procedure")), 
-                                           new Changed(new Vector())));
-                            addRightParen(decl.val.getOrigin());
-                            addOneTokenToTLA("]");
-                            
-                        }
-                        else {
-                            /*
-                             * The source is
-                             * 
-                             *    process (P \in S) variables ... v \in Val
-                             *    
-                             * The TLA+ output is
-                             * 
-                             *    /\ (( v \in [ (( S )) -> (( ValBar )) ] ))
-                             *    
-                             * where ValBar is obtained from Val by replacing each 
-                             * variable w of the process with
-                             * 
-                             *    w[CHOOSE self \in S : TRUE]
-                             *    
-                             * We first set expr to the TLAExpr "CHOOSE self \in S : TRUE".
-                             * (This is Keith's original code.) 
-                             */                            
-                            TLAExpr subexpr = proc.id.cloneAndNormalize();
-                            TLAExpr expr = new TLAExpr();
-                            expr.addLine();
-                            expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN));
-                            expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN));
-                            expr.addToken(new TLAToken("self", 8, TLAToken.IDENT));
-                            expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN));
-                            expr.normalize();
-                            expr.setOrigin(subexpr.getOrigin()); // see what this does.
-                            try
-                            {
-                                subexpr.prepend(expr, 1);
-                                expr = new TLAExpr();
-                                expr.addLine();
-                                expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN));
-                                expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN));
-                                expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN));
-                                expr.prepend(subexpr, 1);
-                            } catch (TLAExprException e)
-                            {
-                                throw new PcalTLAGenException(e.getMessage());
-                            }
-
-                            /*
-                             * Now we output the TLA+ code.
-                             */
-                            is = new StringBuffer(decl.var);
-                            is.append(" \\in [");
-                            addOneTokenToTLA(is.toString());
-                            addLeftParen(proc.id.getOrigin());
-                            addExprToTLA(proc.id);
-                            addRightParen(proc.id.getOrigin());
-                            addOneTokenToTLA(" -> " );
-                            addLeftParen(decl.val.getOrigin());
-                            addExprToTLA(AddSubscriptsToExpr(
-                                          decl.val, expr, new Changed(new Vector())) );
-                            addRightParen(decl.val.getOrigin());
-                            addOneTokenToTLA("]");
-                        }
-                    }
-                    /*
-                     * This adds the final )) .
-                     */
-                    addRightParen(decl.getOrigin());
-                    endCurrentLineOfTLA();
-                    is = new StringBuffer(NSpaces(col));
-                    
-// everything from here down to the end of the for p loop should be commented out                                       
-//                    is.append(decl.var);
-//                    is = new StringBuffer(decl.var);
-//                    if (decl.isEq)
-//                        is.append(" = ");
-//                    else
-//                        is.append(" \\in ");
-//                    /*******************************************************
-//                    * Modified on 31 Jan 2006 by LL to add subscripts to   *
-//                    * initialization expression for process set.  Note     *
-//                    * tricky subscript that is added in expr for           *
-//                    * declaration of form "v \in expr".                    *
-//                    *                                                      *
-//                    * Also modified the whole method of producing the      *
-//                    * variable declaration because the original destroyed  *
-//                    * the formatting of the expression proc.id, leading    *
-//                    * to bad or incorrect formatting if the process id     *
-//                    * set expression was not trivial.                      *
-//                    *******************************************************/
-//                    Vector sv;
-//                    TLAExpr sve;
-//                    if (proc.isEq)
-//                    {
-//                        /***************************************************
-//                        * No substitution unless it's a process set.       *
-//                        ***************************************************/
-//                        sve = decl.val; // .toStringVector();
-//                    } else
-//                    {
-//                        if (decl.isEq)
-//                        {
-//                            /***********************************************
-//                            * For declaration "v = ...", add subscript     *
-//                            * "[self]".                                    *
-//                            ***********************************************/
-//                            sve = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector()));
-//                        } else
-//                        {
-//                            /************************************************
-//                            * For declaration "v \in ...", add subscript    *
-//                            * "[CHOOSE self \in Process Id Set : TRUE]".    *
-//                            *                                               *
-//                            * This weird subscript is needed in the         *
-//                            * following weird case:                         *
-//                            *                                               *
-//                            *    process (P \in S)                          *
-//                            *    variable v \in T, w \in R(v)               *
-//                            *                                               *
-//                            * This produces the following conjunct in       *
-//                            * the initial predicate for w:                  *
-//                            *                                               *
-//                            *   w \in [S -> R(v[CHOOSE self \in S : TRUE])] *
-//                            ************************************************/
-//                            TLAExpr subexpr = proc.id.cloneAndNormalize();
-//
-//                            TLAExpr expr = new TLAExpr();
-//                            expr.addLine();
-//                            expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN));
-//                            expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN));
-//                            expr.addToken(new TLAToken("self", 8, TLAToken.IDENT));
-//                            expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN));
-//                            expr.normalize();
-//
-//                            try
-//                            {
-//                                subexpr.prepend(expr, 1);
-//                                expr = new TLAExpr();
-//                                expr.addLine();
-//                                expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN));
-//                                expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN));
-//                                expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN));
-//                                expr.prepend(subexpr, 1);
-//                            } catch (TLAExprException e)
-//                            {
-//                                throw new PcalTLAGenException(e.getMessage());
-//                            }
-//
-//                            sve = AddSubscriptsToExpr(decl.val, expr, new Changed(new Vector()));
-//                        }
-//                        ;
-//                    }
-//                    ;
-//                    TLAExpr expr = new TLAExpr();
-//                    expr.addLine();
-//                    if (!proc.isEq)
-//                    {
-//                        expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN));
-//                        if (decl.isEq)
-//                        {
-//                            expr.addToken(new TLAToken("self", 1, TLAToken.IDENT));
-//                            expr.addToken(new TLAToken("\\in ", 6, TLAToken.BUILTIN));
-//                        }
-//                        ;
-//                        expr.normalize();
-//                        TLAExpr expr2 = proc.id.cloneAndNormalize();
-//                        try
-//                        {
-//                            expr2.prepend(expr, 0);
-//                            expr = new TLAExpr();
-//                            expr.addLine();
-//                            if (decl.isEq)
-//                            {
-//                                expr.addToken(new TLAToken("|->", 0, TLAToken.BUILTIN));
-//                            } else
-//                            {
-//                                expr.addToken(new TLAToken("->", 0, TLAToken.BUILTIN));
-//                            }
-//                            ;
-//                            expr.prepend(expr2, 1);
-//                            sve.prepend(expr, 1);
-//                        } catch (TLAExprException e)
-//                        {
-//                            throw new PcalTLAGenException(e.getMessage());
-//                        }
-//                    }
-//                    ;
-//                    sv = sve.toStringVector();
-//                    if (proc.isEq)
-//                    {
-//                        sv = Parenthesize(sv);
-//                    }
-//                    ;
-//                    /*****************************************************
-//                    * Call to Parenthesize added by LL on 27 Feb 2008.   *
-//                    * See bug_08-02-18.                                  *
-//                    *****************************************************/
-//                    int col2 = is.length();
-//                    is.append((String) sv.elementAt(0));
-//                    for (int v = 1; v < sv.size(); v++)
-//                    {
-//                        tlacode.addElement(is.toString());
-//                        is = new StringBuffer(NSpaces(col2));
-//                        is.append((String) sv.elementAt(v));
-//                    }
-//                    if (!proc.isEq)
-//                        is.append("]");
-//                    tlacode.addElement(is.toString()); 
-//                    is = new StringBuffer(NSpaces(col));
-// end of section to be commented out 
-                } // end of for p loop.
-            }
-        }
-
-        /* stack initial value */
-        if (procs != null && procs.size() > 0)
-        {
-            if (mp)
-                is.append("/\\ stack = [self \\in ProcSet |-> << >>]");
-            else
-                is.append("/\\ stack = << >>");
-//            tlacode.addElement(is.toString());
-            addOneLineOfTLA(is.toString());
-            is = new StringBuffer(NSpaces(col));
-        }
-        /* pc initial value */
-        if (! ParseAlgorithm.omitPC) {
-          if (mp)
-          {
-              // On 4 May 2012, LL added useCase flag to inhibit adding of CASE for
-              // a single process or process set.
-              boolean useCase = st.processes.size() != 1;
-              if (useCase) {
-                is.append("/\\ pc = [self \\in ProcSet |-> CASE ");
-             } else {
-                 is.append("/\\ pc = [self \\in ProcSet |-> ");
-             }
-              int colPC = is.length();
-              if (boxUnderCASE)
-                  colPC = colPC - 3;
-              for (int p = 0; p < st.processes.size(); p++)
-              {
-                  PcalSymTab.ProcessEntry pe = (PcalSymTab.ProcessEntry) st.processes.elementAt(p);
-                    if (useCase) {
-                        is.append("self ");
-                        if (pe.isEq) {
-                            is.append("= ");
-                            // int colExpr = is.length();
-                            addOneTokenToTLA(is.toString());
-                            addLeftParen(pe.id.getOrigin());
-                            addExprToTLA(pe.id);
-                            addRightParen(pe.id.getOrigin());
-
-                            // Vector sv = pe.id.toStringVector();
-                            // is.append((String) sv.elementAt(0));
-                            // for (int v = 1; v < sv.size(); v++)
-                            // {
-                            // addOneLineOfTLA(is.toString());
-                            // // tlacode.addElement(is.toString());
-                            // is = new StringBuffer(NSpaces(colExpr));
-                            // is.append((String) sv.elementAt(v));
-                            // }
-                        } else {
-                            is.append("\\in ");
-                            // int colExpr = is.length();
-                            // Vector sv = pe.id.toStringVector();
-                            // is.append((String) sv.elementAt(0));
-                            // for (int v = 1; v < sv.size(); v++)
-                            // {
-                            // tlacode.addElement(is.toString());
-                            // is = new StringBuffer(NSpaces(colExpr));
-                            // is.append((String) sv.elementAt(v));
-                            // }
-                            addOneTokenToTLA(is.toString());
-                            addLeftParen(pe.id.getOrigin());
-                            addExprToTLA(pe.id);
-                            addRightParen(pe.id.getOrigin());
-
-                        }
-                        // is.append(" -> \"");
-                        is = new StringBuffer(" -> \"");
-                        is.append(pe.iPC);
-                        if (p == st.processes.size() - 1)
-                            is.append("\"]");
-                        else if (!boxUnderCASE)
-                            is.append("\" []");
-                        else
-                            is.append("\"");
-                    } // end if (useCase)
-                    else {
-                        is.append("\"" + pe.iPC + "\"]");
-                    }
-//                  tlacode.addElement(is.toString());
-                  addOneTokenToTLA(is.toString());
-                  endCurrentLineOfTLA();
-                  is = new StringBuffer(NSpaces(colPC));
-                  if (boxUnderCASE && p < st.processes.size() - 1)
-                      is.append("[] ");
-             }
-          } else
-          {
-              is.append("/\\ pc = \"" + st.iPC + "\"");
-//              tlacode.addElement(is.toString());
-              addOneLineOfTLA(is.toString());
-          }
-        }
-//        tlacode.addElement("");
-        addOneLineOfTLA("");
-    }
-
-    /************************************/
-    /* Generate the Next == definition. */
-    /************************************/
-    private void GenNext()
-    {
-        // It we are omitting pc and this is a uniprocess
-        // algorithm, then the definition of Next has
-        // already been added.
-        if (ParseAlgorithm.omitPC && !mp) {
-            return;
-        }
-        Vector nextS = new Vector();
-        StringBuffer sb = new StringBuffer();
-        int max, col;
-
-        // Steps with no parameter
-        max = wrapColumn - ("Next == \\/ ".length());
-        for (int i = 0; i < nextStep.size(); i++)
-        {
-            String a = (String) nextStep.elementAt(i);
-            if (a.length() + " \\/ ".length() + sb.length() > max)
-            {
-                nextS.addElement(sb.toString());
-                sb = new StringBuffer();
-            }
-            if (sb.length() > 0)
-                sb.append(" \\/ ");
-            sb.append(a);
-        }
-        if (sb.length() > 0)
-            nextS.addElement(sb.toString());
-
-        // Steps with (self) from ProcSet
-        // These are procedures in a multiprocess algorithm
-        Vector nextSS = new Vector();
-        String nextSSstart = "(\\E self \\in ProcSet: ";
-        sb = new StringBuffer();
-        max = wrapColumn - ("Next == \\/ (\\E self \\in ProcSet: \\/ ".length());
-        if (mp && st.procs.size() > 0)
-        {
-            for (int i = 0; i < st.procs.size(); i++)
-            {
-                PcalSymTab.ProcedureEntry p = (PcalSymTab.ProcedureEntry) st.procs.elementAt(i);
-                if ((p.name.length() + "(self) \\/ ".length() + sb.length()) > max)
-                {
-                    nextSS.addElement(sb.toString());
-                    sb = new StringBuffer();
-                }
-                if (sb.length() > 0)
-                    sb.append(" \\/ ");
-                sb.append(p.name);
-                sb.append("(self)");
-            }
-            if (sb.length() > 0)
-                nextSS.addElement(sb.toString() + ")");
-        }
-
-        // Steps with (self) from a set
-        // These are process sets
-        Vector nextSSP = new Vector(); // of Vector
-        if (mp && st.processes.size() > 0)
-            for (int i = 0; i < st.processes.size(); i++)
-            {
-                PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i);
-                if (p.isEq)
-                    continue;
-                Vector vec = new Vector();
-                sb = new StringBuffer();
-                sb.append("(\\E self \\in ");
-                Vector sv = p.id.toStringVector();
-                col = sb.length();
-                sb.append((String) sv.elementAt(0));
-                for (int j = 1; j < sv.size(); j++)
-                {
-                    vec.addElement(sb.toString());
-                    sb = new StringBuffer(NSpaces(col));
-                    sb.append((String) sv.elementAt(j));
-                }
-                sb.append(": ");
-                sb.append(p.name);
-                sb.append("(self))");
-                vec.addElement(sb.toString());
-                nextSSP.addElement(vec);
-            }
-
-        // assemble the line from the pieces
-        sb = new StringBuffer("Next == ");
-        col = sb.length() + 2;
-        for (int i = 0; i < nextS.size(); i++)
-        {
-            sb.append((String) nextS.elementAt(i));
-            addOneLineOfTLA(sb.toString());
-//            tlacode.addElement(sb.toString());
-            sb = new StringBuffer(NSpaces(col) + " \\/ ");
-        }
-        if (nextSS.size() > 0)
-        {
-            sb.append(nextSSstart);
-            int col2 = sb.length();
-            if (nextSS.size() > 1)
-                sb.append(" \\/ ");
-            for (int i = 0; i < nextSS.size(); i++)
-            {
-                sb.append((String) nextSS.elementAt(i));
-                addOneLineOfTLA(sb.toString());
-//                tlacode.addElement(sb.toString());
-                sb = new StringBuffer(NSpaces(col2) + " \\/ ");
-            }
-            sb = new StringBuffer(NSpaces(col) + " \\/ ");
-        }
-        if (nextSSP.size() > 0)
-            for (int i = 0; i < nextSSP.size(); i++)
-            {
-                Vector v = (Vector) nextSSP.elementAt(i);
-                for (int j = 0; j < v.size(); j++)
-                {
-                    String line = (String) v.elementAt(j);
-                    sb.append(line);
-                    addOneLineOfTLA(sb.toString());
-//                    tlacode.addElement(sb.toString());
-                    
-                    // The following if case was added by LL on 22 Jan 2011
-                    // to correct part 1 of bug bug_11_01_13.  This  bug occurs
-                    // when an "\in" process's set is multi-line and that
-                    // process's next-state action comes immediately after 
-                    // the Next == ..., with no " \/ " preceding it.  To fix the
-                    // problem, we must add 6 fewer spaces to all lines after
-                    // the first in that process's set than in other such sets. 
-                    if ((nextS.size() == 0) && (nextSS.size() == 0) && (i == 0)) {
-                        sb = new StringBuffer(NSpaces(col - 2));
-                    } else {
-                        sb = new StringBuffer(NSpaces(col + 4));
-                    }
-                }
-                sb = new StringBuffer(NSpaces(col) + " \\/ ");
-            }
-        if (! (PcalParams.NoDoneDisjunct || ParseAlgorithm.omitStutteringWhenDone))
-         { sb.append("(* Disjunct to prevent deadlock on termination *)");
-           addOneLineOfTLA(sb.toString());
-//           tlacode.addElement(sb.toString());
-           sb = new StringBuffer(NSpaces(col + 4));
-           if (mp)
-               /************************************************************
-               * Bug fix by LL on 6 Sep 2007.  Added parentheses to        *
-               * change                                                    *
-               *                                                           *
-               * (*)    \A self \in ProcSet: ... /\ UNCHANGED vars         *
-               *                                                           *
-               * to                                                        *
-               *                                                           *
-               * (**)   (\A self \in ProcSet: ...)  /\ UNCHANGED vars      *
-               *                                                           *
-               * thus moving the UNCHANGED vars outside the quantifier.    *
-               * Since self does not appear in UNCHANGED vars, the two     *
-               * expressions are equivalent except when ProcSet is the     *
-               * empty set, in which case (*) equals TRUE and (**) equals  *
-               * UNCHANGED vars.                                           *
-               ************************************************************/
-               sb.append("((\\A self \\in ProcSet: pc[self] = \"Done\") /\\ " + "UNCHANGED vars)");
-           else
-               sb.append("(pc = \"Done\" /\\ UNCHANGED vars)");
-               addOneLineOfTLA(sb.toString());
-//               tlacode.addElement(sb.toString());
-         } ;
-         addOneLineOfTLA("");
-//        tlacode.addElement("");
-    }
-
-    /****************************************/
-    /* Generate the Spec == ... definition. */
-    /****************************************/
-    /***********************************************************************
-    * The spec can contain the following conjuncts                         *
-    *                                                                      *
-    * 1. Init /\ [][Next]_vars                                             *
-    *    Always present                                                    *
-    *                                                                      *
-    * 2. WF_var(Next)                                                      *
-    *    present if (a) The wfNext option is specified, or                 *
-    *               (b) It is a uniprocess algorithm and one of the        *
-    *                   options -wf, -sf, -termination is specified.       *
-    *                                                                      *
-    * 3. A sequence of process fairness formulas, containing               *
-    *    one for each process for which there is a fairness condition.     *
-    *                                                                      *
-    * A process has                                                        *
-    *    (a) a WF fairness condition iff                                   *
-    *         (i) it is preceded by the keyword "fair" and the -nof        *
-    *             option is not specified, or                              *
-    *        (ii) the -wf option is specified.                             *
-    *    (b) an SF fairness condition iff it is not preceded               *
-    *        by the keyword "fair" and the -sf option is specified.        *
-    *                                                                      *
-    * Let P be a process specified by either                               *
-    *                                                                      *
-    *     [fair] process (P = exp) ...                                     *
-    *     [fair] process (P \in exp) ...                                   *
-    *                                                                      *
-    * In the first case we say that P is a single process, in the second   *
-    * that it is a process set.  Let                                       *
-    *                                                                      *
-    *   - p_1, ... , p_Np  be the labels of P modified by "+"              *
-    *                                                                      *
-    *   - m_1, ... , m_Nm  be the set of labels of P modified by "-"       *
-    *                                                                      *
-    *   - pSelf = IF P is a single process THEN "pname"                    *
-    *                                      ELSE "self"                     *
-    *                                                                      *
-    *   - qSelf = IF /\ P is a single process                              *
-    *                /\ pSelf a multi-line formula                         *
-    *               THEN "self"                                            *
-    *               ELSE pSelf                                             *
-    *                                                                      *
-    *   - pName = IF P is a single process THEN "P"                        *
-    *                                      ELSE  "P(self)"                 *
-    *                                                                      *
-    * A process fairness formula is described by the following:            *
-    *                                                                      *
-    *    XF:                                                               *
-    *       either WF or SF depending on P's fairness condition            *
-    *                                                                      *
-    *    prefix:                                                           *
-    *       IF P is a single process                                       *
-    *         THEN IF pSelf a multi-line formula                           *
-    *                THEN "LET self = pSelf IN"                            *
-    *                ELSE ""                                               *
-    *         ELSE "\A self \in exp"                                       *
-    *                                                                      *
-    *    wfFormula:                                                        *
-    *       "XF( (pc[qSelf] \notin {"m_1", ... , "m_Np"}) /\ pName )"      *
-    *                                                                      *
-    *    sfFormula:                                                        *
-    *       if XF = SF                                                     *
-    *         then null                                                    *
-    *         else if P a single process                                   *
-    *                then "SF_vars(p_1) /\ ... /\ SF_vars(p_Np)"           *
-    *                else "SF_vars(p_1(self)) /\ ...                       *
-    *                         /\ SF_vars(p_Np(self))"                      *
-    *                                                                      *
-    *    prcdFormulas:                                                     *
-    *       A sequence consisting of the following two formulas for each   *
-    *       procedure D called within P.  Let                              *
-    *                                                                      *
-    *         - pd_1, ... , pd_Npd  be the labels of D modified by "+"     *
-    *         - md_1, ... , md_Nmd  be the labels of D modified by "-"     *
-    *                                                                      *
-    *       in the formulas:                                               *
-    *                                                                      *
-    *         wfFormula:                                                   *
-    *           "XF( (pc[qSelf] \notin {"md_1", ... , "md_Nmd"})           *
-    *                    /\ D(qSelf) )"                                    *
-    *                                                                      *
-    *         sfFormula:                                                   *
-    *            if XF = SF                                                *
-    *              then null                                               *
-    *              else "SF_vars(pd_1(qSelf)) /\ ...                       *
-    *                            /\ SF_vars(pd_Npd(qSelf))"                *
-    *                                                                      *
-    * -------                                                              *
-    *                                                                      *
-    * If there is at least one fairness formula, the definition of Spec    *
-    * will be formatted in one of two ways.  If there is either a          *
-    * WF_vars(Next) condition or a process fairness formula, then it may   *
-    * be formatted as:                                                     *
-    *                                                                      *
-    *   Spec == Init /\ [][Next]_vars                                      *
-    *                                                                      *
-    * otherwise, it will be formatted as                                   *
-    *                                                                      *
-    *   Spec == /\ Init /\ [][Next]_vars                                   *
-    *          [/\ WF_vars(Next)]                                          *
-    *           /\ F_1                                                     *
-    *           ...                                                        *
-    *           /\ F_n                                                     *
-    *                                                                      *
-    * where each F_i is a process fairness formulas.                       *
-    ***********************************************************************/
-    private void GenSpec()
-    {   String safetyFormula = "Init /\\ [][Next]_vars" ;
-    	
-        if (    PcalParams.FairnessOption.equals("nof")
-             || (!mp && PcalParams.FairnessOption.equals(""))) {
-            addOneLineOfTLA("Spec == " + safetyFormula);
-            addOneLineOfTLA("");
-//        	tlacode.addElement("Spec == " + safetyFormula );
-//        	tlacode.addElement("");
-        	return;
-        }
-//System.out.println("foo |-> " + st.UseThis(PcalSymTab.PROCEDURE, "foo", ""));
-//int to = st.FindProc("foo");
-//PcalSymTab.ProcedureEntry pe =
-//    (PcalSymTab.ProcedureEntry) st.procs.elementAt(to);
-//AST.Procedure procAst = pe.ast;
-
-    	StringBuffer sb = new StringBuffer("Spec == ");
-        // Generate the requested fairness conjuncts
-
-    	// wfNextConj is either null or  " /\ WF_(Next)" 
-    	String wfNextConj = null; 
-    	if (   PcalParams.FairnessOption.equals("wfNext")
-    	    || PcalParams.FairAlgorithm
-        	|| (!mp && (   PcalParams.FairnessOption.equals("wf")
-        			    || PcalParams.FairnessOption.equals("sf"))))
-        {
-            // If uniprocess then wf and sf are the same as wfNext
-        	wfNextConj = " /\\ WF_vars(Next)";
-        }         
-    	
-    	// Now compute procFairnessFormulas to equal the processes' fairness 
-    	// formulas, which is never null but may have zero length.
-    	Vector procFairnessFormulas = new Vector() ;
-        if (mp) {
-           for (int i = 0; i < st.processes.size(); i++) {
-        	   PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i);
-        	   AST.Process pAst = p.ast ;
-        	   int fairness = pAst.fairness;
-        	   if (fairness != AST.UNFAIR_PROC) {
-        		   String xf = (fairness == AST.WF_PROC) ? "WF" : "SF";
-        		   
-                   Vector pSelf = p.id.toStringVector();
-                   
-                   // makeLetIn is true iff prefix will be LET self == ... IN
-                   boolean makeLetIn = false ;
-                   
-                   String qSelf = "self";
-                   if (p.isEq) {
-                       if (pSelf.size() > 1) {
-                           makeLetIn = true ;
-                       } else {
-                           qSelf = (String) pSelf.elementAt(0);
-                       }  
-                   }
-                   
-                   Vector prefix = new Vector();
-        		   if (makeLetIn || !p.isEq) {
-                       int prefixSize = pSelf.size();
-                       String prefixBegin;
-                       String prefixEnd;
-                       if (p.isEq) {
-                           prefixBegin = "LET self == ";
-                           prefixEnd = "";
-                       } else {
-                           prefixBegin = "\\A self \\in ";
-                           prefixEnd = " : ";
-                       }
-                       String padding = NSpaces(prefixBegin.length());
-                       for (int j = 0; j < prefixSize; j++) {
-                           String line = (String) pSelf.elementAt(j);
-                           if (j == 0) {
-                               line = prefixBegin + line;
-                           } else {
-                               line = padding + line;
-                           }
-                           if (j == prefixSize - 1) {
-                               line = line + prefixEnd;
-                           }
-                           prefix.addElement(line);
-                       }
-                       if (makeLetIn) {
-                           prefix.addElement("IN ");
-                       }
-        		   } // end if (makeLetIn || !p.isEq)
-        		   
-        		   StringBuffer wfSB = new StringBuffer(xf + "_vars(");
-        		   if (pAst.minusLabels != null && pAst.minusLabels.size() > 0) {
-        		       wfSB.append("(pc[");
-        		       wfSB.append(qSelf);
-        		       if (pAst.minusLabels.size() == 1) {
-        		           wfSB.append("] # \"");
-        		           wfSB.append(pAst.minusLabels.elementAt(0));
-        		           wfSB.append("\"");
-        		       } else {
-        		           wfSB.append("] \\notin {\"");
-        		           for (int j = 0; j < pAst.minusLabels.size(); j++) {
-        		               wfSB.append(pAst.minusLabels.elementAt(j));
-        		               if (j == pAst.minusLabels.size() - 1) {
-        		                   wfSB.append("\"}");
-        		               } else {
-        		                   wfSB.append("\", \"");
-        		               }
-        		           }
-        		       }
-        		       wfSB.append(") /\\ ");
-        		   }
-        		   
-        		   String pName = p.name;
-                   if (!p.isEq) {
-                       pName = p.name + "(self)";
-                   }
-                   wfSB.append(pName);
-        		   wfSB.append(")");
-        		   
-        		   StringBuffer sfSB = null ;
-        		   if (    xf.equals("WF") 
-        		       && (pAst.plusLabels != null) 
-        		       && (pAst.plusLabels.size() != 0)) {
-        		       sfSB = new StringBuffer() ;
-        		       for (int j = 0; j < pAst.plusLabels.size(); j++) {
-        		           if (j != 0) {
-        		               sfSB.append(" /\\ ");
-        		           }
-        		           sfSB.append("SF_vars(");
-        		           sfSB.append(pAst.plusLabels.elementAt(j));
-        		           if (!p.isEq) {
-        		               sfSB.append("(self)");
-        		           }
-        		           sfSB.append(")");
-        		       }
-        		   }
-        	   
-        	       Vector  prcdFormulas = new Vector();
-        	       Vector  procedures = pAst.proceduresCalled;
-                   for (int k = 0; k < procedures.size(); k++) {
-                       String originalName = (String) procedures.elementAt(k);
-                       String name = st.UseThis(PcalSymTab.PROCEDURE, originalName, "");
-                       int procedureIndex = st.FindProc(name);
-                       PcalSymTab.ProcedureEntry pe =
-                          (PcalSymTab.ProcedureEntry) st.procs.elementAt(procedureIndex);
-                       AST.Procedure prcAst = pe.ast;
-
-                       StringBuffer wfPrcSB = new StringBuffer(xf + "_vars(");
-                       if (prcAst.minusLabels != null && prcAst.minusLabels.size() > 0) {
-                           wfPrcSB.append("(pc[");
-                           wfPrcSB.append(qSelf);
-                           if (prcAst.minusLabels.size() == 1) {
-                               wfPrcSB.append("] # \"");
-                               wfPrcSB.append(prcAst.minusLabels.elementAt(0));
-                               wfPrcSB.append("\"");
-                           } else {
-                               wfPrcSB.append("] \\notin {\"");
-                               for (int j = 0; j < prcAst.minusLabels.size(); j++) {
-                                   wfPrcSB.append(prcAst.minusLabels.elementAt(j));
-                                   if (j == prcAst.minusLabels.size() - 1) {
-                                       wfPrcSB.append("\"}");
-                                   } else {
-                                       wfPrcSB.append("\", \"");
-                                   }
-                               }
-                           }
-                           wfPrcSB.append(") /\\ ");
-                       }
-    
-                       String prcName = pe.name + "(" + qSelf + ")";
-                       wfPrcSB.append(prcName);
-                       wfPrcSB.append(")");
-                                          
-                       StringBuffer sfPrcSB = null;
-                       if (    xf.equals("WF") 
-                           && (prcAst.plusLabels != null) 
-                           && (prcAst.plusLabels.size() != 0)) {
-                           sfPrcSB = new StringBuffer() ;
-                           for (int j = 0; j < prcAst.plusLabels.size(); j++) {
-                               if (j != 0) {
-                                   sfPrcSB.append(" /\\ ");
-                               }
-                               sfPrcSB.append("SF_vars(");
-                               sfPrcSB.append(prcAst.plusLabels.elementAt(j));
-                               sfPrcSB.append("(" + qSelf + ")") ;
-                               sfPrcSB.append(")");
-                           }
-                       }
-                       prcdFormulas.addElement(
-                            new FormulaPair(
-                                    wfPrcSB.toString(), 
-                                    (sfPrcSB == null) ? null : sfPrcSB.toString())
-                          ) ;
-                     } // end construction of prcdFormulas
-        	       
-        	       procFairnessFormulas.addElement(
-        	         new ProcessFairness(
-        	                 xf, 
-        	                 prefix, 
-        	                 wfSB.toString(), 
-        	                 (sfSB == null) ? null : sfSB.toString(), 
-        	                 prcdFormulas)
-        	              ) ;
-               } // end if (fairness != AST.UNFAIR_PROC)
-           } 	   
-        } // ends construction of procFairnessFormulas
-           
-        if (wfNextConj == null && procFairnessFormulas.size() == 0) {
-            addOneLineOfTLA("Spec == " + safetyFormula);
-            addOneLineOfTLA("");
-//            tlacode.addElement("Spec == " + safetyFormula);
-//            tlacode.addElement("");
-            return;
-        }
-        addOneLineOfTLA("Spec == /\\ " + safetyFormula);
-//        tlacode.addElement("Spec == /\\ " + safetyFormula) ;
-        int indent = "Spec == /\\ ".length();
-        
-        if (wfNextConj != null) {
-            addOneLineOfTLA("        /\\ WF_vars(Next)");
-//            tlacode.addElement("        /\\ WF_vars(Next)");
-        }
-        for (int i = 0; i < procFairnessFormulas.size(); i++) {
-            /*
-             * The original code called format on the fairness formula, which can
-             * create a string with \n characters embedded.  I've just split the
-             * string into its individual lines and added them to tlacode one at a time.
-             * However, the current and original code can both produce very long lines 
-             * that could be wrapped.  So if this change does make a difference, then 
-             * it would be better to completely rewrite the format method (which is only 
-             * called here).
-             */
-             String str = 
-                  "        /\\ " + 
-                  ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent).toString();
-             String [] splitStr = str.split("\n");
-             for (int j = 0; j < splitStr.length; j++) {
-                 addOneLineOfTLA(splitStr[j]);
-             }
-
-//            tlacode.addElement(
-//                        "        /\\ " +
-//                      ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent)
-//                         );
-        }
-        addOneLineOfTLA("");
-//        tlacode.addElement("");
-        return;
-    }
-
-    /************************************/
-    /* Generate the Termination ==      */
-    /************************************/
-    private void GenTermination()
-    {
-        // if we're omitting the pc or omitting the stuttering-when-done
-        // clause of the Next action, then we shouldn't
-        // generate the Termination definition.
-        // Check of omitStutteringWhenDone added by LL on 30 Mar 2012.
-        if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) {
-            return;
-        }
-        StringBuffer sb = new StringBuffer();
-        sb.append("Termination == <>(");
-        if (mp)
-            sb.append("\\A self \\in ProcSet: pc[self]");
-        else
-            sb.append("pc");
-        sb.append(" = \"Done\")");
-        addOneLineOfTLA(sb.toString());
-        addOneLineOfTLA("");
-//        tlacode.addElement(sb.toString());
-//        tlacode.addElement("");
-    }
-
-    /**********************************************************/
-    /* For variables that need subscripts, add the subscript. */
-    /* These are pc, stack, procedure variables, procedure    */
-    /* parameters, and variables defined in process sets.     */
-    /* Then, add primes to variables that have been changed   */
-    /* according to c.                                        */
-    /*   exprn : the original expression.                     */
-    /*   sub : the subscript to be added (null if none)       */
-    /*   c : the variables that have been changed (so need to */
-    /*       be primed.                                       */
-    /**********************************************************/
-    private TLAExpr AddSubscriptsToExpr(TLAExpr exprn, TLAExpr sub, Changed c) throws PcalTLAGenException
-    {
-        /*
-         * For testing, throw a null pointer exception if the begin/end substitution
-         * mapping vectors are not properly matching in the returned expression
-         */
-//        int[] depths = new int[1000];
-        
-        int parenDepth = 0;
-        for (int i = 0; i < exprn.tokens.size(); i++) {
-            Vector line = (Vector) exprn.tokens.elementAt(i);
-            for (int j = 0; j < line.size(); j++) {
-                TLAToken tok = (TLAToken) line.elementAt(j);
-                parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size();
-                if (parenDepth < 0) {
-                        throw new NullPointerException("argument: begin/end Subst depth negative");
-                }
-            }
-//            depths[i] = parenDepth;
-        }
-        if (parenDepth != 0) {
-            throw new NullPointerException("argument: Unmatched begin Subst");
-        }
-        /*   ------------------ end testing --------------------------*/
-
-        /*
-         * We now set stringVec to the sequence of identifiers that occur in exprn
-         * for which we need to add a subscript or a prime.
-         */
-        Vector exprVec = new Vector(); // the substituting exprs
-        Vector stringVec = new Vector(); // the substituted ids
-        TLAExpr expr = exprn.cloneAndNormalize();  // the expression to be returned
-
-       for (int i = 0; i < expr.tokens.size(); i++)
-        {
-            Vector tv = (Vector) expr.tokens.elementAt(i);
-            for (int j = 0; j < tv.size(); j++)
-            {
-                TLAToken tok = (TLAToken) tv.elementAt(j);
-                boolean prime = ((tok.type == TLAToken.IDENT) && c.IsChanged(tok.string));
-                boolean subr = (sub != null && (tok.type == TLAToken.ADDED || (mp && (tok.type == TLAToken.IDENT) && (IsProcedureVar(tok.string) || IsProcessSetVar(tok.string)))));
-                if ((subr || prime) && !InVector(tok.string, stringVec))
-                {
-                    stringVec.addElement(tok.string);
-                    TLAExpr exp = new TLAExpr();
-                    exp.addLine();
-                    /*
-                     * 15 Dec 2011:  The following code added to replace the
-                     *    
-                     *    exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT));
-                     *    
-                     * that is commented out.  Note that this change can add a token with
-                     * type ADDED rather than IDENT.  I don't think this matters.
-                     */
-                    TLAToken newTok = tok.Clone() ;
-                    /*
-                     * The new token should inherit nothing from the baggage of tok, whose
-                     * only function is to provide the name
-                     */
-                    newTok.setBeginSubst(new Vector(2));
-                    newTok.setEndSubst(new Vector(2));
-                    newTok.source = null;
-                    newTok.column = 0;
-                    exp.addToken(newTok) ;
-                    // exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT));
-                    if (prime) {
-                        /*****************************************************
-                        * Modified by LL on 30 Aug 2007.  The following      *
-                        * call to addTokenOffset was originally a call to    *
-                        * addToken.  See the comments for                    *
-                        * TLAExpr.addTokenOffset().                          *
-                        *****************************************************/
-                        TLAToken primeTok = new TLAToken("'", 0, TLAToken.BUILTIN, true);
-                        // The following stuff added by LL in Dec 2011 is bogus.  The
-                        // token tok is just the first one of many in the exprn with
-                        // the same name for which we want to substitute.  The only
-                        // useful data in the token tok is its string.
-                        //
-                        // if (tok.source != null) {
-                        //   primeTok.source = 
-                        //           new Region(tok.source.getEnd(), tok.source.getEnd());
-                        // }
-                        // if (!subr) {
-                        //    primeTok.setEndSubst(tok.getEndSubst());
-                        //   newTok.setEndSubst(new Vector(2));
-                        // }
-                        exp.addTokenOffset(primeTok, 0);
-                    }
-                    if (subr)
-                    {
-                        TLAExpr subexp = sub.cloneAndNormalize();
-                        
-                        /*
-                         * We now add the end of the origin of tok to beginSubst
-                         * of the first token of subexp and to endSubst of the
-                         * last token of subexp.  This indicates that PCal code
-                         * corresponding to a region of the TLA+ translation that
-                         * includes part of the added subscript and part of the
-                         * original expression is a portion of the source of
-                         * exprn.
-                         */
-                        // This is bogus, because we are adding the location of where
-                        // the identifier first occurs in exprn to the substitution
-                        // vectors of the expression that's going to be substituted in
-                        // all instances.
-                        //
-                        // if (tok.source != null) {
-                        //   PCalLocation endOfTok = tok.source.getEnd();
-                        //  subexp.firstToken().getBeginSubst().add(endOfTok);
-                        //  subexp.lastToken().getEndSubst().add(endOfTok);
-                        // }
-                        /*
-                         * However, we do have to move the token's beginSubst and endSubst vectors
-                         * to the first and lasts token of the subscript.  Since the
-                         * resulting Parens that they generate should be outside
-                         * the ones generated by the endSubst vectors of the expression,
-                         * we have to add them before that expression's Subst vectors.
-                         * 
-                         * No, no!  This tok is just giving us the name of the tok that
-                         * we're going to be substituting for in the expression.  It is not
-                         * necessarily the one that we're going to substitute for.
-                         */
-                        // newTok.getEndSubst().addAll(subexp.lastToken().getEndSubst());
-                        // subexp.lastToken().setEndSubst(newTok.getEndSubst());
-                        // newTok.setEndSubst(new Vector(2));
-                        exp.normalize();
-                        try
-                        {
-                            subexp.prepend(exp, 0);
-                        } catch (TLAExprException e)
-                        {
-                            throw new PcalTLAGenException(e.getMessage());
-                        }
-                        exp = subexp;
-                    }
-                    /**********************************************************
-                    * Modified by LL on 31 Jan 2006 to comment out the call   *
-                    * of MakeExprPretty, since it totally screwed up the      *
-                    * formatting when substituting any string containing      *
-                    * spaces or multiple lines for a variable.                *
-                    **********************************************************/
-                    // MakeExprPretty(exp);
-                    exprVec.addElement(exp);
-                }
-            }
-        }
-        if (exprVec.size() > 0)
-            try
-            {
-                expr.substituteForAll(exprVec, stringVec, false);
-            } catch (TLAExprException e)
-            {
-                throw new PcalTLAGenException(e.getMessage());
-            }
-        /*
-         * For testing, throw a null pointer exception if the begin/end substitution
-         * mapping vectors are not properly matching in the returned expression
-         */
-//        depths = new int[1000];
-        
-        parenDepth = 0;
-        for (int i = 0; i < expr.tokens.size(); i++) {
-            Vector line = (Vector) expr.tokens.elementAt(i);
-            for (int j = 0; j < line.size(); j++) {
-                TLAToken tok = (TLAToken) line.elementAt(j);
-                parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size();
-                if (parenDepth < 0) {
-                        throw new NullPointerException("result: begin/end Subst depth negative");
-                }
-            }
-//            depths[i] = parenDepth;
-        }
-        if (parenDepth != 0) {
-            throw new NullPointerException("result: Unmatched begin/subst");
-        }
-        /*   ------------------ end testing --------------------------*/
-        return expr;
-    }
-
-    /***********************************************************************
-     * Given an expression, makes it into a subscript expr.  It is called   *
-     * only with argument Self(context), which means that it is called      *
-     * only for a single subscript.                                         *
-     *                                                                      *
-     * If string is null, then returns null.                                *
-     *                                                                      *
-     * Since this is used only to add "[self]", there is no need to add     *
-     * any REPLACEMENT tokens to the expression.  Moreover, the added       *
-     * subscript's tokens have a null source field because they don't come  *
-     * from any PCal code.  The new expression is given the same origin as  *
-     * the original one.                                                    *
-     ***********************************************************************/
-    private static TLAExpr SubExpr(TLAExpr sub)
-    {
-        if (sub != null)
-        { 
-            TLAExpr expr = sub.cloneAndNormalize();
-              // This preserves the origin of the expression
-            for (int i = 0; i < expr.tokens.size(); i++) {
-                Vector tokenVec = (Vector) expr.tokens.elementAt(i);
-                for (int j = 0; j < tokenVec.size(); j++) {
-                   TLAToken tok = (TLAToken) tokenVec.elementAt(j);
-                   tok.column = tok.column + 1;
-                }
-                if (i == 0) {
-                    /*
-                     * Set isAppended field of the "[" and "]" to true iff the first
-                     * token of sub has isAppended field true, which should be the
-                     * case iff it is the "self" token.
-                     */
-                    tokenVec.insertElementAt(
-                          new TLAToken("[", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()),
-                          0);
-                }
-            }
-            expr.addTokenOffset(
-                    new TLAToken("]", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()), 0);
-            return expr;
-        } else {
-            return null;
-        }
-    }
-
-    /*********************************************************/
-    /* Gives the string to use when subscripting a variable. */
-    /*********************************************************/
-    // LL comment: it appears that PcalTLAGen.self is the 
-    // current process id if this is being called in the context
-    // of a process declared with `process (P = id)'.
-    private TLAExpr Self(String context)
-    {
-        TLAExpr s = null;
-        if (mp)
-        {
-            if (context.equals("procedure"))
-                s = selfAsExpr();
-            else
-                s = self;
-        }
-        return s;
-    }
-
-    private static TLAExpr  selfAsExpr() {
-        /*
-         * This is a token that does not correspond to anything in the
-         * PCal code, so it should have a null source field.
-         * It has a true isAppended field because it is always appended
-         * as a subscript to a variable.
-         */
-        TLAToken selfToken = new TLAToken("self", 0, TLAToken.IDENT, true);
-        Vector tokenVec = new Vector();
-        tokenVec.addElement(selfToken);
-        Vector tokens = new Vector();
-        tokens.addElement(tokenVec);
-        TLAExpr expr = new TLAExpr(tokens);
-//        expr.anchorTokens = new TLAToken[1];
-//        expr.anchorTokens[0] = selfToken;
-//        expr.anchorTokCol = new int[1];
-//        expr.anchorTokCol[0] = 0;
-        expr.normalize();
-        return expr ;
-    }
-    /***********************************************************************
-    * Comment added by LL: MakeExprPretty should never be called on an     *
-    * expression any part of which was an expression in the input.         *
-    * Fortunately, it is now called only for the expression "[self]", so   *
-    * it is effectively a no-op.                                           *
-    * Comment added 15 Dec 2011: In fact, it's not called at all.          *
-    ***********************************************************************/
-    public static void ObsoleteMakeExprPretty(TLAExpr expr)
-    {
-        /*********************************************************************
-         * Sets columns so this expr looks nice and tight.                   *
-         *********************************************************************/
-        Vector line; /* Vector of TLAToken */
-        boolean spread;
-        int nextCol = 1;
-        for (int i = 0; i < expr.tokens.size(); i++)
-        {
-            line = (Vector) expr.tokens.elementAt(i);
-            for (int j = 0; j < line.size(); j++)
-            {
-                TLAToken tok = ((TLAToken) line.elementAt(j));
-                spread = tok.string.equals("=");
-                tok.column = nextCol + ((spread) ? 1 : 0);
-                nextCol = nextCol + tok.getWidth() + ((spread) ? 2 : 0);
-            }
-        }
-    }
-
-    /***********************************************************/
-    /* v is a sequence of SingleAssign. Return a vector of the */
-    /* same SingleAssign, but sorted in terms of the lhs.var.  */
-    /***********************************************************/
-    private static Vector SortSass(Vector vec)
-    {
-        Vector v = (Vector) vec.clone();
-        Vector r = new Vector(); // The sorted version of v.
-        while (v.size() > 0)
-        { // Good old n^2 insertion sort.
-            AST.SingleAssign candidate = (AST.SingleAssign) v.elementAt(0);
-            int indexC = 0;
-            for (int i = 1; i < v.size(); i++)
-            {
-                AST.SingleAssign sass = (AST.SingleAssign) v.elementAt(i);
-                if (candidate.lhs.var.compareTo(sass.lhs.var) > 0)
-                {
-                    indexC = i;
-                    candidate = sass;
-                }
-            }
-            r.addElement(candidate);
-            v.remove(indexC);
-        }
-        return r;
-    }
-
-    /***********************************************************************
-    * If vec is a StringVector representing an expression, then this       *
-    * returns the StringVector obtained by parenthesizing the expression   *
-    * if it may need parenthesizing.  This is used only to prevent         *
-    * parsing errors when the expression appears immediately to the right  *
-    * of an "=" in the spec.  This is a rare situation, so it would be     *
-    * nice to add the parentheses only if really necessary.  For now, the  *
-    * parentheses are added if one of the following tokens occur outside   *
-    * parentheses and not inside a string:                                 *
-    *                                                                      *
-    *   =                                                                  *
-    *   #                                                                  *
-    *   <  not followed by <                                               *
-    *   >  not followed by > or preceded by =                              *
-    *   |  preceded or followed by -                                       *
-    *   \  not followed by "o" or "X".                                     *
-    *   /  followed by "\"                                                 *
-    *                                                                      *
-    * Left parentheses are                                                 *
-    *                                                                      *
-    *   (  [  {  <<                                                        *
-    *                                                                      *
-    * The handling of "\" is a simplifying hack.  Lots of operators        *
-    * beginning with "\" like "\/", "\gg" and "\subseteq" have precedence  *
-    * greater than or equal to "=".  The only commonly used ones with      *
-    * precedence lower than "=" seem to be "\o" and "\X".  It doesn't      *
-    * seem to be worth the bother of checking for the others just to       *
-    * avoid unnecessarily adding the parentheses when those other rare     *
-    * operators are used.                                                  *
-    *                                                                      *
-    * Perhaps the one improvement that might be worth making in this       *
-    * procedure is to have it not add parentheses because of "dangerous"   *
-    * operations in an IF clause--for example:                             *
-    *                                                                      *
-    *      IF x < 0 THEN ...                                               *
-    *                                                                      *
-    * This would require considering "IF" to be a left parenthesis and     *
-    * "THEN" to be a right parenthesis.  However, that's not trivial to    *
-    * implement because of unlikely things like                            *
-    *                                                                      *
-    *     IFx := 42 ;                                                      *
-    *     x := IFx < THENx                                                 *
-    ***********************************************************************/
-    private static Vector Parenthesize(Vector vec)
-    {
-        /*********************************************************************
-        * Add the parentheses if necessary.                                  *
-        *********************************************************************/
-        if (NeedsParentheses(vec))
-        {
-            vec.setElementAt("(" + ((String) vec.elementAt(0)), 0);
-            for (int i = 1; i < vec.size(); i++)
-            {
-                vec.setElementAt(" " + ((String) vec.elementAt(i)), i);
-            }
-            ;
-            int curLineNum = vec.size() - 1;
-            vec.setElementAt(((String) vec.elementAt(curLineNum)) + ")", curLineNum);
-        }
-        ;
-        return vec;
-    }
-    
-    /**
-     * As part of adding the TLA to PCal translation code, LL removedseparated 
-     * the code that decides if parentheses are needed from the Parenthesize 
-     * method and put it into this method.  The Parenthesize method itself
-     * will not be needed.
-     */
-    public static boolean  NeedsParentheses(Vector vec) {
-        if (vec.size() == 0)
-        {
-            return false;
-        }
-        ;
-        /*******************************************************************
-        * vec shouldn't be empty, but let's not worry about what to do if  *
-        * it is.                                                           *
-        *******************************************************************/
-        int curCharNum = 0;
-        int curLineNum = 0;
-        int parenDepth = 0;
-        boolean inString = false;
-        boolean needParen = false;
-        while ((curLineNum < vec.size()) && (!needParen))
-        {
-            String curLine = (String) vec.elementAt(0);
-            while ((curCharNum < curLine.length()) && (!needParen))
-            {
-                char curChar = curLine.charAt(curCharNum);
-
-                if (inString)
-                {
-                    switch (curChar) {
-                    case '\"':
-                        inString = false;
-                        break;
-                    case '\\':
-                        curCharNum++;
-                        break;
-                    }
-                    ; // end switch
-                } // end if (inString)
-                else
-                {
-                    boolean leftParen = false;
-                    boolean rightParen = false;
-                    boolean mayNeedParen = false;
-                    /***************************************************************
-                    * Set nextChar to the next character on the line, or ' ' if    *
-                    * there is none.                                               *
-                    ***************************************************************/
-                    char nextChar = ' ';
-                    if (curCharNum < curLine.length() - 1)
-                    {
-                        nextChar = curLine.charAt(curCharNum + 1);
-                    }
-                    switch (curChar) {
-                    case '\"':
-                        inString = true;
-                        break;
-                    case '=':
-                        mayNeedParen = true;
-                        break;
-                    case '#':
-                        mayNeedParen = true;
-                        break;
-                    case '<':
-                        if (nextChar == '<')
-                        {
-                            curCharNum++;
-                            leftParen = true;
-                        } else
-                        {
-                            mayNeedParen = true;
-                        }
-                        ;
-                        break;
-                    case '>':
-                        if (nextChar == '>')
-                        {
-                            curCharNum++;
-                            rightParen = true;
-                        } else
-                        {
-                            mayNeedParen = true;
-                        }
-                        ;
-                        break;
-                    case '|':
-                        if ((nextChar == '-') || ((curCharNum > 0) && (curLine.charAt(curCharNum - 1) == '-')))
-                        {
-                            mayNeedParen = true;
-                        }
-                        ;
-                        break;
-                    case '\\':
-                        if (!((nextChar == ' ') || (nextChar == 'o') || (nextChar == 'X')))
-                        {
-                            mayNeedParen = true;
-                        }
-                        ;
-                        break;
-                    case '/':
-                        if (nextChar == '\\')
-                        {
-                            mayNeedParen = true;
-                        }
-                        ;
-                        break;
-                    case '(':
-                    case '[':
-                    case '{':
-                        leftParen = true;
-                        break;
-                    case ')':
-                    case ']':
-                    case '}':
-                        rightParen = true;
-                        break;
-                    }
-                    ;
-                    if (mayNeedParen && (parenDepth == 0))
-                    {
-                        needParen = true;
-                    }
-                    ;
-                    if (leftParen)
-                    {
-                        parenDepth++;
-                    }
-                    ;
-                    if (rightParen)
-                    {
-                        if (parenDepth == 0)
-                        {
-                            needParen = true;
-                        }
-                        ;
-                        parenDepth--;
-                    }
-                }
-                ; // end else ! inString
-                curCharNum++;
-            }
-            ; // end while (curCharNum < curLine.length())
-
-            if (inString)
-            {
-                needParen = true;
-            }
-            ;
-            /*****************************************************************
-            * If there is an unmatched quote, we might as well stop here.    *
-            *****************************************************************/
-            curLineNum++;
-            curCharNum = 0;
-        } // end while (curLineNum < vec.size())
-        
-        return needParen;
-    }
-    
-    /*
-     * The following methods are used to add code to tlacode and add the
-     * appropriate objects to mappingVector
-     */
-    
-    /**
-     * Adds one token to tlacodeNextLine, and adds the appropriate
-     * Begin/EndTLAToken objects to mappingVectorNextLine.  The
-     * ...TLAToken objects mark a region that excludes beginning and
-     * ending space characters of token--unless the token has
-     * only space characters.
-     * 
-     * @param token
-     */
-    private void addOneTokenToTLA(String token) {
-        String trimmedToken = token.trim() ;
-        
-        int numberOfLeftTrimmedTokens = 
-           (trimmedToken.length() == 0) ? -1 :                   
-             token.indexOf(trimmedToken.charAt(0));
-        
-        /**
-         * Handle a token of only space characters. 
-         */
-        if (numberOfLeftTrimmedTokens == -1) {
-            numberOfLeftTrimmedTokens = 0 ;
-            trimmedToken = token ;
-        }
-        
-        int objBegin = tlacodeNextLine.length() + numberOfLeftTrimmedTokens;
-        mappingVectorNextLine.addElement(new MappingObject.BeginTLAToken(objBegin));
-        mappingVectorNextLine.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length()));
-        tlacodeNextLine = tlacodeNextLine + token;
-    }
-    
-    /**
-     * If region is non-null, then adds string str to the TLA output
-     * as a SourceToken object with that region.  Otherwise, it adds
-     * it as a TLAToken, with Begin/EndTLAToken objects.
-     * 
-     * @param str
-     * @param region
-     */
-    private void addOneSourceTokenToTLA(String str, Region region) {
-        if (region == null) {
-            addOneTokenToTLA(str);
-            return;
-        }
-        
-        int beginCol = tlacodeNextLine.length();
-        int endCol = beginCol + str.length();
-        mappingVectorNextLine.addElement(
-                new MappingObject.SourceToken(beginCol, endCol, region));
-             tlacodeNextLine = tlacodeNextLine + str;
-    }
-    /**
-     * Adds a complete line of TLA "code" that does not correspond to
-     * any PlusCal code.  Adds Begin/EndTLAToken objects to the mapping
-     * iff the line does not equal "".
-     * 
-     * @param line
-     */
-    private void addOneLineOfTLA(String line) {
-// temporarily commented out.
-        if(tlacode.size() != mappingVector.size()) {
-            PcalDebug.ReportBug("tlacode and mappingVector have different lengths") ;
-        }
-// The following added during testing.
-//if(tlacode.size() != mappingVector.size()) {
-//   System.out.println("tlacode and mappingVector have different lengths");
-//}
-        endCurrentLineOfTLA();
-        if (line.length() == 0) {
-            mappingVector.addElement(new Vector(2));
-            tlacode.addElement("");
-            return;
-        }
-        addOneTokenToTLA(line);
-        endCurrentLineOfTLA();
-    }
-    
-    /**
-     * If tlacodeNextLine does not equal "", then add it to tlacode
-     * and add mappingVectorNextLine to mappingVector.  If it does equal "",
-     * don't add any lines, but add any potentially legal leftovers in 
-     * mappingVectorNextLine to the previous line.
-     */
-    private void endCurrentLineOfTLA() {
-        if (tlacodeNextLine.length() != 0) {
-            tlacode.addElement(tlacodeNextLine) ;
-            mappingVector.addElement(mappingVectorNextLine) ;
-            tlacodeNextLine = "";
-            mappingVectorNextLine = new Vector() ;
-        } 
-        else {
-            if (mappingVectorNextLine.size() != 0) {
-                /*
-                 * There's something to go in the mappingVector that doesn't
-                 * accompany any text.  It should be one or more RightParen or
-                 * LeftParen objects, (or perhaps, eventually a Break), in which 
-                 * case they should be put at the end of the previous mappingVector 
-                 * line.  Anything else is a mistake.
-                 */
-                Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1);
-                for (int i = 0; i < mappingVectorNextLine.size(); i++) {
-                    MappingObject obj = (MappingObject) mappingVectorNextLine.elementAt(i);
-                    if (obj.getType() == MappingObject.RIGHT_PAREN ||
-                        obj.getType() == MappingObject.LEFT_PAREN||
-                        obj.getType() == MappingObject.BREAK) {
-                       lastLine.add(obj); 
-                    }
-                    else {
-                        PcalDebug.ReportBug("PcalTLAGen.endCurrentLineOfTLA found problem.");
-                    }
-                    mappingVectorNextLine = new Vector() ;
-                }
-            }
-        }
-    }
-    
-    /**
-     * Adds the expression to tlacode / tlacodeNextLine and its
-     * mapping to mappingVector / mappingVectorNextLine.  It adds
-     * no space before the expression and leaves the last line of the
-     * expression (which could be its first line) at the end of 
-     * tlacodeNextLine.
-     * @param expr
-     */
-    private void addExprToTLA(TLAExpr expr) {
-        Vector sv = expr.toStringVector() ;
-        Vector exprMapping = expr.toMappingVector() ;
-        int indent = tlacodeNextLine.length() ; 
-        int nextLine = 0 ; 
-        if (indent != 0) {
-            /*
-             * Need to combine first line of expr with 
-             * tlacodeNextLine.
-             */
-            MappingObject.shiftMappingVector(exprMapping, indent);
-            tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0));
-            mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0));
-            nextLine = 1;
-            if (sv.size() > 1) {
-               endCurrentLineOfTLA();
-            }
-        }
-        if (sv.size() > 1) {
-            String spaces = NSpaces(indent);
-            while (nextLine < sv.size()-1) {
-                tlacode.addElement(spaces + ((String) sv.elementAt(nextLine)));
-                mappingVector.addElement((Vector) exprMapping.elementAt(nextLine));
-                nextLine++ ;
-            }
-            tlacodeNextLine = spaces + ((String) sv.elementAt(nextLine)) ;
-            mappingVectorNextLine = (Vector) exprMapping.elementAt(nextLine);
-        }
-        else if (indent == 0){
-            /*
-             * If indent != 0, then we've already added the one-line expression.
-             */
-            tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0));
-            mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0));
-        }
-    }
-    
-    /**
-     * Subroutine of GenInit that adds to the TLA translation the Init conjunct 
-     * corresponding to the VarDecl decl for a global variable and, in a uniprocess
-     * algorithm for a procedure or process variable It is called with the 
-     * StringBuffer `is' containing the text that precedes the "/\" of the 
-     * conjunct, which will be "Init == " or just spaces.
-     * 
-     * @param decl
-     * @param is
-     */
-    private void addVarDeclToTLA(VarDecl decl, StringBuffer is) {
-        Region origin = decl.getOrigin();
-        is.append("/\\ ");
-        addOneTokenToTLA(is.toString());
-        addLeftParen(decl.getOrigin());
-//        is.append(decl.var);
-        is = new StringBuffer(decl.var);
-        if (decl.isEq)
-            is.append(" = ");
-        else
-            is.append(" \\in ");
-//        int col2 = is.length();
-//        Vector sv = Parenthesize(decl.val.toStringVector());
-//        /*********************************************************
-//        * Call to Parenthesize added by LL on 27 Feb 2008.       *
-//        * See bug_08-02-18.                                      *
-//        *********************************************************/
-//        is.append((String) sv.elementAt(0));
-//        for (int v = 1; v < sv.size(); v++)
-//        {
-//            tlacode.addElement(is.toString());
-//            is = new StringBuffer(NSpaces(col2));
-//            is.append((String) sv.elementAt(v));
-//        }
-//        tlacode.addElement(is.toString());
-        addOneTokenToTLA(is.toString());
-        addLeftParen(decl.val.getOrigin());
-        boolean needsParens = NeedsParentheses(decl.val.toStringVector());
-        if (needsParens) {
-            addOneTokenToTLA("(");
-        }
-        addExprToTLA(decl.val);
-        if (needsParens) {
-            addOneTokenToTLA(")");
-        }
-        addRightParen(decl.val.getOrigin());
-        addRightParen(decl.getOrigin());
-        endCurrentLineOfTLA();
-    }
-    
-    /**
-     * Adds a MappingObject.LeftParen object to the mapping vector
-     * for the beginning of the Region region, if it's not null.
-     * @param region
-     */
-    private void addLeftParen(Region region) {
-        if (region != null) {
-          mappingVectorNextLine.addElement(
-              new MappingObject.LeftParen(region.getBegin()));
-        }
-    }
-    
-    /**
-     * Adds a MappingObject.LeftParen object to the mapping vector
-     * for the beginning of the Region region, if it's not null.
-     * @param region
-     */
-    private void addRightParen(Region region) {
-        if (region != null) {
-          mappingVectorNextLine.addElement(
-              new MappingObject.RightParen(region.getEnd()));
-        }
-    }
-    
-    /**
-     * Like addLeftParen(ast.getOrigin()), except that it uses
-     * loc the location if it is not null.  It is called by
-     * GenLabeledStmt and ast.getOrigin() should never be null.
-     * However, if it is, then we don't add a Paren; this insures 
-     * that the matching calls of addLeftParenV and addRightParenV
-     * both either do or don't add a Paren.
-     *  
-     * @param ast
-     */
-    private void addLeftParenV(AST ast, PCalLocation loc) {
-        if (ast.getOrigin() == null) {
-            return;
-        }
-        if (loc != null) {
-            mappingVectorNextLine.addElement(
-                    new MappingObject.LeftParen(loc));
-        }
-        else {
-            addLeftParen(ast.getOrigin());
-        }
-    }
-
-    /**
-     * Like addRightParen(ast.getOrigin()), except that it uses
-     * loc as the location if it is not null.  It is called by
-     * GenLabeledStmt and ast.getOrigin() should never be null.
-     * However, if it is, then we don't add a Paren; this insures 
-     * that the matching calls of addLeftParenV and addRightParenV
-     * both either do or don't add a Paren.
-     *  
-     * @param ast
-     */
-    private void addRightParenV(AST ast, PCalLocation loc) {
-        if (ast.getOrigin() == null) {
-            return;
-        }
-        if (loc!= null) {
-            mappingVectorNextLine.addElement(
-                    new MappingObject.RightParen(loc));
-        }
-        else {
-            addRightParen(ast.getOrigin());
-        }
-    }
-/* -------------------------------------------------------------------------- */
-    /*
-     * The following methods and classes of objects are used in GenSpec().
-     * See the comments preceding that method above.
-     */
-    
-    /**
-     * A FormulaPair should never have wf = null, but might have sf = null.
-     */
-    public static class FormulaPair {
-    	public String wf ;
-    	public String sf ;
-    	
-    	public FormulaPair(String wfVal, String sfVal) {
-    		this.wf = wfVal;
-    		this.sf = sfVal;
-    	}
-    	
-    	/**
-    	 * The string  wf /\ sf , or just wf if sf is null.
-    	 * @return
-    	 */
-    	public String singleLine() {
-    		if (sf == null) {
-    			return wf ;
-    		} 
-    		return wf + " /\\ " + sf ;
-    	}
-    	
-    	/**
-    	 * The width of the singleLine representation of the 
-    	 * conjunction of the formlas.
-    	 * 
-    	 * @return
-    	 */
-    	public int singleLineWidth() {
-    	    if (sf == null) {
-                return wf.length() ;
-            } 
-            return wf.length() + " /\\ ".length() + sf.length() ;   		
-    	}
-    	
-    	/**
-    	 * The representation of the conjunction of the formulas with
-    	 * prefix /\s, where the first /\ appears in column col (Java
-    	 * numbering), witout any ending "\n"
-    	 * 
-    	 * @return
-    	 */
-    	public String multiLine(int col) {
-    		String val = "/\\ " + wf ;
-    		if (sf == null) {
-    			return val;
-    		}
-    		return val + "\n" + NSpaces(col) + "/\\ " + sf;
-    	}
-    }
-    
-    /**
-     * Describes a process fairness formula, as described in the comments
-     * preceding the  GetSpec() method above.
-     * @author lamport
-     *
-     */
-    public static class ProcessFairness {
-    	public String xf ; // either "WF" or "SF"
-    	public Vector  prefix ; 
-    	    // StringVector either "\A self \in exp : " or
-    	    // "LET self == exp \n IN " (note the ending space) or ""
-    	public FormulaPair bodyFormulas ; // fairness conditions for the proc's body
-    	public Vector prcdFormulas ; // fairness conditions for the procedure
-
-    	/** 
-    	 * The constructor
-    	 * @param xfVal
-    	 * @param prefixVal
-    	 * @param bodyWF : can be null if bodySF is also null
-    	 * @param bodySF : can be null
-    	 * @param prcdVal
-    	 */
-    	public ProcessFairness (String xfVal, Vector  prefixVal, String bodyWF,
-    			                String bodySF, Vector  prcdVal) {
-    		xf = xfVal;
-    		prefix = prefixVal;
-    		bodyFormulas = null ;
-    		if (bodyWF != null) {
-    			bodyFormulas = new FormulaPair(bodyWF, bodySF);
-    		}
-    		prcdFormulas = prcdVal;
-    	}
-    	/**
-    	 * The width of the fairness formula written as a "single-line"
-    	 * formula.  Single-line means that it is not written as a 
-    	 * conjunction list (with a leading /\).  It will actually
-    	 * occupy multiple lines if prefix is a multi-line formula.
-    	 * 
-    	 * @return
-    	 */
-    	public int singleLineWidth() {
-    	    // Set maxPrefixWidth to length of longest non-final
-    	    // line of prefix, width to lenght of final line
-    	    int maxPrefixWidth = 0 ;
-    	    int width = 0  ;
-    	    if (prefix != null && prefix.size() > 0) {
-    	        for (int i = 0; i < prefix.size() - 1; i++) {
-    	            String line =  (String) prefix.elementAt(i);
-    	            if (line.length() > maxPrefixWidth) {
-    	                maxPrefixWidth = line.length();
-    	            }
-    	            String lastLine = (String) prefix.elementAt(prefix.size()-1);
-    	            width = lastLine.length();
-    	        }
-    	    }
-    	    width = width + bodyFormulas.wf.length();
-            if (bodyFormulas.sf != null) {
-                 width = width + bodyFormulas.sf.length();
-            } 
-            if (prcdFormulas != null) {
-                for (int i = 0 ; i < prcdFormulas.size(); i++) {
-                    width = width + ((FormulaPair) prcdFormulas.elementAt(i)).singleLineWidth();
-                }
-            }
-            if (maxPrefixWidth > width) {
-                return maxPrefixWidth;
-            }
-            return width ;
-    	}
-    	
-    	/**
-    	 * Returns the prefix as a StringBuffer, assuming it starts
-    	 * in column col.  That is, all but the first line is indented
-    	 * with col spaces, and all but the last line is ended with
-    	 * a \n .
-    	 * 
-    	 * @param col
-    	 * @return
-    	 */
-    	private StringBuffer prefixAsStringBuffer(int col) {
-    	    StringBuffer val = new StringBuffer();
-            if (prefix != null && prefix.size() > 0) {
-                for (int i = 0; i < prefix.size(); i++) {
-                    String line =  (String) prefix.elementAt(i);
-                    if (i != 0) {
-                        val.append(NSpaces(col));
-                    }
-                    val.append(line) ;
-                    if (i != prefix.size()-1) {
-                        val.append("\n") ;
-                    }                   
-                }
-            }
-            return val;
-    	}
-    	/**
-    	 * The process fairness condition written as a single-line formula,
-    	 * starting in column col.
-    	 * @return
-    	 */
-    	public StringBuffer singleLine(int col) {
-    	    StringBuffer val = prefixAsStringBuffer(col);
-    	    val.append(bodyFormulas.wf);
-    		if (bodyFormulas.sf != null) {
-    			val.append(" /\\ ");
-    			val.append(bodyFormulas.sf);
-    		} 
-    		if (prcdFormulas != null) {
-    			for (int i = 0 ; i < prcdFormulas.size(); i++) {
-    			    val.append(" /\\ ");
-    				val.append(((FormulaPair) prcdFormulas.elementAt(i)).singleLine());
-    			}
-    		}
-    		return val ;
-    	}
-    	
-    	/**
-    	 * Returns true iff format(col) should return a single-line version
-    	 * of the formula.
-    	 * 
-    	 * @param col
-    	 * @return
-    	 */
-    	private boolean fitsAsSingleLine(int col) {
-    	    return     (col + singleLineWidth() <= PcalTLAGen.wrapColumn)
-                    || (bodyFormulas.sf == null 
-                        && (prcdFormulas == null || prcdFormulas.size() == 0));
-    	}
-    	/**
-    	 * The process fairness condition written as a formula that
-    	 * begins in column col (Java numbering) and ends with "\n".
-    	 * It is formatted to try to extend no further than column 
-    	 * PcalTLAGen.wrapColumn, but no individual formula is split
-    	 * across lines.
-    	 * 
-    	 * @param col
-    	 * @return
-    	 */
-    	public StringBuffer format(int col) {
-    		int singleLineWidth = this.singleLineWidth();
-    		/*
-    		 * Return the single-line form if either it fits on the
-    		 * line or if it consists of only the wf formula (so it can't
-    		 * be put on multiple lines).
-    		 */
-    		if (fitsAsSingleLine(col)) {
-    		    return this.singleLine(col);
-    		}
-    		StringBuffer val = prefixAsStringBuffer(col);
-    		int prefixWidth = 0;
-    		if (prefix != null && prefix.size() > 0) {
-    		    prefixWidth = ((String) prefix.elementAt(prefix.size()-1)).length();
-    		}
-    		int curCol = col + prefixWidth;
-    		String line = this.bodyFormulas.singleLine();
-    		if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) {
-    		   val.append("/\\ " + line);
-    		} else {
-    			val.append(this.bodyFormulas.multiLine(curCol));
-    		}
-    		if (prcdFormulas == null) {
-    			return val;
-    		}
-    		for (int i = 0; i < this.prcdFormulas.size(); i++) {
-    		    FormulaPair form = (FormulaPair) this.prcdFormulas.elementAt(i) ;
-    		    line = form.singleLine();
-    		    // On 2 Apr 2013, LL discovered the following totally bizarre
-    		    // line of code, which inserted copies of "THIS_EXTRA_SPACE_INSERTED" into
-    		    // the translation, which of course then didn't parse.  Apparently some
-    		    // change was made and never tested.  The conjuncts being inserted here
-    		    // seem to be the fairness formulas for procedures in a fair process.
-    		    //
-    		    //val.append("\nTHIS_EXTRA_SPACE_INSERTED");
-    		    
-    		    // One experiment seems to indicate that the following statement is needed
-    		    // to put the first of the procedures' liveness formulas where it belongs.
-    		    // However, I don't understand the code so I have no idea what actually
-    		    // should be done.  LL 2 Apr 2013
-    		    // 
-    		    if (i == 0) {
-    		        val.append("\n") ;
-    		    }
-    		    val.append(NSpaces(curCol));
-    		    if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) {
-    	    		   val.append("/\\ " + line + "\n");
-    	    		} else {
-    	    			val.append(form.multiLine(curCol));
-    	    		}
-    		}
-    		return val;
-    	}
-
-    	
-    }
-}
+package pcal;
+
+import java.util.Vector;
+
+import pcal.AST.VarDecl;
+import pcal.exception.PcalTLAGenException;
+import pcal.exception.TLAExprException;
+import util.TLAConstants;
+
+/****************************************************************************
+ * Given an exploded and disambiguated AST, generate the equivalent TLA+.
+ * <br>
+ * {@link PcalTLAGen#generate(AST, PcalSymTab)} returns a vector of Strings, one entry per line of generated TLA+.
+ * 
+ * @version $Id$ 
+ * @author Leslie Lamport (modified on Thu  6 March 2008 at 10:16:22 PST)
+ *                        (minor change on 9 December 2009)            
+ * @author keith (modified on Mon  3 Oct 2005 at 21:43:09 UT)                  
+ *                                                                          
+ ****************************************************************************/
+public class PcalTLAGen
+{
+    // Constants that control formatting
+    public final static boolean boxUnderCASE = true; /* else [] at end of line  */
+
+    // The following two variables made non-final on 9 Dec 2009 so they can
+    // be set by options.  They are initialized in PcalParams.resetParams().
+    public static int wrapColumn ; 
+       /* If the line width will be greater than this, then try to wrap */
+    public static int ssWrapColumn ; 
+       // I think that this is used as follows: 
+       //    when translating an assignment statement (or multiassignment?)
+       //    to  var' = [var EXCEPT ...],  it begins the ... on a new line
+       //    iff  the ... would begin in a column > ssWrapColumn.
+       // For the time being, it is set to wrapColumn - 33.  We may want
+       // to do something cleverer or else make it a user option.
+
+    // Private class variables
+    /** The tlacode field accumulates the translation as it is constructed.  It 
+     * should be a vector of separate lines.  Keiths original implementation put
+     * multiple lines in a single element of tlacode in:
+     *   
+     *   GenVarsAndDefs
+     *   GenVarDecl
+     */
+    private Vector tlacode = new Vector(); /* of lines */
+    
+    /**
+     * The tlacodeNextLine field accumulates characters for the next
+     * line of tlacode.  It is always a string.  It is assumed that
+     * when it equals "", then a new line can be started without
+     * adding the current string in tlacodeNextLine as a new line.
+     */
+    private String tlacodeNextLine = "" ;
+    
+    /**
+     * mappingVector is a local pointer to {@link TLAtoPCalMapping#mappingVector},
+     * which is used to accumulate the TLA+ to PlusCal mapping.  It approximately
+     * reflects the TLA+ that has been inserted in the {@link PcalTLAGen#tlacode}  
+     * vector.  It is set in the {@link TLAtoPCalMapping#generate} method.
+     */
+    private Vector mappingVector;
+    
+    /**
+     * mappingVectorNextLine contains the sequence of MappingObject objects
+     * that correspond to the strings added to tlacodeNextLine.
+     */
+    private Vector mappingVectorNextLine = new Vector() ;
+    
+    /**
+     * The self field is set to "self" by GenProcess when called for a single process
+     * (rather than a process set) and by GenProcedure for a multiprocess algorithm. 
+     * It is set to the process id by GenProcess when called for a single process.
+     * selfIsSelf is set to true when self is set to "self", and to false when self is
+     * set to a process id.  The self field never seems to be reset to null.
+     */
+    private TLAExpr self = null; // changed by LL on 22 jan 2011 from: private String self = null; /* for current process */
+    private boolean selfIsSelf = false; 
+    
+    private final Vector<String> vars = new Vector<String>(); /* list of all disambiguated vars */
+    private Vector pcV = new Vector(); /* sublist of vars of variables representing 
+                                          procedure parameters and procedure variables */
+    private Vector psV = new Vector(); /* sublist of vars local to a process set */
+    private PcalSymTab st = null; /* symbol table */
+    private boolean mp = false; /* true if multiprocess, else unip */
+    private Vector nextStep = new Vector(); /* unparam actions */ // For multiprocess alg, these are the individual (=) processes
+    private Vector nextStepSelf = new Vector(); /* param actions */ // These are process sets (\in processes) and procedures
+    // Following added to keep track of the length of the "lbl... == /\ "
+    // that precedes all the statements in the definition of a label's action
+    // because Keith screwed up and handled the assignment to the pc different 
+    // from that of all other variables, forgetting that the subscript exp
+    // in pc[exp] := ... can be multi-line.
+    private int kludgeToFixPCHandlingBug ;
+    /**
+     * The public method: generate TLA+ as a vector of strings. 
+     * @param ast the AST of the PCal
+     * @param symtab the symbol table
+     * @return the vector of strings with TLA+ code
+     * @throws PcalTLAGenException on any unrecoverable methods
+     */
+    
+    /*
+     * The  string currentProcName is the name of the current process (for a multiprocess algorithm)
+     * or "Next" for a uniprocess algorithm.  When ParseAlgorithm.omitPC is true, this is used
+     * instead of the label when generating the process's or the entire algorithm's next-state
+     * action.  Thus, with a single label lbl, this generates
+     *    Next == Xlation
+     * instead of
+     *    lbl == Xlation
+     *    Next == lbl
+     */
+    private String currentProcName ;
+    
+    /**
+     * Generates the translation.
+     * 
+     * @param ast  The AST produced by parsing and exploding.
+     * @param symtab The symbol table.
+     * @param report  A vector of strings, containing the reports of renaming.
+     * @return A vector of strings.
+     * @throws PcalTLAGenException
+     */
+    public Vector<String> generate(AST ast, PcalSymTab symtab, Vector report) throws PcalTLAGenException
+    {
+        TLAtoPCalMapping map = PcalParams.tlaPcalMapping;
+        mappingVector = new Vector<String>(50);
+        /*
+         * Add the reports of renaming to the output.
+         */
+        for (int i = 0; i < report.size(); i++) {
+            addOneLineOfTLA((String) report.elementAt(i));
+        }
+        
+        st = symtab;
+        GenSym(ast, "");
+        
+        /*
+         * We put at the beginning and end of mappingVector a LeftParen 
+         * and RightParen with location (0, 0), so that location will
+         * be found by the TLA+ to PCal translation algorithm if the
+         * user selects the entire algorithm, in which case it will
+         * return the null region to GotoPCalSourceHandler.execute.
+         */
+        PCalLocation ZeroLocation = new PCalLocation(0, 0);
+        ((Vector) mappingVector.elementAt(0)).
+             add(0, new MappingObject.LeftParen(ZeroLocation));
+        Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1);
+        lastLine.add(lastLine.size(), new MappingObject.RightParen(ZeroLocation));
+
+        /*
+         * For testing, throw a null pointer exception if the parentheses are not
+         * properly matching in mappingVector.
+         */
+        //int[] depths = new int[10000];
+        
+        int parenDepth = 0;
+        for (int i = 0; i < mappingVector.size(); i++) {
+            Vector line = (Vector) mappingVector.elementAt(i);
+            for (int j = 0; j < line.size(); j++) {
+                MappingObject obj = (MappingObject) line.elementAt(j);
+                if (obj.getType() == MappingObject.LEFT_PAREN) {
+                    parenDepth++;
+                }
+                else if (obj.getType() == MappingObject.RIGHT_PAREN) {
+                    parenDepth--;
+                    if (parenDepth < 0) {
+                        throw new NullPointerException("paren depth < 0");
+                    }
+                }
+            }
+        // depths[i] = parenDepth;
+        }
+        if (parenDepth != 0) {
+            throw new NullPointerException("Unmatched Left Paren");
+        }
+        /*   ------------------ end testing --------------------------*/
+        Vector nonredundantMappingVector = 
+            TLAtoPCalMapping.RemoveRedundantParens(mappingVector);
+        map.makeMapping(nonredundantMappingVector);
+        
+//System.out.println("Original mappingvector:");
+//MappingObject.printMappingVector(mappingVector);    
+//System.out.println("RemoveRedundantParens(mappingVector)");
+//MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(mappingVector));
+//System.out.println("Should be original mappingvector:");
+//MappingObject.printMappingVector(mappingVector); 
+// Debugging
+//for (int i = 0; i < tlacode.size(); i++) {
+//System.out.println("\nline " + i);
+//System.out.println((String) tlacode.elementAt(i)) ;
+//}
+//MappingObject.printMappingVector(mappingVector);
+
+        return tlacode;
+    }
+
+    /****************************************************************/
+    /* Returns whether the string is present in a vector of string. */
+    /****************************************************************/
+    private static boolean InVector(String var, Vector v)
+    {
+        for (int i = 0; i < v.size(); i++)
+            if (var.equals((String) v.elementAt(i)))
+                return true;
+        return false;
+    }
+
+    /******************************************************/
+    /* True if var is in the list of procedure variables. */
+    /******************************************************/
+    private boolean IsProcedureVar(String var)
+    {
+        return InVector(var, pcV);
+    }
+
+    /****************************************************/
+    /* True if var is in the list of process variables. */
+    /****************************************************/
+    private boolean IsProcessSetVar(String var)
+    {
+        return InVector(var, psV);
+    }
+
+    /**********************************************/
+    /* Returns a string of length n of all spaces */
+    /**********************************************/
+    private static String NSpaces(int n)
+    {
+        StringBuffer sb = new StringBuffer();
+        AddSpaces(sb, n);
+        return sb.toString();
+    }
+
+    /*********************************************/
+    /* Appends n spaces to the string buffer sb. */
+    /*********************************************/
+    private static void AddSpaces(StringBuffer sb, int num)
+    {
+        for (int i = 0; i < num; i++)
+            sb.append(" ");
+    }
+
+    /****************************************/
+    /* True if expr is an empty expression. */
+    /****************************************/
+    private static boolean EmptyExpr(TLAExpr expr)
+    {
+        if (expr == null)
+            return true;
+        if (expr.tokens == null || expr.tokens.size() == 0)
+            return true;
+        return false;
+    }
+
+    /*****************************************************************/
+    /* Top level routines. Context is "", "procedure", or "process". */
+    /**
+     ****************************************************************/
+    private void GenSym(AST ast, String context) throws PcalTLAGenException
+    {
+        if (ast.getClass().equals(AST.UniprocessObj.getClass()))
+            GenUniprocess((AST.Uniprocess) ast, context);
+        else if (ast.getClass().equals(AST.MultiprocessObj.getClass()))
+            GenMultiprocess((AST.Multiprocess) ast, context);
+        else if (ast.getClass().equals(AST.ProcedureObj.getClass()))
+            GenProcedure((AST.Procedure) ast, context);
+        else if (ast.getClass().equals(AST.ProcessObj.getClass()))
+            GenProcess((AST.Process) ast, context);
+        else if (ast.getClass().equals(AST.LabeledStmtObj.getClass()))
+            GenLabeledStmt((AST.LabeledStmt) ast, context);
+    }
+
+    private void GenUniprocess(AST.Uniprocess ast, String context) throws PcalTLAGenException
+    {
+        mp = false;
+        currentProcName = "Next";
+        GenVarsAndDefs(ast.decls, ast.prcds, null, ast.defs);
+        GenInit(ast.decls, ast.prcds, null);
+        for (int i = 0; i < ast.prcds.size(); i++)
+            GenProcedure((AST.Procedure) ast.prcds.elementAt(i), "");
+        for (int i = 0; i < ast.body.size(); i++)
+        {
+            AST.LabeledStmt ls = (AST.LabeledStmt) ast.body.elementAt(i);
+            /* Add this step to the disjunct of steps */
+            nextStep.addElement(ls.label);
+            GenLabeledStmt(ls, "");
+        }
+        GenNext();
+        GenSpec();
+        GenTermination();
+    }
+
+    private void GenMultiprocess(AST.Multiprocess ast, String context) throws PcalTLAGenException
+    {
+        mp = true;
+        GenVarsAndDefs(ast.decls, ast.prcds, ast.procs, ast.defs);
+        GenProcSet();
+        GenInit(ast.decls, ast.prcds, ast.procs);
+        for (int i = 0; i < ast.prcds.size(); i++)
+            GenProcedure((AST.Procedure) ast.prcds.elementAt(i), "");
+        for (int i = 0; i < ast.procs.size(); i++)
+            GenProcess((AST.Process) ast.procs.elementAt(i), "");
+        GenNext();
+        GenSpec();
+        GenTermination();
+    }
+
+    private void GenProcedure(AST.Procedure ast, String context) throws PcalTLAGenException {
+        /*
+         * First, generate the body's actions.  Must set self and selfIsSelf (?) for
+         * use by GenLabeledStmt.
+         */
+        if (mp) {
+      
+            self = selfAsExpr(); // subscript for variables is "self"
+            selfIsSelf = true;
+//            /* Add this step to the disjunct of steps with (self) */
+            nextStepSelf.addElement(ast.name + "(self)");
+        } else
+        {
+            /* Add this step to the disjunct of steps */
+            nextStep.addElement(ast.name);
+        }
+        for (int i = 0; i < ast.body.size(); i++) {
+            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
+            GenLabeledStmt(stmt, "procedure");
+        }
+        
+        /*
+         * Next add the definition of the procedure--e.g.,
+         * 
+         *    procedureName(self) == label_1(self) \/ ... \/ label_k(self)
+         *    
+         * We put Left/RightParens for the entire procedure around the entire 
+         * definition, and Left/RightParens around each disjunction for
+         * the labeled statement.
+         */
+        addLeftParen(ast.getOrigin());
+        String argument = (mp) ? "(self)" : "";
+        StringBuffer buf = new StringBuffer(ast.name + argument + " == ");
+        addOneTokenToTLA(buf.toString());
+        String indentSpaces = NSpaces(buf.length() + 2);        
+        for (int i = 0; i < ast.body.size(); i++) {
+            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
+            String disjunct = stmt.label + argument;
+            if (   i != 0 
+                && tlacodeNextLine.length() +  7 /* the 7 was obtained empirically */
+                    + disjunct.length() > wrapColumn) {
+                endCurrentLineOfTLA();
+            }
+            if (i != 0) {
+               addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); 
+            }
+            addLeftParen(stmt.getOrigin());
+            addOneTokenToTLA(disjunct);
+            addRightParen(stmt.getOrigin());
+        }
+        addRightParen(ast.getOrigin());
+        addOneLineOfTLA("");
+        
+// The previous version was very convoluted just to avoid having to go through the
+// list of labeled statements twice.  It seemed easier to just reimplement from
+// scratch.
+//        /* ns and nsV accumulate the disjunct of the steps of the procedure, where
+//         * ns contains the contents of the current line and nsV is the vector of
+//         * already accumulated lines.
+//         * 
+//         */
+//        StringBuffer ns = new StringBuffer();
+//        Vector nsV = new Vector();
+//        
+//       int nsC = ast.name.length() + ((mp) ? "(self)".length() : 0) + " == ".length();
+//        if (mp)
+//        {
+//            self = selfAsExpr(); // subscript for variables is "self"
+//            selfIsSelf = true;
+//            /* Add this step to the disjunct of steps with (self) */
+//            nextStepSelf.addElement(ast.name + "(self)");
+//        } else
+//        {
+//            /* Add this step to the disjunct of steps */
+//            nextStep.addElement(ast.name);
+//        }
+//        for (int i = 0; i < ast.body.size(); i++)
+//        {
+//            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
+//            if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((mp) ? "(self)".length() : 0)) > wrapColumn
+//                    - nsC - " \\/ ".length())
+//            {
+//                nsV.addElement(ns.toString());
+//                ns = new StringBuffer();
+//            }
+//            if (ns.length() > 0)
+//                ns.append(" \\/ ");
+//            ns.append(stmt.label);
+//            if (mp)
+//                ns.append("(self)");
+//            GenLabeledStmt(stmt, "procedure");
+//        }
+//        nsV.addElement(ns.toString());
+//        // Generate definition of procedure steps
+//        ns = new StringBuffer();
+//        ns.append(ast.name);
+//        if (mp)
+//            ns.append("(self)");
+//        ns.append(" == ");
+//        ns.append((String) nsV.elementAt(0));
+//        tlacode.addElement(ns.toString());
+//        for (int i = 1; i < nsV.size(); i++)
+//        {
+//            ns = new StringBuffer(NSpaces(nsC + 2));
+//            ns.append(" \\/ ");
+//            ns.append((String) nsV.elementAt(i));
+//            tlacode.addElement(ns.toString());
+//        }
+//        tlacode.addElement("");
+    }
+
+    private void GenProcess(AST.Process ast, String context) throws PcalTLAGenException
+    {
+        currentProcName = ast.name; 
+        
+        /*
+         * Generate the body's actions.   Must set self and selfIsSelf (?) for
+         * use by GenLabeledStmt.
+         */
+        boolean isSet = true;
+        /************************************************************/
+        /* Decide if it is a process set or not. If so, set self to */
+        /* the string "self"; otherwise set self to the process id. */
+        /************************************************************/
+        if (ast.isEq)
+        {
+            self = ast.id ;
+            selfIsSelf = false;
+            isSet = false;
+        } else {
+            self = selfAsExpr();
+            selfIsSelf = true;
+        }
+
+        if (isSet)
+        {
+            nextStepSelf.addElement(ast.name + "(self)");
+        } else
+            nextStep.addElement(ast.name);
+        
+        for (int i = 0; i < ast.body.size(); i++) {
+            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
+            GenLabeledStmt(stmt, "process");
+        }
+        
+        /*
+         * Next add the definition of the process--e.g.,
+         * 
+         *    processName(self) == label_1(self) \/ ... \/ label_k(self)
+         *    
+         * We put Left/RightParens for the entire procedure around the entire 
+         * definition, and Left/RightParens around each disjunction for
+         * the labeled statement.  
+         * 
+         * However, we don't add this definition if we are omitting the pc,
+         * because we have already defined the process name to equal the 
+         * only label action.
+         */
+
+      if (! ParseAlgorithm.omitPC) {
+        addLeftParen(ast.getOrigin());
+        String argument = (isSet) ? "(self)" : "";
+        StringBuffer buf = new StringBuffer(ast.name + argument + " == ");
+        addOneTokenToTLA(buf.toString());
+        String indentSpaces = NSpaces(buf.length() + 2);        
+        for (int i = 0; i < ast.body.size(); i++) {
+            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
+            String disjunct = stmt.label + argument;
+            if (   i != 0 
+                && tlacodeNextLine.length() + 7 /* the 7 was obtained empirically */
+                  + disjunct.length() > wrapColumn) {
+                endCurrentLineOfTLA();
+            }
+            if (i != 0) {
+               addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); 
+            }
+            addLeftParen(stmt.getOrigin());
+            addOneTokenToTLA(disjunct);
+            addRightParen(stmt.getOrigin());
+        }
+        addRightParen(ast.getOrigin());
+        addOneLineOfTLA("");
+      }
+
+        
+// As with GenProcedure, the original implementation was quite convoluted, so
+// it was rewritten.  The code above was copied with modifications from
+// the rewritten GenProcedure code.
+//        /* ns accumulates the disjunt of the steps of the process */
+//        StringBuffer ns = new StringBuffer();
+//        Vector nsV = new Vector();
+//        boolean isSet = true;
+//        /************************************************************/
+//        /* Decide if it is a process set or not. If so, set self to */
+//        /* the string "self"; otherwise set self to the process id. */
+//        /************************************************************/
+//        if (ast.isEq)
+//        {
+//            self = ast.id ;
+//            selfIsSelf = false;
+//            isSet = false;
+//        } else {
+//            self = selfAsExpr();
+//            selfIsSelf = true;
+//        }
+//        
+//        int nsC = ast.name.length() + ((isSet) ? "(self)".length() : 0) + " == ".length();
+//        if (isSet)
+//        {
+//            nextStepSelf.addElement(ast.name + "(self)");
+//        } else
+//            nextStep.addElement(ast.name);
+//        for (int i = 0; i < ast.body.size(); i++)
+//        {
+//            AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i);
+//            if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((isSet) ? "(self)".length() : 0)) > wrapColumn
+//                    - nsC - " \\/ ".length())
+//            {
+//                nsV.addElement(ns.toString());
+//                ns = new StringBuffer();
+//            }
+//            if (ns.length() > 0)
+//                ns.append(" \\/ ");
+//            ns.append(stmt.label);
+//            if (isSet)
+//                ns.append("(self)");
+//            GenLabeledStmt(stmt, "process");
+//        }
+//        nsV.addElement(ns.toString());
+//        // Generate definition of process steps
+//        // This apparently defines the process name
+//        // to equal the disjunction of all the individual
+//        // label-named actions.  If we are omitting
+//        // the pc, we have already defined the process name
+//        // to equal the only label action, so we skip
+//        // this.
+//        if (! ParseAlgorithm.omitPC) {
+//          ns = new StringBuffer();
+//          ns.append(ast.name);
+//          if (isSet)
+//              ns.append("(self)");
+//          ns.append(" == ");
+//          ns.append((String) nsV.elementAt(0));
+//          tlacode.addElement(ns.toString());
+//          for (int i = 1; i < nsV.size(); i++)
+//          {
+//              ns = new StringBuffer(NSpaces(nsC + 2));
+//              ns.append(" \\/ ");
+//              ns.append((String) nsV.elementAt(i));
+//              tlacode.addElement(ns.toString());
+//          }
+//        }
+//        tlacode.addElement("");
+    }
+
+    /*****************************************************/
+    /* Generates an action with name equal to the label. */
+    /**
+     ****************************************************/
+    private void GenLabeledStmt(AST.LabeledStmt ast, String context) throws PcalTLAGenException
+    {
+        // Set actionName to the name of the action being defined.
+        // This is the label, except when we are omitting the PC,
+        // in which case it is "Next" for a uniprocess algorithm
+        // and the process name for a multiprocess algorithm.
+        String actionName = ast.label;
+        if (ParseAlgorithm.omitPC) {
+            actionName = currentProcName;
+        }
+        StringBuffer sb = new StringBuffer(actionName);
+        /* c is used to determine which vars are in UNCHANGED. */
+        Changed c = new Changed(vars);
+        if (mp && (context.equals("procedure") || selfIsSelf)) { // self.equals("self")))
+            sb.append("(self)");
+        }   
+        sb.append(" == ");
+        int col = sb.length();
+        kludgeToFixPCHandlingBug = col;
+        // There's a problem here.  If ast.stmts.size() = 1, then we don't preface
+        // the statement's translation with "/\".  However, that means that if we 
+        // then add an UNCHANGED conjunct, we wind up with  
+        //   A
+        //    /\ UNCHANGED ...
+        // where A is the statement's translation.  This looks bad and could wind up
+        // putting the UNCHANGED inside prefix operator--e.g.,
+        //   x' = CHOOSE t : ...
+        //     /\ UNCHANGED ...
+        // This is seems to be a problem only when omitPC = true, since otherwise the
+        // testing and setting of pc ensures that there is more than one element in ast.stmts.
+        // What we do is always add the "/\ ", but remove it afterwards if there's only
+        // a single statement in ast.stmts and there is no UNCHANGED clause.
+        // The code for doing this is based on the observation that the contents of
+        // StringBuffer sb begin the next line added to tlacode.
+        //
+        /* if (ast.stmts.size() > 1) {  */
+            sb.append("/\\ ");
+            kludgeToFixPCHandlingBug = kludgeToFixPCHandlingBug + 3;
+        /* } */
+            
+        // We set defStartLine to the index of the next line added to tlacode and
+        // colAfterAnd to the column position in that line immediately following
+        // the added "/\ ".  This gives us the information needed to remove the
+        // "/\ " later from tlacode.
+        int defStartLine = tlacode.size();
+        int colAfterAnd = sb.length();
+        
+        /*
+         * Note: it would make sense for this method to insert sb into the tlacode
+         * output, but it seems safer to maintain the current structure in which
+         * each GenX for each statement type X does that.
+         */
+        
+        /*
+         * We set macroBeginLeft to the macroOriginBegin field of the first statement
+         * in ast.stmts with a non-null origin, and macroEndRight to the macroOriginEnd
+         * field of the last statement in ast.stmts with a non-null origin.
+         */
+        PCalLocation macroBeginLeft = null;
+        PCalLocation macroEndRight = null;
+        boolean nonNullNotFound = true;
+        for (int i = 0 ; i < ast.stmts.size(); i++) {
+           AST stmt = (AST) ast.stmts.elementAt(i);
+           if (stmt.getOrigin() != null) {
+               if (nonNullNotFound) {
+                   nonNullNotFound = false;
+                   macroBeginLeft = stmt.macroOriginBegin;
+               }
+               macroEndRight = stmt.macroOriginEnd;
+           }
+        }
+        
+        /*
+         * addLeftParenV used instead of addLeftParen because if the first statement
+         * that came from PlusCal code arose from a macro call, then we want the 
+         * location of the macro call rather than that of the macro's code.
+         */
+        addLeftParenV(ast, macroBeginLeft);
+        for (int i = 0; i < ast.stmts.size(); i++)
+        {
+            GenStmt((AST) ast.stmts.elementAt(i), c, context, sb.toString(), sb.length());
+            sb = new StringBuffer(NSpaces(col));
+            sb.append("/\\ ");
+        }
+        
+        /*
+         * Since the UNCHANGED conjunct just consists of TLATokens, with no
+         * SourceTokens, we can just use the old code, simply replacing each
+         * tlacode.addElement call with a call of addOneLineOfTLA--except that
+         * the last one is replaced with a call of addOneTokenToTLA so we can
+         * put the RightParen object in mappingVector.
+         */
+        Vector unc = c.Unchanged(wrapColumn - col - "/\\ UNCHANGED << ".length());
+        if (c.NumUnchanged() > 1)
+        {
+            sb = new StringBuffer(NSpaces(col));
+            sb.append("/\\ UNCHANGED << ");
+            int here = sb.length();
+            sb.append((String) unc.elementAt(0));
+            for (int i = 1; i < unc.size(); i++)
+            {
+//                tlacode.addElement(sb.toString());
+                addOneLineOfTLA(sb.toString());
+                sb = new StringBuffer(NSpaces(here));
+                sb.append((String) unc.elementAt(i));
+            }
+            sb.append(" >>");
+//            tlacode.addElement(sb.toString());
+            addOneTokenToTLA(sb.toString());
+        } else if (c.NumUnchanged() == 1) {
+        	// Change made by LL on 16 Mar 2011 so that, if there is a single
+        	// unchanged variable v, it produces v' = v if v is a short variable,
+        	// otherwise it produces UNCHANGED v
+        	if (c.Unchanged().length() > 5) {
+//              tlacode.addElement(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged());
+        	    addOneTokenToTLA(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged());
+        	} else {
+//        		tlacode.addElement(NSpaces(col) + "/\\ " + c.Unchanged() + "' = "
+//        				+ c.Unchanged());
+        		 addOneTokenToTLA(NSpaces(col) + "/\\ " + c.Unchanged() + "' = "
+                         + c.Unchanged());
+        	}
+        } else {
+           // No unchanged.  If there was only one conjunction, remove it.
+           // To do that, we must remove the "/\ " and then remove three spaces
+           // from all other lines that were added.  We must also modify the
+           // TLAToken objects appropriately in the corresponding lines of
+           // mappingVector
+           if (ast.stmts.size() == 1) {
+               for (int i = defStartLine; i < tlacode.size(); i++) {
+                  String line = (String) tlacode.elementAt(i);
+                  if (i == defStartLine) {
+                     // remove the "/\ " added                      
+                     tlacode.setElementAt(line.substring(0, colAfterAnd-3) +
+                                          line.substring(colAfterAnd, line.length()) , i);
+                     shiftMappingVectorTokensLeft(i, colAfterAnd, 3);
+                    
+                  } else {
+                     // Remove three blanks from any following lines.  We test the length 
+                     // of the line just in case one or more short (hopefully blank) lines 
+                     // have been added.
+                      if (line.length() > 3) {
+                          tlacode.setElementAt(line.substring(3, line.length()) , i);
+                          shiftMappingVectorTokensLeft(i, colAfterAnd, 3);
+                      }
+                  }
+               }
+           }
+        }
+        /*
+         * We call addRightParenV rather than addRightParen because it the last statement 
+         * that came from the PCal code arose from the expansion of a macro call, then we 
+         * want the RightParen's location to be the end of the call, not the end of the
+         * macro's code.
+         */
+        addRightParenV(ast, macroEndRight);
+        addOneLineOfTLA("");
+//        tlacode.addElement("");
+    }
+    
+    /**
+     * Adjusts the objects in line lineNum of mappingVector so all column
+     * numbers starting with startCol are decreased by `shift'.  If any Begin/EndTLAToken
+     * pairs are changed to have a non-positive width, a bug is reported.
+     * 
+     * Note: It is assumed that there is no aliasing of MappingTokens in mappingVector.
+     * That is, other than in transient local variables, the only pointer to a
+     * MappingToken in mappingVector is the single one in its line of mappingVector.
+     * I can't see how any aliasing of MappingTokens could arise.
+     * 
+     * This method is called only by GenLabeledStmts.
+     * 
+     * @param lineNum
+     * @param startCol
+     * @param shift
+     */
+    private void shiftMappingVectorTokensLeft(int lineNum, int startCol, int shift) {
+        boolean lastWasBeginTLAToken = false;
+        int lastBeginTLATokCol = -777; // to keep the compiler happy.
+        Vector line = (Vector) mappingVector.elementAt(lineNum);
+        for (int i = 0; i < line.size(); i++) {
+            MappingObject obj = (MappingObject) line.elementAt(i);
+            if (obj.getType() == MappingObject.BEGIN_TLATOKEN) {
+               MappingObject.BeginTLAToken tobj = (MappingObject.BeginTLAToken) obj;
+               int col = tobj.getColumn();
+               if (col >= startCol) {
+                   tobj.setColumn(col - shift);
+               }
+               lastWasBeginTLAToken = true;
+               lastBeginTLATokCol = tobj.getColumn();
+            }
+            else {
+                if (obj.getType() == MappingObject.END_TLATOKEN) {
+                    MappingObject.EndTLAToken tobj = (MappingObject.EndTLAToken) obj;
+                    int col = tobj.getColumn();
+                    if (col >= startCol) {
+                        tobj.setColumn(col - shift);
+                    }
+                    if (lastWasBeginTLAToken && tobj.getColumn() <= lastBeginTLATokCol) {
+                        PcalDebug.ReportBug(
+                         "PcalTLAGen.shiftMappingVectorTokensLeft created a null TLA Token");
+                    }
+                }
+                else if (obj.getType() == MappingObject.SOURCE_TOKEN) {
+                    MappingObject.SourceToken tobj = (MappingObject.SourceToken) obj;
+                    int col = tobj.getBeginColumn();
+                    if (col >= startCol) {
+                        tobj.setBeginColumn(col - shift);
+                    }
+                    col = tobj.getEndColumn();
+                    if (col >= startCol) {
+                        tobj.setEndColumn(col - shift);
+                    }
+                    
+                lastWasBeginTLAToken = false;
+            }
+            }
+        }
+        
+        
+    }
+
+    /***************************************************************************
+    * LL Comment added 27 Jan 2006:                                            *
+    *                                                                          *
+    * There is a basic flaw in the way GenStmt works.  It now generates the    *
+    * output on the fly.  This means that                                      *
+    *                                                                          *
+    * - There is no way to avoid the prefix /\ on a one-element conjunct       *
+    *   because GenStmt has no way of knowing if there's another conjunct      *
+    *   coming.                                                                *
+    *                                                                          *
+    * - The handling of the UNCHANGEDs of the THEN and ELSE clauses of         *
+    *   an IF is a kludge, because the UNCHANGED of the THEN clause is         *
+    *   output before it can be known.                                         *
+    *                                                                          *
+    * The correct way of doing things is to define GenStmt so it returns a     *
+    * sequence (vector) of string vectors, each string vector being a          *
+    * conjunction expression (without a leading /\ or any leading spaces) and  *
+    * the new Changed object (which it can do as it now does by modifying its  *
+    * Changed object argument).  It would also be useful to define a           *
+    * GenStmtSeq that simply calls GenStmt iteratively on a sequence of        *
+    * simple statements.  The method that calls GenStmtSeq would then add the  *
+    * Unchanged conjunct and call a method that returns a sequence of          *
+    * conjuncts and a prefix into a string vector containing the prefix and    *
+    * the necessary /\s.                                                       *
+    ***************************************************************************/
+
+    /*****************************************************************/
+    /* General entry for generating the TLA+ for a simple statement. */
+    /* Prefix is the prefix of the first line. Col is where to start */
+    /* subsequent lines (I think we could replace it with the length */
+    /* of prefix).                                                   */
+    /*                                                               */
+    /* And what on earth are `c' and `context'? LL                   */
+    /**
+     ****************************************************************/
+    private void GenStmt(AST ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
+    {
+        if (ast.getClass().equals(AST.AssignObj.getClass()))
+            GenAssign((AST.Assign) ast, c, context, prefix, col);
+        else if (ast.getClass().equals(AST.IfObj.getClass()))
+            GenIf((AST.If) ast, c, context, prefix, col);
+        // Either case added by LL on 27 Jan 2006
+        else if (ast.getClass().equals(AST.EitherObj.getClass()))
+            GenEither((AST.Either) ast, c, context, prefix, col);
+        else if (ast.getClass().equals(AST.WithObj.getClass()))
+            GenWith((AST.With) ast, c, context, prefix, col);
+        else if (ast.getClass().equals(AST.WhenObj.getClass()))
+            GenWhen((AST.When) ast, c, context, prefix, col);
+        else if (ast.getClass().equals(AST.PrintSObj.getClass()))
+            GenPrintS((AST.PrintS) ast, c, context, prefix, col);
+        else if (ast.getClass().equals(AST.AssertObj.getClass()))
+            GenAssert((AST.Assert) ast, c, context, prefix, col);
+        else if (ast.getClass().equals(AST.SkipObj.getClass()))
+            GenSkip((AST.Skip) ast, c, context, prefix, col);
+        else
+            PcalDebug.ReportBug("Unexpected AST type " + ast.toString());
+    }
+
+    /*****************************************************************/
+    /* Generates a sequence of single assigns. Since all of them are */
+    /* executed "at the same time", we accumulate the changes in a   */
+    /* separate Changed cThis, and use c to determine which vars in  */
+    /* the right hand side are primed.                               */
+    /**
+     * ***************************************************************/
+    
+    /**
+     * @param ast
+     * @param c
+     * @param context
+     * @param prefix
+     * @param col
+     * @throws PcalTLAGenException
+     */
+    private void GenAssign(AST.Assign ast, Changed c, String context, String prefix, int col)
+            throws PcalTLAGenException
+    {
+        Changed cThis = new Changed(c);
+        StringBuffer sb = new StringBuffer();
+//        Vector vlines = new Vector();
+        /*
+         * Sort the vector ast.ass so that assignments to the same variable
+         * follow one another.
+         */
+        ast.ass = SortSass(ast.ass);
+        
+        addOneTokenToTLA(prefix);
+        addLeftParen(ast.getOrigin());
+        
+        int i = 0;
+        int numAssigns = 0;
+        /*
+         * hasMultipleVars set true iff the assignment assigns values to
+         * more than one variable, and hence the statement's translation
+         * has multiple conjuncts.
+         */
+        boolean hasMultipleVars = false;
+        while (i < ast.ass.size())
+        {
+            AST.SingleAssign sF = (AST.SingleAssign) ast.ass.elementAt(i);
+            
+           /*
+             * Added by LL and MK on 16 May 2018:
+             * Report an error if the variable being assigned is not a 
+             * variable declared in the algorithm (either not declared
+             * at all or is a constant, bound identifier, ...).
+             */
+			if (!this.vars.contains(sF.lhs.var)) {
+				throw new PcalTLAGenException("Assignment to undeclared variable " + sF.lhs.var, sF);
+			}
+
+            int iFirst = i; 
+            int iLast = i;
+            boolean hasAssignmentWithNoSubscript = false;
+            boolean lastAssignmentHasNoSubscript = EmptyExpr(sF.lhs.sub);
+            AST.SingleAssign sL = (AST.SingleAssign) ast.ass.elementAt(i);
+            while (iLast < ast.ass.size() && sF.lhs.var.equals(sL.lhs.var))
+            {
+                if (lastAssignmentHasNoSubscript) {
+                    hasAssignmentWithNoSubscript = true;
+                }
+                iLast = iLast + 1;
+                if (iLast < ast.ass.size()) {
+                    sL = (AST.SingleAssign) ast.ass.elementAt(iLast);
+                    if (EmptyExpr(sL.lhs.sub)) {
+                        lastAssignmentHasNoSubscript = true;
+                    }
+                }
+            }
+            
+            /*
+             * If there are assignments to multiple variables, then this sets
+             * hasMultiplevars true on the first execution of the outer while loop. 
+             */
+            if (iLast != ast.ass.size()) {
+                hasMultipleVars = true;
+            }
+            
+            iLast = iLast - 1;
+            // All statements from iFirst to iLast are to the same variable
+            
+            /*
+             * Throws an error if there are multiple assignments to the variable
+             * in different statements, or if there are multiple assignments to
+             * the variable in this statement and at least one of them has no 
+             * subscript.
+             */
+            if (cThis.Set(sF.lhs.var) > 1 || 
+                 (iLast - iFirst > 0 && hasAssignmentWithNoSubscript)) {
+                /***********************************************************
+                * The following was changed by LL on 3 Mar 06 to use       *
+                * AST.location to properly report the location of an       *
+                * error in a line created by expanding a macro.            *
+                * However, it doesn't work very well otherwise.  This      *
+                * should be fixed.                                         *
+                ***********************************************************/
+                throw new PcalTLAGenException("Multiple assignment to " + sF.lhs.var, ast /* sF */);
+            }
+            numAssigns = numAssigns + 1;
+            Vector lines = new Vector(); // For collecting generated lines
+
+            if (hasMultipleVars) {
+                sb.append("/\\ ");
+            }
+            if (iFirst == iLast)
+            {
+                /*
+                 * This is a single assignment to the variable.
+                 */
+                AST.SingleAssign sass = sF;
+
+                addLeftParen(sass.getOrigin());
+                TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c);
+                TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c);
+                if (mp
+                        && (sass.lhs.var.equals("pc") || IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var) || sass.lhs.var
+                                .equals("stack")))
+                {
+                    /* Generate single assignment to variable with self subscript */
+                    sb.append(sass.lhs.var);
+                    sb.append("' = [");
+                    int wrapCol = sb.length() + 2;
+                    sb.append(sass.lhs.var);
+                    sb.append(" EXCEPT ");
+                    
+                    Vector selfAsSV = self.toStringVector();
+                    
+                    // The test for selfAsSV size added by LL on 22 Jan 2011
+                    // because wrapping screws up the kludgeToFixPCHandlingBug
+                    // hack.
+                    if ( (sb.length() + prefix.length() > ssWrapColumn)
+                         && (selfAsSV.size() == 0))
+                    {
+//                        lines.addElement(sb.toString());
+                        addOneLineOfTLA(sb.toString());
+                        sb = new StringBuffer(NSpaces(wrapCol));
+                    }
+                    sb.append("![");
+                    addOneTokenToTLA(sb.toString());
+                    addLeftParen(self.getOrigin());
+                    addExprToTLA(self);
+                    addRightParen(self.getOrigin());
+                    
+//                    
+//                    // following code was modified by LL on 22 Jan 2011 as part of
+//                    // fixing bug 11_01_13, which required modifications to handle
+//                    // the case where self is a multi-line formula, which can happen
+//                    // for a "process (P = exp)" when exp is multi-line.
+//                    int here = sb.length();
+//                    for (int idx = 0; idx < selfAsSV.size(); idx++) {
+//                        if (idx > 0) {
+//                            sb.append("\n");
+//                            sb.append(NSpaces(here + kludgeToFixPCHandlingBug));
+//                        }
+//                        sb.append((String) selfAsSV.elementAt(idx)) ;
+//                    }
+////                    sb.append(self);
+//                    sb.append("]");
+//                    here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 1;
+                      addOneTokenToTLA("]");
+                    Vector sv = sub.toStringVector();
+                    /*****************************************************
+                    * Was                                                *
+                    *                                                    *
+                    *       Vector sv = sass.lhs.sub.toStringVector();   *
+                    *                                                    *
+                    * Changed by Chi Ho on 3 Aug 2006 to add             *
+                    * subscript.  See bug_06_08_03.                      *
+                    *****************************************************/
+                    if (sv.size() > 0)
+                    {
+                        addLeftParen(sub.getOrigin());
+                        addExprToTLA(sub);
+                        addRightParen(sub.getOrigin());
+                        
+//                        sb.append((String) sv.elementAt(0));
+//                        for (int v = 1; v < sv.size(); v++)
+//                        {
+//                            lines.addElement(sb.toString());
+//                            sb = new StringBuffer(NSpaces(here));
+//                            sb.append((String) sv.elementAt(v));
+//                        }
+                    }
+                    addOneTokenToTLA(" = ");;
+                    addLeftParen(rhs.getOrigin());
+                    addExprToTLA(rhs);
+                    addRightParen(rhs.getOrigin());
+                    addOneTokenToTLA("]");
+//                    sb.append(" = ");
+//                    here = sb.length();
+//                    sv = rhs.toStringVector();
+//                    sb.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        lines.addElement(sb.toString());
+//                        sb = new StringBuffer(NSpaces(here));
+//                        sb.append((String) sv.elementAt(v));
+//                    }
+//                    sb.append("]");
+//                    lines.addElement(sb.toString());
+                    sb = new StringBuffer();
+                } else if (!EmptyExpr(sass.lhs.sub))
+                {
+                    /* 
+                     * Generate single assignment to variable with no [self] subscript
+                     * but with an explicit subscript. 
+                     */
+                    sb.append(sass.lhs.var);
+                    sb.append("' = [");
+                    sb.append(sass.lhs.var);
+                    sb.append(" EXCEPT !");
+                    addOneTokenToTLA(sb.toString());
+                    addLeftParen(sub.getOrigin());
+                    addExprToTLA(sub);
+                    addRightParen(sub.getOrigin());
+                    addOneTokenToTLA(" = ");
+                    addLeftParen(rhs.getOrigin());
+                    addExprToTLA(rhs);
+                    addRightParen(rhs.getOrigin());
+                    addOneTokenToTLA("]");
+//                    
+//                    int here = sb.length();
+//                    Vector sv = sub.toStringVector();
+//                    sb.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        lines.addElement(sb.toString());
+//                        sb = new StringBuffer(NSpaces(here));
+//                        sb.append((String) sv.elementAt(v));
+//                    }
+//                    sb.append(" = ");
+//                    here = sb.length();
+//                    sv = rhs.toStringVector();
+//                    sb.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        lines.addElement(sb.toString());
+//                        sb = new StringBuffer(NSpaces(here));
+//                        sb.append((String) sv.elementAt(v));
+//                    }
+//                    sb.append("]");
+//                    lines.addElement(sb.toString());
+                    sb = new StringBuffer();
+                } else
+                {
+                    /* 
+                     * Generate assignment to a variable with no subscript at all.
+                     */
+                    sb.append(sass.lhs.var);
+                    sb.append("' = ");
+//                    int here = sb.length();
+                    boolean needsParens = NeedsParentheses(rhs.toStringVector());
+                    if (needsParens) {
+                        sb.append("(");
+                    }
+                    addOneTokenToTLA(sb.toString());
+                    addLeftParen(rhs.getOrigin());
+                    addExprToTLA(rhs);
+                    addRightParen(rhs.getOrigin());
+                    if (needsParens) {
+                        addOneTokenToTLA(")");
+                    }
+                    
+//                    Vector sv = Parenthesize(rhs.toStringVector());
+//                    /*******************************************************
+//                    * Call of Parenthesize added by LL on 27 Feb 2008.     *
+//                    * See bug_08-02-18.                                    *
+//                    *******************************************************/
+//                    for (int v = 0; v < sv.size(); v++)
+//                    {
+//                        sb.append((String) sv.elementAt(v));
+//                        lines.addElement(sb.toString());
+//                        sb = new StringBuffer(NSpaces(here));
+//                    }
+// Debugging
+//Vector exprVec = rhs.toMappingVector();
+//MappingObject.shiftMappingVector(exprVec, here);
+//MappingObject.printMappingVector(exprVec);
+//System.out.println("origin: " + rhs.getOrigin().toString() );
+                    
+                    sb = new StringBuffer();
+                }
+                addRightParen(sass.getOrigin());
+            } else
+            {
+                /*
+                 * Multiple assignments to the same variable, which must therefore
+                 * each have a user-specified subscript.
+                 */
+                AST.SingleAssign sass = sF;
+                sb.append(sass.lhs.var);
+                sb.append("' = [");
+                sb.append(sass.lhs.var);
+                sb.append(" EXCEPT ");
+                int cc = sb.length();
+                /*
+                 * If this  the first variable, so i = 0, then sb does not contain
+                 * any spaces to compensate for the missing prefix; otherwise it
+                 * does.
+                 */
+                if (i == 0) {
+                    cc = cc + prefix.length();
+                }
+                boolean subscript = (mp && (IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var)));
+                while (iFirst <= iLast)
+                {
+                    sass = (AST.SingleAssign) ast.ass.elementAt(iFirst);
+                    TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c);
+                    TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c);
+                    addLeftParen(sass.getOrigin());
+                    sb.append("!");
+                    
+                    // On 21 Jan 2011, LL moved the following statement to below the if
+                    // to correct part 3 of bug_11_01_13.
+                    //
+//                    int here = sb.length();
+                    if (subscript) {
+                        /*
+                         * This variable has a "self" subscript in addition to its user-specified
+                         * subscript.
+                         */
+                        sb.append("[");
+                        addOneTokenToTLA(sb.toString());
+                        TLAExpr self = Self(context);
+                        addLeftParen(self.getOrigin());
+                        addExprToTLA(self);
+                        addOneTokenToTLA("]");
+                        
+//                        Vector selfAsSV = Self(context).toStringVector();
+//                        for (int idx = 0; idx < selfAsSV.size(); idx++) {
+//                          String start = " ";
+//                          if (idx == 0) {
+//                              sb.append("[");
+//                          } else {
+//                              sb.append("\n");
+//                              sb.append(NSpaces(here + 1));
+//                          }
+//                          sb.append((String) selfAsSV.elementAt(idx));
+//                        }
+//                        sb.append("]");
+//                        here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 2;
+                    }
+                    else {
+                        addOneTokenToTLA(sb.toString());
+                    }
+                    
+                    addLeftParen(sub.getOrigin());
+                    addExprToTLA(sub);
+                    addRightParen(sub.getOrigin());
+                    addOneTokenToTLA(" = ");
+                    addLeftParen(rhs.getOrigin());
+                    addExprToTLA(rhs);
+                    addRightParen(rhs.getOrigin());
+                    addRightParen(sass.getOrigin());
+                    addOneTokenToTLA((iFirst == iLast) ? "]" : ",");
+//                    Vector sv = sub.toStringVector();
+//                    if (sv.size() > 0)
+//                    {
+//                        sb.append((String) sv.elementAt(0));
+//                        for (int v = 1; v < sv.size(); v++)
+//                        {
+//                            lines.addElement(sb.toString());
+//                            sb = new StringBuffer(NSpaces(here));
+//                            sb.append((String) sv.elementAt(v));
+//                        }
+//                    }
+//                    sb.append(" = ");
+//                    here = sb.length();
+//                    sv = rhs.toStringVector();
+//                    sb.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        lines.addElement(sb.toString());
+//                        sb = new StringBuffer(NSpaces(here));
+//                        sb.append((String) sv.elementAt(v));
+//                    }
+//                    sb.append(((iFirst == iLast) ? "]" : ","));
+//                    lines.addElement(sb.toString());
+                    sb = new StringBuffer();
+                    if (iFirst < iLast) {
+                        endCurrentLineOfTLA();
+                        AddSpaces(sb, cc);
+                    }
+                    iFirst = iFirst + 1;
+                }
+            }
+
+//            vlines.addElement(lines);
+            i = iLast + 1;
+            if (i <  ast.ass.size()) {
+                endCurrentLineOfTLA();
+                AddSpaces(sb, prefix.length());
+            }
+        }
+        addRightParen(ast.getOrigin());
+        endCurrentLineOfTLA();
+        
+        c.Merge(cThis);
+        // Append generated code to tlacode
+//        sb = new StringBuffer(prefix);
+//        col = sb.length();
+//        if (numAssigns > 1)
+//            sb.append("/\\ ");
+//        if (vlines.size() > 0)
+//        {
+//            for (int v1 = 0; v1 < vlines.size(); v1++)
+//            {
+//                Vector vl = (Vector) vlines.elementAt(v1);
+//                for (int v2 = 0; v2 < vl.size(); v2++)
+//                {
+//                    sb.append((String) vl.elementAt(v2));
+//                    tlacode.addElement(sb.toString());
+//                    sb = new StringBuffer(NSpaces(col));
+//                    if ((v1 > 0 || numAssigns > 1) && (v2 != vl.size() - 1))
+//                        sb.append("   ");
+//                }
+//                sb.append("/\\ ");
+//            }
+//        }
+    }
+
+    /**
+     * Generate TLA+ for if statement. Each branch has its own 
+     * UNCHANGED that lists variables that were changed in the 
+     * other branch. This is a little difficult since we don't 
+     * know the UNCHANGED for the Then branch until the code   
+     * for the Else branch is generated. So, we fix the        
+     * line in the Then branch after the Else branch is done.  
+     * The corresponding mappingVector line also has to be changed,
+     * but that's not a problem because the UNCHANGED is a TLA+
+     * token with no corresponding source.
+     */
+    private void GenIf(AST.If ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
+    {
+        Changed cThen = new Changed(c);
+        Changed cElse = new Changed(c);
+        int lineUncThen;
+        StringBuffer sb = new StringBuffer(prefix);
+        TLAExpr test = null;
+        test = AddSubscriptsToExpr(ast.test, SubExpr(Self(context)), c);
+//        Vector sv = test.toStringVector();
+        sb.append("IF ");
+        int here = sb.length();
+        /*************************************************************
+        * LL removed a bogus "- 1" here on 31 Jan 2006.              *
+        *************************************************************/
+        addLeftParen(ast.getOrigin());
+        addOneTokenToTLA(sb.toString());
+        addExprToTLA(test);
+        endCurrentLineOfTLA();
+        
+//        sb.append((String) sv.elementAt(0));
+//        for (int v = 1; v < sv.size(); v++)
+//        {
+//            tlacode.addElement(sb.toString());
+//            sb = new StringBuffer(NSpaces(here));
+//            sb.append((String) sv.elementAt(v));
+//        }
+//        tlacode.addElement(sb.toString());
+        
+        sb = new StringBuffer(NSpaces(here));
+        sb.append("THEN ");
+        here = sb.length();
+
+        sb.append("/\\ ");
+        for (int i = 0; i < ast.Then.size(); i++)
+        {
+            GenStmt((AST) ast.Then.elementAt(i), cThen, context, sb.toString(),
+            /*******************************************************************
+            * LL added the +3 on 18 Feb 2006 to take account of the            *
+            * indentation of the "IF ".                                        *
+            *******************************************************************/
+            here + 3);
+            sb = new StringBuffer(NSpaces(here) + "/\\ ");
+        }
+        lineUncThen = tlacode.size();
+//        tlacode.addElement(sb.toString());
+        addOneLineOfTLA(sb.toString());
+        sb = new StringBuffer(NSpaces(here - "THEN ".length()) + "ELSE ");
+        here = sb.length();
+        if (ast.Else.size() == 0)
+        {
+            sb.append("/\\ TRUE");
+//            tlacode.addElement(sb.toString());
+            addOneLineOfTLA(sb.toString());
+            
+            sb = new StringBuffer(NSpaces(here) + "/\\ ");
+        } else
+        {
+            sb.append("/\\ ");
+            for (int i = 0; i < ast.Else.size(); i++)
+            {
+                GenStmt((AST) ast.Else.elementAt(i), cElse, context, sb.toString(),
+                /*******************************************************************
+                * LL added the +3 on 18 Feb 2006 to take account of the            *
+                * indentation of the "IF ".                                        *
+                *******************************************************************/
+                here + 3);
+                sb = new StringBuffer(NSpaces(here) + "/\\ ");
+            }
+        }
+        // Generate UNCHANGED for the ELSE branch
+        if (cElse.NumUnchanged(cThen) > 1)
+        {
+            Vector uncElse = cElse.Unchanged(cThen, wrapColumn - sb.length() - "UNCHANGED << ".length());
+            sb.append("UNCHANGED << ");
+            int cc = sb.length();
+            sb.append((String) uncElse.elementAt(0));
+            for (int i = 1; i < uncElse.size(); i++)
+            {
+//                tlacode.addElement(sb.toString());
+                addOneLineOfTLA(sb.toString());
+                sb = new StringBuffer(NSpaces(cc));
+                sb.append((String) uncElse.elementAt(i));
+            }
+            sb.append(" >>");
+//            tlacode.addElement(sb.toString());
+            addOneTokenToTLA(sb.toString());
+            addRightParen(ast.getOrigin());
+            endCurrentLineOfTLA();
+        } else if (cElse.NumUnchanged(cThen) == 1)
+        {   // Change made by LL on 16 Mar 2011 so that, if there is a single
+        	// unchanged variable v, it produces v' = v if v is a short variable,
+        	// otherwise it produces UNCHANGED v
+        	//
+            // sb.append("UNCHANGED " + cElse.Unchanged(cThen));
+        	String uc = cElse.Unchanged(cThen);
+        	if (uc.length() > 5) {
+        		sb.append("UNCHANGED " + uc);
+        	} else {
+        		sb.append(uc + "' = " + uc);
+        	}
+//            tlacode.addElement(sb.toString());
+            addOneTokenToTLA(sb.toString());
+            addRightParen(ast.getOrigin());
+            endCurrentLineOfTLA();
+        } else 
+        {
+            /*
+             * There is no UNCHANGED after the ELSE, so we have to put
+             * the RightParen for the whole if statement at the end of
+             * the last line already generated
+             */
+            ((Vector) mappingVector.elementAt(mappingVector.size()-1))
+               .add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
+        }
+
+        // Patch up the UNCHANGED for the THEN branch
+        sb = new StringBuffer((String) tlacode.elementAt(lineUncThen));
+        tlacode.removeElementAt(lineUncThen);
+        mappingVector.removeElementAt(lineUncThen);
+        if (cThen.NumUnchanged(cElse) > 1)
+        {
+            Vector uncThen = cThen.Unchanged(cElse, wrapColumn - sb.length() - "UNCHANGED << ".length());
+            sb.append("UNCHANGED << ");
+            int cc = sb.length();
+            sb.append((String) uncThen.elementAt(0));
+            for (int i = 1; i < uncThen.size(); i++)
+            {
+                tlacode.insertElementAt(sb.toString(), lineUncThen);
+
+                 //set the mappingVector entry
+                mappingVector.insertElementAt(stringToTLATokens(sb.toString()), lineUncThen);
+
+                lineUncThen = lineUncThen + 1;
+                sb = new StringBuffer(NSpaces(cc));
+                sb.append((String) uncThen.elementAt(i));
+            }
+            sb.append(" >>");
+            tlacode.insertElementAt(sb.toString(), lineUncThen);
+            Vector vec =  stringToTLATokens(sb.toString());
+            
+            // The following is bogus because the RightParen for the
+            // entire procedure is inserted after (or instead of) the
+            // ELSE's UNCHANGED
+            // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
+            
+            mappingVector.insertElementAt(vec, lineUncThen);
+                    
+        } else if (cThen.NumUnchanged(cElse) == 1)
+        {   // Change made by LL on 16 Mar 2011 so that, if there is a single
+        	// unchanged variable v, it produces v' = v if v is a short variable,
+        	// otherwise it produces UNCHANGED v
+        	//
+            // sb.append("UNCHANGED ");
+            // sb.append(cThen.Unchanged(cElse));
+        	String uc = cThen.Unchanged(cElse);
+        	if (uc.length() > 5) {
+        		sb.append("UNCHANGED " + uc);
+        	} else {
+        		sb.append(uc + "' = " + uc);
+        	}
+            tlacode.insertElementAt(sb.toString(), lineUncThen);
+            Vector vec =  stringToTLATokens(sb.toString());
+            // The following is bogus because the RightParen for the
+            // entire procedure is inserted after (or instead of) the
+            // ELSE's UNCHANGED
+            // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
+            mappingVector.insertElementAt(vec, lineUncThen);
+        }
+
+        // Merge the change lists together
+        c.Merge(cThen);
+        c.Merge(cElse);
+    }
+    
+    /**
+     * Returns the vector of MappingObjects containing the BeginTLAToken and
+     * EndTLAToken that are put in the mappingVector by a call of addOneLineOfTLA.
+     * The code was essentially copied from addOneTokenToTLA.
+     * 
+     * @param token
+     * @return
+     */
+    private Vector stringToTLATokens(String token) {
+        Vector result = new Vector(3);
+        
+        String trimmedToken = token.trim() ;
+
+        int numberOfLeftTrimmedTokens = 
+                (trimmedToken.length() == 0) ? -1 :                   
+                  token.indexOf(trimmedToken.charAt(0));
+             
+             /**
+              * Handle a token of only space characters. 
+              */
+             if (numberOfLeftTrimmedTokens == -1) {
+                 numberOfLeftTrimmedTokens = 0 ;
+                 trimmedToken = token ;
+             }
+             
+             int objBegin = numberOfLeftTrimmedTokens;
+             result.addElement(new MappingObject.BeginTLAToken(objBegin));
+             result.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length()));
+             return result;
+    }
+
+    /***********************************************************************
+    * Added by LL on 30 Jan 2006.                                          *
+    *                                                                      *
+    * Generate TLA+ for the `either' statement.  This performs the same    *
+    * sort of hackery as for the `if' statement, necessitated by the       *
+    * design flaw commented on above.                                      
+     **
+    ***********************************************************************/
+    private void GenEither(AST.Either ast, Changed c, String context, String prefix, int col)
+            throws PcalTLAGenException
+    {
+        Changed allC = new Changed(c);
+        /*******************************************************************
+        * Accumulates the variable changes of all the clauses.             *
+        *******************************************************************/
+        Changed[] cOrs = new Changed[ast.ors.size()];
+        /*******************************************************************
+        * cOrs[i] is the Changed vector for the i-th `or' clause.          *
+        *******************************************************************/
+        int[] ucLocs = new int[ast.ors.size()]; // location of unchangeds.
+        /******************************************************************
+        * tlaout.elementAt(ucLocs[i]) is the UNCHANGED clause for the     *
+        * i-th `or' clause.                                               *
+        ******************************************************************/
+        StringBuffer sb = new StringBuffer(prefix);
+        int prefixIndent = sb.length();
+        sb.append("\\/ ");
+        int here = sb.length();
+        /*******************************************************************
+        * The number of columns to the left of the code generated for      *
+        * each `or' clause.                                                *
+        *******************************************************************/
+
+        /*
+         * Add the left paren for the statement.
+         */
+        addLeftParen(ast.getOrigin());
+        /*********************************************************************
+        * Produce the output for the clauses, but with a dummy line in       *
+        * place of the UNCHANGED clause, and compute allC, cOrs, and         *
+        * ucLocs.                                                            *
+        *********************************************************************/
+        for (int i = 0; i < ast.ors.size(); i++)
+        {
+            if (i != 0)
+            {
+                sb = new StringBuffer(NSpaces(prefixIndent) + "\\/ ");
+            }
+            ;
+            sb.append("/\\ ");
+            Vector orClause = (Vector) ast.ors.elementAt(i);
+            Changed cC = new Changed(c);
+            for (int j = 0; j < orClause.size(); j++)
+            {
+                /***********************************************************
+                * On 6 Jun 2010, LL added the "+3" in the following call   *
+                * of GenStmt.  This seems to fix a bug which caused        *
+                *                                                          *
+                *    either when \/ A                                      *
+                *                \/ B                                      *
+                *        or ...                                            *
+                *                                                          *
+                * to produce                                               *
+                *    \/ /\ \/ A                                            *
+                *       \/ B                                               *
+                *    \/ ...                                                *
+                ***********************************************************/
+                GenStmt((AST) orClause.elementAt(j), cC, context, sb.toString(), here + 3); 
+                sb = new StringBuffer(NSpaces(here) + "/\\ ");
+            }
+            ;
+            cOrs[i] = cC;
+            allC.Merge(cC);
+            ucLocs[i] = tlacode.size();
+//            tlacode.addElement("Replace by UNCHANGED"); // 
+            addOneLineOfTLA("Replace by UNCHANGED");
+        }
+        ; // End of for i
+
+        /**********************************************************************
+        * Insert real UNCHANGED clauses.  Note that we have to go through     *
+        * loop backwards since we will remove a line of output for each `or'  *
+        * clause that doesn't get an UNCHANGED.                               *
+        **********************************************************************/
+        int i = ast.ors.size();
+        while (i > 0)
+        {
+            i = i - 1;
+            tlacode.removeElementAt(ucLocs[i]);
+            mappingVector.removeElementAt(ucLocs[i]);
+            int numUnchanged = cOrs[i].NumUnchanged(allC);
+            String NotChanged = cOrs[i].Unchanged(allC);
+            if (numUnchanged > 1)
+            {
+                /*
+                 * The line should be wrapped if it's too long.
+                 */
+                String line = NSpaces(here) + "/\\ UNCHANGED <<" + NotChanged + ">>";
+                tlacode.insertElementAt(line, ucLocs[i]);
+                mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]);
+            } else if (numUnchanged == 1)
+            {   // Change made by LL on 16 Mar 2011 so that, if there is a single
+            	// unchanged variable v, it produces v' = v if v is a short variable,
+            	// otherwise it produces UNCHANGED v
+            	//
+                // tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]);
+            	if (NotChanged.length() > 5) {
+            	    String line = NSpaces(here) + "/\\ UNCHANGED " + NotChanged;
+                    tlacode.insertElementAt(line, ucLocs[i]);
+                    mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]);
+//                    tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]);
+            	} else {
+            	    String line = NSpaces(here) + "/\\ " + NotChanged + "' = " + NotChanged;
+            	    tlacode.insertElementAt(line, ucLocs[i]);
+                    mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]);
+//            		tlacode.insertElementAt(NSpaces(here) + "/\\ " + NotChanged + "' = "
+//            				+ NotChanged, ucLocs[i]);
+            	}
+            } 
+        }
+        ;
+        /*
+         * Add the right paren for the entire statement.
+         */
+        ((Vector) mappingVector.elementAt(mappingVector.size()-1))
+        .add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
+        /**********************************************************************
+        * Add the statement's unchangeds to c.                                *
+        **********************************************************************/
+        c.Merge(allC);
+    }
+
+    private void GenWith(AST.With ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
+    {
+        addLeftParen(ast.getOrigin());
+        StringBuffer sb = new StringBuffer(prefix);
+        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
+//        Vector sv = exp.toStringVector();
+        if (ast.isEq)
+        {
+            /* generate LET statement */
+            sb.append("LET ");
+            sb.append(ast.var);
+            sb.append(" == ");
+            addOneTokenToTLA(sb.toString());
+            addLeftParen(exp.getOrigin());
+            addExprToTLA(exp);
+            addRightParen(exp.getOrigin());
+//            int here = sb.length();
+//            sb.append((String) sv.elementAt(0));
+//            for (int v = 1; v < sv.size(); v++)
+//            {
+//                tlacode.addElement(sb.toString());
+//                sb = new StringBuffer(NSpaces(here));
+//                sb.append((String) sv.elementAt(v));
+//            }
+            addOneTokenToTLA(" IN");
+            endCurrentLineOfTLA();
+//            sb.append(" IN");
+//            tlacode.addElement(sb.toString());
+            sb = new StringBuffer(NSpaces(col + 2));
+            /*************************************************************
+            * LL changed "col + 4" to "col + 2" here to correct an       *
+            * alignment problem on 31 Jan 2006.                          *
+            *************************************************************/
+            if (ast.Do.size() > 1)
+                sb.append("/\\ ");
+        } else
+        {
+            /* generate \E statement */
+            sb.append("\\E ");
+            sb.append(ast.var);
+            sb.append(" \\in ");
+            addOneTokenToTLA(sb.toString());
+            addLeftParen(exp.getOrigin());
+            addExprToTLA(exp);
+            addRightParen(exp.getOrigin());
+//            int here = sb.le
+            addOneTokenToTLA(":");
+            endCurrentLineOfTLA();
+//            sb.append(":");
+//            tlacode.addElement(sb.toString());
+            sb = new StringBuffer(NSpaces(col + 2));
+            if (ast.Do.size() > 1)
+                sb.append("/\\ ");
+        }
+        for (int i = 0; i < ast.Do.size(); i++)
+        {
+            GenStmt((AST) ast.Do.elementAt(i), c, context, sb.toString(), sb.length());
+            sb = new StringBuffer(NSpaces(col + 2) + "/\\ ");
+        }
+        // tlacode.addElement(NSpaces(col) + ")");
+        
+        /*
+         * Add the right paren for the entire statement.
+         */
+        ((Vector) mappingVector.elementAt(mappingVector.size()-1))
+        .add(new MappingObject.RightParen(ast.getOrigin().getEnd()));
+    }
+
+    private void GenWhen(AST.When ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException
+    {
+        addOneTokenToTLA(prefix);
+        
+//        StringBuffer sb = new StringBuffer(prefix);
+        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
+        addLeftParen(exp.getOrigin());
+        addExprToTLA(exp);
+        addRightParen(exp.getOrigin());
+        endCurrentLineOfTLA();
+//        Vector sv = exp.toStringVector();
+//        
+//// Debugging
+////Vector vec = exp.toMappingVector();
+////System.out.println("Original vec:");
+////MappingObject.printMappingVector(vec);    
+////System.out.println("RemoveRedundantParens(vec)");
+////MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(vec));
+////System.out.println("Should be original mappingvector:");
+////MappingObject.printMappingVector(vec); 
+//
+//        sb.append((String) sv.elementAt(0));
+//        for (int v = 1; v < sv.size(); v++)
+//        {
+//            tlacode.addElement(sb.toString());
+//            sb = new StringBuffer(NSpaces(col));
+//            sb.append((String) sv.elementAt(v));
+//        }
+//        tlacode.addElement(sb.toString());
+    }
+
+    private void GenPrintS(AST.PrintS ast, Changed c, String context, String prefix, int col)
+            throws PcalTLAGenException
+    {
+        StringBuffer sb = new StringBuffer(prefix);
+        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
+        addLeftParen(ast.getOrigin());
+        addOneTokenToTLA(prefix + "PrintT(");
+        addExprToTLA(exp);
+        addOneTokenToTLA(")");
+        addRightParen(ast.getOrigin());
+        endCurrentLineOfTLA();
+        
+//        Vector sv = exp.toStringVector();
+//        // The following modified 19 Nov 05 by LL to use PrintT instead of Print
+//        sb.append("PrintT(");
+//        sb.append((String) sv.elementAt(0));
+//        for (int v = 1; v < sv.size(); v++)
+//        {
+//            tlacode.addElement(sb.toString());
+//            sb = new StringBuffer(NSpaces(col + "PrintT(".length()));
+//            sb.append((String) sv.elementAt(v));
+//        }
+//        sb.append(")");
+//        tlacode.addElement(sb.toString());
+    }
+
+    /********************************************************/
+    /* Assert(ast.expr, "Failure of assertion at... ")      */
+    /**
+     *******************************************************/
+    private void GenAssert(AST.Assert ast, Changed c, String context, String prefix, int col)
+            throws PcalTLAGenException
+    {
+        addLeftParen(ast.getOrigin());
+        StringBuffer sb = new StringBuffer(prefix);
+        StringBuffer sc = new StringBuffer();
+        TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c);
+//        Vector sv = exp.toStringVector();
+        sb.append("Assert(");
+        addOneTokenToTLA(sb.toString());
+        addLeftParen(exp.getOrigin());
+        addExprToTLA(exp);
+        addRightParen(exp.getOrigin());
+        int here = sb.length();
+//        sb.append((String) sv.elementAt(0));
+//        for (int v = 1; v < sv.size(); v++)
+//        {
+//            tlacode.addElement(sb.toString());
+//            sb = new StringBuffer(NSpaces(col + "Assert(".length()));
+//            sb.append((String) sv.elementAt(v));
+//        }
+//        sb.append(", ");
+        sb = new StringBuffer(", ");
+        sc.append("\"Failure of assertion at ");
+        sc.append(ast.location());
+        // modified on 23 Mar 2006 by LL to use location() instead of
+        // ast.line and ast.col
+        sc.append(".\")");
+        if (tlacodeNextLine.length() + sb.length() + sc.length() < wrapColumn) {
+            addOneTokenToTLA(sb.toString() + sc.toString());
+        }
+//        if (sb.length() + sc.length() < wrapColumn)
+//            tlacode.addElement(sb.toString() + sc.toString());
+        else
+        {
+            addOneTokenToTLA(sb.toString());
+            endCurrentLineOfTLA();
+            addOneTokenToTLA(NSpaces(here) + sc.toString());
+//            tlacode.addElement(sb.toString());
+//            tlacode.addElement(NSpaces(here) + sc.toString());
+        }
+        addRightParen(ast.getOrigin());
+        endCurrentLineOfTLA();
+    }
+
+    /********************************************************/
+    /* I generate a TRUE conjunct, which is useless, but so */
+    /* is a skip statement.                                 */
+    /********************************************************/
+    private void GenSkip(AST.Skip ast, Changed c, String context, String prefix, int col)
+    {
+//        tlacode.addElement(prefix + "TRUE");
+        addOneTokenToTLA(prefix);
+        addLeftParen(ast.getOrigin());
+        addOneTokenToTLA("TRUE");
+        addRightParen(ast.getOrigin());
+        endCurrentLineOfTLA();
+    }
+
+    /***********************************************************************
+    * Generate the VARIABLES declaration(s), output the TLA+ "code" from   *
+    * a `define' statement, if any, and generate the definition of         *
+    * `vars'.                                                              *
+    *                                                                      *
+    * Method renamed from GenVars and given the defs argument by LL on     *
+    * 25 Jan 2006 to handle the `define' statement.                        *
+    ***********************************************************************/
+    private void GenVarsAndDefs(Vector globals, Vector procs, Vector processes, TLAExpr defs)
+      throws PcalTLAGenException
+    {
+        /*******************************************************************
+        * lVars and gVars are vectors of strings, each element being a     *
+        * variable name.  They hold the local and global variables,        *
+        * respectively.                                                    *
+        *******************************************************************/
+        Vector lVars = new Vector();
+        Vector gVars = new Vector();
+        
+        /**
+         * lVarsSource and gVarsSource are vectors of AST.VarDecl objects that 
+         * generated the elements of lVars and gVars, where PVarDecl objects
+         * are converted to VarDecl objects.
+         */
+        Vector lVarsSource = new Vector();
+        Vector gVarsSource = new Vector();
+
+        /*******************************************************************
+        * Set gVars to the global variables, including pc and `stack' if   *
+        * there are procedures, and add these variables to vars.           *
+        *******************************************************************/
+        if (globals != null)
+            for (int i = 0; i < globals.size(); i++)
+            {
+                AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i);
+                gVars.addElement(decl.var);
+                gVarsSource.addElement(decl);
+                vars.addElement(decl.var);
+            }
+        if (! ParseAlgorithm.omitPC) {
+          gVars.addElement("pc");
+          /**
+           * For added variables, create a VarDecl with null origin.
+           */
+          AST.VarDecl pcVarDecl = new AST.VarDecl();
+          pcVarDecl.var = "pc";
+          gVarsSource.addElement(pcVarDecl);
+          vars.addElement("pc");
+        }
+        if (procs != null && procs.size() > 0)
+        {
+            gVars.addElement("stack");
+            /**
+             * For added variables, create a VarDecl with null origin.
+             */
+            AST.VarDecl pcVarDecl = new AST.VarDecl();
+            pcVarDecl.var = "stack";
+            gVarsSource.addElement(pcVarDecl);
+            vars.addElement("stack");
+        }
+        /*******************************************************************
+        * Add local procedure variables to lVars, vars, and pcV.           *
+        *******************************************************************/
+        if (procs != null)
+            for (int i = 0; i < procs.size(); i++)
+            {
+                AST.Procedure proc = (AST.Procedure) procs.elementAt(i);
+                if (proc.params != null)
+                    for (int p = 0; p < proc.params.size(); p++)
+                    {
+                        AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p);
+                        lVars.addElement(decl.var);
+                        lVarsSource.addElement(decl.toVarDecl()) ;
+                        vars.addElement(decl.var);
+                        pcV.addElement(decl.var);
+                    }
+                if (proc.decls != null)
+                    for (int p = 0; p < proc.decls.size(); p++)
+                    {
+                        AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p);
+                        lVars.addElement(decl.var);
+                        lVarsSource.addElement(decl.toVarDecl()) ;
+                        vars.addElement(decl.var);
+                        pcV.addElement(decl.var);
+                    }
+            }
+
+        /*******************************************************************
+        * Add local process variables to lVars, vars, and psV for          *
+        * variables local to process sets.                                 *
+        *******************************************************************/
+        if (processes != null)
+            for (int i = 0; i < processes.size(); i++)
+            {
+                AST.Process proc = (AST.Process) processes.elementAt(i);
+                if (proc.decls != null)
+                    for (int p = 0; p < proc.decls.size(); p++)
+                    {
+                        AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p);
+                        lVars.addElement(decl.var);
+                        lVarsSource.addElement(decl);
+                        vars.addElement(decl.var);
+                        if (!proc.isEq)
+                            psV.addElement(decl.var);
+                    }
+            }
+
+        /********************************************************************
+        * Add a declaration of the constant defaultInitValue if it is       *
+        * used.  (Added by LL on 22 Aug 2007.)                              *
+        ********************************************************************/
+        if (ParseAlgorithm.hasDefaultInitialization)
+        {
+            addOneLineOfTLA("CONSTANT defaultInitValue");
+        }
+        ;
+
+        if (EmptyExpr(defs))
+        {
+            /******************************************************************
+            * There is no `define' statement.  In this case, generate a       *
+            * single VARIABLES statement and set gVars to vector of all       *
+            * variables.                                                      *
+            ******************************************************************/
+            gVars.addAll(lVars);
+            gVarsSource.addAll(lVarsSource) ;
+            GenVarDecl(gVars, gVarsSource); 
+        } else
+        {
+            /******************************************************************
+            * There is a `define' statement.  In this case, must declare      *
+            * global and local variables separately.  Also, set gVars to      *
+            * vector of all variables.                                        *
+            ******************************************************************/
+            GenVarDecl(gVars, gVarsSource);
+            addOneLineOfTLA("");
+            addOneLineOfTLA("(* define statement *)");
+            addExprToTLA(defs);
+//            Vector sv = defs.toStringVector();
+//            for (int i = 0; i < sv.size(); i++)
+//            {
+//                tlacode.addElement((String) sv.elementAt(i));
+//            }
+            ;
+            addOneLineOfTLA("");
+            GenVarDecl(lVars, lVarsSource); // to be fixed
+            gVars.addAll(lVars);
+            gVarsSource.addAll(lVarsSource);
+        }
+        ;
+        addOneLineOfTLA("");
+
+        /*
+         * We check for the unlikely case in which there are no variables.  
+         * Without this check, the Init is not generated but appears in
+         * the definition of Spec.
+         */
+        if (gVars.size() == 0) {
+            throw new PcalTLAGenException("The algorithm has no variables.");
+        }
+        /*******************************************************************
+        * Generate definition of var.                                     *
+        *******************************************************************/
+//        StringBuffer var = new StringBuffer("vars == << ");
+//        StringBuffer curLine = new StringBuffer("vars == << ");
+        addOneTokenToTLA("vars == << ") ;
+        int indent = tlacodeNextLine.length();
+        for (int i = 0; i < gVars.size(); i++)
+        {
+            if (i > 0)
+            {
+//                var.append(", ");
+//                curLine.append(", ");
+//                tlacodeNextLine = tlacodeNextLine + ", ";
+                addOneTokenToTLA(", ");
+            }
+            ;
+            String vbl = (String) gVars.elementAt(i);
+            AST.VarDecl vblDecl = (AST.VarDecl) gVarsSource.elementAt(i);
+            Region vblOrigin = vblDecl.getOrigin();
+//            if (curLine.length() + vbl.length() + 1 > wrapColumn)
+            if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn)
+            {
+//                curLine = new StringBuffer("vars == << ");
+//                var.append("\n" + NSpaces("vars == << ".length()));
+                endCurrentLineOfTLA();
+                tlacodeNextLine = NSpaces(indent);
+            }
+//            var.append(vbl);
+//            curLine.append(vbl);
+            addOneSourceTokenToTLA(vbl, vblOrigin);
+        }
+//        if (curLine.length() + " >>".length() + 1 > wrapColumn)
+        if (tlacodeNextLine.length() + " >>".length() + 1 > wrapColumn)
+        {
+//            var.append("\n" + NSpaces("vars ==".length()));
+            endCurrentLineOfTLA() ;
+            tlacodeNextLine = NSpaces("vars ==".length());
+        }
+        ;
+//        var.append(" >>");
+//        tlacodeNextLine = tlacodeNextLine + " >>";
+        addOneTokenToTLA(" >>");
+//        tlacode.addElement(var.toString());
+        addOneLineOfTLA("");
+    }
+
+    /**
+     * Generate a VARIABLE(S) declarations.  The varVec argument is a vector of
+     * strings that are the variables to be declared.  It does nothing if
+     * the vector has length 0.  The varVecSource argument is a vector
+     * of the same size as varVec that contains the AST.VarDecl objects.
+     * <p>
+     * Method added by LL on 25 Jan 2006.  
+     * 
+     * Modified 16 Dec 2011 to add varVecSource argument and generate TLA to 
+     * PCal mapping.
+     * 
+     * @param varVec A vector of strings.
+     * 
+     * @param varVecSource A vector of AST.VarDecl objects.
+     */
+    public void GenVarDecl(Vector varVec, Vector varVecSource)
+    {
+//        StringBuffer res = new StringBuffer();
+//        StringBuffer curLine = new StringBuffer("VARIABLES ");
+        // for measuring length
+        if (varVec.size() == 0)
+        {
+            return;
+        }
+        ;
+        if (varVec.size() > 1)
+        {
+//            res.append("VARIABLES ");
+            addOneTokenToTLA("VARIABLES ");
+        } else
+        {
+//            res.append("VARIABLE ");
+            addOneTokenToTLA(TLAConstants.KeyWords.VARIABLE + " ");
+        }
+        ;
+        for (int i = 0; i < varVec.size(); i++)
+        {
+            if (i > 0)
+            {
+//                res.append(", ");
+//                curLine.append(", ");
+                addOneTokenToTLA(", ");
+            }
+            ;
+            String vbl = (String) varVec.elementAt(i);
+            AST vblsource = (AST) varVecSource.elementAt(i);
+//            if (curLine.length() + vbl.length() + 1 > wrapColumn)
+            if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn)
+            {
+//                curLine = new String
+                endCurrentLineOfTLA();
+                if (varVec.size() > 1)
+                {
+//                    res.append(NSpaces("VARIABLES ".length()));
+                    tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLES ".length());
+                } else
+                {
+//                    res.append(NSpaces("VARIABLE ".length()));
+                    tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLE ".length());
+                }
+                ;
+            }
+            ;
+//            res.append(vbl);
+//            curLine.append(vbl);
+            addOneSourceTokenToTLA(vbl, vblsource.getOrigin());
+        }
+        ;
+//        tlacode.addElement(res.toString());
+        endCurrentLineOfTLA();
+    }
+
+     /**
+     * Generates the "ProcSet == ..." output.  It is just a union of all the
+     * process sets, all on one line (except if a process set is a multi-line
+     * expression).  It wouldn't be too hard to break long lines, but that
+     * should be done later, if desired, after the TLA to PCal translation
+     * is finished.  
+     */
+    public void GenProcSet()
+    {
+        StringBuffer ps = new StringBuffer();
+        if (st.processes == null || st.processes.size() == 0)
+            return;
+//        ps.append("ProcSet == ");
+        addOneTokenToTLA("ProcSet == ");
+        for (int i = 0; i < st.processes.size(); i++)
+        {
+            PcalSymTab.ProcessEntry proc = (PcalSymTab.ProcessEntry) st.processes.elementAt(i);
+//            Vector sv = proc.id.toStringVector();
+            if (i > 0) {
+//                ps.append(" \\cup ");
+                addOneTokenToTLA(" \\cup ");
+            }
+            addLeftParen(proc.id.getOrigin());
+            if (proc.isEq) {
+//                ps.append("{");
+                addOneTokenToTLA("{");
+            }
+            else {
+//                ps.append("(");
+                addOneTokenToTLA("(");
+            }
+            int col = ps.length();
+//            ps.append((String) sv.elementAt(0));
+//            for (int v = 1; v < sv.size(); v++)
+//            {
+//                tlacode.addElement(ps.toString());
+//                ps = new StringBuffer(NSpaces(col));
+//                ps.append((String) sv.elementAt(v));
+//            }
+            addExprToTLA(proc.id);
+            if (proc.isEq) {
+//                ps.append("}");
+                addOneTokenToTLA("}");
+            }
+            else {
+//                ps.append(")");
+                addOneTokenToTLA(")");
+            }
+            addRightParen(proc.id.getOrigin());
+        }
+//        tlacode.addElement(ps.toString());
+//        tlacode.addElement("");
+        endCurrentLineOfTLA();
+        addOneLineOfTLA("");
+    }
+
+    /***********************************/
+    /* Generate the Init == statement. */
+    /**
+     **********************************/
+    private void GenInit(Vector globals, Vector procs, Vector processes) throws PcalTLAGenException
+    {
+        int col = "Init == ".length();
+        StringBuffer is = new StringBuffer();
+        is.append("Init == ");
+
+        /* Global variables */
+        if (globals != null && globals.size() > 0)
+        {
+            is.append("(* Global variables *)");
+//            tlacode.addElement(is.toString());
+            addOneLineOfTLA(is.toString()) ;
+            is = new StringBuffer(NSpaces(col));
+            for (int i = 0; i < globals.size(); i++)
+            {
+                AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i);
+                addVarDeclToTLA(decl, is);
+                is = new StringBuffer(NSpaces(col));
+            }
+        }
+        if (procs != null && procs.size() > 0)
+        {
+            /* Procedure variables and parameters */
+            for (int i = 0; i < procs.size(); i++)
+            {
+                AST.Procedure proc = (AST.Procedure) procs.elementAt(i);
+                if (proc.params.size() == 0 && proc.decls.size() == 0)
+                    // No parameters or procedure variables in this procedure
+                    continue;
+                is.append("(* Procedure ");
+                is.append(proc.name);
+                is.append(" *)");
+//                tlacode.addElement(is.toString());
+                addOneLineOfTLA(is.toString());
+                is = new StringBuffer(NSpaces(col));
+                for (int p = 0; p < proc.params.size(); p++)
+                {
+                    AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p);
+                    if (!mp) {
+                        addVarDeclToTLA(decl.toVarDecl(), is);
+                    }
+                    else {
+                        is.append("/\\ ");
+                        addOneTokenToTLA(is.toString());
+                        addLeftParen(decl.getOrigin());
+//                    is.append(decl.var);
+                        is = new StringBuffer(decl.var);
+                    /*******************************************************
+                    * Modified on 31 Jan 2006 by LL to add subscripts to   *
+                    * initialization expression if needed.  Also replaced  *
+                    * test for "\\in" with assertion that it can't occur,  *
+                    * since it's forbidden by the grammar.                 *
+                    *******************************************************/
+                        PcalDebug.Assert(decl.isEq);
+                        is.append(" = ");
+                        
+//                    Vector sv;
+//                    if (mp)
+//                    {
+//                        sv = AddSubscriptsToExpr(decl.val, 
+//                              SubExpr(Self("procedure")), new Changed(new Vector()))
+//                                .toStringVector();
+//                    } else
+//                    {
+//                        sv = Parenthesize(decl.val.toStringVector());
+//                        /*************************************************
+//                        * Call to Parenthesize added by LL on 27 Feb 2008. *
+//                        * See bug_08-02-18.                              *
+//                        *************************************************/
+//                    }
+//                    ;
+//                    if (mp)
+//                    {
+                        is.append("[ self \\in ProcSet |-> ");
+//                    }
+                        addOneTokenToTLA(is.toString());
+                        addLeftParen(decl.val.getOrigin());
+                        addExprToTLA(
+                          AddSubscriptsToExpr(decl.val, 
+                                              SubExpr(Self("procedure")), 
+                                              new Changed(new Vector())));
+                        addRightParen(decl.val.getOrigin());
+                        addOneTokenToTLA("]");
+                        addRightParen(decl.getOrigin());
+                        endCurrentLineOfTLA();
+                        
+//                    int col2 = is.length();
+//                    is.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        tlacode.addElement(is.toString());
+//                        is = new StringBuffer(NSpaces(col2));
+//                        is.append((String) sv.elementAt(v));
+//                    }
+//                    if (mp)
+//                        is.append("]");
+//                    tlacode.addElement(is.toString());
+                    }
+                    is = new StringBuffer(NSpaces(col));
+                }
+                for (int p = 0; p < proc.decls.size(); p++)
+                {
+                    /*
+                     * Note: the following code is identical to the for loop
+                     * code above for procedure variables.  (Well done, Keith!)
+                     * I realized this too late to feel like procedurizing it.
+                     */
+                    AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p);
+                    if (!mp) {
+                        addVarDeclToTLA(decl.toVarDecl(), is);
+                    }
+                    else {
+                        is.append("/\\ ");
+                        addOneTokenToTLA(is.toString());
+                        addLeftParen(decl.getOrigin());
+//                    is.append(decl.var);
+                        is = new StringBuffer(decl.var);
+//                    is.append("/\\ ");
+//                    is.append(decl.var);
+
+                    /*******************************************************
+                    * Modified on 31 Jan 2006 by LL to add subscripts to   *
+                    * initialization expression if needed.  Also replaced  *
+                    * test for "\\in" with assertion that it can't occur,  *
+                    * since it's forbidden by the grammar.                 *
+                    *******************************************************/
+                        PcalDebug.Assert(decl.isEq);
+                        is.append(" = ");
+//                    Vector sv;
+//                    if (mp)
+//                    {
+//                        sv = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector()))
+//                                .toStringVector();
+//                    } else
+//                    {
+//                        sv = Parenthesize(decl.val.toStringVector());
+//                        /*************************************************
+//                        * Call to Parenthesize added by LL on            *
+//                        * 27 Feb 2008.  See bug_08-02-18.                *
+//                        *************************************************/
+//                    }
+//                    ;
+//                    if (mp)
+//                    {
+                        is.append("[ self \\in ProcSet |-> ");
+//                    }
+                        addOneTokenToTLA(is.toString());
+                        addLeftParen(decl.val.getOrigin());
+                        addExprToTLA(AddSubscriptsToExpr(
+                                        decl.val, 
+                                        SubExpr(Self("procedure")), 
+                                        new Changed(new Vector())));
+                        addRightParen(decl.val.getOrigin());
+                        addOneTokenToTLA("]");
+                        addRightParen(decl.getOrigin());
+                        endCurrentLineOfTLA();
+
+//                    int col2 = is.length();
+//                    is.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        tlacode.addElement(is.toString());
+//                        is = new StringBuffer(NSpaces(col2));
+//                        is.append((String) sv.elementAt(v));
+//                    }
+//                    if (mp)
+//                        is.append("]");
+//                    tlacode.addElement(is.toString());
+                    }
+                    is = new StringBuffer(NSpaces(col));
+                }
+            }
+        }
+        if (processes != null && processes.size() > 0)
+        {
+            /* Process variables */
+            for (int i = 0; i < processes.size(); i++)
+            {
+                AST.Process proc = (AST.Process) processes.elementAt(i);
+                if (proc.decls.size() == 0) // No variables in this procedure
+                    continue;
+                is.append("(* Process ");
+                is.append(proc.name);
+                is.append(" *)");
+//                tlacode.addElement(is.toString());
+                addOneLineOfTLA(is.toString());
+                is = new StringBuffer(NSpaces(col));
+                for (int p = 0; p < proc.decls.size(); p++)
+                {
+                    /*
+                     * In the comments below, (( and )) represent
+                     * MappingObject.LeftParen and MappingObject.RightParen
+                     * objects.
+                     */
+                    AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p);
+                    is.append("/\\ ");
+                    /*
+                     * The following adds  /\ ((  to the TLA+ output.
+                     */
+                    addOneTokenToTLA(is.toString());
+                    addLeftParen(decl.getOrigin());
+                    
+                    
+                    if (proc.isEq) {
+                        /*
+                         * The source is
+                         * 
+                         *     process (P = S) variables ... v  @@  Val
+                         *     
+                         * where @@ is either "=" or "\in".  The TLA+ output is
+                         * 
+                         *    /\ (( v @@ (( Val )) ))
+                         */
+                        is = new StringBuffer(decl.var);
+                        if (decl.isEq) {
+                            is.append(" = ");
+                        }
+                        else {
+                            is.append(" \\in ");
+                        }
+                        addOneTokenToTLA(is.toString()); 
+                        addLeftParen(decl.val.getOrigin());
+                        addExprToTLA(decl.val);
+                        addRightParen(decl.val.getOrigin());
+                    }
+                    else {
+                        if (decl.isEq) {
+                            /*
+                             * The source is
+                             *    
+                             *     process (P \in S) variables ... v = Val
+                             *     
+                             * The TLA+ output is
+                             * 
+                             *    /\ (( v = [self \in (( S )) |-> (( ValBar )) ] ))
+                             *    
+                             * where ValBar obtained from Val by replacing each 
+                             * variable w of the process with w[self].   
+                             */
+                            is = new StringBuffer(decl.var);
+                            is.append(" = [self \\in ");
+                            addOneTokenToTLA(is.toString());
+                            addLeftParen(proc.id.getOrigin());
+                            addExprToTLA(proc.id);
+                            addRightParen(proc.id.getOrigin());
+                            addOneTokenToTLA(TLAConstants.RECORD_ARROW);
+                            addLeftParen(decl.val.getOrigin());
+                            addExprToTLA(AddSubscriptsToExpr(
+                                           decl.val,
+                                           SubExpr(Self("procedure")), 
+                                           new Changed(new Vector())));
+                            addRightParen(decl.val.getOrigin());
+                            addOneTokenToTLA("]");
+                            
+                        }
+                        else {
+                            /*
+                             * The source is
+                             * 
+                             *    process (P \in S) variables ... v \in Val
+                             *    
+                             * The TLA+ output is
+                             * 
+                             *    /\ (( v \in [ (( S )) -> (( ValBar )) ] ))
+                             *    
+                             * where ValBar is obtained from Val by replacing each 
+                             * variable w of the process with
+                             * 
+                             *    w[CHOOSE self \in S : TRUE]
+                             *    
+                             * We first set expr to the TLAExpr "CHOOSE self \in S : TRUE".
+                             * (This is Keith's original code.) 
+                             */                            
+                            TLAExpr subexpr = proc.id.cloneAndNormalize();
+                            TLAExpr expr = new TLAExpr();
+                            expr.addLine();
+                            expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN));
+                            expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN));
+                            expr.addToken(new TLAToken("self", 8, TLAToken.IDENT));
+                            expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN));
+                            expr.normalize();
+                            expr.setOrigin(subexpr.getOrigin()); // see what this does.
+                            try
+                            {
+                                subexpr.prepend(expr, 1);
+                                expr = new TLAExpr();
+                                expr.addLine();
+                                expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN));
+                                expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN));
+                                expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN));
+                                expr.prepend(subexpr, 1);
+                            } catch (TLAExprException e)
+                            {
+                                throw new PcalTLAGenException(e.getMessage());
+                            }
+
+                            /*
+                             * Now we output the TLA+ code.
+                             */
+                            is = new StringBuffer(decl.var);
+                            is.append(" \\in [");
+                            addOneTokenToTLA(is.toString());
+                            addLeftParen(proc.id.getOrigin());
+                            addExprToTLA(proc.id);
+                            addRightParen(proc.id.getOrigin());
+                            addOneTokenToTLA(" -> " );
+                            addLeftParen(decl.val.getOrigin());
+                            addExprToTLA(AddSubscriptsToExpr(
+                                          decl.val, expr, new Changed(new Vector())) );
+                            addRightParen(decl.val.getOrigin());
+                            addOneTokenToTLA("]");
+                        }
+                    }
+                    /*
+                     * This adds the final )) .
+                     */
+                    addRightParen(decl.getOrigin());
+                    endCurrentLineOfTLA();
+                    is = new StringBuffer(NSpaces(col));
+                    
+// everything from here down to the end of the for p loop should be commented out                                       
+//                    is.append(decl.var);
+//                    is = new StringBuffer(decl.var);
+//                    if (decl.isEq)
+//                        is.append(" = ");
+//                    else
+//                        is.append(" \\in ");
+//                    /*******************************************************
+//                    * Modified on 31 Jan 2006 by LL to add subscripts to   *
+//                    * initialization expression for process set.  Note     *
+//                    * tricky subscript that is added in expr for           *
+//                    * declaration of form "v \in expr".                    *
+//                    *                                                      *
+//                    * Also modified the whole method of producing the      *
+//                    * variable declaration because the original destroyed  *
+//                    * the formatting of the expression proc.id, leading    *
+//                    * to bad or incorrect formatting if the process id     *
+//                    * set expression was not trivial.                      *
+//                    *******************************************************/
+//                    Vector sv;
+//                    TLAExpr sve;
+//                    if (proc.isEq)
+//                    {
+//                        /***************************************************
+//                        * No substitution unless it's a process set.       *
+//                        ***************************************************/
+//                        sve = decl.val; // .toStringVector();
+//                    } else
+//                    {
+//                        if (decl.isEq)
+//                        {
+//                            /***********************************************
+//                            * For declaration "v = ...", add subscript     *
+//                            * "[self]".                                    *
+//                            ***********************************************/
+//                            sve = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector()));
+//                        } else
+//                        {
+//                            /************************************************
+//                            * For declaration "v \in ...", add subscript    *
+//                            * "[CHOOSE self \in Process Id Set : TRUE]".    *
+//                            *                                               *
+//                            * This weird subscript is needed in the         *
+//                            * following weird case:                         *
+//                            *                                               *
+//                            *    process (P \in S)                          *
+//                            *    variable v \in T, w \in R(v)               *
+//                            *                                               *
+//                            * This produces the following conjunct in       *
+//                            * the initial predicate for w:                  *
+//                            *                                               *
+//                            *   w \in [S -> R(v[CHOOSE self \in S : TRUE])] *
+//                            ************************************************/
+//                            TLAExpr subexpr = proc.id.cloneAndNormalize();
+//
+//                            TLAExpr expr = new TLAExpr();
+//                            expr.addLine();
+//                            expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN));
+//                            expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN));
+//                            expr.addToken(new TLAToken("self", 8, TLAToken.IDENT));
+//                            expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN));
+//                            expr.normalize();
+//
+//                            try
+//                            {
+//                                subexpr.prepend(expr, 1);
+//                                expr = new TLAExpr();
+//                                expr.addLine();
+//                                expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN));
+//                                expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN));
+//                                expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN));
+//                                expr.prepend(subexpr, 1);
+//                            } catch (TLAExprException e)
+//                            {
+//                                throw new PcalTLAGenException(e.getMessage());
+//                            }
+//
+//                            sve = AddSubscriptsToExpr(decl.val, expr, new Changed(new Vector()));
+//                        }
+//                        ;
+//                    }
+//                    ;
+//                    TLAExpr expr = new TLAExpr();
+//                    expr.addLine();
+//                    if (!proc.isEq)
+//                    {
+//                        expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN));
+//                        if (decl.isEq)
+//                        {
+//                            expr.addToken(new TLAToken("self", 1, TLAToken.IDENT));
+//                            expr.addToken(new TLAToken("\\in ", 6, TLAToken.BUILTIN));
+//                        }
+//                        ;
+//                        expr.normalize();
+//                        TLAExpr expr2 = proc.id.cloneAndNormalize();
+//                        try
+//                        {
+//                            expr2.prepend(expr, 0);
+//                            expr = new TLAExpr();
+//                            expr.addLine();
+//                            if (decl.isEq)
+//                            {
+//                                expr.addToken(new TLAToken("|->", 0, TLAToken.BUILTIN));
+//                            } else
+//                            {
+//                                expr.addToken(new TLAToken("->", 0, TLAToken.BUILTIN));
+//                            }
+//                            ;
+//                            expr.prepend(expr2, 1);
+//                            sve.prepend(expr, 1);
+//                        } catch (TLAExprException e)
+//                        {
+//                            throw new PcalTLAGenException(e.getMessage());
+//                        }
+//                    }
+//                    ;
+//                    sv = sve.toStringVector();
+//                    if (proc.isEq)
+//                    {
+//                        sv = Parenthesize(sv);
+//                    }
+//                    ;
+//                    /*****************************************************
+//                    * Call to Parenthesize added by LL on 27 Feb 2008.   *
+//                    * See bug_08-02-18.                                  *
+//                    *****************************************************/
+//                    int col2 = is.length();
+//                    is.append((String) sv.elementAt(0));
+//                    for (int v = 1; v < sv.size(); v++)
+//                    {
+//                        tlacode.addElement(is.toString());
+//                        is = new StringBuffer(NSpaces(col2));
+//                        is.append((String) sv.elementAt(v));
+//                    }
+//                    if (!proc.isEq)
+//                        is.append("]");
+//                    tlacode.addElement(is.toString()); 
+//                    is = new StringBuffer(NSpaces(col));
+// end of section to be commented out 
+                } // end of for p loop.
+            }
+        }
+
+        /* stack initial value */
+        if (procs != null && procs.size() > 0)
+        {
+            if (mp)
+                is.append("/\\ stack = [self \\in ProcSet |-> << >>]");
+            else
+                is.append("/\\ stack = << >>");
+//            tlacode.addElement(is.toString());
+            addOneLineOfTLA(is.toString());
+            is = new StringBuffer(NSpaces(col));
+        }
+        /* pc initial value */
+        if (! ParseAlgorithm.omitPC) {
+          if (mp)
+          {
+              // On 4 May 2012, LL added useCase flag to inhibit adding of CASE for
+              // a single process or process set.
+              boolean useCase = st.processes.size() != 1;
+              if (useCase) {
+                is.append("/\\ pc = [self \\in ProcSet |-> CASE ");
+             } else {
+                 is.append("/\\ pc = [self \\in ProcSet |-> ");
+             }
+              int colPC = is.length();
+              if (boxUnderCASE)
+                  colPC = colPC - 3;
+              for (int p = 0; p < st.processes.size(); p++)
+              {
+                  PcalSymTab.ProcessEntry pe = (PcalSymTab.ProcessEntry) st.processes.elementAt(p);
+                    if (useCase) {
+                        is.append("self ");
+                        if (pe.isEq) {
+                            is.append("= ");
+                            // int colExpr = is.length();
+                            addOneTokenToTLA(is.toString());
+                            addLeftParen(pe.id.getOrigin());
+                            addExprToTLA(pe.id);
+                            addRightParen(pe.id.getOrigin());
+
+                            // Vector sv = pe.id.toStringVector();
+                            // is.append((String) sv.elementAt(0));
+                            // for (int v = 1; v < sv.size(); v++)
+                            // {
+                            // addOneLineOfTLA(is.toString());
+                            // // tlacode.addElement(is.toString());
+                            // is = new StringBuffer(NSpaces(colExpr));
+                            // is.append((String) sv.elementAt(v));
+                            // }
+                        } else {
+                            is.append("\\in ");
+                            // int colExpr = is.length();
+                            // Vector sv = pe.id.toStringVector();
+                            // is.append((String) sv.elementAt(0));
+                            // for (int v = 1; v < sv.size(); v++)
+                            // {
+                            // tlacode.addElement(is.toString());
+                            // is = new StringBuffer(NSpaces(colExpr));
+                            // is.append((String) sv.elementAt(v));
+                            // }
+                            addOneTokenToTLA(is.toString());
+                            addLeftParen(pe.id.getOrigin());
+                            addExprToTLA(pe.id);
+                            addRightParen(pe.id.getOrigin());
+
+                        }
+                        // is.append(" -> \"");
+                        is = new StringBuffer(" -> \"");
+                        is.append(pe.iPC);
+                        if (p == st.processes.size() - 1)
+                            is.append("\"]");
+                        else if (!boxUnderCASE)
+                            is.append("\" []");
+                        else
+                            is.append("\"");
+                    } // end if (useCase)
+                    else {
+                        is.append("\"" + pe.iPC + "\"]");
+                    }
+//                  tlacode.addElement(is.toString());
+                  addOneTokenToTLA(is.toString());
+                  endCurrentLineOfTLA();
+                  is = new StringBuffer(NSpaces(colPC));
+                  if (boxUnderCASE && p < st.processes.size() - 1)
+                      is.append("[] ");
+             }
+          } else
+          {
+              is.append("/\\ pc = \"" + st.iPC + "\"");
+//              tlacode.addElement(is.toString());
+              addOneLineOfTLA(is.toString());
+          }
+        }
+//        tlacode.addElement("");
+        addOneLineOfTLA("");
+    }
+
+    /************************************/
+    /* Generate the Next == definition. */
+    /************************************/
+    private void GenNext()
+    {
+        // It we are omitting pc and this is a uniprocess
+        // algorithm, then the definition of Next has
+        // already been added.
+        if (ParseAlgorithm.omitPC && !mp) {
+            return;
+        }
+        Vector nextS = new Vector();
+        StringBuffer sb = new StringBuffer();
+        int max, col;
+        
+        if (! (PcalParams.NoDoneDisjunct || ParseAlgorithm.omitStutteringWhenDone))
+        { 
+//          tlacode.addElement(sb.toString());
+          sb.append("(* Allow infinite stuttering to prevent deadlock on termination. *)");
+          addOneLineOfTLA(sb.toString());
+          
+          sb = new StringBuffer("Terminating == ");
+          if (mp) {
+              /************************************************************
+              * Bug fix by LL on 6 Sep 2007.  Added parentheses to        *
+              * change                                                    *
+              *                                                           *
+              * (*)    \A self \in ProcSet: ... /\ UNCHANGED vars         *
+              *                                                           *
+              * to                                                        *
+              *                                                           *
+              * (**)   (\A self \in ProcSet: ...)  /\ UNCHANGED vars      *
+              *                                                           *
+              * thus moving the UNCHANGED vars outside the quantifier.    *
+              * Since self does not appear in UNCHANGED vars, the two     *
+              * expressions are equivalent except when ProcSet is the     *
+              * empty set, in which case (*) equals TRUE and (**) equals  *
+              * UNCHANGED vars.                                           *
+              ************************************************************/
+              /************************************************************
+              * Changed by MK on 19 Jun 2019 into a conjunct list         *
+              * after modifying GenNext to generate an explicit           *
+              * Terminating action before Next instead of the old         *
+              * implicit disjunct-to-prevent-deadlock-on-termination.     *
+              * This change also entailed to copy this if-block from the  *
+              * end of GenNext here modifying the original one to         *
+              * generate a call to Terminating.                           *
+              *                                                           *
+              * The rational for this change results from the recently    *
+              * introduced TLC profiler. The profiler reports the number  *
+              * of distinct successor states per action.                  *
+              * A terminating PlusCal algorithm has the implicit          *
+              * disjunct-to-prevent-deadlock-on-termination sub-action of *
+              * the Next next-state action.  Since the sub-action has no  *
+              * identifier, the profiler has to report Next as generating *
+              * no successor states (which is bogus).  With this change,  *
+              * the profiler will report the Terminating sub-action to    *
+              * generate no (distinct) successor states instead, which    *
+              * is perfectly correct and easy to understand.              *
+              ************************************************************/
+              sb.append("/\\ \\A self \\in ProcSet: pc[self] = \"Done\"");
+              addOneLineOfTLA(sb.toString());
+              sb = new StringBuffer(NSpaces("Terminating == ".length()));
+              sb.append("/\\ UNCHANGED vars");
+          } else {
+              sb.append("pc = \"Done\" /\\ UNCHANGED vars");
+//              tlacode.addElement(sb.toString());
+          }
+          addOneLineOfTLA(sb.toString());
+          addOneLineOfTLA("");
+        } ;
+        sb = new StringBuffer();
+        		
+        // Steps with no parameter
+        max = wrapColumn - ("Next == \\/ ".length());
+        for (int i = 0; i < nextStep.size(); i++)
+        {
+            String a = (String) nextStep.elementAt(i);
+            if (a.length() + " \\/ ".length() + sb.length() > max)
+            {
+                nextS.addElement(sb.toString());
+                sb = new StringBuffer();
+            }
+            if (sb.length() > 0)
+                sb.append(" \\/ ");
+            sb.append(a);
+        }
+        if (sb.length() > 0)
+            nextS.addElement(sb.toString());
+
+        // Steps with (self) from ProcSet
+        // These are procedures in a multiprocess algorithm
+        Vector nextSS = new Vector();
+        String nextSSstart = "(\\E self \\in ProcSet: ";
+        sb = new StringBuffer();
+        max = wrapColumn - ("Next == \\/ (\\E self \\in ProcSet: \\/ ".length());
+        if (mp && st.procs.size() > 0)
+        {
+            for (int i = 0; i < st.procs.size(); i++)
+            {
+                PcalSymTab.ProcedureEntry p = (PcalSymTab.ProcedureEntry) st.procs.elementAt(i);
+                if ((p.name.length() + "(self) \\/ ".length() + sb.length()) > max)
+                {
+                    nextSS.addElement(sb.toString());
+                    sb = new StringBuffer();
+                }
+                if (sb.length() > 0)
+                    sb.append(" \\/ ");
+                sb.append(p.name);
+                sb.append("(self)");
+            }
+            if (sb.length() > 0)
+                nextSS.addElement(sb.toString() + ")");
+        }
+
+        // Steps with (self) from a set
+        // These are process sets
+        Vector nextSSP = new Vector(); // of Vector
+        if (mp && st.processes.size() > 0)
+            for (int i = 0; i < st.processes.size(); i++)
+            {
+                PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i);
+                if (p.isEq)
+                    continue;
+                Vector vec = new Vector();
+                sb = new StringBuffer();
+                sb.append("(\\E self \\in ");
+                Vector sv = p.id.toStringVector();
+                col = sb.length();
+                sb.append((String) sv.elementAt(0));
+                for (int j = 1; j < sv.size(); j++)
+                {
+                    vec.addElement(sb.toString());
+                    sb = new StringBuffer(NSpaces(col));
+                    sb.append((String) sv.elementAt(j));
+                }
+                sb.append(": ");
+                sb.append(p.name);
+                sb.append("(self))");
+                vec.addElement(sb.toString());
+                nextSSP.addElement(vec);
+            }
+
+        // assemble the line from the pieces
+        sb = new StringBuffer("Next == ");
+        col = sb.length() + 2;
+        for (int i = 0; i < nextS.size(); i++)
+        {
+            sb.append((String) nextS.elementAt(i));
+            addOneLineOfTLA(sb.toString());
+//            tlacode.addElement(sb.toString());
+            sb = new StringBuffer(NSpaces(col) + " \\/ ");
+        }
+        if (nextSS.size() > 0)
+        {
+            sb.append(nextSSstart);
+            int col2 = sb.length();
+            if (nextSS.size() > 1)
+                sb.append(" \\/ ");
+            for (int i = 0; i < nextSS.size(); i++)
+            {
+                sb.append((String) nextSS.elementAt(i));
+                addOneLineOfTLA(sb.toString());
+//                tlacode.addElement(sb.toString());
+                sb = new StringBuffer(NSpaces(col2) + " \\/ ");
+            }
+            sb = new StringBuffer(NSpaces(col) + " \\/ ");
+        }
+        if (nextSSP.size() > 0)
+            for (int i = 0; i < nextSSP.size(); i++)
+            {
+                Vector v = (Vector) nextSSP.elementAt(i);
+                for (int j = 0; j < v.size(); j++)
+                {
+                    String line = (String) v.elementAt(j);
+                    sb.append(line);
+                    addOneLineOfTLA(sb.toString());
+//                    tlacode.addElement(sb.toString());
+                    
+                    // The following if case was added by LL on 22 Jan 2011
+                    // to correct part 1 of bug bug_11_01_13.  This  bug occurs
+                    // when an "\in" process's set is multi-line and that
+                    // process's next-state action comes immediately after 
+                    // the Next == ..., with no " \/ " preceding it.  To fix the
+                    // problem, we must add 6 fewer spaces to all lines after
+                    // the first in that process's set than in other such sets. 
+                    if ((nextS.size() == 0) && (nextSS.size() == 0) && (i == 0)) {
+                        sb = new StringBuffer(NSpaces(col - 2));
+                    } else {
+                        sb = new StringBuffer(NSpaces(col + 4));
+                    }
+                }
+                sb = new StringBuffer(NSpaces(col) + " \\/ ");
+            }
+        if (! (PcalParams.NoDoneDisjunct || ParseAlgorithm.omitStutteringWhenDone))
+        { 
+          addOneLineOfTLA(sb.append("Terminating").toString());
+        }
+         addOneLineOfTLA("");
+//        tlacode.addElement("");
+    }
+
+    /****************************************/
+    /* Generate the Spec == ... definition. */
+    /****************************************/
+    /***********************************************************************
+    * The spec can contain the following conjuncts                         *
+    *                                                                      *
+    * 1. Init /\ [][Next]_vars                                             *
+    *    Always present                                                    *
+    *                                                                      *
+    * 2. WF_var(Next)                                                      *
+    *    present if (a) The wfNext option is specified, or                 *
+    *               (b) It is a uniprocess algorithm and one of the        *
+    *                   options -wf, -sf, -termination is specified.       *
+    *                                                                      *
+    * 3. A sequence of process fairness formulas, containing               *
+    *    one for each process for which there is a fairness condition.     *
+    *                                                                      *
+    * A process has                                                        *
+    *    (a) a WF fairness condition iff                                   *
+    *         (i) it is preceded by the keyword "fair" and the -nof        *
+    *             option is not specified, or                              *
+    *        (ii) the -wf option is specified.                             *
+    *    (b) an SF fairness condition iff it is not preceded               *
+    *        by the keyword "fair" and the -sf option is specified.        *
+    *                                                                      *
+    * Let P be a process specified by either                               *
+    *                                                                      *
+    *     [fair] process (P = exp) ...                                     *
+    *     [fair] process (P \in exp) ...                                   *
+    *                                                                      *
+    * In the first case we say that P is a single process, in the second   *
+    * that it is a process set.  Let                                       *
+    *                                                                      *
+    *   - p_1, ... , p_Np  be the labels of P modified by "+"              *
+    *                                                                      *
+    *   - m_1, ... , m_Nm  be the set of labels of P modified by "-"       *
+    *                                                                      *
+    *   - pSelf = IF P is a single process THEN "pname"                    *
+    *                                      ELSE "self"                     *
+    *                                                                      *
+    *   - qSelf = IF /\ P is a single process                              *
+    *                /\ pSelf a multi-line formula                         *
+    *               THEN "self"                                            *
+    *               ELSE pSelf                                             *
+    *                                                                      *
+    *   - pName = IF P is a single process THEN "P"                        *
+    *                                      ELSE  "P(self)"                 *
+    *                                                                      *
+    * A process fairness formula is described by the following:            *
+    *                                                                      *
+    *    XF:                                                               *
+    *       either WF or SF depending on P's fairness condition            *
+    *                                                                      *
+    *    prefix:                                                           *
+    *       IF P is a single process                                       *
+    *         THEN IF pSelf a multi-line formula                           *
+    *                THEN "LET self = pSelf IN"                            *
+    *                ELSE ""                                               *
+    *         ELSE "\A self \in exp"                                       *
+    *                                                                      *
+    *    wfFormula:                                                        *
+    *       "XF( (pc[qSelf] \notin {"m_1", ... , "m_Np"}) /\ pName )"      *
+    *                                                                      *
+    *    sfFormula:                                                        *
+    *       if XF = SF                                                     *
+    *         then null                                                    *
+    *         else if P a single process                                   *
+    *                then "SF_vars(p_1) /\ ... /\ SF_vars(p_Np)"           *
+    *                else "SF_vars(p_1(self)) /\ ...                       *
+    *                         /\ SF_vars(p_Np(self))"                      *
+    *                                                                      *
+    *    prcdFormulas:                                                     *
+    *       A sequence consisting of the following two formulas for each   *
+    *       procedure D called within P.  Let                              *
+    *                                                                      *
+    *         - pd_1, ... , pd_Npd  be the labels of D modified by "+"     *
+    *         - md_1, ... , md_Nmd  be the labels of D modified by "-"     *
+    *                                                                      *
+    *       in the formulas:                                               *
+    *                                                                      *
+    *         wfFormula:                                                   *
+    *           "XF( (pc[qSelf] \notin {"md_1", ... , "md_Nmd"})           *
+    *                    /\ D(qSelf) )"                                    *
+    *                                                                      *
+    *         sfFormula:                                                   *
+    *            if XF = SF                                                *
+    *              then null                                               *
+    *              else "SF_vars(pd_1(qSelf)) /\ ...                       *
+    *                            /\ SF_vars(pd_Npd(qSelf))"                *
+    *                                                                      *
+    * -------                                                              *
+    *                                                                      *
+    * If there is at least one fairness formula, the definition of Spec    *
+    * will be formatted in one of two ways.  If there is either a          *
+    * WF_vars(Next) condition or a process fairness formula, then it may   *
+    * be formatted as:                                                     *
+    *                                                                      *
+    *   Spec == Init /\ [][Next]_vars                                      *
+    *                                                                      *
+    * otherwise, it will be formatted as                                   *
+    *                                                                      *
+    *   Spec == /\ Init /\ [][Next]_vars                                   *
+    *          [/\ WF_vars(Next)]                                          *
+    *           /\ F_1                                                     *
+    *           ...                                                        *
+    *           /\ F_n                                                     *
+    *                                                                      *
+    * where each F_i is a process fairness formulas.                       *
+    ***********************************************************************/
+    private void GenSpec()
+    {   String safetyFormula = "Init /\\ [][Next]_vars" ;
+    	
+        if (    PcalParams.FairnessOption.equals("nof")
+             || (!mp && PcalParams.FairnessOption.equals(""))) {
+            addOneLineOfTLA("Spec == " + safetyFormula);
+            addOneLineOfTLA("");
+//        	tlacode.addElement("Spec == " + safetyFormula );
+//        	tlacode.addElement("");
+        	return;
+        }
+//System.out.println("foo |-> " + st.UseThis(PcalSymTab.PROCEDURE, "foo", ""));
+//int to = st.FindProc("foo");
+//PcalSymTab.ProcedureEntry pe =
+//    (PcalSymTab.ProcedureEntry) st.procs.elementAt(to);
+//AST.Procedure procAst = pe.ast;
+
+    	StringBuffer sb = new StringBuffer("Spec == ");
+        // Generate the requested fairness conjuncts
+
+    	// wfNextConj is either null or  " /\ WF_(Next)" 
+    	String wfNextConj = null; 
+    	if (   PcalParams.FairnessOption.equals("wfNext")
+    	    || PcalParams.FairAlgorithm
+        	|| (!mp && (   PcalParams.FairnessOption.equals("wf")
+        			    || PcalParams.FairnessOption.equals("sf"))))
+        {
+            // If uniprocess then wf and sf are the same as wfNext
+        	wfNextConj = " /\\ WF_vars(Next)";
+        }         
+    	
+    	// Now compute procFairnessFormulas to equal the processes' fairness 
+    	// formulas, which is never null but may have zero length.
+    	Vector procFairnessFormulas = new Vector() ;
+        if (mp) {
+           for (int i = 0; i < st.processes.size(); i++) {
+        	   PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i);
+        	   AST.Process pAst = p.ast ;
+        	   int fairness = pAst.fairness;
+        	   if (fairness != AST.UNFAIR_PROC) {
+        		   String xf = (fairness == AST.WF_PROC) ? "WF" : "SF";
+        		   
+                   Vector pSelf = p.id.toStringVector();
+                   
+                   // makeLetIn is true iff prefix will be LET self == ... IN
+                   boolean makeLetIn = false ;
+                   
+                   String qSelf = "self";
+                   if (p.isEq) {
+                       if (pSelf.size() > 1) {
+                           makeLetIn = true ;
+                       } else {
+                           qSelf = (String) pSelf.elementAt(0);
+                       }  
+                   }
+                   
+                   Vector prefix = new Vector();
+        		   if (makeLetIn || !p.isEq) {
+                       int prefixSize = pSelf.size();
+                       String prefixBegin;
+                       String prefixEnd;
+                       if (p.isEq) {
+                           prefixBegin = "LET self == ";
+                           prefixEnd = "";
+                       } else {
+                           prefixBegin = "\\A self \\in ";
+                           prefixEnd = " : ";
+                       }
+                       String padding = NSpaces(prefixBegin.length());
+                       for (int j = 0; j < prefixSize; j++) {
+                           String line = (String) pSelf.elementAt(j);
+                           if (j == 0) {
+                               line = prefixBegin + line;
+                           } else {
+                               line = padding + line;
+                           }
+                           if (j == prefixSize - 1) {
+                               line = line + prefixEnd;
+                           }
+                           prefix.addElement(line);
+                       }
+                       if (makeLetIn) {
+                           prefix.addElement("IN ");
+                       }
+        		   } // end if (makeLetIn || !p.isEq)
+        		   
+        		   StringBuffer wfSB = new StringBuffer(xf + "_vars(");
+        		   if (pAst.minusLabels != null && pAst.minusLabels.size() > 0) {
+        		       wfSB.append("(pc[");
+        		       wfSB.append(qSelf);
+        		       if (pAst.minusLabels.size() == 1) {
+        		           wfSB.append("] # \"");
+        		           wfSB.append(pAst.minusLabels.elementAt(0));
+        		           wfSB.append("\"");
+        		       } else {
+        		           wfSB.append("] \\notin {\"");
+        		           for (int j = 0; j < pAst.minusLabels.size(); j++) {
+        		               wfSB.append(pAst.minusLabels.elementAt(j));
+        		               if (j == pAst.minusLabels.size() - 1) {
+        		                   wfSB.append("\"}");
+        		               } else {
+        		                   wfSB.append("\", \"");
+        		               }
+        		           }
+        		       }
+        		       wfSB.append(") /\\ ");
+        		   }
+        		   
+        		   String pName = p.name;
+                   if (!p.isEq) {
+                       pName = p.name + "(self)";
+                   }
+                   wfSB.append(pName);
+        		   wfSB.append(")");
+        		   
+        		   StringBuffer sfSB = null ;
+        		   if (    xf.equals("WF") 
+        		       && (pAst.plusLabels != null) 
+        		       && (pAst.plusLabels.size() != 0)) {
+        		       sfSB = new StringBuffer() ;
+        		       for (int j = 0; j < pAst.plusLabels.size(); j++) {
+        		           if (j != 0) {
+        		               sfSB.append(" /\\ ");
+        		           }
+        		           sfSB.append("SF_vars(");
+        		           sfSB.append(pAst.plusLabels.elementAt(j));
+        		           if (!p.isEq) {
+        		               sfSB.append("(self)");
+        		           }
+        		           sfSB.append(")");
+        		       }
+        		   }
+        	   
+        	       Vector  prcdFormulas = new Vector();
+        	       Vector  procedures = pAst.proceduresCalled;
+                   for (int k = 0; k < procedures.size(); k++) {
+                       String originalName = (String) procedures.elementAt(k);
+                       String name = st.UseThis(PcalSymTab.PROCEDURE, originalName, "");
+                       int procedureIndex = st.FindProc(name);
+                       PcalSymTab.ProcedureEntry pe =
+                          (PcalSymTab.ProcedureEntry) st.procs.elementAt(procedureIndex);
+                       AST.Procedure prcAst = pe.ast;
+
+                       StringBuffer wfPrcSB = new StringBuffer(xf + "_vars(");
+                       if (prcAst.minusLabels != null && prcAst.minusLabels.size() > 0) {
+                           wfPrcSB.append("(pc[");
+                           wfPrcSB.append(qSelf);
+                           if (prcAst.minusLabels.size() == 1) {
+                               wfPrcSB.append("] # \"");
+                               wfPrcSB.append(prcAst.minusLabels.elementAt(0));
+                               wfPrcSB.append("\"");
+                           } else {
+                               wfPrcSB.append("] \\notin {\"");
+                               for (int j = 0; j < prcAst.minusLabels.size(); j++) {
+                                   wfPrcSB.append(prcAst.minusLabels.elementAt(j));
+                                   if (j == prcAst.minusLabels.size() - 1) {
+                                       wfPrcSB.append("\"}");
+                                   } else {
+                                       wfPrcSB.append("\", \"");
+                                   }
+                               }
+                           }
+                           wfPrcSB.append(") /\\ ");
+                       }
+    
+                       String prcName = pe.name + "(" + qSelf + ")";
+                       wfPrcSB.append(prcName);
+                       wfPrcSB.append(")");
+                                          
+                       StringBuffer sfPrcSB = null;
+                       if (    xf.equals("WF") 
+                           && (prcAst.plusLabels != null) 
+                           && (prcAst.plusLabels.size() != 0)) {
+                           sfPrcSB = new StringBuffer() ;
+                           for (int j = 0; j < prcAst.plusLabels.size(); j++) {
+                               if (j != 0) {
+                                   sfPrcSB.append(" /\\ ");
+                               }
+                               sfPrcSB.append("SF_vars(");
+                               sfPrcSB.append(prcAst.plusLabels.elementAt(j));
+                               sfPrcSB.append("(" + qSelf + ")") ;
+                               sfPrcSB.append(")");
+                           }
+                       }
+                       prcdFormulas.addElement(
+                            new FormulaPair(
+                                    wfPrcSB.toString(), 
+                                    (sfPrcSB == null) ? null : sfPrcSB.toString())
+                          ) ;
+                     } // end construction of prcdFormulas
+        	       
+        	       procFairnessFormulas.addElement(
+        	         new ProcessFairness(
+        	                 xf, 
+        	                 prefix, 
+        	                 wfSB.toString(), 
+        	                 (sfSB == null) ? null : sfSB.toString(), 
+        	                 prcdFormulas)
+        	              ) ;
+               } // end if (fairness != AST.UNFAIR_PROC)
+           } 	   
+        } // ends construction of procFairnessFormulas
+           
+        if (wfNextConj == null && procFairnessFormulas.size() == 0) {
+            addOneLineOfTLA("Spec == " + safetyFormula);
+            addOneLineOfTLA("");
+//            tlacode.addElement("Spec == " + safetyFormula);
+//            tlacode.addElement("");
+            return;
+        }
+        addOneLineOfTLA("Spec == /\\ " + safetyFormula);
+//        tlacode.addElement("Spec == /\\ " + safetyFormula) ;
+        int indent = "Spec == /\\ ".length();
+        
+        if (wfNextConj != null) {
+            addOneLineOfTLA("        /\\ WF_vars(Next)");
+//            tlacode.addElement("        /\\ WF_vars(Next)");
+        }
+        for (int i = 0; i < procFairnessFormulas.size(); i++) {
+            /*
+             * The original code called format on the fairness formula, which can
+             * create a string with \n characters embedded.  I've just split the
+             * string into its individual lines and added them to tlacode one at a time.
+             * However, the current and original code can both produce very long lines 
+             * that could be wrapped.  So if this change does make a difference, then 
+             * it would be better to completely rewrite the format method (which is only 
+             * called here).
+             */
+             String str = 
+                  "        /\\ " + 
+                  ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent).toString();
+             String [] splitStr = str.split("\n");
+             for (int j = 0; j < splitStr.length; j++) {
+                 addOneLineOfTLA(splitStr[j]);
+             }
+
+//            tlacode.addElement(
+//                        "        /\\ " +
+//                      ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent)
+//                         );
+        }
+        addOneLineOfTLA("");
+//        tlacode.addElement("");
+        return;
+    }
+
+    /************************************/
+    /* Generate the Termination ==      */
+    /************************************/
+    private void GenTermination()
+    {
+        // if we're omitting the pc or omitting the stuttering-when-done
+        // clause of the Next action, then we shouldn't
+        // generate the Termination definition.
+        // Check of omitStutteringWhenDone added by LL on 30 Mar 2012.
+        if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) {
+            return;
+        }
+        StringBuffer sb = new StringBuffer();
+        sb.append("Termination == <>(");
+        if (mp)
+            sb.append("\\A self \\in ProcSet: pc[self]");
+        else
+            sb.append("pc");
+        sb.append(" = \"Done\")");
+        addOneLineOfTLA(sb.toString());
+        addOneLineOfTLA("");
+//        tlacode.addElement(sb.toString());
+//        tlacode.addElement("");
+    }
+
+    /**********************************************************/
+    /* For variables that need subscripts, add the subscript. */
+    /* These are pc, stack, procedure variables, procedure    */
+    /* parameters, and variables defined in process sets.     */
+    /* Then, add primes to variables that have been changed   */
+    /* according to c.                                        */
+    /*   exprn : the original expression.                     */
+    /*   sub : the subscript to be added (null if none)       */
+    /*   c : the variables that have been changed (so need to */
+    /*       be primed.                                       */
+    /**********************************************************/
+    private TLAExpr AddSubscriptsToExpr(TLAExpr exprn, TLAExpr sub, Changed c) throws PcalTLAGenException
+    {
+        /*
+         * For testing, throw a null pointer exception if the begin/end substitution
+         * mapping vectors are not properly matching in the returned expression
+         */
+//        int[] depths = new int[1000];
+        
+        int parenDepth = 0;
+        for (int i = 0; i < exprn.tokens.size(); i++) {
+            Vector line = (Vector) exprn.tokens.elementAt(i);
+            for (int j = 0; j < line.size(); j++) {
+                TLAToken tok = (TLAToken) line.elementAt(j);
+                parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size();
+                if (parenDepth < 0) {
+                        throw new NullPointerException("argument: begin/end Subst depth negative");
+                }
+            }
+//            depths[i] = parenDepth;
+        }
+        if (parenDepth != 0) {
+            throw new NullPointerException("argument: Unmatched begin Subst");
+        }
+        /*   ------------------ end testing --------------------------*/
+
+        /*
+         * We now set stringVec to the sequence of identifiers that occur in exprn
+         * for which we need to add a subscript or a prime.
+         */
+        Vector exprVec = new Vector(); // the substituting exprs
+        Vector stringVec = new Vector(); // the substituted ids
+        TLAExpr expr = exprn.cloneAndNormalize();  // the expression to be returned
+
+       for (int i = 0; i < expr.tokens.size(); i++)
+        {
+            Vector tv = (Vector) expr.tokens.elementAt(i);
+            for (int j = 0; j < tv.size(); j++)
+            {
+                TLAToken tok = (TLAToken) tv.elementAt(j);
+                boolean prime = ((tok.type == TLAToken.IDENT) && c.IsChanged(tok.string));
+                boolean subr = (sub != null && (tok.type == TLAToken.ADDED || (mp && (tok.type == TLAToken.IDENT) && (IsProcedureVar(tok.string) || IsProcessSetVar(tok.string)))));
+                if ((subr || prime) && !InVector(tok.string, stringVec))
+                {
+                    stringVec.addElement(tok.string);
+                    TLAExpr exp = new TLAExpr();
+                    exp.addLine();
+                    /*
+                     * 15 Dec 2011:  The following code added to replace the
+                     *    
+                     *    exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT));
+                     *    
+                     * that is commented out.  Note that this change can add a token with
+                     * type ADDED rather than IDENT.  I don't think this matters.
+                     */
+                    TLAToken newTok = tok.Clone() ;
+                    /*
+                     * The new token should inherit nothing from the baggage of tok, whose
+                     * only function is to provide the name
+                     */
+                    newTok.setBeginSubst(new Vector(2));
+                    newTok.setEndSubst(new Vector(2));
+                    newTok.source = null;
+                    newTok.column = 0;
+                    exp.addToken(newTok) ;
+                    // exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT));
+                    if (prime) {
+                        /*****************************************************
+                        * Modified by LL on 30 Aug 2007.  The following      *
+                        * call to addTokenOffset was originally a call to    *
+                        * addToken.  See the comments for                    *
+                        * TLAExpr.addTokenOffset().                          *
+                        *****************************************************/
+                        TLAToken primeTok = new TLAToken("'", 0, TLAToken.BUILTIN, true);
+                        // The following stuff added by LL in Dec 2011 is bogus.  The
+                        // token tok is just the first one of many in the exprn with
+                        // the same name for which we want to substitute.  The only
+                        // useful data in the token tok is its string.
+                        //
+                        // if (tok.source != null) {
+                        //   primeTok.source = 
+                        //           new Region(tok.source.getEnd(), tok.source.getEnd());
+                        // }
+                        // if (!subr) {
+                        //    primeTok.setEndSubst(tok.getEndSubst());
+                        //   newTok.setEndSubst(new Vector(2));
+                        // }
+                        exp.addTokenOffset(primeTok, 0);
+                    }
+                    if (subr)
+                    {
+                        TLAExpr subexp = sub.cloneAndNormalize();
+                        
+                        /*
+                         * We now add the end of the origin of tok to beginSubst
+                         * of the first token of subexp and to endSubst of the
+                         * last token of subexp.  This indicates that PCal code
+                         * corresponding to a region of the TLA+ translation that
+                         * includes part of the added subscript and part of the
+                         * original expression is a portion of the source of
+                         * exprn.
+                         */
+                        // This is bogus, because we are adding the location of where
+                        // the identifier first occurs in exprn to the substitution
+                        // vectors of the expression that's going to be substituted in
+                        // all instances.
+                        //
+                        // if (tok.source != null) {
+                        //   PCalLocation endOfTok = tok.source.getEnd();
+                        //  subexp.firstToken().getBeginSubst().add(endOfTok);
+                        //  subexp.lastToken().getEndSubst().add(endOfTok);
+                        // }
+                        /*
+                         * However, we do have to move the token's beginSubst and endSubst vectors
+                         * to the first and lasts token of the subscript.  Since the
+                         * resulting Parens that they generate should be outside
+                         * the ones generated by the endSubst vectors of the expression,
+                         * we have to add them before that expression's Subst vectors.
+                         * 
+                         * No, no!  This tok is just giving us the name of the tok that
+                         * we're going to be substituting for in the expression.  It is not
+                         * necessarily the one that we're going to substitute for.
+                         */
+                        // newTok.getEndSubst().addAll(subexp.lastToken().getEndSubst());
+                        // subexp.lastToken().setEndSubst(newTok.getEndSubst());
+                        // newTok.setEndSubst(new Vector(2));
+                        exp.normalize();
+                        try
+                        {
+                            subexp.prepend(exp, 0);
+                        } catch (TLAExprException e)
+                        {
+                            throw new PcalTLAGenException(e.getMessage());
+                        }
+                        exp = subexp;
+                    }
+                    /**********************************************************
+                    * Modified by LL on 31 Jan 2006 to comment out the call   *
+                    * of MakeExprPretty, since it totally screwed up the      *
+                    * formatting when substituting any string containing      *
+                    * spaces or multiple lines for a variable.                *
+                    **********************************************************/
+                    // MakeExprPretty(exp);
+                    exprVec.addElement(exp);
+                }
+            }
+        }
+        if (exprVec.size() > 0)
+            try
+            {
+                expr.substituteForAll(exprVec, stringVec, false);
+            } catch (TLAExprException e)
+            {
+                throw new PcalTLAGenException(e.getMessage());
+            }
+        /*
+         * For testing, throw a null pointer exception if the begin/end substitution
+         * mapping vectors are not properly matching in the returned expression
+         */
+//        depths = new int[1000];
+        
+        parenDepth = 0;
+        for (int i = 0; i < expr.tokens.size(); i++) {
+            Vector line = (Vector) expr.tokens.elementAt(i);
+            for (int j = 0; j < line.size(); j++) {
+                TLAToken tok = (TLAToken) line.elementAt(j);
+                parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size();
+                if (parenDepth < 0) {
+                        throw new NullPointerException("result: begin/end Subst depth negative");
+                }
+            }
+//            depths[i] = parenDepth;
+        }
+        if (parenDepth != 0) {
+            throw new NullPointerException("result: Unmatched begin/subst");
+        }
+        /*   ------------------ end testing --------------------------*/
+        return expr;
+    }
+
+    /***********************************************************************
+     * Given an expression, makes it into a subscript expr.  It is called   *
+     * only with argument Self(context), which means that it is called      *
+     * only for a single subscript.                                         *
+     *                                                                      *
+     * If string is null, then returns null.                                *
+     *                                                                      *
+     * Since this is used only to add "[self]", there is no need to add     *
+     * any REPLACEMENT tokens to the expression.  Moreover, the added       *
+     * subscript's tokens have a null source field because they don't come  *
+     * from any PCal code.  The new expression is given the same origin as  *
+     * the original one.                                                    *
+     ***********************************************************************/
+    private static TLAExpr SubExpr(TLAExpr sub)
+    {
+        if (sub != null)
+        { 
+            TLAExpr expr = sub.cloneAndNormalize();
+              // This preserves the origin of the expression
+            for (int i = 0; i < expr.tokens.size(); i++) {
+                Vector tokenVec = (Vector) expr.tokens.elementAt(i);
+                for (int j = 0; j < tokenVec.size(); j++) {
+                   TLAToken tok = (TLAToken) tokenVec.elementAt(j);
+                   tok.column = tok.column + 1;
+                }
+                if (i == 0) {
+                    /*
+                     * Set isAppended field of the "[" and "]" to true iff the first
+                     * token of sub has isAppended field true, which should be the
+                     * case iff it is the "self" token.
+                     */
+                    tokenVec.insertElementAt(
+                          new TLAToken("[", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()),
+                          0);
+                }
+            }
+            expr.addTokenOffset(
+                    new TLAToken("]", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()), 0);
+            return expr;
+        } else {
+            return null;
+        }
+    }
+
+    /*********************************************************/
+    /* Gives the string to use when subscripting a variable. */
+    /*********************************************************/
+    // LL comment: it appears that PcalTLAGen.self is the 
+    // current process id if this is being called in the context
+    // of a process declared with `process (P = id)'.
+    private TLAExpr Self(String context)
+    {
+        TLAExpr s = null;
+        if (mp)
+        {
+            if (context.equals("procedure"))
+                s = selfAsExpr();
+            else
+                s = self;
+        }
+        return s;
+    }
+
+    private static TLAExpr  selfAsExpr() {
+        /*
+         * This is a token that does not correspond to anything in the
+         * PCal code, so it should have a null source field.
+         * It has a true isAppended field because it is always appended
+         * as a subscript to a variable.
+         */
+        TLAToken selfToken = new TLAToken("self", 0, TLAToken.IDENT, true);
+        Vector tokenVec = new Vector();
+        tokenVec.addElement(selfToken);
+        Vector tokens = new Vector();
+        tokens.addElement(tokenVec);
+        TLAExpr expr = new TLAExpr(tokens);
+//        expr.anchorTokens = new TLAToken[1];
+//        expr.anchorTokens[0] = selfToken;
+//        expr.anchorTokCol = new int[1];
+//        expr.anchorTokCol[0] = 0;
+        expr.normalize();
+        return expr ;
+    }
+    /***********************************************************************
+    * Comment added by LL: MakeExprPretty should never be called on an     *
+    * expression any part of which was an expression in the input.         *
+    * Fortunately, it is now called only for the expression "[self]", so   *
+    * it is effectively a no-op.                                           *
+    * Comment added 15 Dec 2011: In fact, it's not called at all.          *
+    ***********************************************************************/
+    public static void ObsoleteMakeExprPretty(TLAExpr expr)
+    {
+        /*********************************************************************
+         * Sets columns so this expr looks nice and tight.                   *
+         *********************************************************************/
+        Vector line; /* Vector of TLAToken */
+        boolean spread;
+        int nextCol = 1;
+        for (int i = 0; i < expr.tokens.size(); i++)
+        {
+            line = (Vector) expr.tokens.elementAt(i);
+            for (int j = 0; j < line.size(); j++)
+            {
+                TLAToken tok = ((TLAToken) line.elementAt(j));
+                spread = tok.string.equals("=");
+                tok.column = nextCol + ((spread) ? 1 : 0);
+                nextCol = nextCol + tok.getWidth() + ((spread) ? 2 : 0);
+            }
+        }
+    }
+
+    /***********************************************************/
+    /* v is a sequence of SingleAssign. Return a vector of the */
+    /* same SingleAssign, but sorted in terms of the lhs.var.  */
+    /***********************************************************/
+    private static Vector SortSass(Vector vec)
+    {
+        Vector v = (Vector) vec.clone();
+        Vector r = new Vector(); // The sorted version of v.
+        while (v.size() > 0)
+        { // Good old n^2 insertion sort.
+            AST.SingleAssign candidate = (AST.SingleAssign) v.elementAt(0);
+            int indexC = 0;
+            for (int i = 1; i < v.size(); i++)
+            {
+                AST.SingleAssign sass = (AST.SingleAssign) v.elementAt(i);
+                if (candidate.lhs.var.compareTo(sass.lhs.var) > 0)
+                {
+                    indexC = i;
+                    candidate = sass;
+                }
+            }
+            r.addElement(candidate);
+            v.remove(indexC);
+        }
+        return r;
+    }
+
+    /***********************************************************************
+    * If vec is a StringVector representing an expression, then this       *
+    * returns the StringVector obtained by parenthesizing the expression   *
+    * if it may need parenthesizing.  This is used only to prevent         *
+    * parsing errors when the expression appears immediately to the right  *
+    * of an "=" in the spec.  This is a rare situation, so it would be     *
+    * nice to add the parentheses only if really necessary.  For now, the  *
+    * parentheses are added if one of the following tokens occur outside   *
+    * parentheses and not inside a string:                                 *
+    *                                                                      *
+    *   =                                                                  *
+    *   #                                                                  *
+    *   <  not followed by <                                               *
+    *   >  not followed by > or preceded by =                              *
+    *   |  preceded or followed by -                                       *
+    *   \  not followed by "o" or "X".                                     *
+    *   /  followed by "\"                                                 *
+    *                                                                      *
+    * Left parentheses are                                                 *
+    *                                                                      *
+    *   (  [  {  <<                                                        *
+    *                                                                      *
+    * The handling of "\" is a simplifying hack.  Lots of operators        *
+    * beginning with "\" like "\/", "\gg" and "\subseteq" have precedence  *
+    * greater than or equal to "=".  The only commonly used ones with      *
+    * precedence lower than "=" seem to be "\o" and "\X".  It doesn't      *
+    * seem to be worth the bother of checking for the others just to       *
+    * avoid unnecessarily adding the parentheses when those other rare     *
+    * operators are used.                                                  *
+    *                                                                      *
+    * Perhaps the one improvement that might be worth making in this       *
+    * procedure is to have it not add parentheses because of "dangerous"   *
+    * operations in an IF clause--for example:                             *
+    *                                                                      *
+    *      IF x < 0 THEN ...                                               *
+    *                                                                      *
+    * This would require considering "IF" to be a left parenthesis and     *
+    * "THEN" to be a right parenthesis.  However, that's not trivial to    *
+    * implement because of unlikely things like                            *
+    *                                                                      *
+    *     IFx := 42 ;                                                      *
+    *     x := IFx < THENx                                                 *
+    ***********************************************************************/
+    private static Vector Parenthesize(Vector vec)
+    {
+        /*********************************************************************
+        * Add the parentheses if necessary.                                  *
+        *********************************************************************/
+        if (NeedsParentheses(vec))
+        {
+            vec.setElementAt("(" + ((String) vec.elementAt(0)), 0);
+            for (int i = 1; i < vec.size(); i++)
+            {
+                vec.setElementAt(" " + ((String) vec.elementAt(i)), i);
+            }
+            ;
+            int curLineNum = vec.size() - 1;
+            vec.setElementAt(((String) vec.elementAt(curLineNum)) + ")", curLineNum);
+        }
+        ;
+        return vec;
+    }
+    
+    /**
+     * As part of adding the TLA to PCal translation code, LL removedseparated 
+     * the code that decides if parentheses are needed from the Parenthesize 
+     * method and put it into this method.  The Parenthesize method itself
+     * will not be needed.
+     */
+    public static boolean  NeedsParentheses(Vector vec) {
+        if (vec.size() == 0)
+        {
+            return false;
+        }
+        ;
+        /*******************************************************************
+        * vec shouldn't be empty, but let's not worry about what to do if  *
+        * it is.                                                           *
+        *******************************************************************/
+        int curCharNum = 0;
+        int curLineNum = 0;
+        int parenDepth = 0;
+        boolean inString = false;
+        boolean needParen = false;
+        while ((curLineNum < vec.size()) && (!needParen))
+        {
+            String curLine = (String) vec.elementAt(0);
+            while ((curCharNum < curLine.length()) && (!needParen))
+            {
+                char curChar = curLine.charAt(curCharNum);
+
+                if (inString)
+                {
+                    switch (curChar) {
+                    case '\"':
+                        inString = false;
+                        break;
+                    case '\\':
+                        curCharNum++;
+                        break;
+                    }
+                    ; // end switch
+                } // end if (inString)
+                else
+                {
+                    boolean leftParen = false;
+                    boolean rightParen = false;
+                    boolean mayNeedParen = false;
+                    /***************************************************************
+                    * Set nextChar to the next character on the line, or ' ' if    *
+                    * there is none.                                               *
+                    ***************************************************************/
+                    char nextChar = ' ';
+                    if (curCharNum < curLine.length() - 1)
+                    {
+                        nextChar = curLine.charAt(curCharNum + 1);
+                    }
+                    switch (curChar) {
+                    case '\"':
+                        inString = true;
+                        break;
+                    case '=':
+                        mayNeedParen = true;
+                        break;
+                    case '#':
+                        mayNeedParen = true;
+                        break;
+                    case '<':
+                        if (nextChar == '<')
+                        {
+                            curCharNum++;
+                            leftParen = true;
+                        } else
+                        {
+                            mayNeedParen = true;
+                        }
+                        ;
+                        break;
+                    case '>':
+                        if (nextChar == '>')
+                        {
+                            curCharNum++;
+                            rightParen = true;
+                        } else
+                        {
+                            mayNeedParen = true;
+                        }
+                        ;
+                        break;
+                    case '|':
+                        if ((nextChar == '-') || ((curCharNum > 0) && (curLine.charAt(curCharNum - 1) == '-')))
+                        {
+                            mayNeedParen = true;
+                        }
+                        ;
+                        break;
+                    case '\\':
+                        if (!((nextChar == ' ') || (nextChar == 'o') || (nextChar == 'X')))
+                        {
+                            mayNeedParen = true;
+                        }
+                        ;
+                        break;
+                    case '/':
+                        if (nextChar == '\\')
+                        {
+                            mayNeedParen = true;
+                        }
+                        ;
+                        break;
+                    case '(':
+                    case '[':
+                    case '{':
+                        leftParen = true;
+                        break;
+                    case ')':
+                    case ']':
+                    case '}':
+                        rightParen = true;
+                        break;
+                    }
+                    ;
+                    if (mayNeedParen && (parenDepth == 0))
+                    {
+                        needParen = true;
+                    }
+                    ;
+                    if (leftParen)
+                    {
+                        parenDepth++;
+                    }
+                    ;
+                    if (rightParen)
+                    {
+                        if (parenDepth == 0)
+                        {
+                            needParen = true;
+                        }
+                        ;
+                        parenDepth--;
+                    }
+                }
+                ; // end else ! inString
+                curCharNum++;
+            }
+            ; // end while (curCharNum < curLine.length())
+
+            if (inString)
+            {
+                needParen = true;
+            }
+            ;
+            /*****************************************************************
+            * If there is an unmatched quote, we might as well stop here.    *
+            *****************************************************************/
+            curLineNum++;
+            curCharNum = 0;
+        } // end while (curLineNum < vec.size())
+        
+        return needParen;
+    }
+    
+    /*
+     * The following methods are used to add code to tlacode and add the
+     * appropriate objects to mappingVector
+     */
+    
+    /**
+     * Adds one token to tlacodeNextLine, and adds the appropriate
+     * Begin/EndTLAToken objects to mappingVectorNextLine.  The
+     * ...TLAToken objects mark a region that excludes beginning and
+     * ending space characters of token--unless the token has
+     * only space characters.
+     * 
+     * @param token
+     */
+    private void addOneTokenToTLA(String token) {
+        String trimmedToken = token.trim() ;
+        
+        int numberOfLeftTrimmedTokens = 
+           (trimmedToken.length() == 0) ? -1 :                   
+             token.indexOf(trimmedToken.charAt(0));
+        
+        /**
+         * Handle a token of only space characters. 
+         */
+        if (numberOfLeftTrimmedTokens == -1) {
+            numberOfLeftTrimmedTokens = 0 ;
+            trimmedToken = token ;
+        }
+        
+        int objBegin = tlacodeNextLine.length() + numberOfLeftTrimmedTokens;
+        mappingVectorNextLine.addElement(new MappingObject.BeginTLAToken(objBegin));
+        mappingVectorNextLine.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length()));
+        tlacodeNextLine = tlacodeNextLine + token;
+    }
+    
+    /**
+     * If region is non-null, then adds string str to the TLA output
+     * as a SourceToken object with that region.  Otherwise, it adds
+     * it as a TLAToken, with Begin/EndTLAToken objects.
+     * 
+     * @param str
+     * @param region
+     */
+    private void addOneSourceTokenToTLA(String str, Region region) {
+        if (region == null) {
+            addOneTokenToTLA(str);
+            return;
+        }
+        
+        int beginCol = tlacodeNextLine.length();
+        int endCol = beginCol + str.length();
+        mappingVectorNextLine.addElement(
+                new MappingObject.SourceToken(beginCol, endCol, region));
+             tlacodeNextLine = tlacodeNextLine + str;
+    }
+    /**
+     * Adds a complete line of TLA "code" that does not correspond to
+     * any PlusCal code.  Adds Begin/EndTLAToken objects to the mapping
+     * iff the line does not equal "".
+     * 
+     * @param line
+     */
+    private void addOneLineOfTLA(String line) {
+// temporarily commented out.
+        if(tlacode.size() != mappingVector.size()) {
+            PcalDebug.ReportBug("tlacode and mappingVector have different lengths") ;
+        }
+// The following added during testing.
+//if(tlacode.size() != mappingVector.size()) {
+//   System.out.println("tlacode and mappingVector have different lengths");
+//}
+        endCurrentLineOfTLA();
+        if (line.length() == 0) {
+            mappingVector.addElement(new Vector(2));
+            tlacode.addElement("");
+            return;
+        }
+        addOneTokenToTLA(line);
+        endCurrentLineOfTLA();
+    }
+    
+    /**
+     * If tlacodeNextLine does not equal "", then add it to tlacode
+     * and add mappingVectorNextLine to mappingVector.  If it does equal "",
+     * don't add any lines, but add any potentially legal leftovers in 
+     * mappingVectorNextLine to the previous line.
+     */
+    private void endCurrentLineOfTLA() {
+        if (tlacodeNextLine.length() != 0) {
+            tlacode.addElement(tlacodeNextLine) ;
+            mappingVector.addElement(mappingVectorNextLine) ;
+            tlacodeNextLine = "";
+            mappingVectorNextLine = new Vector() ;
+        } 
+        else {
+            if (mappingVectorNextLine.size() != 0) {
+                /*
+                 * There's something to go in the mappingVector that doesn't
+                 * accompany any text.  It should be one or more RightParen or
+                 * LeftParen objects, (or perhaps, eventually a Break), in which 
+                 * case they should be put at the end of the previous mappingVector 
+                 * line.  Anything else is a mistake.
+                 */
+                Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1);
+                for (int i = 0; i < mappingVectorNextLine.size(); i++) {
+                    MappingObject obj = (MappingObject) mappingVectorNextLine.elementAt(i);
+                    if (obj.getType() == MappingObject.RIGHT_PAREN ||
+                        obj.getType() == MappingObject.LEFT_PAREN||
+                        obj.getType() == MappingObject.BREAK) {
+                       lastLine.add(obj); 
+                    }
+                    else {
+                        PcalDebug.ReportBug("PcalTLAGen.endCurrentLineOfTLA found problem.");
+                    }
+                    mappingVectorNextLine = new Vector() ;
+                }
+            }
+        }
+    }
+    
+    /**
+     * Adds the expression to tlacode / tlacodeNextLine and its
+     * mapping to mappingVector / mappingVectorNextLine.  It adds
+     * no space before the expression and leaves the last line of the
+     * expression (which could be its first line) at the end of 
+     * tlacodeNextLine.
+     * @param expr
+     */
+    private void addExprToTLA(TLAExpr expr) {
+        Vector sv = expr.toStringVector() ;
+        Vector exprMapping = expr.toMappingVector() ;
+        int indent = tlacodeNextLine.length() ; 
+        int nextLine = 0 ; 
+        if (indent != 0) {
+            /*
+             * Need to combine first line of expr with 
+             * tlacodeNextLine.
+             */
+            MappingObject.shiftMappingVector(exprMapping, indent);
+            tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0));
+            mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0));
+            nextLine = 1;
+            if (sv.size() > 1) {
+               endCurrentLineOfTLA();
+            }
+        }
+        if (sv.size() > 1) {
+            String spaces = NSpaces(indent);
+            while (nextLine < sv.size()-1) {
+                tlacode.addElement(spaces + ((String) sv.elementAt(nextLine)));
+                mappingVector.addElement((Vector) exprMapping.elementAt(nextLine));
+                nextLine++ ;
+            }
+            tlacodeNextLine = spaces + ((String) sv.elementAt(nextLine)) ;
+            mappingVectorNextLine = (Vector) exprMapping.elementAt(nextLine);
+        }
+        else if (indent == 0){
+            /*
+             * If indent != 0, then we've already added the one-line expression.
+             */
+            tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0));
+            mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0));
+        }
+    }
+    
+    /**
+     * Subroutine of GenInit that adds to the TLA translation the Init conjunct 
+     * corresponding to the VarDecl decl for a global variable and, in a uniprocess
+     * algorithm for a procedure or process variable It is called with the 
+     * StringBuffer `is' containing the text that precedes the "/\" of the 
+     * conjunct, which will be "Init == " or just spaces.
+     * 
+     * @param decl
+     * @param is
+     */
+    private void addVarDeclToTLA(VarDecl decl, StringBuffer is) {
+        Region origin = decl.getOrigin();
+        is.append("/\\ ");
+        addOneTokenToTLA(is.toString());
+        addLeftParen(decl.getOrigin());
+//        is.append(decl.var);
+        is = new StringBuffer(decl.var);
+        if (decl.isEq)
+            is.append(" = ");
+        else
+            is.append(" \\in ");
+//        int col2 = is.length();
+//        Vector sv = Parenthesize(decl.val.toStringVector());
+//        /*********************************************************
+//        * Call to Parenthesize added by LL on 27 Feb 2008.       *
+//        * See bug_08-02-18.                                      *
+//        *********************************************************/
+//        is.append((String) sv.elementAt(0));
+//        for (int v = 1; v < sv.size(); v++)
+//        {
+//            tlacode.addElement(is.toString());
+//            is = new StringBuffer(NSpaces(col2));
+//            is.append((String) sv.elementAt(v));
+//        }
+//        tlacode.addElement(is.toString());
+        addOneTokenToTLA(is.toString());
+        addLeftParen(decl.val.getOrigin());
+        boolean needsParens = NeedsParentheses(decl.val.toStringVector());
+        if (needsParens) {
+            addOneTokenToTLA("(");
+        }
+        addExprToTLA(decl.val);
+        if (needsParens) {
+            addOneTokenToTLA(")");
+        }
+        addRightParen(decl.val.getOrigin());
+        addRightParen(decl.getOrigin());
+        endCurrentLineOfTLA();
+    }
+    
+    /**
+     * Adds a MappingObject.LeftParen object to the mapping vector
+     * for the beginning of the Region region, if it's not null.
+     * @param region
+     */
+    private void addLeftParen(Region region) {
+        if (region != null) {
+          mappingVectorNextLine.addElement(
+              new MappingObject.LeftParen(region.getBegin()));
+        }
+    }
+    
+    /**
+     * Adds a MappingObject.LeftParen object to the mapping vector
+     * for the beginning of the Region region, if it's not null.
+     * @param region
+     */
+    private void addRightParen(Region region) {
+        if (region != null) {
+          mappingVectorNextLine.addElement(
+              new MappingObject.RightParen(region.getEnd()));
+        }
+    }
+    
+    /**
+     * Like addLeftParen(ast.getOrigin()), except that it uses
+     * loc the location if it is not null.  It is called by
+     * GenLabeledStmt and ast.getOrigin() should never be null.
+     * However, if it is, then we don't add a Paren; this insures 
+     * that the matching calls of addLeftParenV and addRightParenV
+     * both either do or don't add a Paren.
+     *  
+     * @param ast
+     */
+    private void addLeftParenV(AST ast, PCalLocation loc) {
+        if (ast.getOrigin() == null) {
+            return;
+        }
+        if (loc != null) {
+            mappingVectorNextLine.addElement(
+                    new MappingObject.LeftParen(loc));
+        }
+        else {
+            addLeftParen(ast.getOrigin());
+        }
+    }
+
+    /**
+     * Like addRightParen(ast.getOrigin()), except that it uses
+     * loc as the location if it is not null.  It is called by
+     * GenLabeledStmt and ast.getOrigin() should never be null.
+     * However, if it is, then we don't add a Paren; this insures 
+     * that the matching calls of addLeftParenV and addRightParenV
+     * both either do or don't add a Paren.
+     *  
+     * @param ast
+     */
+    private void addRightParenV(AST ast, PCalLocation loc) {
+        if (ast.getOrigin() == null) {
+            return;
+        }
+        if (loc!= null) {
+            mappingVectorNextLine.addElement(
+                    new MappingObject.RightParen(loc));
+        }
+        else {
+            addRightParen(ast.getOrigin());
+        }
+    }
+/* -------------------------------------------------------------------------- */
+    /*
+     * The following methods and classes of objects are used in GenSpec().
+     * See the comments preceding that method above.
+     */
+    
+    /**
+     * A FormulaPair should never have wf = null, but might have sf = null.
+     */
+    public static class FormulaPair {
+    	public String wf ;
+    	public String sf ;
+    	
+    	public FormulaPair(String wfVal, String sfVal) {
+    		this.wf = wfVal;
+    		this.sf = sfVal;
+    	}
+    	
+    	/**
+    	 * The string  wf /\ sf , or just wf if sf is null.
+    	 * @return
+    	 */
+    	public String singleLine() {
+    		if (sf == null) {
+    			return wf ;
+    		} 
+    		return wf + " /\\ " + sf ;
+    	}
+    	
+    	/**
+    	 * The width of the singleLine representation of the 
+    	 * conjunction of the formlas.
+    	 * 
+    	 * @return
+    	 */
+    	public int singleLineWidth() {
+    	    if (sf == null) {
+                return wf.length() ;
+            } 
+            return wf.length() + " /\\ ".length() + sf.length() ;   		
+    	}
+    	
+    	/**
+    	 * The representation of the conjunction of the formulas with
+    	 * prefix /\s, where the first /\ appears in column col (Java
+    	 * numbering), witout any ending "\n"
+    	 * 
+    	 * @return
+    	 */
+    	public String multiLine(int col) {
+    		String val = "/\\ " + wf ;
+    		if (sf == null) {
+    			return val;
+    		}
+    		return val + "\n" + NSpaces(col) + "/\\ " + sf;
+    	}
+    }
+    
+    /**
+     * Describes a process fairness formula, as described in the comments
+     * preceding the  GetSpec() method above.
+     * @author lamport
+     *
+     */
+    public static class ProcessFairness {
+    	public String xf ; // either "WF" or "SF"
+    	public Vector  prefix ; 
+    	    // StringVector either "\A self \in exp : " or
+    	    // "LET self == exp \n IN " (note the ending space) or ""
+    	public FormulaPair bodyFormulas ; // fairness conditions for the proc's body
+    	public Vector prcdFormulas ; // fairness conditions for the procedure
+
+    	/** 
+    	 * The constructor
+    	 * @param xfVal
+    	 * @param prefixVal
+    	 * @param bodyWF : can be null if bodySF is also null
+    	 * @param bodySF : can be null
+    	 * @param prcdVal
+    	 */
+    	public ProcessFairness (String xfVal, Vector  prefixVal, String bodyWF,
+    			                String bodySF, Vector  prcdVal) {
+    		xf = xfVal;
+    		prefix = prefixVal;
+    		bodyFormulas = null ;
+    		if (bodyWF != null) {
+    			bodyFormulas = new FormulaPair(bodyWF, bodySF);
+    		}
+    		prcdFormulas = prcdVal;
+    	}
+    	/**
+    	 * The width of the fairness formula written as a "single-line"
+    	 * formula.  Single-line means that it is not written as a 
+    	 * conjunction list (with a leading /\).  It will actually
+    	 * occupy multiple lines if prefix is a multi-line formula.
+    	 * 
+    	 * @return
+    	 */
+    	public int singleLineWidth() {
+    	    // Set maxPrefixWidth to length of longest non-final
+    	    // line of prefix, width to lenght of final line
+    	    int maxPrefixWidth = 0 ;
+    	    int width = 0  ;
+    	    if (prefix != null && prefix.size() > 0) {
+    	        for (int i = 0; i < prefix.size() - 1; i++) {
+    	            String line =  (String) prefix.elementAt(i);
+    	            if (line.length() > maxPrefixWidth) {
+    	                maxPrefixWidth = line.length();
+    	            }
+    	            String lastLine = (String) prefix.elementAt(prefix.size()-1);
+    	            width = lastLine.length();
+    	        }
+    	    }
+    	    width = width + bodyFormulas.wf.length();
+            if (bodyFormulas.sf != null) {
+                 width = width + bodyFormulas.sf.length();
+            } 
+            if (prcdFormulas != null) {
+                for (int i = 0 ; i < prcdFormulas.size(); i++) {
+                    width = width + ((FormulaPair) prcdFormulas.elementAt(i)).singleLineWidth();
+                }
+            }
+            if (maxPrefixWidth > width) {
+                return maxPrefixWidth;
+            }
+            return width ;
+    	}
+    	
+    	/**
+    	 * Returns the prefix as a StringBuffer, assuming it starts
+    	 * in column col.  That is, all but the first line is indented
+    	 * with col spaces, and all but the last line is ended with
+    	 * a \n .
+    	 * 
+    	 * @param col
+    	 * @return
+    	 */
+    	private StringBuffer prefixAsStringBuffer(int col) {
+    	    StringBuffer val = new StringBuffer();
+            if (prefix != null && prefix.size() > 0) {
+                for (int i = 0; i < prefix.size(); i++) {
+                    String line =  (String) prefix.elementAt(i);
+                    if (i != 0) {
+                        val.append(NSpaces(col));
+                    }
+                    val.append(line) ;
+                    if (i != prefix.size()-1) {
+                        val.append("\n") ;
+                    }                   
+                }
+            }
+            return val;
+    	}
+    	/**
+    	 * The process fairness condition written as a single-line formula,
+    	 * starting in column col.
+    	 * @return
+    	 */
+    	public StringBuffer singleLine(int col) {
+    	    StringBuffer val = prefixAsStringBuffer(col);
+    	    val.append(bodyFormulas.wf);
+    		if (bodyFormulas.sf != null) {
+    			val.append(" /\\ ");
+    			val.append(bodyFormulas.sf);
+    		} 
+    		if (prcdFormulas != null) {
+    			for (int i = 0 ; i < prcdFormulas.size(); i++) {
+    			    val.append(" /\\ ");
+    				val.append(((FormulaPair) prcdFormulas.elementAt(i)).singleLine());
+    			}
+    		}
+    		return val ;
+    	}
+    	
+    	/**
+    	 * Returns true iff format(col) should return a single-line version
+    	 * of the formula.
+    	 * 
+    	 * @param col
+    	 * @return
+    	 */
+    	private boolean fitsAsSingleLine(int col) {
+    	    return     (col + singleLineWidth() <= PcalTLAGen.wrapColumn)
+                    || (bodyFormulas.sf == null 
+                        && (prcdFormulas == null || prcdFormulas.size() == 0));
+    	}
+    	/**
+    	 * The process fairness condition written as a formula that
+    	 * begins in column col (Java numbering) and ends with "\n".
+    	 * It is formatted to try to extend no further than column 
+    	 * PcalTLAGen.wrapColumn, but no individual formula is split
+    	 * across lines.
+    	 * 
+    	 * @param col
+    	 * @return
+    	 */
+    	public StringBuffer format(int col) {
+    		int singleLineWidth = this.singleLineWidth();
+    		/*
+    		 * Return the single-line form if either it fits on the
+    		 * line or if it consists of only the wf formula (so it can't
+    		 * be put on multiple lines).
+    		 */
+    		if (fitsAsSingleLine(col)) {
+    		    return this.singleLine(col);
+    		}
+    		StringBuffer val = prefixAsStringBuffer(col);
+    		int prefixWidth = 0;
+    		if (prefix != null && prefix.size() > 0) {
+    		    prefixWidth = ((String) prefix.elementAt(prefix.size()-1)).length();
+    		}
+    		int curCol = col + prefixWidth;
+    		String line = this.bodyFormulas.singleLine();
+    		if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) {
+    		   val.append("/\\ " + line);
+    		} else {
+    			val.append(this.bodyFormulas.multiLine(curCol));
+    		}
+    		if (prcdFormulas == null) {
+    			return val;
+    		}
+    		for (int i = 0; i < this.prcdFormulas.size(); i++) {
+    		    FormulaPair form = (FormulaPair) this.prcdFormulas.elementAt(i) ;
+    		    line = form.singleLine();
+    		    // On 2 Apr 2013, LL discovered the following totally bizarre
+    		    // line of code, which inserted copies of "THIS_EXTRA_SPACE_INSERTED" into
+    		    // the translation, which of course then didn't parse.  Apparently some
+    		    // change was made and never tested.  The conjuncts being inserted here
+    		    // seem to be the fairness formulas for procedures in a fair process.
+    		    //
+    		    //val.append("\nTHIS_EXTRA_SPACE_INSERTED");
+    		    
+    		    // One experiment seems to indicate that the following statement is needed
+    		    // to put the first of the procedures' liveness formulas where it belongs.
+    		    // However, I don't understand the code so I have no idea what actually
+    		    // should be done.  LL 2 Apr 2013
+    		    // 
+    		    if (i == 0) {
+    		        val.append("\n") ;
+    		    }
+    		    val.append(NSpaces(curCol));
+    		    if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) {
+    	    		   val.append("/\\ " + line + "\n");
+    	    		} else {
+    	    			val.append(form.multiLine(curCol));
+    	    		}
+    		}
+    		return val;
+    	}
+
+    	
+    }
+}
diff --git a/tlatools/src/pcal/PcalTranslate.java b/tlatools/src/pcal/PcalTranslate.java
index f77ae608e5b6b8a59a3d769c8c12d68049c0e89d..8a586a35d2352826283c0d12875685846506315e 100644
--- a/tlatools/src/pcal/PcalTranslate.java
+++ b/tlatools/src/pcal/PcalTranslate.java
@@ -556,6 +556,10 @@ public class PcalTranslate {
                 result1.removeElementAt(result1.size()-1);
                 result1.addAll(ExplodeCallReturn((AST.CallReturn) last, next));
             }
+            else if (last.getClass().equals(AST.CallGotoObj.getClass())) {
+                result1.removeElementAt(result1.size()-1);
+                result1.addAll(ExplodeCallGoto((AST.CallGoto) last, next));
+            }
             else if (last.getClass().equals(AST.IfObj.getClass())) {
                 AST.If If = (AST.If) last;
                 Vector p1 = CopyAndExplodeLastStmt(If.Then, next);
@@ -1479,4 +1483,18 @@ public class PcalTranslate {
         result.addElement(UpdatePC(peTo.iPC));
         return result;
     }
+
+    /***********************************************************************
+    * Generate sequence of statements corresponding to call followed by a  *
+    * goto.                                                                *
+    ***********************************************************************/
+    private static Vector ExplodeCallGoto(AST.CallGoto ast, String next) throws PcalTranslateException {
+      AST.Call call = new AST.Call();
+      call.to = ast.to;
+      call.args = ast.args;
+      call.line = ast.line;
+      call.col = ast.col;
+      call.setOrigin(ast.getOrigin());
+      return ExplodeCall(call, ast.after);
+    }
 }
diff --git a/tlatools/src/pcal/Region.java b/tlatools/src/pcal/Region.java
index 548681978b01fba42469c74408e11ec4b5c58a17..5089e3c441c400a19770e6ebac4a75d8f39f6be2 100644
--- a/tlatools/src/pcal/Region.java
+++ b/tlatools/src/pcal/Region.java
@@ -55,11 +55,6 @@ public class Region implements Serializable {
 	}
 	
 	public String toString() {
-//	  return "[begin |-> " + begin.toString() + ", end |-> "
-//			   + end.toString() + "]";
-	  if (this == null) {
-	      return "null";
-	  }
 	  return "[" + begin.toString() + "-" + end.toString() + "]";
 	}
 
diff --git a/tlatools/src/pcal/Tokenize.java b/tlatools/src/pcal/Tokenize.java
index d81c982c210c75e442a98c31648261b607e29861..45a071c408c8fc51e59deb99459437af583c1218 100644
--- a/tlatools/src/pcal/Tokenize.java
+++ b/tlatools/src/pcal/Tokenize.java
@@ -814,6 +814,7 @@ public class Tokenize
                 || tok.equals("procedure")
                 || tok.equals("define")
                 || tok.equals("process")
+                || tok.equals("fair")
                ) ;
       }
 
diff --git a/tlatools/src/pcal/Translator.java b/tlatools/src/pcal/Translator.java
index cc67c5e367341782adf444ac9e95cf9abef90247..b9a880530b8e338c588c4a907e31a4a090bc9102 100644
--- a/tlatools/src/pcal/Translator.java
+++ b/tlatools/src/pcal/Translator.java
@@ -1,64 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Simon Zambrowski - initial API and implementation
+ *   Markus Alexander Kuppe - Refactoring
+ ******************************************************************************/
 package pcal;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import util.ToolIO;
 
 /**
  * Launcher for the PCal Translator for running out-of-the-tool
  * @author Simon Zambrovski
- * @version $Id$
  */
 public class Translator
 {
-    /**
-     * Resets the Tool IO
-     */
-    public void init()
-    {
-        ToolIO.reset();
-        ToolIO.setMode(ToolIO.TOOL);
+    private String output;
+
+    private final String input;
+    
+    public Translator(final String anInput, final String[] args) {
+		this.input = anInput;
+		ToolIO.reset();
+		ToolIO.setMode(ToolIO.TOOL);
+		PcalParams.resetParams();
+		PcalParams.tlaPcalMapping = new TLAtoPCalMapping();
+		trans.parseAndProcessArguments(args);
+    }
+    
+    public Translator(final String anInput, final List<String> args) {
+    	this(anInput, args.toArray(new String[args.size()]));
     }
 
     /**
      * delegates the call to the {@link trans#main()}
      * @param args
+     * @return 
      */
-//    public int runTranslation(String[] args)
-    public TLAtoPCalMapping runTranslation(String[] args) 
-    {
-        init();
-//        int status = trans.runMe(args);
-        TLAtoPCalMapping status = trans.runMe(args);  
-        return status;
-    }
+	public boolean translate(final ValidationCallBack cb) {
+		// The input .tla file might have unix or windows line ending. If we fail to
+		// properly split the input (a line per array cell), the pcal translator will
+		// silently fail as well.
+		final String[] lines = input.split("\\r?\\n");
+		final List<String> in = Arrays.asList(lines);
+		
+		final List<String> out = trans.performTranslation(in, cb);
+		if (out != null) {
+			final StringBuilder buf = new StringBuilder();
+			final String lineSeparator = System.getProperty("line.separator");
+			for (final String line : out) {
+				buf.append(line);
+				// The output .tla file will use the OS's line ending which is in line with the
+				// the translator's legacy behavior.
+				buf.append(lineSeparator);
+			}
+			output = buf.toString();
+		}
+		
+		return output != null && PcalParams.tlaPcalMapping != null;
+	}
+	
+	public String getOutput() {
+		return output;
+	}
+	
+	public boolean hasChanged() {
+		return !input.equals(output);
+	}
+	
+	public TLAtoPCalMapping getMapping() {
+		return PcalParams.tlaPcalMapping;
+	}
 
     /**
      * Retrieves the errors recorded during the execution
      * @return
      */
-    public List getErrorMessages()
-    {
-        String[] messages = ToolIO.getAllMessages();
-        Vector errorMessages = new Vector();
-        System.out.println("Found " + messages.length + " messages");
-        int position;
-        String cleanMessage = null;
-        for (int i = 0; i < messages.length; i++)
-        {
-            position = messages[i].indexOf(PcalDebug.UNRECOVERABLE_ERROR);
-            if (position != -1)
-            {
-                cleanMessage = messages[i].substring(position, messages[i].length() - PcalDebug.ERROR_POSTFIX.length());
-                errorMessages.add(cleanMessage);
-            } else
-            {
-                cleanMessage = messages[i];
-                // unknown error format
-                System.out.println(cleanMessage);
-            }
-        }
-        return errorMessages;
-    }
+	public List<Error> getErrors() {
+		final String[] messages = ToolIO.getAllMessages();
+		final Vector<Error> errorMessages = new Vector<Error>();
+		for (int i = 0; i < messages.length; i++) {
+			int position = messages[i].indexOf(PcalDebug.UNRECOVERABLE_ERROR);
+			if (position != -1) {
+				errorMessages.add(new Error(messages[i].substring(position,
+						messages[i].length() - PcalDebug.ERROR_POSTFIX.length())));
+			}
+		}
+		return errorMessages;
+	}
+	
+	public static class Error {
+
+		private static final String LINE = "line ";
+		private static final String COLUMN = ", column ";
+		
+		private final String error;
+
+		public Error(String anError) {
+			this.error = anError;
+		}
+		
+		/* (non-Javadoc)
+		 * @see java.lang.Object#toString()
+		 */
+		public String toString() {
+			return error;
+		}
+		
+		public int[] getLocation() {
+			final int lineStarts = error.indexOf(LINE);
+			final int lineEnds = error.indexOf(COLUMN);
+			if (lineStarts != -1 && lineEnds != -1) {
+				final String line = error.substring(lineStarts + LINE.length(), lineEnds);
+				/*
+				 * afterColumnString is the substring of message that comes after the first
+				 * occurance of ", column" in message.
+				 */
+				final String afterColumnString = error.substring(lineEnds + COLUMN.length());
+				// match any number of white spaces followed by the first string of digits.
+				final Matcher matcher = Pattern.compile("\\s*\\d+").matcher(afterColumnString);
+				matcher.find();
+				// the column string that should be a parsable int
+				final String column = matcher.group().trim();
+				
+				int lineNumber = -1;
+				int columnNumber = -1;
+				try {
+					lineNumber = Integer.parseInt(line);
+				} catch (final NumberFormatException e) {
+				}
+				try {
+					columnNumber = Integer.parseInt(column);
+				} catch (final NumberFormatException e) {
+				}
+				return new int[] { lineNumber, columnNumber, lineNumber, columnNumber + 1 };
+			}
+			return new int[] { -1, -1, -1, -1 };
+		}
+	}
 }
diff --git a/tlatools/src/pcal/ValidationCallBack.java b/tlatools/src/pcal/ValidationCallBack.java
new file mode 100644
index 0000000000000000000000000000000000000000..91c8558039998c133aa0a367d0179c3926c997e1
--- /dev/null
+++ b/tlatools/src/pcal/ValidationCallBack.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+public interface ValidationCallBack {
+
+	public enum Generate {
+		NOT_NOW, IGNORE, DO_IT;
+	}
+
+	public class Noop implements ValidationCallBack {
+		@Override
+		public boolean shouldCancel() {
+			return false;
+		}
+
+		@Override
+		public Generate shouldGenerate() {
+			return Generate.NOT_NOW;
+		}
+	}
+
+	boolean shouldCancel();
+	
+	Generate shouldGenerate();
+}
diff --git a/tlatools/src/pcal/Validator.java b/tlatools/src/pcal/Validator.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c3660e8c2c3b9d51a510891393fd702b17f0549
--- /dev/null
+++ b/tlatools/src/pcal/Validator.java
@@ -0,0 +1,297 @@
+package pcal;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.CRC32;
+
+import pcal.exception.ParseAlgorithmException;
+import pcal.exception.RemoveNameConflictsException;
+import tla2sany.modanalyzer.ParseUnit;
+import tla2sany.st.Location;
+import util.TLAConstants;
+
+/**
+ * This class validates the recorded checksums generated from an input specification's PlusCal and translated PlusCal
+ * blocks.
+ */
+public class Validator {
+
+	public enum ValidationResult {
+		/** No PlusCal algorithm exists in the specification */
+		NO_PLUSCAL_EXISTS,
+		
+		/** No translation exists - BUT THERE IS PLUSCAL IN THE SPECIFICATION! */
+		NO_TRANSLATION_EXISTS,
+		
+		NO_TLA_CHECKSUMS_EXIST,
+		NO_PCAL_CHECKSUMS_EXIST,
+		
+		TLA_DIVERGENCE_EXISTS,
+		PCAL_DIVERGENCE_EXISTS,
+		
+		/** A Java error was encountered while attempting to validate. */
+		ERROR_ENCOUNTERED,
+		
+		/** Everything checks out. */
+		NO_DIVERGENCE;
+	}
+	
+	static final String PCAL_CHECKSUM = "pcalchecksum";
+	static final String TLA_CHECKSUM = "tlachecksum";
+	
+	static final String CHECKSUM_TEMPLATE_IGNORE = "(chksum(pcal) \\in STRING /\\ chksum(tla) \\in STRING)";
+	static final String CHECKSUM_TEMPLATE = "(chksum(pcal) = \"%s\" /\\ chksum(tla) = \"%s\")";
+	
+	static final Pattern CHECKSUM_PATTERN = Pattern
+			.compile("\\\\\\* BEGIN TRANSLATION\\s+\\(\\s*((?i)ch(ec)?ksum\\(p(lus)?cal\\)(?-i))\\s*(=\\s*\\\"(?<"
+					+ PCAL_CHECKSUM + ">[0-9A-Fa-f]*)\\\"|\\\\in\\s*"
+					+ "STRING)\\s*\\/\\\\\\s*((?i)ch(ec)?ksum\\(tla\\+?\\)(?-i))\\s*(=\\s*\\\"(?<" + TLA_CHECKSUM
+					+ ">[0-9A-Fa-f]*)\\\"|\\\\in\\s*STRING)\\s*\\)");
+
+	private static final Pattern MODULE_CLOSING_PATTERN = Pattern.compile(TLAConstants.MODULE_CLOSING_REGEX);
+
+	public static Set<ValidationResult> validate(ParseUnit parseUnit, InputStream inputStream)
+			throws IOException {
+		final BufferedReader reader = new BufferedReader(
+				new InputStreamReader((inputStream instanceof BufferedInputStream) ? (BufferedInputStream) inputStream
+						: new BufferedInputStream(inputStream)));
+		return validate(parseUnit, reader);
+	}
+	
+	public static Set<ValidationResult> validate(final ParseUnit parseUnit, final BufferedReader reader) throws IOException {
+		// Instead of matching the module start and end markers, whe while loop use SANY's
+		// parse tree that has the location of the start and end markers.
+		final Location loc = parseUnit.getParseTree().getLocation();
+		
+		// Pre-allocate the number of lines we will read below.
+		final List<String> lines = new ArrayList<>(loc.endLine() - loc.beginLine());
+		
+		// TODO It would be faster to let SANY look for a PlusCal algorithm when it
+		// parses the TLA+ spec (although SANY probably doesn't parse comments, it
+		// could look for the "--algorithm" or "--fair algorithm" tokens).
+		boolean seenAlgo = false;
+		int cnt = 1; // Location is 1-indexed.
+		String line;
+		while ((line = reader.readLine()) != null) {
+			// Skip lines that are not within the range given by location.
+			// This implies that this loop will miss "pluscal options" before
+			// or after the module's start and end markers.  While it is legal to
+			// put options there, I've decided we don't want to pay the price
+			// of parsing the lines preceding or after a module.  Users can
+			// put the options into a comment inside the module, which I
+			// believe to be the choice for most users anyway.
+			if (loc.beginLine() <= cnt && cnt <= loc.endLine()) {
+				if (line.indexOf(PcalParams.BeginAlg.substring(2)) > 0) {
+					seenAlgo = true;
+				}
+				lines.add(line);
+			} else if (cnt >= loc.endLine()) {
+				break;
+			}
+			cnt++;
+		}
+		
+		if (!seenAlgo) {
+			// No "algorithm" string in the input => No PlusCal algorithm.
+			// The appearance of "algorithm", however, might be a false
+			// positive of which validate will take care of (I don't have
+			// time to move the logic up here).
+			return setOf(ValidationResult.NO_PLUSCAL_EXISTS);
+		}
+		return validate(lines);
+	}
+	
+	/**
+	 * There is some redundancy between this and {@link trans#performTranslation(List)} - it would be nice to make a
+	 * 	common method, extended by each.
+	 * 
+	 * @param specificationText the entire specification, line by line - for historical reasons.
+	 * @return the result of the validation, as enumerated by the inner enum of this class.
+	 */
+	private static Set<ValidationResult>  validate(final List<String> specificationText) {
+        final Vector<String> deTabbedSpecification = trans.removeTabs(specificationText);
+		
+        final IntPair searchLoc = new IntPair(0, 0);
+        boolean notDone = true;
+		while (notDone) {
+			try {
+				/*
+				 * As mentioned in #413, this is a performance bottleneck point; unfortunately we need process the
+				 *		options as it affects the production of the AST and we base the checksum on the AST.
+				 * We have addressed a use case in which there is a long run of line prefacing the module specification
+				 * 		in the {@link #validate(InputStream)} method, but that doesn't address a long spec.
+				 * If we wanted to devote more time to this, we should examine the performance difference between
+				 * 		the character-by-character marching done in the ParseAlgorithm code versus using a
+				 * 		regex matcher to split apart lines.
+				 */
+                ParseAlgorithm.FindToken("PlusCal", deTabbedSpecification, searchLoc, "");
+                final String line = ParseAlgorithm.GotoNextNonSpace(deTabbedSpecification, searchLoc);
+                final String restOfLine = line.substring(searchLoc.two);
+				if (restOfLine.startsWith("options")) {
+                    // The first string after "PlusCal" not starting with a
+                    // space character is "options"
+					if (ParseAlgorithm.NextNonIdChar(restOfLine, 0) == 7) {
+                        // The "options" should begin an options line
+                        PcalParams.optionsInFile = true;
+                        ParseAlgorithm.ProcessOptions(deTabbedSpecification, searchLoc);
+                        notDone = false;
+                    }
+                }
+			} catch (ParseAlgorithmException e) {
+                // The token "PlusCal" not found.
+                notDone = false;
+            }
+        }
+        
+        int algLine = 0;
+        int algCol = -1;
+        // Search for "--algorithm" or "--fair".
+        // If found set algLine and algCol right after the last char,
+        // set foundBegin true, and set foundFairBegin true iff it
+        // was "--fair".  Otherwise, set foundBegin false.
+        boolean foundBegin = false;
+        boolean foundFairBegin = false;
+		while ((algLine < deTabbedSpecification.size()) && !foundBegin) {
+			final String line = deTabbedSpecification.elementAt(algLine);
+			final Matcher m = MODULE_CLOSING_PATTERN.matcher(line);
+			if (m.matches()) {
+				break;
+			}
+			
+			algCol = line.indexOf(PcalParams.BeginAlg);
+			if (algCol != -1) {
+				algCol = algCol + PcalParams.BeginAlg.length();
+				foundBegin = true;
+			} else {
+            	algCol = line.indexOf(PcalParams.BeginFairAlg);
+            	if (algCol != -1) {
+            		// Found the "--fair".  The more structurally nice thing to
+            		// do here would be to move past the following "algorithm".
+            		// However, it's easier to pass a parameter to the ParseAlgorithm
+            		// class's GetAlgorithm method that tells it to go past the
+            		// "algorithm" token.
+            		 algCol = algCol + PcalParams.BeginFairAlg.length();
+                     foundBegin = true;
+                     foundFairBegin = true;
+            		
+            	} else {
+            		algLine = algLine + 1;
+            	} 
+            }
+        }
+	       
+		final Set<ValidationResult> res = new HashSet<>();
+
+		
+		// TLA+ translation
+		final int translationLine = trans.findTokenPair(deTabbedSpecification, 0,
+														PcalParams.BeginXlation1, PcalParams.BeginXlation2);
+		if (translationLine == -1) {
+			res.add(ValidationResult.NO_TRANSLATION_EXISTS);
+		}
+		
+		final int endTranslationLine = trans.findTokenPair(deTabbedSpecification, translationLine + 1,
+				PcalParams.EndXlation1, PcalParams.EndXlation2);
+		if (endTranslationLine == -1) {
+			res.add(ValidationResult.NO_TRANSLATION_EXISTS);
+		}
+		if (translationLine == -1 && endTranslationLine == -1) {
+			return res;
+		}
+
+		// PlusCal algorithm
+		if (!foundBegin) {
+			res.add(ValidationResult.NO_PLUSCAL_EXISTS);
+		} else {
+
+	    	// Get the PlusCal AST by parsing it.
+			try {
+				ParseAlgorithm.uncomment(deTabbedSpecification, algLine, algCol);
+			} catch (ParseAlgorithmException e) {
+	            PcalDebug.reportError(e);
+	            return setOf(ValidationResult.ERROR_ENCOUNTERED);
+	        }
+			
+	        final TLAtoPCalMapping mapping = new TLAtoPCalMapping() ;
+	        mapping.algColumn = algCol;
+	        mapping.algLine = algLine;
+	        PcalParams.tlaPcalMapping = mapping;
+			
+			AST ast = new AST();
+			try {
+				final PcalCharReader reader = new PcalCharReader(deTabbedSpecification, algLine, algCol,
+						specificationText.size(), 0);
+				ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin);
+				
+				// The call translate mutates the ast in pcal.PcalTranslate.Explode(AST, PcalSymTab).
+				// I'm ignoring the PcalParams.tlcTranslation() alternative in trans.java
+				// because I doubt the "-spec" feature is used widely (if at all).
+		        final PCalTLAGenerator pcalTLAGenerator = new PCalTLAGenerator(ast);
+	            pcalTLAGenerator.removeNameConflicts();
+                pcalTLAGenerator.translate();
+			} catch (ParseAlgorithmException | RemoveNameConflictsException e) {
+				PcalDebug.reportError(e);
+				// The PlusCal algorithm doesn't parse because of a syntax error. This indicates
+				// that the algorithm has been modified since it wouldn't have been possible to
+				// calculate a checksum earlier. ast will be empty string and, thus, not match below.
+			}
+	        
+			// Calculate the checksum value for the PlusCal's AST and compare unless no
+			// checksum to compare it with is given (or it has been disabled).
+			final Matcher m = Validator.CHECKSUM_PATTERN.matcher(deTabbedSpecification.get(translationLine));
+	    	if (m.find()) {
+	    		if (m.group(Validator.PCAL_CHECKSUM) != null) {
+
+	    			final String chksumPCalAST = Validator.checksum(ast.toString());
+	    			if (!m.group(Validator.PCAL_CHECKSUM).equals(chksumPCalAST)) {
+	    				// Mismatch between the PlusCal algorithm and its checksum.
+	    				res.add(ValidationResult.PCAL_DIVERGENCE_EXISTS);
+	    			}
+	    		}
+	    		if (m.group(Validator.TLA_CHECKSUM) != null) {
+					final Vector<String> translation = new Vector<>(
+							specificationText.subList((translationLine + 1), endTranslationLine));
+					final String chksumTLATranslation = Validator.checksum(translation);
+	    			
+	    			if (!m.group(Validator.TLA_CHECKSUM).equals(chksumTLATranslation)) {
+	    				// Mismatch between the TLA+ translation and its checksum.
+	    				res.add(ValidationResult.TLA_DIVERGENCE_EXISTS);
+	    			}
+	    		}
+	    	} else {
+	    		res.add(ValidationResult.NO_PCAL_CHECKSUMS_EXIST);
+	    	}
+		}
+		return res.isEmpty() ? setOf(ValidationResult.NO_DIVERGENCE) : res;
+	}
+    
+    public static String checksum(final Vector<String> lines) {
+    	final StringBuilder sb = new StringBuilder();
+    	for (final String str : lines) {
+    		sb.append(str);
+    	}
+    	
+    	return checksum(sb.toString());
+    }
+    
+    static String checksum(final String string) {
+    	final CRC32 crc32 = new CRC32();
+    	crc32.update(string.getBytes());
+    	return Long.toHexString(crc32.getValue());
+    }
+
+    private static Set<ValidationResult> setOf(ValidationResult... res) {
+    	return new HashSet<>(Arrays.asList(res));
+    }
+}
diff --git a/tlatools/src/pcal/trans.java b/tlatools/src/pcal/trans.java
index 37f7237776d3bf62301115a8e7fb531cc89700fd..428dbcda55863f27945be154b49e9ac8b6d99dad 100644
--- a/tlatools/src/pcal/trans.java
+++ b/tlatools/src/pcal/trans.java
@@ -1,1970 +1,2021 @@
-package pcal;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Vector;
-
-import pcal.MappingObject.Break;
-import pcal.exception.FileToStringVectorException;
-import pcal.exception.ParseAlgorithmException;
-import pcal.exception.PcalResourceFileReaderException;
-import pcal.exception.RemoveNameConflictsException;
-import pcal.exception.StringVectorToFileException;
-import pcal.exception.TLCTranslationException;
-import pcal.exception.UnrecoverableException;
-import tla2tex.Debug;
-import util.ToolIO;
-
-/***************************************************************************
-* <pre>
-* CLASS trans                                                              *
-*                                                                          *
-*  BUGS:                                                                   *
-*     Accepts if (...) {...} ; else {...}                                  *
-*     Generates code as if the ";" were not there.                         *
-* -----------------------------------------------------------------        *
-* History:                                                                 *
-*   Version 1.0: Original release.                                         *
-*                                                                          *
-*   Version 1.1: (March 2006)                                              *
-*                Introduced ability to have the translator                 *
-*                add missing labels.                                       *
-*                                                                          *
-*   Version 1.2: (August 2007)                                             *
-*                Introduced defaultInitValue so liveness can be            *
-*                checked even with "uninitialized" variables.              *
-*                                                                          *
-*   Version 1.3: (February 2008)                                           *
-*                Added "await" as a synonym for "when"                     *
-*                                                                          *
-*   Version 1.4: (June 2010)                                               *
-*                Added the options statement and the -lineWidth option.    *
-*                                                                          *               
-*   Version 1.5: (Jan 2011)                                                *
-*                 1. Added the -noDoneDisjunct option.                     *
-*                 2. Added the new way of specifying fairness, with        *
-*                    the "fair" keyword with "+" modifier, and the "+"     *
-*                    and "-" label modifiers.                              *
-*                 3. Automatically removes the stuttering-on-termination   *
-*                    disjunction if all processes are "while (TRUE)"       *
-*                    statements with no gotos to "Done".                   *
-*                 4. In addition, if the "while (TRUE)"s have no           *
-*                    internal labels, it removes the pc variable.          *
-*                 5. Changed the representation of Process and Procedure   *
-*                    nodes in the abstract syntax tree produced by         *
-*                    the -writeAST option and used when TLC is producing   *
-*                    the translation.                                      *
-*                 The changes 3-5 are not implemented when the -version    *
-*                 option specifies an earlier version.                     *
-*                                                                          *
-*   Version 1.6: (July 2011)                                               *
-*                 - Added the "--fair algorithm" syntax for specifying     *
-*                   weak fairness of the next-state action. (This          *
-*                   changes the way fairness of a uniprocess algorithm was *
-*                   specified in Version 1.5.  All legal Version 1.5       *
-*                   algorithms should work with the "version 1.5" option.) *
-*                 - Permitted previously defined macros to be called       *
-*                   inside a macro definition.                             *
-*                                                                          *
-*   Version 1.7: (19 January 2012)                                         *
-*                 - Translator adds "BEGIN/END TRANSLATION" if needed.     *
-*                 - Added support for Toolbox Goto PCal Source command.    *
-*                                                                          *
-*   Version 1.8: (30 Mar 2012)                                             *
-*                 - Changed translation to remove stuttering-on-           *
-*                   termination disjunction if some process is             *
-*                   "while (TRUE)".                                        *
-*                 - Omitted the Termination definition if the stuttering-  *
-*                   on-termination disjunction is removed, since that      *
-*                   implies Termination is always FALSE.                   *
-*                (4 May 2012)                                              *
-*                 - Removed the unnecessary CASE in the pc = ... clause of *
-*                   the Init predicate when there is only a single process *
-*                   statement.                                             *
-* -----------------------------------------------------------------        *
-*                                                                          *
-* This is the main method of the +CAL to TLA+ translation program.         *
-* the program has the following command-line options. Only the ones        *
-* marked with ** besides them can be specified in the file's               *
-* options statement.  The "-" can be omitted when the option is in         *
-* the options statement.                                                   *
-*                                                                          *
-*   -help  : Type a help file instead of running the program.              *
-*                                                                          *
-*   -spec name : Uses TLC and the TLA+ specification name.tla to do        *
-*                the translation.  The files name.tla and name.cfg         *
-*                are copied from the java/ directory to the current        *
-*                directory; the file AST.tla that defines `fairness'       *
-*                to equal the fairness option and `ast' to equal           *
-*                the the AST data structure representing the algorithm     *
-*                is written to the current directory; and TLC is run       *
-*                on name.tla to produce the translation.                   *
-*                                                                          *
-*   -myspec name : Like -spec, except it finds the files names.tla         *
-*                  and names.cfg in the current directory, instead         *
-*                  of writing them there.                                  *
-*                                                                          *
-*   -spec2   name                                                          *
-*   -myspec2 name : Like -spec and -myspec, except they use TLC2           *
-*                   instead of TLC (aka TLC1).                             *
-*                                                                          *
-*   -writeAST : Writes the AST file as in the -spec option, but does       *
-*               not perform the translation.                               *
-*                                                                          *
-*   -debug : Run in debugging mode, whatever that means.  For the          *
-*            parser, it just means that the toString() methods             *
-*            output the line and column number along with the              *
-*            node.                                                         *
-*                                                                          *
-*   -unixEOL : Forces the use of Unix end-of-line convention, regardless   *
-*              of the system's default.  Without this, when run on         *
-*              Windows, the files it writes seem to have a "^M" at the     *
-*              end of every line when viewed with Emacs.                   *
-*                                                                          *
-*** -wf : Conjoin to formula Spec weak fairness of each process's          *
-*         next-state action                                                *
-*                                                                          *
-*** -sf : Conjoin to formula Spec strong fairness of each process's        *
-*         next-state action                                                *
-*                                                                          *
-*** -wfNext : Conjoin to formula Spec weak fairness of the entire          *
-*             next-state action                                            *
-*                                                                          *
-*** -nof : Conjoin no fairness formula to Spec.  This is the default,      *
-*          except when the -termination option is chosen.                  *
-*                                                                          *
-*** -termination : Add to the .cfg file the command                        *
-*                     PROPERTY Termination                                 *
-*                  With this option, the default fairness option           *
-*                  becomes -wf.                                            *
-*                                                                          *
-*   -nocfg : Suppress writing of the .cfg file.                            *
-*                                                                          *
-*** -noDoneDisjunct : Suppress the disjunct of the next-state              *
-*                     relation that describes stuttering steps taken       *
-*                     when the algorithm has halted.                       *
-*                                                                          *
-*** -label : Tells the translator to add missing labels.  This is          *
-*            the default only for a uniprocess algorithm in which          *
-*            the user has typed no labels.                                 *
-*                                                                          *
-*   -reportLabels : True if the translator should print the names          *
-*                   and locations of all labels it adds.  Like             *
-*                   -label, it tells the translator to add missing         *
-*                   labels.                                                *
-*                                                                          *
-*** -labelRoot name : If the translator adds missing labels, it            *
-*                     names them name1, name2, etc.  Default value         *
-*                     is "Lbl_".                                           *
-*                                                                          *
-*  THE FOLLOWING OPTION ADDED IN VERSION 1.31                              *
-*                                                                          *
-*** -lineWidth : The translation tries to perform the translation so       *
-*                lines have this maximum width.  (It will often            *
-*                fail.)  Default is 78, minimum value is 60.               *
-*                                                                          *
-*  THE FOLLOWING OPTIONS ADDED IN VERSION 1.4                              *
-*                                                                          *
-*** -lineWidth : The translation tries to perform the translation so       *
-*                lines have this maximum width.  (It will often            *
-*                fail.)  Default is 78, minimum value is 60.               *
-*                                                                          *
-*** -version : The version of PlusCal for which this algorithm is          *
-*              written.  If the language is ever changed to make           *
-*              algorithms written for earlier versions no longer           *
-*              legal, then the translator should do the appropriate        *
-*              thing when the earlier version number is specified.         *
-* ------------------------------------------------------------------------ *
-*                                                                          *
-* The program uses vector objects from the Vector class to implement       *
-* sequences (lists).  This generates a compiler warning.                   *
-*                                                                          *
-* In Java data structures like arrays and Vectors, numbering starts with   *
-* 0.  Unlike programmers, human beings count from 1.  I use the term "Java *
-* ordinal" to refer a number that denotes a position that represents the   *
-* first item as 0, and the term "human ordinal" to refer to an ordinary    *
-* ordinal that counts the first item as 1.                                 *
-*                                                                          *
-*                                                                          *
-* NOTE:                                                                    *
-*                                                                          *
-* One process should be able to read the pc or stack of another.  There    *
-* is no logical reason to forbid this.  However, the definition of         *
-* Translation in PlusCal.tla does not distinguish between instances of pc  *
-* in the original algorithm and ones inserted by the translation.  The     *
-* latter instances must be subscripted--that is replaced by something      *
-* like pc[self].  Therefore, the Translation operator subscripts the       *
-* instances of pc from the original algorithm.  The Java Translate method  *
-* must not do this, but must subscript (and prime) only the instances of   *
-* pc and stack introduced during the translation process.                  *
-*                                                                          *
-*                                                                          *
-* The following bugs should all have been fixed by the addition of         *
-* ParseAlgorithm.Uncommment by LL on 18 Feb 2006.                          *
-*                                                                          *
-*  - There cannot be a comment between a label and the                     *
-*    following ":".                                                        *
-*                                                                          *
-*  - There cannot be a comment immediately before the ")" that ends        *
-*    the list of arguments in a call statement.                            *
-*                                                                          *
-*  - The code for reporting the location of an error has the               *
-*    following problem.  If the token where the error occurs is            *
-*    preceded by a comment, then the position reported is that of the      *
-*    beginning of the comment rather than that of the token.               *
-*                                                                          *
-* TENTATIVE CHANGE MADE                                                    *
-*                                                                          *
-* The following change was made along with a modification to the parser    *
-* to allow semi-colons to be omitted when they're obviously unnecessary.   *
-*                                                                          *
-* The parser does not parse expressions in the +CAL algorithm; it just     *
-* scans ahead to the first token that it can determine is not part of the  *
-* expression.  To make this work, the following tokens that are legal in   *
-* a TLA+ expression are illegal in a +CAL expression:                      *
-*                                                                          *
-*   variable   variables   begin   do   then   :=   ||                     *
-*                                                                          *
-* Making additional tokens illegal might help the parser find errors       *
-* sooner.  For example,  example, if one forgets the ";" and writes        *
-*                                                                          *
-*        x := x + 1                                                        *
-*       if x > y + 17 then ...                                             *
-*                                                                          *
-* the parser takes everything up to the "then" to be the right-hand side   *
-* of the "x :=" assignment.  Making "if" illegal in an expression would    *
-* allow the parser to catch the error at the "if".                         *
-* </pre>
-***************************************************************************/
-class trans
-{
-    /** Status indicating no errors and successful process */
-    static final int STATUS_OK = 1;
-    /** Status of no errors, but abort of the translation */
-    private static final int STATUS_EXIT_WITHOUT_ERROR = 0;
-    /** Status of present errors and abort of the translation */
-    static final int STATUS_EXIT_WITH_ERRORS = -1;
-
-    /**
-     * Main function called from the command line
-     * @param args, command line arguments
-     */
-    public static void main(String[] args)
-    {
-        runMe(args);
-    }
-
-    /**
-     * The main translator method
-     * @return one of {@link trans#STATUS_OK}, {@link trans#STATUS_EXIT_WITH_ERRORS}, 
-     * {@link trans#STATUS_EXIT_WITH_ERRORS}
-     * indicating the status
-     * 
-     * Modified by LL on 16 Dec 2011.  Changed the return value to the
-     * TLAtoPCalMapping object for the translation.  (The status return
-     * value was not being used.)  If the translation fails, it returns
-     * null.
-     */
-    /**
-     * @param args
-     * @return
-     */
-    /**
-     * @param args
-     * @return
-     */
-//    public static int runMe(String[] args)  
-    public static TLAtoPCalMapping runMe(String[] args)   // added for testing
-    {
-        /*********************************************************************
-        * Get and print version number.                                      *
-        *********************************************************************/
-        // String lastModified =
-        // "last modified on Wed 11 March 2009 at 14:52:58 PST by lamport";
-        /*******************************************************************
-        * This string is inserted by an Emacs macro when a new version is  *
-        * saved.  Unfortunately, Eclipse isn't Emacs, so the modification  *
-        * date must be entered manually in the PcalParams module.          *
-        *******************************************************************/
-
-        if (ToolIO.getMode() == ToolIO.SYSTEM)
-        {
-            PcalDebug.reportInfo("pcal.trans Version " + PcalParams.version + " of " + PcalParams.modDate);
-        }
-
-        // SZ Mar 9, 2009:
-        /*
-         * This method is called in order to make sure, that  the
-         * parameters are not sticky because these are could have been initialized
-         * by the previous run  
-         */
-        PcalParams.resetParams();
-        /*********************************************************************
-        * Get and process arguments.                                         
-        *********************************************************************/
-        
-        /**
-         * Create the new TLAtoPCalMapping object, call it mapping
-         * here and set PcalParams.tlaPcalMapping to point to it.
-         */
-        TLAtoPCalMapping mapping = new TLAtoPCalMapping() ;
-        PcalParams.tlaPcalMapping = mapping;
-        
-        int status = parseAndProcessArguments(args);
-
-        if (status != STATUS_OK)
-        {
-//            return exitWithStatus(status);
-            return new TLAtoPCalMapping() ; // added for testing
-        }
-
-        /*********************************************************************
-        * Read the input file, and set the Vector inputVec equal to its      *
-        * contents, where inputVec[i] is the string containing the contents  *
-        * of line i+1 of the input file.                                     *
-        *********************************************************************/
-        Vector inputVec = null;
-        try
-        {
-            inputVec = fileToStringVector(PcalParams.TLAInputFile + /* (PcalParams.fromPcalFile ? ".pcal" : */".tla" /*)*/);
-        } catch (FileToStringVectorException e)
-        {
-            PcalDebug.reportError(e);
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-
-        /*********************************************************************
-        * outputVec is an alias for inputVec if the input is a .tla file,    *
-        * which was not always the case in the aborted version 1.31.         *
-        *********************************************************************/
-        // Vector outputVec = PcalParams.fromPcalFile ? new Vector() : inputVec;
-        Vector outputVec = inputVec;
-
-        /*********************************************************************
-        * Set untabInputVec to be the vector of strings obtained from        *
-        * inputVec by replacing tabs with spaces.                            *
-        *                                                                    *
-        * Tabs are date from the days when memory cost $1 per bit and are a  *
-        * stupid anachronism.  They should be banned.  Although the various  *
-        * methods taken from TLATeX should deal with tabs, there are         *
-        * undoubtedly corner cases that don't work right.  In particular, I  *
-        * think there's one case where PcalCharReader.backspace() might be   *
-        * called to backspace over a tab.  It's easier to simply get rid of  *
-        * the tabs than to try to make it work.                              *
-        *                                                                    *
-        * Since the user might be evil enough to prefer tabs, with tla-file  *
-        * input, the parts of the output file that are not produced by the   *
-        * translator are copied from inputVec, so any tabs the user wants    *
-        * are kept.                                                          *
-        *********************************************************************/
-        Vector untabInputVec = removeTabs(inputVec);
-
-        /**
-         *  Look through the file for PlusCal options.  They are put anywhere
-         *  in the file (either before or after the module or in a comment)
-         *  with the following sequence
-         *     PlusCal <optional white space> options <optional white space>
-         *        ( <options> )
-         *  
-         *  where <options> has the same format as options on the command
-         *  line.
-         */
-        IntPair searchLoc = new IntPair(0, 0);
-        boolean notDone = true;
-        while (notDone)
-        {
-            try
-            {
-                ParseAlgorithm.FindToken("PlusCal", untabInputVec, searchLoc, "");
-                String line = ParseAlgorithm.GotoNextNonSpace(untabInputVec, searchLoc);
-                String restOfLine = line.substring(searchLoc.two);
-                if (restOfLine.startsWith("options"))
-                {
-                    // The first string after "PlusCal" not starting with a
-                    // space character is "options"
-                    if (ParseAlgorithm.NextNonIdChar(restOfLine, 0) == 7)
-                    {
-                        // The "options" should begin an options line
-                        PcalParams.optionsInFile = true;
-                        ParseAlgorithm.ProcessOptions(untabInputVec, searchLoc);
-                        notDone = false;
-                    }
-                }
-            } catch (ParseAlgorithmException e)
-            {
-                // The token "PlusCal" not found.
-                notDone = false;
-            }
-        }
-
-        /**
-         * translationLine is set to the line of the output file at which
-         * the \* BEGIN TRANSLATION appears--whether it is inserted into the
-         * tla-file input by the user, or inserted into the output by the
-         * translator for pcal-file input.
-         */
-        int translationLine = 0;
-
-        /*********************************************************************
-         * Set algLine, algCol to the line and column just after the string   *
-         * [--]algorithm that begins the algorithm.  (These are Java          *
-         * ordinals, in which counting begins at 0.)                          *
-         *                                                                    *
-         * Modified by LL on 18 Feb 2006 to use untabInputVec instead of      *
-         * inputVec, to correct bug that occurred when tabs preceded the      *
-         * "--algorithm".                                                     *
-         *                                                                    *
-         * For the code to handle pcal-input, I introduced the use of         *
-         * IntPair objects to hold <line, column> Java coordinates (counting  *
-         * from zero) in a file (or an image of a file in a String Vector).   *
-         * For methods that advance through the file, the IntPair object is   *
-         * passed as an argument and is advanced by the method.  This is      *
-         * what I should have been doing from the start, but I wasn't smart   *
-         * enough The IntPair curLoc is the main one used in the part of the  *
-         * following code that handles pcal-file input.                       *
-         *********************************************************************/
-        int algLine = 0;
-        int algCol = -1;
-        /*******************************************************************
-        * If the BEGIN/END TRANSLATION region exists, then set             *
-        * translationLine to the number of the line after which the        *
-        * translation is to be inserted and delete the previous version    *
-        * of the translation (if it exists) from inputVec.  (Line          *
-        * numbering is by Java ordinals.)  If the region doesn't exist,    *
-        * set translationLine to -1.                                       *
-        *                                                                  *
-        * Note: we remove the previous version from inputVec, because      *
-        * that's where the translated output is going to go, and also      *
-        * from untabInputVec, because we will then detect if the begin     *
-        * and end translation lines contain part of the algorithm within   *
-        * them.                                                            *
-        *******************************************************************/
-        translationLine = findTokenPair(untabInputVec, 0, PcalParams.BeginXlation1, PcalParams.BeginXlation2);
-        if (translationLine != -1)
-        {
-            
-
-            int endTranslationLine = findTokenPair(untabInputVec, translationLine + 1, PcalParams.EndXlation1,
-                PcalParams.EndXlation2);
-            if (endTranslationLine == -1)
-            {
-                PcalDebug.reportError("No line containing `" + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2);
-//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                return null;
-            }
-
-            endTranslationLine = endTranslationLine - 1;
-            while (translationLine < endTranslationLine)
-            {
-                inputVec.remove(endTranslationLine);
-                untabInputVec.remove(endTranslationLine);
-                endTranslationLine = endTranslationLine - 1;
-            }
-        }
-
-        // Search for "--algorithm" or "--fair".
-        // If found set algLine and algCol right after the last char,
-        // set foundBegin true, and set foundFairBegin true iff it
-        // was "--fair".  Otherwise, set foundBegin false.
-        boolean foundBegin = false;
-        boolean foundFairBegin = false;
-        while ((algLine < untabInputVec.size()) && !foundBegin)
-        {
-            String line = (String) untabInputVec.elementAt(algLine);
-            algCol = line.indexOf(PcalParams.BeginAlg);
-            if (algCol != -1)
-            {
-                algCol = algCol + PcalParams.BeginAlg.length();
-                foundBegin = true;
-            } else
-            {
-            	algCol = line.indexOf(PcalParams.BeginFairAlg);
-            	if (algCol != -1) {
-            		// Found the "--fair".  The more structurally nice thing to
-            		// do here would be to move past the following "algorithm".
-            		// However, it's easier to pass a parameter to the ParseAlgorithm
-            		// class's GetAlgorithm method that tells it to go past the
-            		// "algorithm" token.
-            		 algCol = algCol + PcalParams.BeginFairAlg.length();
-                     foundBegin = true;
-                     foundFairBegin = true;
-            		
-            	} else {
-            		algLine = algLine + 1;
-            	} 
-            }
-            ;
-        }
-        ;
-        if (!foundBegin)
-        {
-            PcalDebug.reportError("Beginning of algorithm string " + PcalParams.BeginAlg + " not found.");
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-        ;
-        
-        /*
-         * Set the algColumn and algLine fields of the mapping object.
-         */
-        mapping.algColumn = algCol;
-        mapping.algLine = algLine;
-        
-        if (translationLine == -1) 
-        {
-           /****************************************************************
-           * Insert BEGIN/END TRANSLATION comments immediately after the   *
-           * end of the comment that contains the beginning of the         *
-           * algorithm.  Set translationLine to the (Java) line number of  *
-           * the BEGIN TRANSLATION.                                        *
-           ****************************************************************/
-        	
-            // Set ecLine, ecCol to the position immediately after the
-            // *) that closes the current comment.
-        	int depth = 1 ;
-            int ecLine = algLine ;
-            int ecCol  = algCol ;
-            boolean notFound = true ;
-            while (notFound && ecLine < untabInputVec.size()) {
-            	char[] line = ((String) untabInputVec.elementAt(ecLine)).toCharArray();
-            	
-                // check current line 
-                while (notFound && ecCol < line.length-1)	 {
-                	char ch = line[ecCol] ;
-                	char ch2 = line[ecCol+1] ;
-                	
-// The following code isn't needed because the algorithm is inside a comment, and 
-// quotes and \* have no effect in determining where the comment ends.
-//
-//                	if (ch == '"') {
-//                		// gobble string
-//                		ch = ch2 ;
-//                		ecCol++ ;
-//                		while (ch != '"') {
-//                			if (ch == '\\') {
-//                				ecCol = ecCol + 2;
-//                			} 
-//                			else {
-//                				ecCol++ ;
-//                			} ;
-//                			if (ecCol < line.length - 1) {
-//                			   ch = line[ecCol] ;
-//                			}
-//                			else {
-//                				ch = '"' ;
-//                			}
-//                		} ;
-//                		ecCol++ ;
-//                	} 
-//                        	
-//                	if (ch == '\\' && ch2 == '*' ) {
-//                		// end of line comment, skip to end of line
-//                		ecCol = 214748364;  // a very large int
-//                	}
-                	if (ch == '(' && ch2 == '*') {
-                		// left comment delimiter
-                    	depth++;
-                    	ecCol = ecCol + 2;
-                	}
-                	else if (ch == '*' && ch2 == ')') {
-                		// right comment delimiter
-                		depth--;
-                		ecCol = ecCol + 2;
-                		if (depth == 0) {
-                			notFound = false ;
-                		}
-                	}
-                	else {
-                		// not an interesting character
-                		ecCol++ ;
-                	}
-                }
-            	
-            	// if not found, go to next line
-            	if (notFound) {
-            		ecLine++ ;
-            		ecCol = 0;
-            	}
-            }
-            
-            if (notFound) {
-            	PcalDebug.reportError("Algorithm not in properly terminated comment");
-//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                return null ; // added for testing
-            }
-            
-            // Report an error  if there's something else on the line that doesn't begin with "\*".  This is probably
-            
-            String endStuff = ((String) untabInputVec.elementAt(ecLine)).substring(ecCol).trim() ;
-            
-            if (!endStuff.equals("") && !endStuff.startsWith("\\*")) {
-            	PcalDebug.reportError("Text on same line following `*)' that ends the \n   comment containing the algorithm.");
-//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                return null ; // added for testing
-            } ;
-            
-            inputVec.insertElementAt("\\* BEGIN TRANSLATION", ecLine+1) ;
-            untabInputVec.insertElementAt("\\* BEGIN TRANSLATION", ecLine+1) ;
-            inputVec.insertElementAt("\\* END TRANSLATION", ecLine+2) ;
-            untabInputVec.insertElementAt("\\* END TRANSLATION", ecLine+2) ;
-
-            translationLine = ecLine + 1;
-//System.out.println(ecLine + ", " + ecCol);
-//Debug.printVector(inputVec, "foo");
-        }
-        
-        /*
-         * Set the mappings start line.
-         */
-        mapping.tlaStartLine = translationLine + 1; 
-
-        /*********************************************************************
-        * Added by LL on 18 Feb 2006 to fix bugs related to handling of      *
-        * comments.                                                          *
-        *                                                                    *
-        * Remove all comments from the algorithm in untabInputVec,           *
-        * replacing (* *) comments by spaces to keep the algorithm tokens    *
-        * in the same positions for error reporting.                         *
-        *********************************************************************/
-        try
-        {
-            ParseAlgorithm.uncomment(untabInputVec, algLine, algCol);
-        } catch (ParseAlgorithmException e)
-        {
-            PcalDebug.reportError(e);
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-        // } // end else of if (PcalParams.fromPcalFile) -- i.e., end processing
-        // of .tla input file.
-
-        /*********************************************************************
-        * Set reader to a PcalCharReader for the input file (with tabs and   *
-        * the previous translation removed), starting right after the        *
-        * PcalParams.BeginAlg string.                                        *
-        *********************************************************************/
-        PcalCharReader reader = new PcalCharReader(untabInputVec, algLine, algCol, inputVec.size(), 0);
-
-        /*********************************************************************
-        * Set ast to the AST node representing the entire algorithm.         *
-        *********************************************************************/
-        AST ast = null;
-        try
-        {
-            ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin);
-// System.out.println(ast.toString());
-// For testing, we print out when the new code for eliminating the 
-// suttering-on-done and pc is used.
-// if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) {
-//  System.out.println("omit pc = " + ParseAlgorithm.omitPC + 
-//          ", omitStutteringWhenDone = " + ParseAlgorithm.omitStutteringWhenDone);
-// }
-
-        } catch (ParseAlgorithmException e)
-        {
-            PcalDebug.reportError(e);
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-        PcalDebug.reportInfo("Parsing completed.");
-// tla-pcal debugging
-//System.out.println("Translation Output:");
-//System.out.println(ast.toString());
-        /*********************************************************************
-        * For -writeAST option, just write the file AST.tla and halt.        *
-        *********************************************************************/
-        if (PcalParams.WriteASTFlag)
-        {
-            WriteAST(ast);
-//            return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR);
-            return null ; // added for testing
-        }
-        ;
-
-        /*********************************************************************
-        * Rename algorithm variables to eliminate name conflicts--for        *
-        * example, if the same variable is declared inside different         *
-        * procedures, if a variable name and a label are the same, or if     *
-        * the same label is used in to different procedures.  This should    *
-        * also report an error and terminate if it discovers a conflict      *
-        * between the variable of a `with' statement and some other          *
-        * identifier in the algorithm.  It should also detect other          *
-        * conflicts--for example, if there is a variable named "Init" or     *
-        * "TRUE".  However, there are conflicts that the translator can't    *
-        * spot--for example, if a variable name is the same as the name of   *
-        * some operator defined elsewhere in the TLA+ module.  So it's not   *
-        * worth going overboard in this checking.                            *
-        *********************************************************************/
-
-        // SZ February.15 2009: made non-static to make PCal stateless for tool runs
-        PCalTLAGenerator pcalTLAGenerator = new PCalTLAGenerator(ast);
-        try
-        {
-            pcalTLAGenerator.removeNameConflicts();
-        } catch (RemoveNameConflictsException e1)
-        {
-            PcalDebug.reportError(e1);
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-
-        /*********************************************************************
-        * Set the vector `translation' to the translation of the algorithm   *
-        * represented by the AST ast.  If called with the -spec option,      *
-        * do the translation by calling TLC. Otherwise, call the ordinary    *
-        * Translate method.                                                  *
-        *********************************************************************/
-        Vector translation = null;
-        boolean tlcTranslation = PcalParams.SpecOption || PcalParams.MyspecOption || PcalParams.Spec2Option
-                || PcalParams.Myspec2Option;
-
-        if (tlcTranslation)
-        {
-            try
-            {
-                translation = TLCTranslate(ast);
-            } catch (TLCTranslationException e)
-            {
-                PcalDebug.reportError(e);
-//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                return null ; // added for testing
-            }
-        } else
-        {
-            try
-            {
-                translation = pcalTLAGenerator.translate();
-            } catch (RemoveNameConflictsException e)
-            {
-                PcalDebug.reportError(e);
-//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                return null ; // added for testing
-            }
-        }
-        ;
-
-        PcalDebug.reportInfo("Translation completed.");
-// tla-pcal Debugging
-//System.exit(0);
-        /*********************************************************************
-        * For .tla input:                                                    *
-        * Rename the old file by changing its extension from "tla" to "old". *
-        *********************************************************************/
-        // if (!PcalParams.fromPcalFile)
-        // {
-        File file;
-        try
-        {
-            file = new File(PcalParams.TLAInputFile + ".old");
-            if (file.exists())
-            {
-                file.delete();
-            }
-            ;
-            file = new File(PcalParams.TLAInputFile + ".tla");
-            file.renameTo(new File(PcalParams.TLAInputFile + ".old"));
-        } catch (Exception e)
-        {
-            PcalDebug.reportError("Could not rename input file " + PcalParams.TLAInputFile + ".tla" + " to "
-                    + PcalParams.TLAInputFile + ".old");
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-        ;
-        // }
-
-        /*********************************************************************
-        * Add the translation to outputVec.                                  *
-        *********************************************************************/
-        int i = 0;
-        while (i < translation.size())
-        {
-            outputVec.insertElementAt(translation.elementAt(i), i + translationLine + 1);
-            i = i + 1;
-        }
-
-        /*********************************************************************
-        * Code from aborted version 1.31.                                    *
-        * For .pcal input, set outputSuffixLoc and add the rest of the       *
-        * input file to the output.                                          *
-        *********************************************************************/
-        // if (PcalParams.fromPcalFile)
-        // {
-        // PcalParams.outputSuffixLoc = new IntPair(outputVec.size(), 0);
-        // // if there's stuff in the suffix on the same line with the
-        // // end of the algorithm, write it on a separate line.
-        // IntPair curLoc = new IntPair(PcalParams.inputSuffixLoc.one, PcalParams.inputSuffixLoc.two);
-        // if (curLoc.one < untabInputVec.size())
-        // {
-        // String lastLine = (String) untabInputVec.elementAt(curLoc.one);
-        // if (curLoc.two < lastLine.length())
-        // {
-        // outputVec.addElement(lastLine.substring(curLoc.two));
-        // }
-        // curLoc.one++;
-        // }
-        // // Copy the rest of the input file into the output file.
-        // for (int ii = curLoc.one; ii < untabInputVec.size(); ii++)
-        // {
-        // outputVec.addElement((String) untabInputVec.elementAt(ii));
-        // }
-        // }
-        /*********************************************************************
-        * Write the output file.                                             *
-        *********************************************************************/
-        try
-        {
-            WriteStringVectorToFile(outputVec, PcalParams.TLAInputFile + ".tla");
-        } catch (StringVectorToFileException e)
-        {
-            PcalDebug.reportError(e);
-//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-            return null ; // added for testing
-        }
-
-        PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + ".tla" + " written.");
-
-        /*********************************************************************
-        * Write the cfg file, unless the -nocfg option is used.              *
-        *********************************************************************/
-        File cfgFile = new File(PcalParams.TLAInputFile + ".cfg");
-        Vector cfg = null;
-        boolean writeCfg = !PcalParams.Nocfg;
-        if (writeCfg && cfgFile.exists())
-        {
-            if (cfgFile.canRead())
-            {
-                try
-                {
-                    cfg = fileToStringVector(PcalParams.TLAInputFile + ".cfg");
-                } catch (FileToStringVectorException e)
-                {
-                    PcalDebug.reportError(e);
-//                    return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                    return null ; // added for testing
-                }
-            } else
-            {
-                /*************************************************************
-                * cfg file is read-only.                                     *
-                *************************************************************/
-                writeCfg = false;
-                PcalDebug.reportInfo("File " + PcalParams.TLAInputFile + ".cfg is read only, new version not written.");
-            }
-        } else
-        {
-            cfg = new Vector();
-            cfg.addElement(PcalParams.CfgFileDelimiter);
-        }
-        ;
-
-        /*********************************************************************
-        * Delete previously written part of cfg file.                        *
-        *********************************************************************/
-        if (writeCfg)
-        {
-            i = 0;
-            boolean done = false;
-            while ((!done) && (cfg.size() > i))
-            {
-                if (((String) cfg.elementAt(i)).indexOf(PcalParams.CfgFileDelimiter) == -1)
-                {
-                    i = i + 1;
-                } else
-                {
-                    done = true;
-                }
-            }
-            if (done)
-            {
-                /*************************************************************
-                * Delete all lines before the delimiting comment string.     *
-                *************************************************************/
-                while (i > 0)
-                {
-                    cfg.removeElementAt(0);
-                    i = i - 1;
-                }
-            } else
-            {
-                /*************************************************************
-                * The delimiting comment string written by the translator    *
-                * not found in the cfg file, so presumably the user created  *
-                * the cfg file before running the translator on the input    *
-                * file.  We insert the delimiter.                            *
-                *************************************************************/
-                cfg.add(0, PcalParams.CfgFileDelimiter);
-            }
-            ;
-
-            /******************************************************************
-            * If defaultInitValue is used, add a CONSTANT statement setting   *
-            * it to a model value of the same name.                           *
-            * (Added 22 Aug 2007 by LL.)                                      *
-            ******************************************************************/
-            if (tlcTranslation || ParseAlgorithm.hasDefaultInitialization)
-            {
-                cfg.add(0, "CONSTANT defaultInitValue = defaultInitValue");
-            }
-            ;
-            /******************************************************************
-            * Insert the `PROPERTY Termination' line if requested.            *
-            ******************************************************************/
-            if (PcalParams.CheckTermination)
-            {
-                cfg.add(0, "PROPERTY Termination");
-            }
-            ;
-
-            /******************************************************************
-            * Insert the SPECIFICATION line if there isn't already one.       *
-            ******************************************************************/
-            i = 0;
-            boolean hasSpec = false;
-            while (i < cfg.size())
-            {
-                String thisLine = (String) cfg.elementAt(i);
-                if ((thisLine.indexOf("SPECIFICATION") != -1)
-                        && ((thisLine.indexOf("\\*") == -1) || (thisLine.indexOf("\\*") > thisLine
-                                .indexOf("SPECIFICATION"))))
-                {
-                    hasSpec = true;
-                }
-                ;
-                i = i + 1;
-            }
-            ;
-            if (hasSpec)
-            {
-                PcalDebug.reportInfo("File " + PcalParams.TLAInputFile
-                        + ".cfg already contains SPECIFICATION statement," + "\n   so new one not written.");
-            } else
-            {
-                cfg.add(0, "SPECIFICATION Spec");
-            }
-            ;
-            try
-            {
-                WriteStringVectorToFile(cfg, PcalParams.TLAInputFile + ".cfg");
-            } catch (StringVectorToFileException e)
-            {
-                PcalDebug.reportError(e);
-//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
-                return null ; // added for testing
-            }
-            PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + ".cfg" + " written.");
-        }
-        ;
-
-//        return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR);
-        return PcalParams.tlaPcalMapping ; // added for testing
-    } // END main
-
-    /**
-     * If run in the system mode, exits the program, in tool mode returns the status
-     * @param status
-     */
-    private static int exitWithStatus(int status)
-    {
-        if (ToolIO.getMode() == ToolIO.SYSTEM)
-        {
-            // return exit status in system mode
-            System.exit(status);
-        }
-
-        // just exit the function in tool mode
-        return status;
-    }
-
-    /********************** Writing the AST ************************************/
-    private static boolean WriteAST(AST ast)
-    {
-        Vector astFile = new Vector();
-        astFile.addElement("------ MODULE AST -------");
-        astFile.addElement("EXTENDS TLC");
-        astFile.addElement("fairness == \"" + PcalParams.FairnessOption + "\"");
-        astFile.addElement(" ");
-        astFile.addElement("ast == ");
-        astFile.addElement(ast.toString());
-        astFile.addElement("==========================");
-        try
-        {
-            WriteStringVectorToFile(astFile, "AST.tla");
-        } catch (StringVectorToFileException e)
-        {
-            PcalDebug.reportError(e);
-            return false;
-        }
-        PcalDebug.reportInfo("Wrote file AST.tla.");
-        return true;
-    }
-
-    /************************* THE TLC TRANSLATION *****************************/
-
-    private static Vector TLCTranslate(AST ast) throws TLCTranslationException
-    /***********************************************************************
-    * The result is a translation of the algorithm represented by ast      *
-    * obtained by using TLC to execute the definition of Translation(ast)  *
-    * in the TLA+ module PlusCal.  It equals a vector with a single        *
-    * element, which is the entire translation as a single string.         *
-    *                                                                      *
-    * This method relies on a bug in TLC's pretty-print routine that       *
-    * causes it not to work properly on the output produced by the TLA     *
-    * spec.  Instead of prettyprinting the output as                       *
-    *                                                                      *
-    *   << "VARIABLES ...",                                                *
-    *      "vars == ... ",                                                 *
-    *      ...                                                             *
-    *   >>                                                                 *
-    *                                                                      *
-    * it prints the entire translation on a single line as                 *
-    *                                                                      *
-    *   << "VARIABLES ...", "vars == ... ", ... >>                         *
-    *                                                                      *
-    * This allows the method to find the entire translation as the single  *
-    * line that begins with "<<".  If this TLC bug is fixed, then the      *
-    * TLCTranslate method will have to be modified to read the spec as a   *
-    * sequence of lines.  This will probably require the TLA module that   *
-    * translates the spec to print a special marker line to indicate the   *
-    * end of the translation.                                              *
-    ***********************************************************************/
-    {
-        /*********************************************************************
-        * Write the file AST.tla that contains                               *
-        *********************************************************************/
-        WriteAST(ast);
-
-        /*********************************************************************
-        * For the -spec (rather than -myspec) option, copy the               *
-        * specification's .tla and .cfg files PlusCal.tla to current         *
-        * directory.                                                         *
-        *********************************************************************/
-        if (PcalParams.SpecOption || PcalParams.Spec2Option)
-        {
-            try
-            {
-                Vector parseFile = PcalResourceFileReader.ResourceFileToStringVector(PcalParams.SpecFile + ".tla");
-
-                WriteStringVectorToFile(parseFile, PcalParams.SpecFile + ".tla");
-                parseFile = PcalResourceFileReader.ResourceFileToStringVector(PcalParams.SpecFile + ".cfg");
-                WriteStringVectorToFile(parseFile, PcalParams.SpecFile + ".cfg");
-
-                PcalDebug
-                        .reportInfo("Wrote files " + PcalParams.SpecFile + ".tla and " + PcalParams.SpecFile + ".cfg.");
-
-            } catch (UnrecoverableException e)
-            {
-                throw new TLCTranslationException(e.getMessage());
-            }
-
-        }
-        ;
-        /*********************************************************************
-        * Run TLC on the specification file and set tlcOut to TLC's output.  *
-        *********************************************************************/
-        String javaInvocation;
-        if (PcalParams.SpecOption || PcalParams.MyspecOption)
-        {
-            // Modified on 29 May 2010 by LL so tlc2 is run in
-            // all cases.
-            PcalDebug.reportInfo("Running TLC2.");
-            javaInvocation = "java -Xss1m tlc2.TLC ";
-        } else
-        {
-            PcalDebug.reportInfo("Running TLC2.");
-            javaInvocation = "java -Xss1m tlc2.TLC ";
-        }
-        ;
-        String tlcOut = "      ";
-        Runtime rt = Runtime.getRuntime();
-        try
-        {
-            // Modified on 29 May 2010 by LL to replace getErrorStream() with 
-            // getInputStream(), which by Java logic gets standard out.  (And no,
-            // getErrorStream() did not get standard non-error.)  Apparently,
-            // TLC has been changed to put its output on stdout.
-            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(rt.exec(
-                    javaInvocation + PcalParams.SpecFile).getInputStream()));
-            while (tlcOut.indexOf("<<") == -1)
-            {
-                tlcOut = bufferedReader.readLine();
-            }
-            ;
-            bufferedReader.close();
-        } catch (Exception e)
-        {
-            throw new TLCTranslationException("Error reading output of TLC");
-        }
-        ;
-
-        /*********************************************************************
-        * Test if the translation failed and reported an error message,      *
-        * bracketed by "@Error@" and "@EndError@" strings.  If it did,       *
-        * report the error and halt.  If not, set tlcOut to the value of     *
-        * Translation(ast) with the outermost "<<" and ">>" removed.         *
-        *********************************************************************/
-        if (tlcOut.indexOf("@Error@") != -1)
-        {
-            throw new TLCTranslationException("TLC's translation of the parsed algorithm failed with\n  Error: "
-                    + tlcOut.substring(tlcOut.indexOf("@Error@") + 7, tlcOut.indexOf("@EndError@")));
-        }
-        ;
-        tlcOut = tlcOut.substring(2, tlcOut.lastIndexOf(">>")) + "  ";
-        PcalDebug.reportInfo("Read TLC output.");
-
-        /*********************************************************************
-        * Set transl to the string obtained by converting tlcOut, which is   *
-        * a comma-separated sequence of strings, to the single string that   *
-        * they represent.  See PlusCal.tla for an explanation of the         *
-        * encoding of TLA+ statements as sequences of strings.               *
-        *********************************************************************/
-        int i = 0;
-        String transl = "";
-        while (i < tlcOut.length())
-        {
-            if (tlcOut.charAt(i) == '"')
-            {
-                i = i + 1;
-                if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '"'))
-                /*******************************************************
-                * This is a quoted string.                             *
-                *******************************************************/
-                {
-                    if (tlcOut.charAt(i + 2) != '"')
-                    {
-                        throw new TLCTranslationException("I'm confused");
-
-                    }
-                    ;
-                    i = i + 3;
-                    while (tlcOut.charAt(i) != '"')
-                    {
-                        i = i + 1;
-                    }
-                    i = i + 1;
-                    transl = transl + "\"";
-                    while (tlcOut.charAt(i) != '"') // "
-                    {
-                        if (tlcOut.charAt(i) == '\\')
-                        {
-                            /***********************************************
-                            * Get special character.                       *
-                            ***********************************************/
-                            transl = transl + tlcOut.substring(i, i + 2);
-                            i = i + 2;
-                        } else
-                        {
-                            transl = transl + tlcOut.substring(i, i + 1);
-                            i = i + 1;
-                        }
-                        ;
-                    }
-                    ;
-                    i = i + 8;
-                    transl = transl + "\"";
-                } else
-                {
-                    while (tlcOut.charAt(i) != '"')
-                    {
-                        if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '\\'))
-                        {
-                            i = i + 1;
-                        }
-                        ;
-                        transl = transl + tlcOut.substring(i, i + 1);
-                        i = i + 1;
-                    }
-                    ;
-                    i = i + 1;
-                }
-                ;
-            } // END if (tlcOut.charAt(i) == '"')
-            else if (tlcOut.charAt(i) == ',')
-            {
-                i = i + 1;
-            } else
-            {
-                if (tlcOut.charAt(i) != ' ')
-                {
-                    throw new TLCTranslationException("Expected space but found `" + tlcOut.charAt(i) + "'");
-                }
-                ;
-                transl = transl + tlcOut.substring(i, i + 1);
-                i = i + 1;
-            }
-            ;
-        }
-        ; // END while
-        /* ******************************************************************
-         * Wrap the translated string into approximately 80 character lines *
-         *******************************************************************/
-        transl = WrapString(transl, 78);
-        Vector result = new Vector();
-        result.addElement(transl);
-        return result;
-    }
-
-    /***************** METHODS FOR READING AND WRITING FILES *****************/
-
-    private static void WriteStringVectorToFile(Vector inputVec, String fileName) throws StringVectorToFileException
-    /***********************************************************************
-    * Writes the Vector of strings inputVec to file named fileName, with   *
-    * each element of inputVec written on a new line.                      *
-    ***********************************************************************/
-    {
-        try
-        {
-            // I have no idea what Java does if you try to write a new version
-            // of a read-only file. On Windows, it's happy to write it. Who
-            // the hell knows what it does on other operating systems? So, something
-            // like the following code could be necessary. However, the setWritable()
-            // method was introduced in Java 1.6, and in December 2009, that version
-            // isn't available on the Mac. And I can't find out how to set a file
-            // to be writable in any earlier version of Java. On the web, the advice
-            // is to copy the file, delete the old version, and rename the copy.
-            // But the File method's documentation actually says that delete may or
-            // may not delete the read-only file, depending on the OS.
-            //
-            // File file = new File(fileName);
-            // if (! file.canWrite()) {
-            // file.setWritable(true);
-            // }
-            BufferedWriter fileW = new BufferedWriter(new FileWriter(fileName));
-            int lineNum = 0;
-            while (lineNum < inputVec.size())
-            {
-                fileW.write((String) inputVec.elementAt(lineNum));
-                fileW.newLine();
-                lineNum = lineNum + 1;
-            }
-            ;
-            fileW.close();
-        } catch (Exception e)
-        {
-            throw new StringVectorToFileException("Could not write file " + fileName);
-        }
-        ;
-
-    }
-
-    private static Vector fileToStringVector(String fileName) throws FileToStringVectorException
-    /***********************************************************************
-    * Reads file fileName into a StringVector, a vector in which each      *
-    * element is a line of the file.                                       *
-    ***********************************************************************/
-    {
-        Vector inputVec = new Vector(100);
-        try
-        {
-            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
-            try
-            {
-                String nextLine = bufferedReader.readLine();
-                while (nextLine != null)
-                {
-                    inputVec.addElement(nextLine);
-                    nextLine = bufferedReader.readLine();
-                }
-                ;
-                bufferedReader.close();
-            } catch (IOException e)
-            {
-                /*********************************************************
-                * Error while reading input file.                        *
-                *********************************************************/
-                throw new FileToStringVectorException("Error reading file " + fileName + ".");
-            }
-        }
-
-        catch (FileNotFoundException e)
-        {
-            /**************************************************************
-            * Input file could not be found.                              *
-            **************************************************************/
-            throw new FileToStringVectorException("Input file " + fileName + " not found.");
-        }
-        ;
-        return inputVec;
-    }
-
-    /********************* PROCESSING THE COMMAND LINE ***********************/
-
-    /**
-     * Processes the command line arguments
-     * 
-     * This method changes values of public static variables of the {@link PcalParams} 
-     * 
-     * SZ: This will cause problems, due to the fact that the PCalParams are static.
-     * Any initialization should overwrite the previous, which is currently NOT the case
-     * Should be re-factored to non-static access to the properties
-     * 
-     * @return status of processing. 
-     *  the status {@link trans#STATUS_OK} indicates that no errors has been detected.
-     *  the status {@link trans#STATUS_EXIT_WITHOUT_ERROR} indicates that no errors has been found but translation
-     *   should not be started (e.G -help call)
-     *  the status {@link trans#STATUS_EXIT_WITH_ERRORS} indicates errors 
-     *  
-     * Change made on 9 December 2009 for pcal-file input.  This procedure is
-     * called a second time if there is pcal-file input with an options statement.
-     * It will be the second call iff {@link PcalParams#optionsInFile} equals true.
-     * The second call should have a dummy extra argument in place of the 
-     * command-line's file-name argument.   When pcal files were eliminated, this
-     * kludgy mechanism was kept and used to indicate if the method is being called
-     * for options specified inside the module. 
-     */
-    static int parseAndProcessArguments(String[] args)
-    {
-
-        /** *******************************************************************
-         *<pre>
-         * Get the command-line arguments and set the appropriate parameters.  *
-         * The following command line arguments are handled.  Only the ones    *
-         * marked with ** besides them can be specified in the module file's   *
-         * options statement.  The "-" can be omitted when the option is in    *
-         * the module file's options statement.                                *
-         *                                                                     *
-         *   -help  : Type a help file instead of running the program.         *
-         *                                                                     *
-         *** -spec name : Uses TLC and the TLA+ specification name.tla to do   *
-         *                the translation.  The files name.tla and name.cfg    *
-         *                are copied from the java/ directory to the current   *
-         *                directory; the file AST.tla that defines `fairness'  *
-         *                to equal the fairness option and `ast' to equal      *
-         *                the the AST data structure representing the          *
-         *                algorithm is written to the current directory; and   *
-         *                TLC is run on name.tla to produce the translation.   *
-         *                                                                     *
-         *** -myspec name : Like -spec, except it finds the files names.tla    *
-         *                  and names.cfg in the current directory, instead    *
-         *                  of writing them there.                             *
-         *                                                                     *
-         *   -spec2   name                                                     *
-         *   -myspec2 name : Like -spec and -myspec, except they use TLC2      *
-         *                   instead of TLC (aka TLC1).                        *
-         *                                                                     *
-         *   -writeAST : Writes the AST file as in the -spec option, but does  *
-         *               not perform the translation.                          *
-         *                                                                     *
-         *   -debug : Run in debugging mode, whatever that means.  For the     *
-         *            parser, it just means that the toString() methods        *
-         *            output the line and column number along with the         *
-         *            node.                                                    *
-         *                                                                     *
-         *   -unixEOL : Forces the use of Unix end-of-line convention,         *
-         *              regardless of the system's default.  Without this,     *
-         *              when run on Windows, the files it writes seem to have  *
-         *              a "^M" at the end of every line when viewed with       *
-         *              Emacs.                                                 *
-         *                                                                     *
-         *** -wf : Conjoin to formula Spec weak fairness of each process's     *
-         *         next-state action                                           *
-         *                                                                     *
-         *** -sf : Conjoin to formula Spec strong fairness of each process's   *
-         *         next-state action                                           *
-         *                                                                     *
-         *** -wfNext : Conjoin to formula Spec weak fairness of the entire     *
-         *             next-state action                                       *
-         *                                                                     *
-         *** -nof : Conjoin no fairness formula to Spec.  This is the default, *
-         *          except when the -termination option is chosen.             *
-         *                                                                     *
-         *** -termination : Add to the .cfg file the command                   *
-         *                     PROPERTY Termination                            *
-         *                                                                     *
-         *   -nocfg : Suppress writing of the .cfg file.                       *
-         *                                                                     *
-         *                                                                     *
-         *** -noDoneDisjunct : Suppress the disjunct of the next-state         *
-         *                     relation that describes stuttering steps taken  *
-         *                     when the algorithm has halted.                  *
-         *                                                                     *
-         *** -label : Tells the translator to add missing labels.  This is     *
-         *            the default only for a uniprocess algorithm in which     *
-         *            the user has typed no labels.                            *
-         *                                                                     *
-         *   -reportLabels : True if the translator should print the names     *
-         *                   and locations of all labels it adds.  Like        *
-         *                   -label, it tells the translator to add missing    *
-         *                   labels.                                           *
-         *                                                                     *
-         *** -labelRoot name : If the translator adds missing labels, it       *
-         *                     names them name1, name2, etc.  Default value    *
-         *                     is "Lbl_".                                      *
-         *                                                                     *
-         *  THE FOLLOWING OPTIONS ADDED IN VERSION 1.4                         *
-         *                                                                     *
-         *** -lineWidth : The translation tries to perform the translation so  *
-         *                lines have this maximum width.  (It will often       *
-         *                fail.)  Default is 78, minimum value is 60.          *
-         *                                                                     *
-         *** -version : The version of PlusCal for which this algorithm is     *
-         *              written.  If the language is ever changed to make      *
-         *              algorithms written for earlier versions no longer      *
-         *              legal, then the translator should do the appropriate   *
-         *              thing when the earlier version number is specified.    *                
-         *</pre>
-         ********************************************************************* */
-        boolean inFile = PcalParams.optionsInFile;
-        boolean notInFile = !inFile;
-        // Just convenient abbreviations
-        boolean firstFairness = inFile;
-        // Used to allow a fairness property specified by a command-line
-        // option to be overridden by one in the pcal-file's options statement.
-        // It is set false when the first fairness property is set from
-        // the options statement.
-        boolean explicitNof = false;
-        // Set true when the "nof" fairness option is set by an explicit
-        // user request, rather than by default. It was added to fix
-        // a bug in -termination introduced in version 1.4 by having
-        // the options statement in the file. I think option processing
-        // can be simplified to eliminate this, but it's easier to add
-        // this kludge.
-        int nextArg = 0;
-        /******************************************************************
-        * The number of the argument being processed.                     *
-        ******************************************************************/
-        int maxArg = args.length - 1;
-        /******************************************************************
-        * The number of option arguments.  (For processing command-line   *
-        * arguments, the last element of args is the input-file name.)    *
-        ******************************************************************/
-        if (maxArg < 0)
-        {
-            return CommandLineError("No arguments specified");
-        }
-
-        if (notInFile && (args[maxArg].length() != 0) && (args[maxArg].charAt(0) == '-'))
-        /******************************************************************
-        * If the last argument begins with "-", then no file has been     *
-        * specified.  This should mean that the user has typed "-help",   *
-        * but it could be a mistake.  But let's just assume she typed     *
-        * "-help", since she either wants or needs help.                  *
-        ******************************************************************/
-        {
-            if (OutputHelpMessage())
-            {
-                return STATUS_EXIT_WITHOUT_ERROR;
-
-            } else
-            {
-                return STATUS_EXIT_WITH_ERRORS;
-            }
-        }
-
-        while (nextArg < maxArg)
-        /*******************************************************************
-        * Process all the arguments, except for the input-file name.       *
-        *******************************************************************/
-        {
-            String option = args[nextArg];
-            if (notInFile && option.equals("-help"))
-            {
-                if (OutputHelpMessage())
-                {
-                    return STATUS_EXIT_WITHOUT_ERROR;
-                } else
-                {
-                    return STATUS_EXIT_WITH_ERRORS;
-                }
-            } else if (notInFile && option.equals("-writeAST"))
-            {
-                PcalParams.WriteASTFlag = true;
-                if (CheckForConflictingSpecOptions())
-                {
-                    return STATUS_EXIT_WITH_ERRORS;
-                }
-            } else if (option.equals("-spec") || 
-                        (inFile && option.equals("spec")))
-            {
-                PcalParams.SpecOption = true;
-                if (CheckForConflictingSpecOptions())
-                {
-                    return STATUS_EXIT_WITH_ERRORS;
-                }
-                nextArg = nextArg + 1;
-                if (nextArg == maxArg)
-                {
-                    return CommandLineError("Specification name must follow `-spec' option");
-                }
-                PcalParams.SpecFile = args[nextArg];
-            } else if (option.equals("-myspec") || 
-                    (inFile && option.equals("myspec")))
-            {
-                PcalParams.MyspecOption = true;
-                if (CheckForConflictingSpecOptions())
-                {
-                    return STATUS_EXIT_WITH_ERRORS;
-                }
-                nextArg = nextArg + 1;
-                if (nextArg == maxArg)
-                {
-                    return CommandLineError("Specification name must follow `-myspec' option");
-                }
-                PcalParams.SpecFile = args[nextArg];
-            } else if (notInFile && option.equals("-spec2"))
-            {
-                PcalParams.Spec2Option = true;
-                if (CheckForConflictingSpecOptions())
-                {
-                    return STATUS_EXIT_WITH_ERRORS;
-                }
-                ;
-                nextArg = nextArg + 1;
-                if (nextArg == maxArg)
-                {
-                    return CommandLineError("Specification name must follow `-spec' option");
-                }
-                PcalParams.SpecFile = args[nextArg];
-            } else if (notInFile && option.equals("-myspec2"))
-            {
-                PcalParams.Myspec2Option = true;
-                if (CheckForConflictingSpecOptions())
-                {
-                    return STATUS_EXIT_WITH_ERRORS;
-                }
-                ;
-                nextArg = nextArg + 1;
-                if (nextArg == maxArg)
-                {
-                    return CommandLineError("Specification name must follow `-myspec' option");
-                }
-                PcalParams.SpecFile = args[nextArg];
-            } else if (notInFile && option.equals("-debug"))
-            {
-                PcalParams.Debug = true;
-            } else if (notInFile && option.equals("-unixEOL"))
-            {
-                System.setProperty("line.separator", "\n");
-            } else if (option.equals("-termination") || (inFile && option.equals("termination")))
-            {
-                PcalParams.CheckTermination = true;
-            } else if (option.equals("-nocfg"))
-            {
-                PcalParams.Nocfg = true;
-            } else if (option.equals("-noDoneDisjunct") || (inFile && option.equals("noDoneDisjunct")))
-            {
-                PcalParams.NoDoneDisjunct = true;
-            } else if (option.equals("-wf") || (inFile && option.equals("wf")))
-            {
-                if (firstFairness)
-                {
-                    PcalParams.FairnessOption = "";
-                    firstFairness = false;
-                }
-                if (!PcalParams.FairnessOption.equals(""))
-                {
-                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
-                }
-                PcalParams.FairnessOption = "wf";
-            } else if (option.equals("-sf") || (inFile && option.equals("sf")))
-            {
-                if (firstFairness)
-                {
-                    PcalParams.FairnessOption = "";
-                    firstFairness = false;
-                }
-                if (!PcalParams.FairnessOption.equals(""))
-                {
-                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
-                }
-                PcalParams.FairnessOption = "sf";
-            } else if (option.equals("-wfNext") || (inFile && option.equals("wfNext")))
-            {
-                if (firstFairness)
-                {
-                    PcalParams.FairnessOption = "";
-                    firstFairness = false;
-                }
-                if (!PcalParams.FairnessOption.equals(""))
-                {
-                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
-                }
-                PcalParams.FairnessOption = "wfNext";
-            } else if (option.equals("-nof") || (inFile && option.equals("nof")))
-            {
-                if (firstFairness)
-                {
-                    PcalParams.FairnessOption = "";
-                    firstFairness = false;
-                }
-                if (!PcalParams.FairnessOption.equals(""))
-                {
-                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
-                }
-                PcalParams.FairnessOption = "nof";
-                explicitNof = true;
-            } else if (option.equals("-label") || (inFile && option.equals("label")))
-            {
-                PcalParams.LabelFlag = true;
-            } else if (notInFile && option.equals("-reportLabels"))
-            {
-                PcalParams.ReportLabelsFlag = true;
-                PcalParams.LabelFlag = true;
-            } else if (option.equals("-labelRoot") || (inFile && option.equals("labelRoot")))
-            {
-                nextArg = nextArg + 1;
-                if (nextArg == maxArg)
-                {
-                    return CommandLineError("Label root must follow `-labelRoot' option");
-                }
-                PcalParams.LabelRoot = args[nextArg];
-            }
-            // else if (option.equals("-readOnly") || (pcal && option.equals("readOnly"))) {
-            // PcalParams.readOnly = true;
-            // }
-            // else if (option.equals("-writable") || (pcal && option.equals("writable"))) {
-            // PcalParams.readOnly = false;
-            // }
-            else if (option.equals("-version") || (inFile && option.equals("version")))
-            {
-                nextArg = nextArg + 1;
-                if (nextArg == maxArg)
-                {
-                    return CommandLineError("Version number must follow `-version' option");
-                }
-                if (!PcalParams.ProcessVersion(args[nextArg]))
-                {
-                    return CommandLineError("Bad version number");
-                }
-
-            } else if (option.equals("-lineWidth"))
-            {
-                nextArg = nextArg + 1;
-                try
-                {
-                    if (nextArg == maxArg)
-                    {
-                        throw new NumberFormatException();
-                    }
-                    int a = new Integer(args[nextArg]).intValue();
-                    if (a < 60)
-                    {
-                        throw new NumberFormatException();
-                    }
-                    PcalTLAGen.wrapColumn = a;
-                    PcalTLAGen.ssWrapColumn = a - 33;
-                } catch (Exception e)
-                {
-                    return CommandLineError("Integer value at least 60 must follow `-lineWidth' option");
-                }
-
-            } else
-            {
-                if (notInFile)
-                {
-                    return CommandLineError("Unknown command-line option: " + option);
-                } else
-                {
-                    return CommandLineError("Unknown or illegal option in options statement: " + option);
-                }
-            }
-            ;
-            nextArg = nextArg + 1;
-        } // END while (nextArg < maxArg)
-
-        if (nextArg > maxArg)
-        /******************************************************************
-        * The last option took an argument that was the last              *
-        * command-line argument.                                          *
-        ******************************************************************/
-        {
-            return CommandLineError("No input file specified");
-        }
-
-        // SZ 02.16.2009: since this is a modification of the parameters, moved
-        // to the parameter handling method
-        if (PcalParams.FairnessOption.equals("-nof"))
-        {
-            PcalParams.FairnessOption = "";
-        }
-        if (PcalParams.CheckTermination && PcalParams.FairnessOption.equals("")  && !explicitNof)
-        {
-            PcalParams.FairnessOption = "wf";
-
-        }
-
-        /********************************************************************
-         * If we are processing the command-line arguments, we need to get  *
-         * the input-file name.  Otherwise, we're done.                     *     
-         *******************************************************************/
-        if (inFile)
-        {
-            return STATUS_OK;
-        }
-
-        /********************************************************************
-        * Set PcalParams.TLAInputFile to the last argument, removing a      *
-        * "tla" extension if it has one.                                    *
-        ********************************************************************/
-        /*
-        int dotIndex = args[maxArg].lastIndexOf(".") ;
-        if (dotIndex == -1) 
-        { 
-          PcalParams.TLAInputFile = args[maxArg]; 
-        } 
-        else if (args[maxArg].substring(dotIndex).equals(".tla"))
-        { 
-          PcalParams.TLAInputFile = args[maxArg].substring(0, dotIndex); 
-        }
-        else 
-        {  
-          return CommandLineError("Input file has extension other than tla"); 
-        }
-        */
-
-        // SZ 02.16.2009: check for correct file extension (ignoring case)
-        // and file existence. also handles dots in the pathname
-        File file = new File(args[maxArg]);
-        boolean hasExtension = false;
-        if (file.getName().lastIndexOf(".") == -1)
-        {
-            // no extension
-            PcalParams.TLAInputFile = file.getPath();
-        } else
-        {
-            // extension present
-            if (file.getName().toLowerCase().endsWith(".tla"))
-            {
-                hasExtension = true;
-            }
-            // Aborted version 1.31 code
-            // else if (file.getName().toLowerCase().endsWith(".pcal")){
-            // hasExtension = true;
-            // PcalParams.fromPcalFile = true;
-            // }
-            else
-            {
-                return CommandLineError("Input file has extension other than " /* pcal or */+ "tla");
-            }
-        }
-        if (hasExtension)
-        {
-            // cut the extension
-            PcalParams.TLAInputFile = file.getPath().substring(0, file.getPath().lastIndexOf("."));
-            if (!file.exists())
-            {
-                return CommandLineError("Input file " + file.getPath() + " does not exist.");
-            }
-        } else
-        {
-            // aborted version 1.31 code
-            // file = new File(PcalParams.TLAInputFile + ".pcal");
-            // if (file.exists())
-            // {
-            // PcalParams.fromPcalFile = true;
-            // } else
-            // {
-            file = new File(PcalParams.TLAInputFile + ".tla");
-            if (!file.exists())
-            {
-                return CommandLineError("Input file " + PcalParams.TLAInputFile + ".pcal and " + file.getPath()
-                        + ".tla not found");
-            }
-            // }
-        }
-        // file = new File(PcalParams.TLAInputFile + (PcalParams.fromPcalFile?".pcal":".tla"));
-        // if (!file.exists())
-        // {
-        // return CommandLineError("Input file " + file.getPath() + " not found");
-        // }
-
-        return STATUS_OK;
-    }
-
-    /**
-     * Prints out the help message
-     * @return status if it has been successfully printed
-     */
-    private static boolean OutputHelpMessage()
-    {
-        Vector helpVec = null;
-        try
-        {
-            helpVec = PcalResourceFileReader.ResourceFileToStringVector("help.txt");
-        } catch (PcalResourceFileReaderException e)
-        {
-            PcalDebug.reportError(e);
-            return false;
-        }
-        int i = 0;
-        while (i < helpVec.size())
-        {
-            ToolIO.out.println((String) helpVec.elementAt(i));
-            i = i + 1;
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns if the options are conflicting
-     * @return true if the provided options are conflicting, false otherwise
-     */
-    private static boolean CheckForConflictingSpecOptions()
-    {
-        if ((PcalParams.SpecOption ? 1 : 0) + (PcalParams.MyspecOption ? 1 : 0) + (PcalParams.Spec2Option ? 1 : 0)
-                + (PcalParams.Myspec2Option ? 1 : 0) + (PcalParams.WriteASTFlag ? 1 : 0) > 1)
-        {
-            CommandLineError("\nCan have at most one of the options " + "-spec, -myspec, -spec2, -myspec2, writeAST");
-            return true;
-        }
-        ;
-        return false;
-    }
-
-    private static int CommandLineError(String msg)
-    /*********************************************************************
-    * Announce a command line error with the string indicating the       *
-    * explanation and halt.                                              *
-    *********************************************************************/
-    {
-          PcalDebug.reportError("Command-line error: " + msg + ".");
-//        ToolIO.out.println("Command-line error: " + msg + ".");
-//        ToolIO.out.println("Use -help option for more information.");
-        return STATUS_EXIT_WITH_ERRORS;
-    }
-
-    private static int findTokenPair(Vector vec, int lineNum, String tok1, String tok2)
-    /*********************************************************************
-    * Returns the number of the first line at or after lineNum in the    *
-    * vector of strings vec containing tok1 followed by 1 or more        *
-    * spaces followed by tok2.  Returns -1 if such a line is not found.  *
-    *********************************************************************/
-    {
-        int i = lineNum;
-        while (i < vec.size())
-        {
-            String line = (String) vec.elementAt(i);
-            int col = line.indexOf(tok1);
-            int nextcol = col + tok1.length();
-            if (col != -1)
-            {
-                while ((nextcol < line.length()) && (line.charAt(nextcol) == ' '))
-                {
-                    nextcol = nextcol + 1;
-                }
-                ;
-                if ((nextcol < line.length()) && (nextcol == line.indexOf(tok2)))
-                {
-                    return i;
-                }
-            }
-            ;
-            i = i + 1;
-        }
-        ;
-        return -1;
-    }
-
-    /**************************  RemoveTabs  *********************************/
-
-    public static Vector removeTabs(Vector vec)
-    {
-        /********************************************************************
-        * Returns a string vector obtained from the string vector vec by   *
-        * replacing any evil tabs with the appropriate number of spaces,   *
-        * where "appropriate" means adding from 1 to 8 spaces in order to  *
-        * make the next character fall on a column with Java column        *
-        * number (counting from 0) congruent to 0 mod 8.  This is what     *
-        * Emacs does when told to remove tabs, which makes it good enough  *
-        * for me.                                                          *
-         ********************************************************************/
-        Vector newVec = new Vector();
-        int i = 0;
-        while (i < vec.size())
-        {
-            String oldline = (String) vec.elementAt(i);
-            String newline = "";
-            int next = 0;
-            while (next < oldline.length())
-            {
-                if (oldline.charAt(next) == '\t')
-                {
-                    int toAdd = 8 - (newline.length() % 8);
-                    while (toAdd > 0)
-                    {
-                        newline = newline + " ";
-                        toAdd = toAdd - 1;
-                    }
-                } else
-                {
-                    newline = newline + oldline.substring(next, next + 1);
-                }
-                ;
-                next = next + 1;
-            }
-            newVec.addElement(newline);
-            i = i + 1;
-        }
-        ;
-        return newVec;
-    }
-
-    /********************* STRING UTILITY FUNCTIONS ***********************/
-
-    private static int NextSpace(String s, int cur)
-    /********************************************************************
-    * Returns the first space in s at or after col. If there is none,   *
-    * return the index of the last character in s. Spaces in strings    *
-    * are not treated as spaces. Assumes s[cur] is not in a string.     *
-    ********************************************************************/
-    {
-        int i = cur;
-        boolean inString = false;
-        while ((i < s.length()) && ((s.charAt(i) != ' ') || inString))
-        {
-            if ((s.charAt(i) == '"') && ((i == 0) || (s.charAt(i - 1) != '\\')))
-                inString = !inString;
-            i = i + 1;
-        }
-        if (i == s.length())
-            return i - 1;
-        else
-            return i;
-    }
-
-    private static String WrapString(String inString, int col)
-    /*********************************************************************
-    * Returns the string inString with lines wrapped approximately at    *
-    * col, taking care not to wrap in a string.                          *
-    *********************************************************************/
-    {
-        int i = 0;
-        int ccol = 1;
-        StringBuffer sb = new StringBuffer();
-        while (i < inString.length())
-        {
-            if (inString.charAt(i) == ' ') // An initial space or a space
-            {
-                sb.append(' '); // that follows a space. It
-                i = i + 1; // can always be appended to a line.
-                ccol = ccol + 1;
-            } else
-            // Find next word, which starts at i.
-            {
-                int j = NextSpace(inString, i);
-                if (ccol + (j - i + 1) > col)
-                {
-                    sb.append('\n');
-                    ccol = 1;
-                }
-                while (i <= j) // If this overflows col, then the word
-                {
-                    sb.append(inString.charAt(i));
-                    // is longer than col.
-                    i = i + 1;
-                    ccol = ccol + 1;
-                }
-            }
-        }
-        return sb.toString();
-    }
-
-}
+package pcal;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Matcher;
+
+import pcal.ValidationCallBack.Generate;
+import pcal.exception.FileToStringVectorException;
+import pcal.exception.ParseAlgorithmException;
+import pcal.exception.PcalResourceFileReaderException;
+import pcal.exception.RemoveNameConflictsException;
+import pcal.exception.StringVectorToFileException;
+import pcal.exception.TLCTranslationException;
+import pcal.exception.UnrecoverableException;
+import util.TLAConstants;
+import util.ToolIO;
+
+/***************************************************************************
+* <pre>
+* CLASS trans                                                              *
+*                                                                          *
+*  BUGS:                                                                   *   
+*    - Interprets                                                          *
+*                                                                          *
+*         if (...) call f(...) ; return                                    *
+*                                                                          *        
+*      as if it were                                                       *
+*                                                                          *     
+*         if (...) {call f(...) ; return }                                 *
+*                                                                          *
+*      See 2 Dec 2015 Tlaplus Google group post by Jaak Ristioja           *
+*                                                                          *     
+*    - Accepts if (...) {...} ; else {...}                                 *
+*      Generates code as if the ";" were not there.                        *
+*      
+*    - When the body of a macro contains a statement with(v \in ...)
+*      and v is a macro parameter, the argument is not being substituted
+*      for v.
+*      
+*    - I came across a  "TLAExpr.renormalize() found anchor has moved to left"
+*      error, apparently caused by a substitution of an expression longer
+*      than the macro parameter it is instantiating in some weird case.
+*                                                                          *
+*  POSSIBLE FEATURE:                                                       *
+*     Adds the pc variable if a label has a + or - modifier.  It's         *
+*     not needed for a + modifier (and all the other conditions            *
+*     for eliminating pc hold).  The solution is not to try to handle      *
+*     this case, but to add a "strongly fair process" construct, since     *
+*     the pc can be eliminated only when there's just a single label.      *
+*     This would be easy to implement if + modifiers are ignored or        *
+*     cause an error and - modifiers mean no fairness.  Then "strongly     *
+*     fair" would act like "fair" except that + modifiers do nothing       *
+*     and an SF instead of a WF is produced.                               *
+* -----------------------------------------------------------------        *
+* History:                                                                 *
+*   Version 1.0: Original release.                                         *
+*                                                                          *
+*   Version 1.1: (March 2006)                                              *
+*                Introduced ability to have the translator                 *
+*                add missing labels.                                       *
+*                                                                          *
+*   Version 1.2: (August 2007)                                             *
+*                Introduced defaultInitValue so liveness can be            *
+*                checked even with "uninitialized" variables.              *
+*                                                                          *
+*   Version 1.3: (February 2008)                                           *
+*                Added "await" as a synonym for "when"                     *
+*                                                                          *
+*   Version 1.4: (June 2010)                                               *
+*                Added the options statement and the -lineWidth option.    *
+*                                                                          *               
+*   Version 1.5: (Jan 2011)                                                *
+*                 1. Added the -noDoneDisjunct option.                     *
+*                 2. Added the new way of specifying fairness, with        *
+*                    the "fair" keyword with "+" modifier, and the "+"     *
+*                    and "-" label modifiers.                              *
+*                 3. Automatically removes the stuttering-on-termination   *
+*                    disjunction if all processes are "while (TRUE)"       *
+*                    statements with no gotos to "Done".                   *
+*                 4. In addition, if the "while (TRUE)"s have no           *
+*                    internal labels, it removes the pc variable.          *
+*                 5. Changed the representation of Process and Procedure   *
+*                    nodes in the abstract syntax tree produced by         *
+*                    the -writeAST option and used when TLC is producing   *
+*                    the translation.                                      *
+*                 The changes 3-5 are not implemented when the -version    *
+*                 option specifies an earlier version.                     *
+*                                                                          *
+*   Version 1.6: (July 2011)                                               *
+*                 - Added the "--fair algorithm" syntax for specifying     *
+*                   weak fairness of the next-state action. (This          *
+*                   changes the way fairness of a uniprocess algorithm was *
+*                   specified in Version 1.5.  All legal Version 1.5       *
+*                   algorithms should work with the "version 1.5" option.) *
+*                 - Permitted previously defined macros to be called       *
+*                   inside a macro definition.                             *
+*                                                                          *
+*   Version 1.7: (19 January 2012)                                         *
+*                 - Translator adds "BEGIN/END TRANSLATION" if needed.     *
+*                 - Added support for Toolbox Goto PCal Source command.    *
+*                                                                          *
+*   Version 1.8: (30 Mar 2012)                                             *
+*                 - Changed translation to remove stuttering-on-           *
+*                   termination disjunction if some process is             *
+*                   "while (TRUE)".                                        *
+*                 - Omitted the Termination definition if the stuttering-  *
+*                   on-termination disjunction is removed, since that      *
+*                   implies Termination is always FALSE.                   *
+*                (4 May 2012)                                              *
+*                 - Removed the unnecessary CASE in the pc = ... clause of *
+*                   the Init predicate when there is only a single process *
+*                   statement.                                             *
+*   Version 1.9: (10 July 2019)                                            *
+*                 - Change translation to generate an explicit             *
+*                   Terminating action instead of the implicit one which   *
+*                   is the disjunct of the next-state relation.            *
+*                 - Support in-memory only translation for better          *
+*                   Toolbox integration (old file based translation was    *
+*                   racy).                                                 *
+*                 - Minor changes related to better error reporting.       *
+* -----------------------------------------------------------------        *
+*                                                                          *
+* This is the main method of the +CAL to TLA+ translation program.         *
+* the program has the following command-line options. Only the ones        *
+* marked with ** besides them can be specified in the file's               *
+* options statement.  The "-" can be omitted when the option is in         *
+* the options statement.                                                   *
+*                                                                          *
+*   -help  : Type a help file instead of running the program.              *
+*                                                                          *
+*   -spec name : Uses TLC and the TLA+ specification name.tla to do        *
+*                the translation.  The files name.tla and name.cfg         *
+*                are copied from the java/ directory to the current        *
+*                directory; the file AST.tla that defines `fairness'       *
+*                to equal the fairness option and `ast' to equal           *
+*                the the AST data structure representing the algorithm     *
+*                is written to the current directory; and TLC is run       *
+*                on name.tla to produce the translation.                   *
+*                                                                          *
+*   -myspec name : Like -spec, except it finds the files names.tla         *
+*                  and names.cfg in the current directory, instead         *
+*                  of writing them there.                                  *
+*                                                                          *
+*   -spec2   name                                                          *
+*   -myspec2 name : Like -spec and -myspec, except they use TLC2           *
+*                   instead of TLC (aka TLC1).                             *
+*                                                                          *
+*   -writeAST : Writes the AST file as in the -spec option, but does       *
+*               not perform the translation.                               *
+*                                                                          *
+*   -debug : Run in debugging mode, whatever that means.  For the          *
+*            parser, it just means that the toString() methods             *
+*            output the line and column number along with the              *
+*            node.                                                         *
+*                                                                          *
+*   -unixEOL : Forces the use of Unix end-of-line convention, regardless   *
+*              of the system's default.  Without this, when run on         *
+*              Windows, the files it writes seem to have a "^M" at the     *
+*              end of every line when viewed with Emacs.                   *
+*                                                                          *
+*** -wf : Conjoin to formula Spec weak fairness of each process's          *
+*         next-state action                                                *
+*                                                                          *
+*** -sf : Conjoin to formula Spec strong fairness of each process's        *
+*         next-state action                                                *
+*                                                                          *
+*** -wfNext : Conjoin to formula Spec weak fairness of the entire          *
+*             next-state action                                            *
+*                                                                          *
+*** -nof : Conjoin no fairness formula to Spec.  This is the default,      *
+*          except when the -termination option is chosen.                  *
+*                                                                          *
+*** -termination : Add to the .cfg file the command                        *
+*                     PROPERTY Termination                                 *
+*                  With this option, the default fairness option           *
+*                  becomes -wf.                                            *
+*                                                                          *
+*   -nocfg : Suppress writing of the .cfg file.                            *
+*                                                                          *
+*** -noDoneDisjunct : Suppress the disjunct of the next-state              *
+*                     relation that describes stuttering steps taken       *
+*                     when the algorithm has halted.                       *
+*                                                                          *
+*** -label : Tells the translator to add missing labels.  This is          *
+*            the default only for a uniprocess algorithm in which          *
+*            the user has typed no labels.                                 *
+*                                                                          *
+*   -reportLabels : True if the translator should print the names          *
+*                   and locations of all labels it adds.  Like             *
+*                   -label, it tells the translator to add missing         *
+*                   labels.                                                *
+*                                                                          *
+*** -labelRoot name : If the translator adds missing labels, it            *
+*                     names them name1, name2, etc.  Default value         *
+*                     is "Lbl_".                                           *
+*                                                                          *
+*  THE FOLLOWING OPTION ADDED IN VERSION 1.31                              *
+*                                                                          *
+*** -lineWidth : The translation tries to perform the translation so       *
+*                lines have this maximum width.  (It will often            *
+*                fail.)  Default is 78, minimum value is 60.               *
+*                                                                          *
+*  THE FOLLOWING OPTIONS ADDED IN VERSION 1.4                              *
+*                                                                          *
+*** -lineWidth : The translation tries to perform the translation so       *
+*                lines have this maximum width.  (It will often            *
+*                fail.)  Default is 78, minimum value is 60.               *
+*                                                                          *
+*** -version : The version of PlusCal for which this algorithm is          *
+*              written.  If the language is ever changed to make           *
+*              algorithms written for earlier versions no longer           *
+*              legal, then the translator should do the appropriate        *
+*              thing when the earlier version number is specified.         *
+* ------------------------------------------------------------------------ *
+*                                                                          *
+* The program uses vector objects from the Vector class to implement       *
+* sequences (lists).  This generates a compiler warning.                   *
+*                                                                          *
+* In Java data structures like arrays and Vectors, numbering starts with   *
+* 0.  Unlike programmers, human beings count from 1.  I use the term "Java *
+* ordinal" to refer a number that denotes a position that represents the   *
+* first item as 0, and the term "human ordinal" to refer to an ordinary    *
+* ordinal that counts the first item as 1.                                 *
+*                                                                          *
+*                                                                          *
+* NOTE:                                                                    *
+*                                                                          *
+* One process should be able to read the pc or stack of another.  There    *
+* is no logical reason to forbid this.  However, the definition of         *
+* Translation in PlusCal.tla does not distinguish between instances of pc  *
+* in the original algorithm and ones inserted by the translation.  The     *
+* latter instances must be subscripted--that is replaced by something      *
+* like pc[self].  Therefore, the Translation operator subscripts the       *
+* instances of pc from the original algorithm.  The Java Translate method  *
+* must not do this, but must subscript (and prime) only the instances of   *
+* pc and stack introduced during the translation process.                  *
+*                                                                          *
+*                                                                          *
+* The following bugs should all have been fixed by the addition of         *
+* ParseAlgorithm.Uncommment by LL on 18 Feb 2006.                          *
+*                                                                          *
+*  - There cannot be a comment between a label and the                     *
+*    following ":".                                                        *
+*                                                                          *
+*  - There cannot be a comment immediately before the ")" that ends        *
+*    the list of arguments in a call statement.                            *
+*                                                                          *
+*  - The code for reporting the location of an error has the               *
+*    following problem.  If the token where the error occurs is            *
+*    preceded by a comment, then the position reported is that of the      *
+*    beginning of the comment rather than that of the token.               *
+*                                                                          *
+* TENTATIVE CHANGE MADE                                                    *
+*                                                                          *
+* The following change was made along with a modification to the parser    *
+* to allow semi-colons to be omitted when they're obviously unnecessary.   *
+*                                                                          *
+* The parser does not parse expressions in the +CAL algorithm; it just     *
+* scans ahead to the first token that it can determine is not part of the  *
+* expression.  To make this work, the following tokens that are legal in   *
+* a TLA+ expression are illegal in a +CAL expression:                      *
+*                                                                          *
+*   variable   variables   begin   do   then   :=   ||                     *
+*                                                                          *
+* Making additional tokens illegal might help the parser find errors       *
+* sooner.  For example,  example, if one forgets the ";" and writes        *
+*                                                                          *
+*        x := x + 1                                                        *
+*       if x > y + 17 then ...                                             *
+*                                                                          *
+* the parser takes everything up to the "then" to be the right-hand side   *
+* of the "x :=" assignment.  Making "if" illegal in an expression would    *
+* allow the parser to catch the error at the "if".                         *
+* </pre>
+***************************************************************************/
+class trans {
+    /** Status indicating no errors and successful process */
+    static final int STATUS_OK = 1;
+    /** Status of no errors, but abort of the translation */
+    static final int STATUS_EXIT_WITHOUT_ERROR = 0;
+    /** Status of present errors and abort of the translation */
+    static final int STATUS_EXIT_WITH_ERRORS = -1;
+    
+    private static final String PCAL_TRANSLATION_COMMENT_LINE_PREFIX
+    		= "\\* " + PcalParams.BeginXlation1 + " " + PcalParams.BeginXlation2;
+    private static final String TLA_TRANSLATION_COMMENT_LINE_PREFIX
+    		= "\\* " + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2;
+    
+    
+    /**
+     * Main function called from the command line
+     * @param args, command line arguments
+     */
+    public static void main(String[] args)
+    {
+        runMe(args);
+    }
+
+    /**
+     * The main translator method
+     * @return one of {@link trans#STATUS_OK}, {@link trans#STATUS_EXIT_WITH_ERRORS}, 
+     * {@link trans#STATUS_EXIT_WITH_ERRORS}
+     * indicating the status
+     * 
+     * Modified by LL on 16 Dec 2011.  Changed the return value to the
+     * TLAtoPCalMapping object for the translation.  (The status return
+     * value was not being used.)  If the translation fails, it returns
+     * null.
+     */
+    public static int runMe(String[] args)  
+    {
+        /*********************************************************************
+        * Get and print version number.                                      *
+        *********************************************************************/
+        // String lastModified =
+        // "last modified on Wed 11 March 2009 at 14:52:58 PST by lamport";
+        /*******************************************************************
+        * This string is inserted by an Emacs macro when a new version is  *
+        * saved.  Unfortunately, Eclipse isn't Emacs, so the modification  *
+        * date must be entered manually in the PcalParams module.          *
+        *******************************************************************/
+
+        if (ToolIO.getMode() == ToolIO.SYSTEM)
+        {
+            PcalDebug.reportInfo("pcal.trans Version " + PcalParams.version + " of " + PcalParams.modDate);
+        }
+
+        // SZ Mar 9, 2009:
+        /*
+         * This method is called in order to make sure, that  the
+         * parameters are not sticky because these are could have been initialized
+         * by the previous run  
+         */
+        PcalParams.resetParams();
+        /*********************************************************************
+        * Get and process arguments.                                         
+        *********************************************************************/
+        int status = parseAndProcessArguments(args);
+
+        if (status != STATUS_OK)
+        {
+            return exitWithStatus(status);
+        }
+
+        /*********************************************************************
+        * Read the input file, and set the Vector inputVec equal to its      *
+        * contents, where inputVec[i] is the string containing the contents  *
+        * of line i+1 of the input file.                                     *
+        *********************************************************************/
+        List<String> inputVec = null;
+        try
+        {
+            inputVec = fileToStringVector(PcalParams.TLAInputFile + /* (PcalParams.fromPcalFile ? ".pcal" : */TLAConstants.Files.TLA_EXTENSION /*)*/);
+        } catch (FileToStringVectorException e)
+        {
+            PcalDebug.reportError(e);
+            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+        }
+
+        /*********************************************************************
+        * outputVec is an alias for inputVec if the input is a .tla file,    *
+        * which was not always the case in the aborted version 1.31.         *
+        *********************************************************************/
+        // Vector outputVec = PcalParams.fromPcalFile ? new Vector() : inputVec;
+        final List<String> outputVec = performTranslation(inputVec);
+        if (outputVec == null) {
+        	return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+        }
+        
+        /*********************************************************************
+        * For .tla input:                                                    *
+        * Rename the old file by changing its extension from "tla" to "old". *
+        *********************************************************************/
+        // if (!PcalParams.fromPcalFile)
+        // {
+        File file;
+        try
+        {
+            file = new File(PcalParams.TLAInputFile + ".old");
+            if (file.exists())
+            {
+                file.delete();
+            }
+            ;
+            file = new File(PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION);
+            file.renameTo(new File(PcalParams.TLAInputFile + ".old"));
+        } catch (Exception e)
+        {
+            PcalDebug.reportError("Could not rename input file " + PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION + " to "
+                    + PcalParams.TLAInputFile + ".old");
+            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+        }
+        ;
+        // }
+
+        /*********************************************************************
+        * Code from aborted version 1.31.                                    *
+        * For .pcal input, set outputSuffixLoc and add the rest of the       *
+        * input file to the output.                                          *
+        *********************************************************************/
+        // if (PcalParams.fromPcalFile)
+        // {
+        // PcalParams.outputSuffixLoc = new IntPair(outputVec.size(), 0);
+        // // if there's stuff in the suffix on the same line with the
+        // // end of the algorithm, write it on a separate line.
+        // IntPair curLoc = new IntPair(PcalParams.inputSuffixLoc.one, PcalParams.inputSuffixLoc.two);
+        // if (curLoc.one < untabInputVec.size())
+        // {
+        // String lastLine = (String) untabInputVec.elementAt(curLoc.one);
+        // if (curLoc.two < lastLine.length())
+        // {
+        // outputVec.addElement(lastLine.substring(curLoc.two));
+        // }
+        // curLoc.one++;
+        // }
+        // // Copy the rest of the input file into the output file.
+        // for (int ii = curLoc.one; ii < untabInputVec.size(); ii++)
+        // {
+        // outputVec.addElement((String) untabInputVec.elementAt(ii));
+        // }
+        // }
+        /*********************************************************************
+        * Write the output file.                                             *
+        *********************************************************************/
+        try
+        {
+            WriteStringVectorToFile(outputVec, PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION);
+        } catch (StringVectorToFileException e)
+        {
+            PcalDebug.reportError(e);
+            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+        }
+
+        PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION + " written.");
+
+        /*********************************************************************
+        * Write the cfg file, unless the -nocfg option is used.              *
+        *********************************************************************/
+        final File cfgFile = new File(PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION);
+        List<String> cfg = null;
+        boolean writeCfg = !PcalParams.Nocfg;
+        if (writeCfg && cfgFile.exists())
+        {
+            if (cfgFile.canRead())
+            {
+                try
+                {
+                    cfg = fileToStringVector(PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION);
+                } catch (FileToStringVectorException e)
+                {
+                    PcalDebug.reportError(e);
+                    return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+                }
+            } else
+            {
+                /*************************************************************
+                * cfg file is read-only.                                     *
+                *************************************************************/
+                writeCfg = false;
+                PcalDebug.reportInfo("File " + PcalParams.TLAInputFile + ".cfg is read only, new version not written.");
+            }
+        } else
+        {
+            cfg = new ArrayList<>();
+            cfg.add(PcalParams.CfgFileDelimiter);
+        }
+
+        /*********************************************************************
+        * Delete previously written part of cfg file.                        *
+        *********************************************************************/
+        if (writeCfg)
+        {
+            int j = 0;
+            boolean done = false;
+            while ((!done) && (cfg.size() > j))
+            {
+                if (((String) cfg.get(j)).indexOf(PcalParams.CfgFileDelimiter) == -1)
+                {
+                    j = j + 1;
+                } else
+                {
+                    done = true;
+                }
+            }
+            if (done)
+            {
+                /*************************************************************
+                * Delete all lines before the delimiting comment string.     *
+                *************************************************************/
+                while (j > 0)
+                {
+                    cfg.remove(0);
+                    j = j - 1;
+                }
+            } else
+            {
+                /*************************************************************
+                * The delimiting comment string written by the translator    *
+                * not found in the cfg file, so presumably the user created  *
+                * the cfg file before running the translator on the input    *
+                * file.  We insert the delimiter.                            *
+                *************************************************************/
+                cfg.add(0, PcalParams.CfgFileDelimiter);
+            }
+
+            /******************************************************************
+            * If defaultInitValue is used, add a CONSTANT statement setting   *
+            * it to a model value of the same name.                           *
+            * (Added 22 Aug 2007 by LL.)                                      *
+            ******************************************************************/
+            if (PcalParams.tlcTranslation() || ParseAlgorithm.hasDefaultInitialization)
+            {
+                cfg.add(0, "CONSTANT defaultInitValue = defaultInitValue");
+            }
+            
+            /******************************************************************
+            * Insert the `PROPERTY Termination' line if requested.            *
+            ******************************************************************/
+            if (PcalParams.CheckTermination)
+            {
+                cfg.add(0, "PROPERTY Termination");
+            }
+
+            /******************************************************************
+            * Insert the SPECIFICATION line if there isn't already one.       *
+            ******************************************************************/
+            boolean hasSpec = false;
+            for (final String thisLine : cfg) {
+                if ((thisLine.indexOf(TLAConstants.KeyWords.SPECIFICATION) != -1)
+                        && ((thisLine.indexOf("\\*") == -1) || (thisLine.indexOf("\\*") > thisLine
+                                .indexOf(TLAConstants.KeyWords.SPECIFICATION)))) {
+                    hasSpec = true;
+                    break;
+                }
+            }
+
+            if (hasSpec)
+            {
+                PcalDebug.reportInfo("File " + PcalParams.TLAInputFile
+                        + ".cfg already contains " + TLAConstants.KeyWords.SPECIFICATION
+                        + " statement," + "\n   so new one not written.");
+            } else
+            {
+                cfg.add(0, TLAConstants.KeyWords.SPECIFICATION + " Spec");
+            }
+
+            try
+            {
+                WriteStringVectorToFile(cfg, PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION);
+            } catch (StringVectorToFileException e)
+            {
+                PcalDebug.reportError(e);
+                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+            }
+            PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION + " written.");
+        }
+
+        return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR);
+    } // END main
+
+    // This is called from the main-invoked {@link #runMe(String[])}
+    // For some reason this method used to both mutate the argument, and then also returns that argument... ?
+    //		Now we copy the argument, mutate the copy, and return that.
+    public static List<String> performTranslation(final List<String> specificationText) {
+    	return performTranslation(specificationText, new ValidationCallBack.Noop());
+    }
+    
+    public static List<String> performTranslation(final List<String> specificationText, ValidationCallBack cb) {
+        /**
+         * Create the new TLAtoPCalMapping object, call it mapping
+         * here and set PcalParams.tlaPcalMapping to point to it.
+         */
+        final TLAtoPCalMapping mapping = new TLAtoPCalMapping() ;
+        PcalParams.tlaPcalMapping = mapping;
+        
+        /*********************************************************************
+        * Set untabInputVec to be the vector of strings obtained from        *
+        * inputVec by replacing tabs with spaces.                            *
+        *                                                                    *
+        * Tabs are date from the days when memory cost $1 per bit and are a  *
+        * stupid anachronism.  They should be banned.  Although the various  *
+        * methods taken from TLATeX should deal with tabs, there are         *
+        * undoubtedly corner cases that don't work right.  In particular, I  *
+        * think there's one case where PcalCharReader.backspace() might be   *
+        * called to backspace over a tab.  It's easier to simply get rid of  *
+        * the tabs than to try to make it work.                              *
+        *                                                                    *
+        * Since the user might be evil enough to prefer tabs, with tla-file  *
+        * input, the parts of the output file that are not produced by the   *
+        * translator are copied from inputVec, so any tabs the user wants    *
+        * are kept.                                                          *
+        *********************************************************************/
+        final Vector<String> untabInputVec = removeTabs(specificationText);
+
+        /**
+         *  Look through the file for PlusCal options.  They are put anywhere
+         *  in the file (either before or after the module or in a comment)
+         *  with the following sequence
+         *     PlusCal <optional white space> options <optional white space>
+         *        ( <options> )
+         *  
+         *  where <options> has the same format as options on the command
+         *  line.
+         */
+        IntPair searchLoc = new IntPair(0, 0);
+        boolean notDone = true;
+        while (notDone)
+        {
+            try
+            {
+                ParseAlgorithm.FindToken("PlusCal", untabInputVec, searchLoc, "");
+                String line = ParseAlgorithm.GotoNextNonSpace(untabInputVec, searchLoc);
+                String restOfLine = line.substring(searchLoc.two);
+                if (restOfLine.startsWith("options"))
+                {
+                    // The first string after "PlusCal" not starting with a
+                    // space character is "options"
+                    if (ParseAlgorithm.NextNonIdChar(restOfLine, 0) == 7)
+                    {
+                        // The "options" should begin an options line
+                        PcalParams.optionsInFile = true;
+                        ParseAlgorithm.ProcessOptions(untabInputVec, searchLoc);
+                        notDone = false;
+                    }
+                }
+            } catch (ParseAlgorithmException e)
+            {
+                // The token "PlusCal" not found.
+                notDone = false;
+            }
+        }
+
+        /**
+         * translationLine is set to the line of the output file at which
+         * the \* BEGIN TRANSLATION appears--whether it is inserted into the
+         * tla-file input by the user, or inserted into the output by the
+         * translator for pcal-file input.
+         */
+        int translationLine = 0;
+
+        /*********************************************************************
+         * Set algLine, algCol to the line and column just after the string   *
+         * [--]algorithm that begins the algorithm.  (These are Java          *
+         * ordinals, in which counting begins at 0.)                          *
+         *                                                                    *
+         * Modified by LL on 18 Feb 2006 to use untabInputVec instead of      *
+         * inputVec, to correct bug that occurred when tabs preceded the      *
+         * "--algorithm".                                                     *
+         *                                                                    *
+         * For the code to handle pcal-input, I introduced the use of         *
+         * IntPair objects to hold <line, column> Java coordinates (counting  *
+         * from zero) in a file (or an image of a file in a String Vector).   *
+         * For methods that advance through the file, the IntPair object is   *
+         * passed as an argument and is advanced by the method.  This is      *
+         * what I should have been doing from the start, but I wasn't smart   *
+         * enough The IntPair curLoc is the main one used in the part of the  *
+         * following code that handles pcal-file input.                       *
+         *********************************************************************/
+        int algLine = 0;
+        int algCol = -1;
+        /*******************************************************************
+        * If the BEGIN/END TRANSLATION region exists, then set             *
+        * translationLine to the number of the line after which the        *
+        * translation is to be inserted and delete the previous version    *
+        * of the translation (if it exists) from inputVec.  (Line          *
+        * numbering is by Java ordinals.)  If the region doesn't exist,    *
+        * set translationLine to -1.                                       *
+        *                                                                  *
+        * Note: we remove the previous version from inputVec, because      *
+        * that's where the translated output is going to go, and also      *
+        * from untabInputVec, because we will then detect if the begin     *
+        * and end translation lines contain part of the algorithm within   *
+        * them.                                                            *
+        *******************************************************************/
+    	final ArrayList<String> output = new ArrayList<>(specificationText);
+
+        translationLine = findTokenPair(untabInputVec, 0, PcalParams.BeginXlation1, PcalParams.BeginXlation2);
+        int endTranslationLine = -1;
+        if (translationLine != -1)
+        {
+            endTranslationLine = findTokenPair(untabInputVec, translationLine + 1,
+            									   PcalParams.EndXlation1, PcalParams.EndXlation2);
+            if (endTranslationLine == -1)
+            {
+                PcalDebug.reportError("No line containing `" + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2);
+                return null;
+            }
+
+            
+            int etl = endTranslationLine - 1;
+            while (translationLine < etl)
+            {
+            	output.remove(etl);
+                untabInputVec.remove(etl);
+                etl--;
+            }
+        }
+
+        // Search for "--algorithm" or "--fair".
+        // If found set algLine and algCol right after the last char,
+        // set foundBegin true, and set foundFairBegin true iff it
+        // was "--fair".  Otherwise, set foundBegin false.
+        boolean foundBegin = false;
+        boolean foundFairBegin = false;
+        while ((algLine < untabInputVec.size()) && !foundBegin)
+        {
+            String line = untabInputVec.elementAt(algLine);
+            algCol = line.indexOf(PcalParams.BeginAlg);
+            if (algCol != -1)
+            {
+                algCol = algCol + PcalParams.BeginAlg.length();
+                foundBegin = true;
+            } else
+            {
+            	algCol = line.indexOf(PcalParams.BeginFairAlg);
+            	if (algCol != -1) {
+            		// Found the "--fair".  The more structurally nice thing to
+            		// do here would be to move past the following "algorithm".
+            		// However, it's easier to pass a parameter to the ParseAlgorithm
+            		// class's GetAlgorithm method that tells it to go past the
+            		// "algorithm" token.
+            		 algCol = algCol + PcalParams.BeginFairAlg.length();
+                     foundBegin = true;
+                     foundFairBegin = true;
+            		
+            	} else {
+            		algLine = algLine + 1;
+            	} 
+            }
+            ;
+        }
+        ;
+        if (!foundBegin)
+        {
+            PcalDebug.reportError("Beginning of algorithm string " + PcalParams.BeginAlg + " not found.");
+            return null;
+        }
+        ;
+        
+        /*
+         * Set the algColumn and algLine fields of the mapping object.
+         */
+        mapping.algColumn = algCol;
+        mapping.algLine = algLine;
+
+        if (translationLine == -1) 
+        {
+           /****************************************************************
+           * Insert BEGIN/END TRANSLATION comments immediately after the   *
+           * end of the comment that contains the beginning of the         *
+           * algorithm.  Set translationLine to the (Java) line number of  *
+           * the BEGIN TRANSLATION.                                        *
+           ****************************************************************/
+        	
+            // Set ecLine, ecCol to the position immediately after the
+            // *) that closes the current comment.
+        	int depth = 1 ;
+            int ecLine = algLine ;
+            int ecCol  = algCol ;
+            boolean notFound = true ;
+            while (notFound && ecLine < untabInputVec.size()) {
+            	char[] line = ((String) untabInputVec.elementAt(ecLine)).toCharArray();
+            	
+                // check current line 
+                while (notFound && ecCol < line.length-1)	 {
+                	char ch = line[ecCol] ;
+                	char ch2 = line[ecCol+1] ;
+                	
+// The following code isn't needed because the algorithm is inside a comment, and 
+// quotes and \* have no effect in determining where the comment ends.
+//
+//                	if (ch == '"') {
+//                		// gobble string
+//                		ch = ch2 ;
+//                		ecCol++ ;
+//                		while (ch != '"') {
+//                			if (ch == '\\') {
+//                				ecCol = ecCol + 2;
+//                			} 
+//                			else {
+//                				ecCol++ ;
+//                			} ;
+//                			if (ecCol < line.length - 1) {
+//                			   ch = line[ecCol] ;
+//                			}
+//                			else {
+//                				ch = '"' ;
+//                			}
+//                		} ;
+//                		ecCol++ ;
+//                	} 
+//                        	
+//                	if (ch == '\\' && ch2 == '*' ) {
+//                		// end of line comment, skip to end of line
+//                		ecCol = 214748364;  // a very large int
+//                	}
+                	if (ch == '(' && ch2 == '*') {
+                		// left comment delimiter
+                    	depth++;
+                    	ecCol = ecCol + 2;
+                	}
+                	else if (ch == '*' && ch2 == ')') {
+                		// right comment delimiter
+                		depth--;
+                		ecCol = ecCol + 2;
+                		if (depth == 0) {
+                			notFound = false ;
+                		}
+                	}
+                	else {
+                		// not an interesting character
+                		ecCol++ ;
+                	}
+                }
+            	
+            	// if not found, go to next line
+            	if (notFound) {
+            		ecLine++ ;
+            		ecCol = 0;
+            	}
+            }
+            
+            if (notFound) {
+            	PcalDebug.reportError("Algorithm not in properly terminated comment");
+                return null;
+            }
+            
+            // Report an error  if there's something else on the line that doesn't begin with "\*".  This is probably
+            
+            String endStuff = ((String) untabInputVec.elementAt(ecLine)).substring(ecCol).trim() ;
+            
+            if (!endStuff.equals("") && !endStuff.startsWith("\\*")) {
+            	PcalDebug.reportError("Text on same line following `*)' that ends the \n   comment containing the algorithm.");
+                return null ;
+            } ;
+            
+			output.add((ecLine + 1), (PCAL_TRANSLATION_COMMENT_LINE_PREFIX + " "
+					+ String.format(Validator.CHECKSUM_TEMPLATE, "ffffffff", "ffffffff")));
+            untabInputVec.insertElementAt(PCAL_TRANSLATION_COMMENT_LINE_PREFIX, (ecLine + 1));
+            output.add((ecLine + 2), (TLA_TRANSLATION_COMMENT_LINE_PREFIX + " "));
+            untabInputVec.insertElementAt(TLA_TRANSLATION_COMMENT_LINE_PREFIX, (ecLine + 2));
+
+            translationLine = ecLine + 1;
+//System.out.println(ecLine + ", " + ecCol);
+//Debug.printVector(inputVec, "foo");
+        }
+        else {			
+			// Check if the existing TLA+ translation has been modified by the user and
+			// raise a warning (via cb) if translation should be cancelled to not
+			// lose/overwrite the user changes.
+			final Matcher m = Validator.CHECKSUM_PATTERN.matcher(output.get(translationLine));
+			if (m.find() && m.group(Validator.TLA_CHECKSUM) != null) {
+				final String checksumTLATranslation = Validator
+						.checksum(new Vector<>(specificationText.subList((translationLine + 1), endTranslationLine)));
+				if (!m.group(Validator.TLA_CHECKSUM).equals(checksumTLATranslation) && cb.shouldCancel()) {
+					return null;
+				}
+			}
+        }
+        
+        /*
+         * Set the mappings start line.
+         */
+        mapping.tlaStartLine = translationLine + 1; 
+
+        /*********************************************************************
+        * Added by LL on 18 Feb 2006 to fix bugs related to handling of      *
+        * comments.                                                          *
+        *                                                                    *
+        * Remove all comments from the algorithm in untabInputVec,           *
+        * replacing (* *) comments by spaces to keep the algorithm tokens    *
+        * in the same positions for error reporting.                         *
+        *********************************************************************/
+        try
+        {
+            ParseAlgorithm.uncomment(untabInputVec, algLine, algCol);
+        } catch (ParseAlgorithmException e)
+        {
+            PcalDebug.reportError(e);
+//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+            return null ; // added for testing
+        }
+        // } // end else of if (PcalParams.fromPcalFile) -- i.e., end processing
+        // of .tla input file.
+
+        /*********************************************************************
+        * Set reader to a PcalCharReader for the input file (with tabs and   *
+        * the previous translation removed), starting right after the        *
+        * PcalParams.BeginAlg string.                                        *
+        *********************************************************************/
+        PcalCharReader reader = new PcalCharReader(untabInputVec, algLine, algCol, output.size(), 0);
+
+        /*********************************************************************
+        * Set ast to the AST node representing the entire algorithm.         *
+        *********************************************************************/
+        AST ast = null;
+        try
+        {
+            ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin);
+// System.out.println(ast.toString());
+// For testing, we print out when the new code for eliminating the 
+// suttering-on-done and pc is used.
+// if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) {
+//  System.out.println("omit pc = " + ParseAlgorithm.omitPC + 
+//          ", omitStutteringWhenDone = " + ParseAlgorithm.omitStutteringWhenDone);
+// }
+
+        } catch (ParseAlgorithmException e)
+        {
+            PcalDebug.reportError(e);
+//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+            return null ; // added for testing
+        }
+        PcalDebug.reportInfo("Parsing completed.");
+        
+// tla-pcal debugging
+//System.out.println("Translation Output:");
+//System.out.println(ast.toString());
+        /*********************************************************************
+        * For -writeAST option, just write the file AST.tla and halt.        *
+        *********************************************************************/
+        if (PcalParams.WriteASTFlag)
+        {
+            WriteAST(ast);
+//            return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR);
+            return null ; // added for testing
+        }
+        ;
+
+        /*********************************************************************
+        * Rename algorithm variables to eliminate name conflicts--for        *
+        * example, if the same variable is declared inside different         *
+        * procedures, if a variable name and a label are the same, or if     *
+        * the same label is used in to different procedures.  This should    *
+        * also report an error and terminate if it discovers a conflict      *
+        * between the variable of a `with' statement and some other          *
+        * identifier in the algorithm.  It should also detect other          *
+        * conflicts--for example, if there is a variable named "Init" or     *
+        * "TRUE".  However, there are conflicts that the translator can't    *
+        * spot--for example, if a variable name is the same as the name of   *
+        * some operator defined elsewhere in the TLA+ module.  So it's not   *
+        * worth going overboard in this checking.                            *
+        *********************************************************************/
+
+        // SZ February.15 2009: made non-static to make PCal stateless for tool runs
+        PCalTLAGenerator pcalTLAGenerator = new PCalTLAGenerator(ast);
+        try
+        {
+            pcalTLAGenerator.removeNameConflicts();
+        } catch (RemoveNameConflictsException e1)
+        {
+            PcalDebug.reportError(e1);
+//            return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+            return null ; // added for testing
+        }
+
+        /*********************************************************************
+        * Set the vector `translation' to the translation of the algorithm   *
+        * represented by the AST ast.  If called with the -spec option,      *
+        * do the translation by calling TLC. Otherwise, call the ordinary    *
+        * Translate method.                                                  *
+        *********************************************************************/
+        Vector<String> translation = null;
+
+        if (PcalParams.tlcTranslation())
+        {
+            try
+            {
+                translation = TLCTranslate(ast);
+            } catch (TLCTranslationException e)
+            {
+                PcalDebug.reportError(e);
+//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+                return null ; // added for testing
+            }
+        } else
+        {
+            try
+            {
+                translation = pcalTLAGenerator.translate();
+            } catch (RemoveNameConflictsException e)
+            {
+                PcalDebug.reportError(e);
+//                return exitWithStatus(STATUS_EXIT_WITH_ERRORS);
+                return null ; // added for testing
+            }
+        }
+
+		final Matcher m = Validator.CHECKSUM_PATTERN.matcher(output.get(mapping.tlaStartLine - 1));
+		ValidationCallBack.Generate g = null;
+		if (m.find()) {
+			// Do TLA_CHECKSUM first because doing PCAL_CHECKSUM (at the front of the
+			// string) invalidates start end enf of the TLA_CHECKSUM match.
+			if (m.group(Validator.TLA_CHECKSUM) != null) {
+				output.set(mapping.tlaStartLine - 1,
+						new StringBuilder(output.get(mapping.tlaStartLine - 1)).replace(m.start(Validator.TLA_CHECKSUM),
+								m.end(Validator.TLA_CHECKSUM), Validator.checksum(translation)).toString());
+			}
+			if (m.group(Validator.PCAL_CHECKSUM) != null) {
+				output.set(mapping.tlaStartLine - 1,
+						new StringBuilder(output.get(mapping.tlaStartLine - 1))
+						.replace(m.start(Validator.PCAL_CHECKSUM), m.end(Validator.PCAL_CHECKSUM),
+								Validator.checksum(ast.toString()))
+						.toString());
+			}
+		} else if ((g = cb.shouldGenerate()) != Generate.NOT_NOW) {
+			if (g == Generate.DO_IT) {
+				output.set(mapping.tlaStartLine - 1,
+						output.get(mapping.tlaStartLine - 1) + " " + String.format(Validator.CHECKSUM_TEMPLATE,
+								Validator.checksum(ast.toString()), Validator.checksum(translation)));
+			} else {
+				output.set(mapping.tlaStartLine - 1,
+						output.get(mapping.tlaStartLine - 1) + " " + Validator.CHECKSUM_TEMPLATE_IGNORE);
+			}
+		}
+
+        /*********************************************************************
+        * Add the translation to outputVec.                                  *
+        *********************************************************************/
+        int i = 0;
+        while (i < translation.size())
+        {
+        	output.add((i + translationLine + 1), translation.elementAt(i));
+            i = i + 1;
+        }
+
+        PcalDebug.reportInfo("Translation completed.");
+        return output;
+// tla-pcal Debugging
+//System.exit(0);
+	}
+
+	/**
+     * If run in the system mode, exits the program, in tool mode returns the status
+     * @param status
+     */
+	private static int exitWithStatus(int status)
+    {
+        if (ToolIO.getMode() == ToolIO.SYSTEM)
+        {
+            // return exit status in system mode
+            System.exit(status);
+        }
+
+        // just exit the function in tool mode
+        return status;
+    }
+
+    /********************** Writing the AST ************************************/
+    private static boolean WriteAST(AST ast)
+    {
+        Vector<String> astFile = new Vector<String>();
+        astFile.addElement("------ MODULE AST -------");
+        astFile.addElement("EXTENDS TLC");
+        astFile.addElement("fairness == \"" + PcalParams.FairnessOption + "\"");
+        astFile.addElement(" ");
+        astFile.addElement("ast == ");
+        astFile.addElement(ast.toString());
+        astFile.addElement("==========================");
+        try
+        {
+            WriteStringVectorToFile(astFile, "AST.tla");
+        } catch (StringVectorToFileException e)
+        {
+            PcalDebug.reportError(e);
+            return false;
+        }
+        PcalDebug.reportInfo("Wrote file AST.tla.");
+        return true;
+    }
+
+    /************************* THE TLC TRANSLATION *****************************/
+
+    private static Vector<String> TLCTranslate(AST ast) throws TLCTranslationException
+    /***********************************************************************
+    * The result is a translation of the algorithm represented by ast      *
+    * obtained by using TLC to execute the definition of Translation(ast)  *
+    * in the TLA+ module PlusCal.  It equals a vector with a single        *
+    * element, which is the entire translation as a single string.         *
+    *                                                                      *
+    * This method relies on a bug in TLC's pretty-print routine that       *
+    * causes it not to work properly on the output produced by the TLA     *
+    * spec.  Instead of prettyprinting the output as                       *
+    *                                                                      *
+    *   << "VARIABLES ...",                                                *
+    *      "vars == ... ",                                                 *
+    *      ...                                                             *
+    *   >>                                                                 *
+    *                                                                      *
+    * it prints the entire translation on a single line as                 *
+    *                                                                      *
+    *   << "VARIABLES ...", "vars == ... ", ... >>                         *
+    *                                                                      *
+    * This allows the method to find the entire translation as the single  *
+    * line that begins with "<<".  If this TLC bug is fixed, then the      *
+    * TLCTranslate method will have to be modified to read the spec as a   *
+    * sequence of lines.  This will probably require the TLA module that   *
+    * translates the spec to print a special marker line to indicate the   *
+    * end of the translation.                                              *
+    ***********************************************************************/
+    {
+        /*********************************************************************
+        * Write the file AST.tla that contains                               *
+        *********************************************************************/
+        WriteAST(ast);
+
+        /*********************************************************************
+        * For the -spec (rather than -myspec) option, copy the               *
+        * specification's .tla and .cfg files PlusCal.tla to current         *
+        * directory.                                                         *
+        *********************************************************************/
+        if (PcalParams.SpecOption || PcalParams.Spec2Option)
+        {
+            try
+            {
+				Vector<String> parseFile = PcalResourceFileReader
+						.ResourceFileToStringVector(PcalParams.SpecFile + TLAConstants.Files.TLA_EXTENSION);
+
+				WriteStringVectorToFile(parseFile, PcalParams.SpecFile + TLAConstants.Files.TLA_EXTENSION);
+				parseFile = PcalResourceFileReader
+						.ResourceFileToStringVector(PcalParams.SpecFile + TLAConstants.Files.CONFIG_EXTENSION);
+				WriteStringVectorToFile(parseFile, PcalParams.SpecFile + TLAConstants.Files.CONFIG_EXTENSION);
+
+				PcalDebug.reportInfo("Wrote files " + PcalParams.SpecFile + TLAConstants.Files.TLA_EXTENSION + " and "
+						+ PcalParams.SpecFile + TLAConstants.Files.CONFIG_EXTENSION + ".");
+            } catch (UnrecoverableException e)
+            {
+                throw new TLCTranslationException(e.getMessage());
+            }
+
+        }
+        ;
+        /*********************************************************************
+        * Run TLC on the specification file and set tlcOut to TLC's output.  *
+        *********************************************************************/
+        String javaInvocation;
+        if (PcalParams.SpecOption || PcalParams.MyspecOption)
+        {
+            // Modified on 29 May 2010 by LL so tlc2 is run in
+            // all cases.
+            PcalDebug.reportInfo("Running TLC2.");
+            javaInvocation = "java -Xss1m tlc2.TLC ";
+        } else
+        {
+            PcalDebug.reportInfo("Running TLC2.");
+            javaInvocation = "java -Xss1m tlc2.TLC ";
+        }
+        ;
+        String tlcOut = "      ";
+        Runtime rt = Runtime.getRuntime();
+        try
+        {
+            // Modified on 29 May 2010 by LL to replace getErrorStream() with 
+            // getInputStream(), which by Java logic gets standard out.  (And no,
+            // getErrorStream() did not get standard non-error.)  Apparently,
+            // TLC has been changed to put its output on stdout.
+            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(rt.exec(
+                    javaInvocation + PcalParams.SpecFile).getInputStream()));
+            while (tlcOut.indexOf("<<") == -1)
+            {
+                tlcOut = bufferedReader.readLine();
+            }
+            ;
+            bufferedReader.close();
+        } catch (Exception e)
+        {
+            throw new TLCTranslationException("Error reading output of TLC");
+        }
+        ;
+
+        /*********************************************************************
+        * Test if the translation failed and reported an error message,      *
+        * bracketed by "@Error@" and "@EndError@" strings.  If it did,       *
+        * report the error and halt.  If not, set tlcOut to the value of     *
+        * Translation(ast) with the outermost "<<" and ">>" removed.         *
+        *********************************************************************/
+        if (tlcOut.indexOf("@Error@") != -1)
+        {
+            throw new TLCTranslationException("TLC's translation of the parsed algorithm failed with\n  Error: "
+                    + tlcOut.substring(tlcOut.indexOf("@Error@") + 7, tlcOut.indexOf("@EndError@")));
+        }
+        ;
+        tlcOut = tlcOut.substring(2, tlcOut.lastIndexOf(">>")) + "  ";
+        PcalDebug.reportInfo("Read TLC output.");
+
+        /*********************************************************************
+        * Set transl to the string obtained by converting tlcOut, which is   *
+        * a comma-separated sequence of strings, to the single string that   *
+        * they represent.  See PlusCal.tla for an explanation of the         *
+        * encoding of TLA+ statements as sequences of strings.               *
+        *********************************************************************/
+        int i = 0;
+        String transl = "";
+        while (i < tlcOut.length())
+        {
+            if (tlcOut.charAt(i) == '"')
+            {
+                i = i + 1;
+                if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '"'))
+                /*******************************************************
+                * This is a quoted string.                             *
+                *******************************************************/
+                {
+                    if (tlcOut.charAt(i + 2) != '"')
+                    {
+                        throw new TLCTranslationException("I'm confused");
+
+                    }
+                    ;
+                    i = i + 3;
+                    while (tlcOut.charAt(i) != '"')
+                    {
+                        i = i + 1;
+                    }
+                    i = i + 1;
+                    transl = transl + "\"";
+                    while (tlcOut.charAt(i) != '"') // "
+                    {
+                        if (tlcOut.charAt(i) == '\\')
+                        {
+                            /***********************************************
+                            * Get special character.                       *
+                            ***********************************************/
+                            transl = transl + tlcOut.substring(i, i + 2);
+                            i = i + 2;
+                        } else
+                        {
+                            transl = transl + tlcOut.substring(i, i + 1);
+                            i = i + 1;
+                        }
+                        ;
+                    }
+                    ;
+                    i = i + 8;
+                    transl = transl + "\"";
+                } else
+                {
+                    while (tlcOut.charAt(i) != '"')
+                    {
+                        if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '\\'))
+                        {
+                            i = i + 1;
+                        }
+                        ;
+                        transl = transl + tlcOut.substring(i, i + 1);
+                        i = i + 1;
+                    }
+                    ;
+                    i = i + 1;
+                }
+                ;
+            } // END if (tlcOut.charAt(i) == '"')
+            else if (tlcOut.charAt(i) == ',')
+            {
+                i = i + 1;
+            } else
+            {
+                if (tlcOut.charAt(i) != ' ')
+                {
+                    throw new TLCTranslationException("Expected space but found `" + tlcOut.charAt(i) + "'");
+                }
+                ;
+                transl = transl + tlcOut.substring(i, i + 1);
+                i = i + 1;
+            }
+            ;
+        }
+        ; // END while
+        /* ******************************************************************
+         * Wrap the translated string into approximately 80 character lines *
+         *******************************************************************/
+        transl = WrapString(transl, 78);
+        Vector<String> result = new Vector<String>();
+        result.addElement(transl);
+        return result;
+    }
+
+    /***************** METHODS FOR READING AND WRITING FILES *****************/
+
+    private static void WriteStringVectorToFile(final List<String> inputVec, String fileName) throws StringVectorToFileException
+    /***********************************************************************
+    * Writes the List of strings inputVec to file named fileName, with     *
+    * each element of inputVec written on a new line.                      *
+    ***********************************************************************/
+    {
+    	// TODO use Apache Commons for this
+        try (final BufferedWriter fileW = new BufferedWriter(new FileWriter(fileName))) {
+            // I have no idea what Java does if you try to write a new version
+            // of a read-only file. On Windows, it's happy to write it. Who
+            // the hell knows what it does on other operating systems? So, something
+            // like the following code could be necessary. However, the setWritable()
+            // method was introduced in Java 1.6, and in December 2009, that version
+            // isn't available on the Mac. And I can't find out how to set a file
+            // to be writable in any earlier version of Java. On the web, the advice
+            // is to copy the file, delete the old version, and rename the copy.
+            // But the File method's documentation actually says that delete may or
+            // may not delete the read-only file, depending on the OS.
+            //
+            // File file = new File(fileName);
+            // if (! file.canWrite()) {
+            // file.setWritable(true);
+            // }
+        	for (final String line : inputVec) {
+                fileW.write(line);
+                fileW.newLine();
+            }
+        } catch (Exception e) {
+            throw new StringVectorToFileException("Could not write file " + fileName);
+        }
+    }
+
+    private static List<String> fileToStringVector(String fileName) throws FileToStringVectorException
+    /***********************************************************************
+    * Reads file fileName into a StringVector, a vector in which each      *
+    * element is a line of the file.                                       *
+    ***********************************************************************/
+    {
+        final List<String> inputVec = new ArrayList<>(100);
+    	// TODO use Apache Commons for this
+        try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)))) {
+            String nextLine = bufferedReader.readLine();
+            while (nextLine != null) {
+                inputVec.add(nextLine);
+                nextLine = bufferedReader.readLine();
+            }
+        } catch (FileNotFoundException e) {
+            /**************************************************************
+            * Input file could not be found.                              *
+            **************************************************************/
+            throw new FileToStringVectorException("Input file " + fileName + " not found.");
+        } catch (IOException e) {
+            /*********************************************************
+            * Error while reading input file.                        *
+            *********************************************************/
+            throw new FileToStringVectorException("Error reading file " + fileName + ".");
+        }
+        
+        return inputVec;
+    }
+
+    /********************* PROCESSING THE COMMAND LINE ***********************/
+
+    /**
+     * Processes the command line arguments
+     * 
+     * This method changes values of public static variables of the {@link PcalParams} 
+     * 
+     * SZ: This will cause problems, due to the fact that the PCalParams are static.
+     * Any initialization should overwrite the previous, which is currently NOT the case
+     * Should be re-factored to non-static access to the properties
+     * 
+     * @return status of processing. 
+     *  the status {@link trans#STATUS_OK} indicates that no errors has been detected.
+     *  the status {@link trans#STATUS_EXIT_WITHOUT_ERROR} indicates that no errors has been found but translation
+     *   should not be started (e.G -help call)
+     *  the status {@link trans#STATUS_EXIT_WITH_ERRORS} indicates errors 
+     *  
+     * Change made on 9 December 2009 for pcal-file input.  This procedure is
+     * called a second time if there is pcal-file input with an options statement.
+     * It will be the second call iff {@link PcalParams#optionsInFile} equals true.
+     * The second call should have a dummy extra argument in place of the 
+     * command-line's file-name argument.   When pcal files were eliminated, this
+     * kludgy mechanism was kept and used to indicate if the method is being called
+     * for options specified inside the module. 
+     */
+    static int parseAndProcessArguments(String[] args)
+    {
+
+        /** *******************************************************************
+         *<pre>
+         * Get the command-line arguments and set the appropriate parameters.  *
+         * The following command line arguments are handled.  Only the ones    *
+         * marked with ** besides them can be specified in the module file's   *
+         * options statement.  The "-" can be omitted when the option is in    *
+         * the module file's options statement.                                *
+         *                                                                     *
+         *   -help  : Type a help file instead of running the program.         *
+         *                                                                     *
+         *** -spec name : Uses TLC and the TLA+ specification name.tla to do   *
+         *                the translation.  The files name.tla and name.cfg    *
+         *                are copied from the java/ directory to the current   *
+         *                directory; the file AST.tla that defines `fairness'  *
+         *                to equal the fairness option and `ast' to equal      *
+         *                the the AST data structure representing the          *
+         *                algorithm is written to the current directory; and   *
+         *                TLC is run on name.tla to produce the translation.   *
+         *                                                                     *
+         *** -myspec name : Like -spec, except it finds the files names.tla    *
+         *                  and names.cfg in the current directory, instead    *
+         *                  of writing them there.                             *
+         *                                                                     *
+         *   -spec2   name                                                     *
+         *   -myspec2 name : Like -spec and -myspec, except they use TLC2      *
+         *                   instead of TLC (aka TLC1).                        *
+         *                                                                     *
+         *   -writeAST : Writes the AST file as in the -spec option, but does  *
+         *               not perform the translation.                          *
+         *                                                                     *
+         *   -debug : Run in debugging mode, whatever that means.  For the     *
+         *            parser, it just means that the toString() methods        *
+         *            output the line and column number along with the         *
+         *            node.                                                    *
+         *                                                                     *
+         *   -unixEOL : Forces the use of Unix end-of-line convention,         *
+         *              regardless of the system's default.  Without this,     *
+         *              when run on Windows, the files it writes seem to have  *
+         *              a "^M" at the end of every line when viewed with       *
+         *              Emacs.                                                 *
+         *                                                                     *
+         *** -wf : Conjoin to formula Spec weak fairness of each process's     *
+         *         next-state action                                           *
+         *                                                                     *
+         *** -sf : Conjoin to formula Spec strong fairness of each process's   *
+         *         next-state action                                           *
+         *                                                                     *
+         *** -wfNext : Conjoin to formula Spec weak fairness of the entire     *
+         *             next-state action                                       *
+         *                                                                     *
+         *** -nof : Conjoin no fairness formula to Spec.  This is the default, *
+         *          except when the -termination option is chosen.             *
+         *                                                                     *
+         *** -termination : Add to the .cfg file the command                   *
+         *                     PROPERTY Termination                            *
+         *                                                                     *
+         *   -nocfg : Suppress writing of the .cfg file.                       *
+         *                                                                     *
+         *                                                                     *
+         *** -noDoneDisjunct : Suppress the disjunct of the next-state         *
+         *                     relation that describes stuttering steps taken  *
+         *                     when the algorithm has halted.                  *
+         *                                                                     *
+         *** -label : Tells the translator to add missing labels.  This is     *
+         *            the default only for a uniprocess algorithm in which     *
+         *            the user has typed no labels.                            *
+         *                                                                     *
+         *   -reportLabels : True if the translator should print the names     *
+         *                   and locations of all labels it adds.  Like        *
+         *                   -label, it tells the translator to add missing    *
+         *                   labels.                                           *
+         *                                                                     *
+         *** -labelRoot name : If the translator adds missing labels, it       *
+         *                     names them name1, name2, etc.  Default value    *
+         *                     is "Lbl_".                                      *
+         *                                                                     *
+         *  THE FOLLOWING OPTIONS ADDED IN VERSION 1.4                         *
+         *                                                                     *
+         *** -lineWidth : The translation tries to perform the translation so  *
+         *                lines have this maximum width.  (It will often       *
+         *                fail.)  Default is 78, minimum value is 60.          *
+         *                                                                     *
+         *** -version : The version of PlusCal for which this algorithm is     *
+         *              written.  If the language is ever changed to make      *
+         *              algorithms written for earlier versions no longer      *
+         *              legal, then the translator should do the appropriate   *
+         *              thing when the earlier version number is specified.    *                
+         *</pre>
+         ********************************************************************* */
+        boolean inFile = PcalParams.optionsInFile;
+        boolean notInFile = !inFile;
+        // Just convenient abbreviations
+        boolean firstFairness = inFile;
+        // Used to allow a fairness property specified by a command-line
+        // option to be overridden by one in the pcal-file's options statement.
+        // It is set false when the first fairness property is set from
+        // the options statement.
+        boolean explicitNof = false;
+        // Set true when the "nof" fairness option is set by an explicit
+        // user request, rather than by default. It was added to fix
+        // a bug in -termination introduced in version 1.4 by having
+        // the options statement in the file. I think option processing
+        // can be simplified to eliminate this, but it's easier to add
+        // this kludge.
+        int nextArg = 0;
+        /******************************************************************
+        * The number of the argument being processed.                     *
+        ******************************************************************/
+        int maxArg = args.length - 1;
+        /******************************************************************
+        * The number of option arguments.  (For processing command-line   *
+        * arguments, the last element of args is the input-file name.)    *
+        ******************************************************************/
+        if (maxArg < 0)
+        {
+            return CommandLineError("No arguments specified");
+        }
+
+        if (notInFile && (args[maxArg].length() != 0) && (args[maxArg].charAt(0) == '-'))
+        /******************************************************************
+        * If the last argument begins with "-", then no file has been     *
+        * specified.  This should mean that the user has typed "-help",   *
+        * but it could be a mistake.  But let's just assume she typed     *
+        * "-help", since she either wants or needs help.                  *
+        ******************************************************************/
+        {
+            if (OutputHelpMessage())
+            {
+                return STATUS_EXIT_WITHOUT_ERROR;
+
+            } else
+            {
+                return STATUS_EXIT_WITH_ERRORS;
+            }
+        }
+
+        while (nextArg < maxArg)
+        /*******************************************************************
+        * Process all the arguments, except for the input-file name.       *
+        *******************************************************************/
+        {
+            String option = args[nextArg];
+            if (notInFile && option.equals("-help"))
+            {
+                if (OutputHelpMessage())
+                {
+                    return STATUS_EXIT_WITHOUT_ERROR;
+                } else
+                {
+                    return STATUS_EXIT_WITH_ERRORS;
+                }
+            } else if (notInFile && option.equals("-writeAST"))
+            {
+                PcalParams.WriteASTFlag = true;
+                if (CheckForConflictingSpecOptions())
+                {
+                    return STATUS_EXIT_WITH_ERRORS;
+                }
+            } else if (option.equals("-spec") || 
+                        (inFile && option.equals("spec")))
+            {
+                PcalParams.SpecOption = true;
+                if (CheckForConflictingSpecOptions())
+                {
+                    return STATUS_EXIT_WITH_ERRORS;
+                }
+                nextArg = nextArg + 1;
+                if (nextArg == maxArg)
+                {
+                    return CommandLineError("Specification name must follow `-spec' option");
+                }
+                PcalParams.SpecFile = args[nextArg];
+            } else if (option.equals("-myspec") || 
+                    (inFile && option.equals("myspec")))
+            {
+                PcalParams.MyspecOption = true;
+                if (CheckForConflictingSpecOptions())
+                {
+                    return STATUS_EXIT_WITH_ERRORS;
+                }
+                nextArg = nextArg + 1;
+                if (nextArg == maxArg)
+                {
+                    return CommandLineError("Specification name must follow `-myspec' option");
+                }
+                PcalParams.SpecFile = args[nextArg];
+            } else if (notInFile && option.equals("-spec2"))
+            {
+                PcalParams.Spec2Option = true;
+                if (CheckForConflictingSpecOptions())
+                {
+                    return STATUS_EXIT_WITH_ERRORS;
+                }
+                ;
+                nextArg = nextArg + 1;
+                if (nextArg == maxArg)
+                {
+                    return CommandLineError("Specification name must follow `-spec' option");
+                }
+                PcalParams.SpecFile = args[nextArg];
+            } else if (notInFile && option.equals("-myspec2"))
+            {
+                PcalParams.Myspec2Option = true;
+                if (CheckForConflictingSpecOptions())
+                {
+                    return STATUS_EXIT_WITH_ERRORS;
+                }
+                ;
+                nextArg = nextArg + 1;
+                if (nextArg == maxArg)
+                {
+                    return CommandLineError("Specification name must follow `-myspec' option");
+                }
+                PcalParams.SpecFile = args[nextArg];
+            } else if (notInFile && option.equals("-debug"))
+            {
+                PcalParams.Debug = true;
+            } else if (notInFile && option.equals("-unixEOL"))
+            {
+                System.setProperty("line.separator", "\n");
+            } else if (option.equals("-termination") || (inFile && option.equals("termination")))
+            {
+                PcalParams.CheckTermination = true;
+            } else if (option.equals("-nocfg"))
+            {
+                PcalParams.Nocfg = true;
+            } else if (option.equals("-noDoneDisjunct") || (inFile && option.equals("noDoneDisjunct")))
+            {
+                PcalParams.NoDoneDisjunct = true;
+            } else if (option.equals("-wf") || (inFile && option.equals("wf")))
+            {
+                if (firstFairness)
+                {
+                    PcalParams.FairnessOption = "";
+                    firstFairness = false;
+                }
+                if (!PcalParams.FairnessOption.equals(""))
+                {
+                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
+                }
+                PcalParams.FairnessOption = "wf";
+            } else if (option.equals("-sf") || (inFile && option.equals("sf")))
+            {
+                if (firstFairness)
+                {
+                    PcalParams.FairnessOption = "";
+                    firstFairness = false;
+                }
+                if (!PcalParams.FairnessOption.equals(""))
+                {
+                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
+                }
+                PcalParams.FairnessOption = "sf";
+            } else if (option.equals("-wfNext") || (inFile && option.equals("wfNext")))
+            {
+                if (firstFairness)
+                {
+                    PcalParams.FairnessOption = "";
+                    firstFairness = false;
+                }
+                if (!PcalParams.FairnessOption.equals(""))
+                {
+                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
+                }
+                PcalParams.FairnessOption = "wfNext";
+            } else if (option.equals("-nof") || (inFile && option.equals("nof")))
+            {
+                if (firstFairness)
+                {
+                    PcalParams.FairnessOption = "";
+                    firstFairness = false;
+                }
+                if (!PcalParams.FairnessOption.equals(""))
+                {
+                    return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options");
+                }
+                PcalParams.FairnessOption = "nof";
+                explicitNof = true;
+            } else if (option.equals("-label") || (inFile && option.equals("label")))
+            {
+                PcalParams.LabelFlag = true;
+            } else if (notInFile && option.equals("-reportLabels"))
+            {
+                PcalParams.ReportLabelsFlag = true;
+                PcalParams.LabelFlag = true;
+            } else if (option.equals("-labelRoot") || (inFile && option.equals("labelRoot")))
+            {
+                nextArg = nextArg + 1;
+                if (nextArg == maxArg)
+                {
+                    return CommandLineError("Label root must follow `-labelRoot' option");
+                }
+                PcalParams.LabelRoot = args[nextArg];
+            }
+            // else if (option.equals("-readOnly") || (pcal && option.equals("readOnly"))) {
+            // PcalParams.readOnly = true;
+            // }
+            // else if (option.equals("-writable") || (pcal && option.equals("writable"))) {
+            // PcalParams.readOnly = false;
+            // }
+            else if (option.equals("-version") || (inFile && option.equals("version")))
+            {
+                nextArg = nextArg + 1;
+                if (nextArg == maxArg)
+                {
+                    return CommandLineError("Version number must follow `-version' option");
+                }
+                if (!PcalParams.ProcessVersion(args[nextArg]))
+                {
+                    return CommandLineError("Bad version number");
+                }
+
+            } else if (option.equals("-lineWidth"))
+            {
+                nextArg = nextArg + 1;
+                try
+                {
+                    if (nextArg == maxArg)
+                    {
+                        throw new NumberFormatException();
+                    }
+                    int a = new Integer(args[nextArg]).intValue();
+                    if (a < 60)
+                    {
+                        throw new NumberFormatException();
+                    }
+                    PcalTLAGen.wrapColumn = a;
+                    PcalTLAGen.ssWrapColumn = a - 33;
+                } catch (Exception e)
+                {
+                    return CommandLineError("Integer value at least 60 must follow `-lineWidth' option");
+                }
+
+            } else
+            {
+                if (notInFile)
+                {
+                    return CommandLineError("Unknown command-line option: " + option);
+                } else
+                {
+                    return CommandLineError("Unknown or illegal option in options statement: " + option);
+                }
+            }
+            ;
+            nextArg = nextArg + 1;
+        } // END while (nextArg < maxArg)
+
+        if (nextArg > maxArg)
+        /******************************************************************
+        * The last option took an argument that was the last              *
+        * command-line argument.                                          *
+        ******************************************************************/
+        {
+            return CommandLineError("No input file specified");
+        }
+
+        // SZ 02.16.2009: since this is a modification of the parameters, moved
+        // to the parameter handling method
+        if (PcalParams.FairnessOption.equals("-nof"))
+        {
+            PcalParams.FairnessOption = "";
+        }
+        if (PcalParams.CheckTermination && PcalParams.FairnessOption.equals("")  && !explicitNof)
+        {
+            PcalParams.FairnessOption = "wf";
+
+        }
+
+        /********************************************************************
+         * If we are processing the command-line arguments, we need to get  *
+         * the input-file name.  Otherwise, we're done.                     *     
+         *******************************************************************/
+        if (inFile)
+        {
+            return STATUS_OK;
+        }
+
+        /********************************************************************
+        * Set PcalParams.TLAInputFile to the last argument, removing a      *
+        * "tla" extension if it has one.                                    *
+        ********************************************************************/
+        /*
+        int dotIndex = args[maxArg].lastIndexOf(".") ;
+        if (dotIndex == -1) 
+        { 
+          PcalParams.TLAInputFile = args[maxArg]; 
+        } 
+        else if (args[maxArg].substring(dotIndex).equals(TLAConstants.FILE_TLA_EXTENSION))
+        { 
+          PcalParams.TLAInputFile = args[maxArg].substring(0, dotIndex); 
+        }
+        else 
+        {  
+          return CommandLineError("Input file has extension other than tla"); 
+        }
+        */
+
+        // SZ 02.16.2009: check for correct file extension (ignoring case)
+        // and file existence. also handles dots in the pathname
+        File file = new File(args[maxArg]);
+        boolean hasExtension = false;
+        if (file.getName().lastIndexOf(".") == -1)
+        {
+            // no extension
+            PcalParams.TLAInputFile = file.getPath();
+        } else
+        {
+            // extension present
+            if (file.getName().toLowerCase().endsWith(TLAConstants.Files.TLA_EXTENSION))
+            {
+                hasExtension = true;
+            }
+            // Aborted version 1.31 code
+            // else if (file.getName().toLowerCase().endsWith(".pcal")){
+            // hasExtension = true;
+            // PcalParams.fromPcalFile = true;
+            // }
+            else
+            {
+                return CommandLineError("Input file has extension other than " /* pcal or */+ "tla");
+            }
+        }
+        if (hasExtension)
+        {
+            // cut the extension
+            PcalParams.TLAInputFile = file.getPath().substring(0, file.getPath().lastIndexOf("."));
+            if (!file.exists())
+            {
+                return CommandLineError("Input file " + file.getPath() + " does not exist.");
+            }
+        } else
+        {
+            // aborted version 1.31 code
+            // file = new File(PcalParams.TLAInputFile + ".pcal");
+            // if (file.exists())
+            // {
+            // PcalParams.fromPcalFile = true;
+            // } else
+            // {
+            file = new File(PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION);
+            if (!file.exists())
+            {
+                return CommandLineError("Input file " + PcalParams.TLAInputFile + ".pcal and " + file.getPath()
+                        + ".tla not found");
+            }
+            // }
+        }
+        // file = new File(PcalParams.TLAInputFile + (PcalParams.fromPcalFile?".pcal":TLAConstants.FILE_TLA_EXTENSION));
+        // if (!file.exists())
+        // {
+        // return CommandLineError("Input file " + file.getPath() + " not found");
+        // }
+
+        return STATUS_OK;
+    }
+
+    /**
+     * Prints out the help message
+     * @return status if it has been successfully printed
+     */
+    private static boolean OutputHelpMessage()
+    {
+        Vector<String> helpVec = null;
+        try
+        {
+            helpVec = PcalResourceFileReader.ResourceFileToStringVector("help.txt");
+        } catch (PcalResourceFileReaderException e)
+        {
+            PcalDebug.reportError(e);
+            return false;
+        }
+        int i = 0;
+        while (i < helpVec.size())
+        {
+            ToolIO.out.println((String) helpVec.elementAt(i));
+            i = i + 1;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns if the options are conflicting
+     * @return true if the provided options are conflicting, false otherwise
+     */
+    private static boolean CheckForConflictingSpecOptions()
+    {
+        if ((PcalParams.SpecOption ? 1 : 0) + (PcalParams.MyspecOption ? 1 : 0) + (PcalParams.Spec2Option ? 1 : 0)
+                + (PcalParams.Myspec2Option ? 1 : 0) + (PcalParams.WriteASTFlag ? 1 : 0) > 1)
+        {
+            CommandLineError("\nCan have at most one of the options " + "-spec, -myspec, -spec2, -myspec2, writeAST");
+            return true;
+        }
+        ;
+        return false;
+    }
+
+    private static int CommandLineError(String msg)
+    /*********************************************************************
+    * Announce a command line error with the string indicating the       *
+    * explanation and halt.                                              *
+    *********************************************************************/
+    {
+          PcalDebug.reportError("Command-line error: " + msg + ".");
+//        ToolIO.out.println("Command-line error: " + msg + ".");
+//        ToolIO.out.println("Use -help option for more information.");
+        return STATUS_EXIT_WITH_ERRORS;
+    }
+
+    static int findTokenPair(Vector<String> vec, int lineNum, String tok1, String tok2)
+    /*********************************************************************
+    * Returns the number of the first line at or after lineNum in the    *
+    * vector of strings vec containing tok1 followed by 1 or more        *
+    * spaces followed by tok2.  Returns -1 if such a line is not found.  *
+    *********************************************************************/
+    {
+        int i = lineNum;
+        while (i < vec.size())
+        {
+            String line = vec.elementAt(i);
+            int col = line.indexOf(tok1);
+            int nextcol = col + tok1.length();
+            if (col != -1)
+            {
+                while ((nextcol < line.length()) && (line.charAt(nextcol) == ' '))
+                {
+                    nextcol = nextcol + 1;
+                }
+                ;
+                if ((nextcol < line.length()) && (nextcol == line.indexOf(tok2)))
+                {
+                    return i;
+                }
+            }
+            ;
+            i = i + 1;
+        }
+        ;
+        return -1;
+    }
+
+    /**************************  RemoveTabs  *********************************/
+
+    public static Vector<String> removeTabs(List<String> input) {
+        /********************************************************************
+        * Returns a string vector obtained from the string vector vec by   *
+        * replacing any evil tabs with the appropriate number of spaces,   *
+        * where "appropriate" means adding from 1 to 8 spaces in order to  *
+        * make the next character fall on a column with Java column        *
+        * number (counting from 0) congruent to 0 mod 8.  This is what     *
+        * Emacs does when told to remove tabs, which makes it good enough  *
+        * for me.                                                          *
+         ********************************************************************/
+        final Vector<String> newVec = new Vector<>();
+        for (final String oldLine : input) {
+            String newLine = "";
+            int next = 0;
+            while (next < oldLine.length()) {
+                if (oldLine.charAt(next) == '\t') {
+                    int toAdd = 8 - (newLine.length() % 8);
+                    while (toAdd > 0) {
+                        newLine = newLine + " ";
+                        toAdd = toAdd - 1;
+                    }
+                } else {
+                    newLine = newLine + oldLine.substring(next, next + 1);
+                }
+                next = next + 1;
+            }
+            // The following line is a hack to eliminate a rare bug that caused 
+            // the translation to loop forever if a line ended with a symbol
+            // that is the prefix of a legal BUILT_IN token and that is not
+            // a legal token but has a prefix that is a legal token--for
+            // example "(+" and "::" (since ::= is a legal operator).
+            // It was added by LL on 13 May 2020            
+            newLine = newLine + " ";
+            
+            newVec.add(newLine);
+        }
+
+        return newVec;
+    }
+
+    /********************* STRING UTILITY FUNCTIONS ***********************/
+
+    private static int NextSpace(String s, int cur)
+    /********************************************************************
+    * Returns the first space in s at or after col. If there is none,   *
+    * return the index of the last character in s. Spaces in strings    *
+    * are not treated as spaces. Assumes s[cur] is not in a string.     *
+    ********************************************************************/
+    {
+        int i = cur;
+        boolean inString = false;
+        while ((i < s.length()) && ((s.charAt(i) != ' ') || inString))
+        {
+            if ((s.charAt(i) == '"') && ((i == 0) || (s.charAt(i - 1) != '\\')))
+                inString = !inString;
+            i = i + 1;
+        }
+        if (i == s.length())
+            return i - 1;
+        else
+            return i;
+    }
+
+    private static String WrapString(String inString, int col)
+    /*********************************************************************
+    * Returns the string inString with lines wrapped approximately at    *
+    * col, taking care not to wrap in a string.                          *
+    *********************************************************************/
+    {
+        int i = 0;
+        int ccol = 1;
+        StringBuffer sb = new StringBuffer();
+        while (i < inString.length())
+        {
+            if (inString.charAt(i) == ' ') // An initial space or a space
+            {
+                sb.append(' '); // that follows a space. It
+                i = i + 1; // can always be appended to a line.
+                ccol = ccol + 1;
+            } else
+            // Find next word, which starts at i.
+            {
+                int j = NextSpace(inString, i);
+                if (ccol + (j - i + 1) > col)
+                {
+                    sb.append('\n');
+                    ccol = 1;
+                }
+                while (i <= j) // If this overflows col, then the word
+                {
+                    sb.append(inString.charAt(i));
+                    // is longer than col.
+                    i = i + 1;
+                    ccol = ccol + 1;
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/tlatools/src/tla2sany/StandardModules/Bags.tla b/tlatools/src/tla2sany/StandardModules/Bags.tla
index 7be7a4420403141009471a0c2c3804cf12a28615..265045e3a0842f467419ca02b51cecba5e1aac85 100644
--- a/tlatools/src/tla2sany/StandardModules/Bags.tla
+++ b/tlatools/src/tla2sany/StandardModules/Bags.tla
@@ -8,6 +8,13 @@
 (* subset of the positive integers.  An element e belongs to bag B iff e  *)
 (* is in the domain of B, in which case bag B contains B[e] copies of e.  *)
 (**************************************************************************)
+(**************************************************************************)
+(* All these definitions other than SubBag are overridden by TLC in the   *)
+(* Java class tlc2.module.Bags. Each operator is overridden by the Java   *)
+(* method with the same name, except that the mapping for TLA+ infix      *)
+(* operators is defined in the static block at the beginning of the Java  *)
+(* class.                                                                 *)
+(**************************************************************************)
 EXTENDS TLC
 LOCAL INSTANCE Naturals
 
@@ -22,7 +29,7 @@ BagToSet(B) == DOMAIN B
   (* The set of elements at least one copy of which is in B.              *)
   (************************************************************************)
 
-SetToBag(S) == [e \in S |-> 1]  
+SetToBag(S) == [e \in S |-> 1]
   (************************************************************************)
   (* The bag that contains one copy of every element of the set S.        *)
   (************************************************************************)
@@ -42,7 +49,7 @@ B1 (+) B2  ==
       (IF e \in DOMAIN B1 THEN B1[e] ELSE 0) 
     + (IF e \in DOMAIN B2 THEN B2[e] ELSE 0) ]
   
-B1 (-) B2  == 
+B1 (-) B2  ==
   (************************************************************************)
   (* The bag B1 with the elements of B2 removed--that is, with one copy   *)
   (* of an element removed from B1 for each copy of the same element in   *)
diff --git a/tlatools/src/tla2sany/StandardModules/FiniteSets.tla b/tlatools/src/tla2sany/StandardModules/FiniteSets.tla
index 57ac40235075b64119e4ab2ddf92abeab9120a5a..601ab16beeab8c7fea1f137f8b626771ee262753 100644
--- a/tlatools/src/tla2sany/StandardModules/FiniteSets.tla
+++ b/tlatools/src/tla2sany/StandardModules/FiniteSets.tla
@@ -1,4 +1,9 @@
 ---------------------------- MODULE FiniteSets -----------------------------
+(***************************************************************************)
+(* The two definitions in this standard module are overridden by TLC in    *)
+(* the Java class tlc2.module.FiniteSets.  Each operator is overridden by  *)
+(* the Java method with the same name.                                     *)
+(***************************************************************************)
 LOCAL INSTANCE Naturals
 LOCAL INSTANCE Sequences
   (*************************************************************************)
diff --git a/tlatools/src/tla2sany/StandardModules/Integers.tla b/tlatools/src/tla2sany/StandardModules/Integers.tla
index 2df47a87a219821e9bbd243defdc826992636431..2dd1cc9daa3b2ca327ac098604793293eef4e070 100644
--- a/tlatools/src/tla2sany/StandardModules/Integers.tla
+++ b/tlatools/src/tla2sany/StandardModules/Integers.tla
@@ -1,10 +1,23 @@
 -------------------------------- MODULE Integers ----------------------------
 (***************************************************************************)
-(* A dummy module that declares the operators that are defined in the      *)
-(* real Integers module.                                                   *)
+(* This module provides dummy definitions of the operators that are        *)
+(* defined by the real Integers module.  It is expected that any tool will *)
+(* provide its own implementations of these operators.  See the book       *)
+(* "Specifying Systems" for the real Integers module.                      *)
+(***************************************************************************)
+(***************************************************************************)
+(* The two definitions here and the definitions imported from the Naturals *)
+(* module are overridden by TLC in the Java class tlc2.module.Integers.    *)
+(* Each operator is overridden by the Java method with the same name,      *)
+(* except that the mappings for the prefix - operator and the TLA+ infix   *)
+(* operators are defined in the static block at the beginning of the Java  *)
+(* class.                                                                  *)
 (***************************************************************************)
 EXTENDS Naturals
 
 Int  ==  { }
+(***************************************************************************)
+(* This defines the prefix - operator.                                     *)
+(***************************************************************************)
 -. a == 0 - a
 =============================================================================
diff --git a/tlatools/src/tla2sany/StandardModules/Naturals.tla b/tlatools/src/tla2sany/StandardModules/Naturals.tla
index 6dc13fea6ae556e21f74d4f0322d0f7751c6a448..4c4de1defdba8624d2f296f839d810fe3faab92a 100644
--- a/tlatools/src/tla2sany/StandardModules/Naturals.tla
+++ b/tlatools/src/tla2sany/StandardModules/Naturals.tla
@@ -1,18 +1,35 @@
 -------------------------------- MODULE Naturals ----------------------------
 (***************************************************************************)
-(* A dummy module that defines the operators that are defined by the       *)
-(* real Naturals module.                                                   *)
+(* This module provides dummy definitions of the operators that are        *)
+(* defined by the real Naturals module.  It is expected that any tool will *)
+(* provide its own implementations of these operators.  See the book       *)
+(* "Specifying Systems" for the real Naturals module.                      *)
+(***************************************************************************)
+(***************************************************************************)
+(* These definitions are all overridden by TLC in the Java class           *)
+(* tlc2.module.Naturals. Each operator is overridden by the Java method    *)
+(* with the same name, except that the mapping for TLA+ infix operators    *)
+(* is defined in the static block at the beginning of the Java class.      *)
 (***************************************************************************)
-
 Nat       == { }
 a+b       == {a, b}
-a-b       == {a, b}
-a*b       == {a, b}
+
+a-b       == CHOOSE n : b + n = a
+a*b       == TRUE
 a^b       == {a, b}
 a<b       ==  a = b
 a>b       ==  a = b
 a \leq b  ==  a = b
 a \geq b  ==  a = b
+(***************************************************************************)
+(* a .. b  is defined to equal  {i \in Int : (a \leq i) /\ (i \leq b)}     *)
+(* where  Int  is the set of all integers.                                 *)
+(*                                                                         *)
+(* a % b  and  a \div b  are defined so that for any integers  a  and  b   *)
+(* with  b > 0 , the following formula is true:                            *)
+(*                                                                         *)
+(*    a  =  b * (a \div b) + (a % b)                                       *)
+(***************************************************************************)
 a % b     ==  {a, b}
 a \div b  ==  {a, b}
 a .. b    ==  {a, b}
diff --git a/tlatools/src/tla2sany/StandardModules/Randomization.tla b/tlatools/src/tla2sany/StandardModules/Randomization.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a1266fee4f6067a0b8c857f3dd2a31298f3bec71
--- /dev/null
+++ b/tlatools/src/tla2sany/StandardModules/Randomization.tla
@@ -0,0 +1,64 @@
+-------------------------- MODULE Randomization------------------------------
+(***************************************************************************)
+(* This module defines operators for choosing pseudo-random subsets of a   *)
+(* set.  It is useful for inductive invariance checking, where the         *)
+(* operators appear only in the initial predicate.  However, it may have   *)
+(* other uses.                                                             *)
+(*                                                                         *)
+(* In breadth-first search model checking, the pseudo-random choices made  *)
+(* when computing possible steps satisfying the next-state relation are    *)
+(* determined by the first state of the step.  Thus, the choices made for  *)
+(* a particular state will be the same in successive runs of TLC.  This is *)
+(* done to permit TLC to generate an error trace if an error is found.     *)
+(* This applies only when TLC is run in breadth-first search mode.  In     *)
+(* particular, the choices made in simulation mode are independent of the  *)
+(* state for which they are made.                                          *)
+(***************************************************************************)
+
+(***************************************************************************)
+(* Except for TestRandomSetOfSubsets, all these definitions are overridden *)
+(* by TLC in the Java class tlc2.module.Randomization.  Each operator is   *)
+(* overridden by the Java method with the same name.                       *)
+(***************************************************************************)
+LOCAL INSTANCE Naturals
+LOCAL INSTANCE FiniteSets
+
+(***************************************************************************)
+(* RandomSubset(k, S) equals a randomly chosen subset of S containing k    *)
+(* elements, where 0 < k < Cardinality(S).                                 *)
+(***************************************************************************)
+RandomSubset(k, S) == CHOOSE T \in SUBSET S : Cardinality(T) = T
+
+(***************************************************************************)
+(* RandomSetOfSubsets(k, n, S) equals a pseudo-randomly chosen set of      *)
+(* subsets of S -- that is, a randomly chosen subset of SUBSET S .  Thus,  *)
+(* each element T of this set is a subset of S.  Each such T is chosen so  *)
+(* that each element of S has a probability n / Cardinality(S) of being in *)
+(* T.  Thus, the average number of elements in each chosen subset T is n.  *)
+(* The set RandomSetOfSubsets(k, n, S) is obtained by making k such        *)
+(* choices of T .  Because this can produce duplicate choices, the number  *)
+(* of elements T in this set may be less than k.  The average number of    *)
+(* elements in RandomSetOfSubsets(k, n, S) seems to be difficult to        *)
+(* compute in general.  However, there is very little variation in the     *)
+(* actual number of elements in the chosen set for fixed values of k, n,   *)
+(* and Cardinality(S).  You can therefore use the operator                 *)
+(* TestRandomSetOfSubsets defined below to find out approximately how      *)
+(* close to k the cardinality of the chosen set of subsets is.             *)
+(***************************************************************************)
+RandomSetOfSubsets(k, n, S) == CHOOSE T \in SUBSET SUBSET S :
+											  Cardinality(T) \leq k
+
+(***************************************************************************)
+(* The value of TestRandomSetOfSubsets(k, n, S) is a sequence of five      *)
+(* values that are the cardinality of the set of subsets produced by five  *)
+(* executions of RandomSetOfSubsets(k, n, S).  For constant values of k,   *)
+(* n, and S, you can enter TestRandomSetOfSubsets(k, n, S) in the Evaluate *)
+(* Constant Expression section of a TLC model in the TLA+ Toolbox.         *)
+(* Running TLC will then tell you the approximate number of elements in    *)
+(* the set of subsets produced by RandomSetOfSubsets for these parameters. *)
+(* You can then choose k to obtain a set of the desired size.              *)
+(***************************************************************************)
+TestRandomSetOfSubsets(k, n, S) ==
+              [i \in 1..5 |-> Cardinality(RandomSetOfSubsets(k, n, S))]
+
+=============================================================================
diff --git a/tlatools/src/tla2sany/StandardModules/RealTime.tla b/tlatools/src/tla2sany/StandardModules/RealTime.tla
index 1026c66a4b853a896a3b6f3374b02292f0562753..c326bfa0a0075428864f425f6c741fc13ac11810 100644
--- a/tlatools/src/tla2sany/StandardModules/RealTime.tla
+++ b/tlatools/src/tla2sany/StandardModules/RealTime.tla
@@ -1,4 +1,9 @@
 ----------------------------- MODULE RealTime -------------------------------
+(***************************************************************************)
+(* This standard module is described in Chapter 9 of the book "Specifying  *)
+(* Systems".  The type of specification described there cannot be handled  *)
+(* by the TLC model checker.                                               *)
+(***************************************************************************)
 EXTENDS Reals 
 VARIABLE now
 
diff --git a/tlatools/src/tla2sany/StandardModules/Reals.tla b/tlatools/src/tla2sany/StandardModules/Reals.tla
index f7aa44677fcb75da6c0717f71188d65886a8fa84..c63cd93dbea6a35ca6152cf24aba396fc2c90fe3 100644
--- a/tlatools/src/tla2sany/StandardModules/Reals.tla
+++ b/tlatools/src/tla2sany/StandardModules/Reals.tla
@@ -1,8 +1,14 @@
 -------------------------------- MODULE Reals -------------------------------
 (***************************************************************************)
-(* A dummy module that declares the operators that are defined in the      *)
-(* real Reals module.  It should produce an error if TLC tries to          *)
-(* evaluate these operators when it shouldn't.                             *)
+(* This module provides dummy definitions of the operators that are        *)
+(* defined by the real Reals module.  It is expected that any tool that    *)
+(* handles specifications that use real numbers will provide its own       *)
+(* implementations of these operators.  TLC currently does not handle real *)
+(* numbers and produces an error if a specification requires TLC to        *)
+(* evaluate the operators defined here or to evaluate any operator defined *)
+(* in the Integers module that is applied to non-integer real numbers.     *)
+(*                                                                         *)
+(* See the book "Specifying Systems" for the real Reals module.            *)
 (***************************************************************************)
 EXTENDS Integers
 
diff --git a/tlatools/src/tla2sany/StandardModules/Sequences.tla b/tlatools/src/tla2sany/StandardModules/Sequences.tla
index df77a97facdf6d62ce44aa6d4dd304894742fc1d..d179cf8a59913d24c0c56dbe61493fd892a11a1d 100644
--- a/tlatools/src/tla2sany/StandardModules/Sequences.tla
+++ b/tlatools/src/tla2sany/StandardModules/Sequences.tla
@@ -5,10 +5,16 @@
 (* {1, 2, ... , n}).  This is also how TLA+ defines an n-tuple, so         *)
 (* tuples are sequences.                                                   *)
 (***************************************************************************)
+(***************************************************************************)
+(* These definitions are all overridden by TLC in the Java class           *)
+(* tlc2.module.Sequences. Each operator is overridden by the Java method   *)
+(* with the same name, except that the mapping for TLA+ infix operators    *)
+(* is defined in the static block at the beginning of the Java class.      *)
+(***************************************************************************)
 
 LOCAL INSTANCE Naturals
   (*************************************************************************)
-  (* Imports the definitions from Naturals, but don't export them.         *)
+  (* Imports the definitions from Naturals, but doesn't export them.       *)
   (*************************************************************************)
   
 Seq(S) == UNION {[1..n -> S] : n \in Nat}
@@ -51,7 +57,7 @@ SelectSeq(s, Test(_)) ==
   (*************************************************************************)
   LET F[i \in 0..Len(s)] == 
         (*******************************************************************)
-        (* F[i] equals SelectSeq(SubSeq(s, 1, i), Test]                    *)
+        (* F[i] equals SelectSeq(SubSeq(s, 1, i), Test) .                  *)
         (*******************************************************************)
         IF i = 0 THEN << >>
                  ELSE IF Test(s[i]) THEN Append(F[i-1], s[i])
diff --git a/tlatools/src/tla2sany/StandardModules/TLC.tla b/tlatools/src/tla2sany/StandardModules/TLC.tla
index a9567ac5272fc5fcd620e58e13a9f1652510b3bd..0384c039761a1a517955481c153b2f5ea7f301bf 100644
--- a/tlatools/src/tla2sany/StandardModules/TLC.tla
+++ b/tlatools/src/tla2sany/StandardModules/TLC.tla
@@ -1,12 +1,77 @@
 ------------------------------- MODULE TLC ----------------------------------
+(***************************************************************************)
+(* This is a standard module for use with the TLC model checker.           *)
+(* Operators not explained by comments here are explained in the book      *)
+(* "Specifying Systems".                                                   *)
+(*                                                                         *)
+(* The definitions of all the operators in this module are overridden by   *)
+(* TLC with methods defined in the Java class tlc2.module.TLC.  Each       *)
+(* definition is overridden with the method of the same name, except that  *)
+(* the mapping from infix operators to Java methods is specified in the    *)
+(* static block at the beginning of the Java class.                        *)
+(***************************************************************************)
 LOCAL INSTANCE Naturals
 LOCAL INSTANCE Sequences
+LOCAL INSTANCE FiniteSets
 -----------------------------------------------------------------------------
 Print(out, val) == val
 PrintT(out) == TRUE
+   (************************************************************************)
+   (* This expression equals TRUE, but evaluating it causes TLC to print   *)
+   (* the value of out.                                                    *)
+   (************************************************************************)
+   
 Assert(val, out) == IF val = TRUE THEN TRUE
                                   ELSE CHOOSE v : TRUE
 JavaTime == CHOOSE n : n \in Nat
+
+(***************************************************************************)
+(* TLC can read and set a special list of values while evaluating          *)
+(* expressions using the operators TLCSet and TLCGet.  When TLC evaluates  *)
+(* TLCSet(i,v), for any positive integer i and arbitrary value v, it       *)
+(* obtains the value TRUE and sets element number i of the list to v.      *)
+(* When TLC evaluates TLCGet(i), the value it obtains is the current value *)
+(* of the element number i of this list.                                   *)
+(*                                                                         *)
+(* One use of this feature is to check TLC's progress during long          *)
+(* computations.  For example, suppose TLC is evaluating a formula         *)
+(* \A x \in S : P where S is a large set, so it evaluates P many times.    *)
+(* You can use TLCGet, TLCSet, and Print to print something after every    *)
+(* 1000 times TLC evaluates P.                                             *)
+(*                                                                         *)
+(* As explained in the description of the TLCEval operator below, you may  *)
+(* also want to use this feature to count how many times TLC is evaluating *)
+(* an expression e.  To use value number i as the counter, just replace e  *)
+(* by                                                                      *)
+(*                                                                         *)
+(*   IF TLCSet(i, TLCGet(i)+1) THEN e ELSE 42                              *)
+(*                                                                         *)
+(* (The ELSE expression is never evaluated.)                               *)
+(*                                                                         *)
+(* TLCGet accepts some pre-defined string values to query TLC state. The   *)
+(* values are as follows:                                                  *)
+(*                                                                         *)
+(*   - TLCGet("distinct") evaluates to the total number of distinct states *)
+(*     found by TLC so far, globally.                                      *)
+(*   - TLCGet("queue") evaluates to the number of states currently in the  *)
+(*     queue to be checked.                                                *)
+(*   - TLCGet("duration") evaluates to the number of seconds elapsed since *)
+(*     model checking began.                                               *)
+(*   - TLCGet("diameter") evaluates to the length of the longest behavior  *)
+(*     found by TLC so far, globally (equals one in the initial predicate).*)
+(*   - TLCGet("level") is the length of the current behavior (equals zero  *)
+(*     in the evaluation of the initial predicate).                        *)
+(*                                                                         *)
+(* For reasons of efficiency, TLCGet and TLCSet behave somewhat strangely  *)
+(* when TLC is run with multiple worker threads.  Each worker thread       *)
+(* maintains its own individual copy of the list of values on which it     *)
+(* evaluates TLCGet and TLCSet.  The worker threads are activated only     *)
+(* after the computation and invariance checking of the initial states.    *)
+(* Before then, evaluating TLCSet(i,v) sets the element i of the list      *)
+(* maintained by all threads.  Thus, the lists of all the worker threads   *)
+(* can be initialized by putting the appropriate TLCSet expression in an   *)
+(* ASSUME expression or in the initial predicate.                          *)
+(***************************************************************************)
 TLCGet(i) == CHOOSE n : TRUE
 TLCSet(i, v) == TRUE
 -----------------------------------------------------------------------------
@@ -26,11 +91,57 @@ SortSeq(s, Op(_, _)) ==
                      (i < j) => Op(s[p[i]], s[p[j]]) \/ (s[p[i]] = s[p[j]])
     IN  [i \in 1..Len(s) |-> s[Perm[i]]]
 
+(***************************************************************************)
+(* TLC evaluates RandomElement(S) to be a pseudo-randomly chosen element   *)
+(* of the set S, where each element of S is chosen with equal probability. *)
+(* This feature was added to enable the computation of statistical         *)
+(* properties of a specification's executions by running TLC in simulation *)
+(* mode.  We don't know if anyone has ever done this.                      *)
+(*                                                                         *)
+(* In breadth-first search model checking, the pseudo-random choices made  *)
+(* when computing possible steps satisfying the next-state relation are    *)
+(* determined by the first state of the step.  Thus, the choices made for  *)
+(* a particular state will be the same in successive runs of TLC.  This is *)
+(* done to permit TLC to generate an error trace if an error is found.     *)
+(* This applies only when TLC is run in breadth-first search mode.  The    *)
+(* random choices made in simulation mode are independent of the state for *)
+(* which they are made.                                                    *)
+(***************************************************************************)
 RandomElement(s) == CHOOSE x \in s : TRUE
 
+(***************************************************************************)
+(* The constant value Any has the special property that, for any value v,  *)
+(* TLC evaluates the expression  v \in Any  to equal TRUE.  The special    *)
+(* value Any was introduced because TLA+ originally allowed only functions *)
+(* to be defined recursively, and it was sometimes convenient to define a  *)
+(* function with domain Any.  It is retained for backwards compatibility.  *)
+(***************************************************************************)
 Any == CHOOSE x : TRUE
 
 ToString(v) == (CHOOSE x \in [a : v, b : STRING] : TRUE).b
+   (************************************************************************)
+   (* This equals a string that is the TLA+ representation of the value    *)
+   (* that TLC obtains by evaluating v.                                    *)
+   (************************************************************************)
 
+(***************************************************************************)
+(* TLC often uses lazy evaluation.  For example, it may not enumerate the  *)
+(* elements of a set of the form {x \in T : P(x)} unless it has to; and it *)
+(* doesn't have to if it only needs to check if an element e is in that    *)
+(* set.  (TLC can do that by evaluating x \in T and P(e).) TLC uses        *)
+(* heuristics to determine when it should completely evaluate an           *)
+(* expression.  Those heuristics work well most of the time.  However,     *)
+(* sometimes lazy evaluation can result in the expression ultimately being *)
+(* evaluated multiple times instead of just once.  This can especially be  *)
+(* a problem when evaluating a recursively defined operator.  You can get  *)
+(* TLC to fully evaluate an expression exp and not use lazy evaluation by  *)
+(* replacing exp with TLCEval(exp).                                        *)
+(*                                                                         *)
+(* If TLC is taking a long time to evaluate something, you can check if    *)
+(* lazy evaluation is the source of the problem by using the TLCSet and    *)
+(* TLCGet operators to count how many times expressions are being          *)
+(* evaluated.                                                              *)
+(***************************************************************************)
 TLCEval(v) == v
-=============================================================================
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/src/tla2sany/StandardModules/Toolbox.tla b/tlatools/src/tla2sany/StandardModules/Toolbox.tla
new file mode 100644
index 0000000000000000000000000000000000000000..113eb3b690bda01e066ca6dd906e03acc272b968
--- /dev/null
+++ b/tlatools/src/tla2sany/StandardModules/Toolbox.tla
@@ -0,0 +1,23 @@
+----------------------------- MODULE Toolbox --------------------------------
+(***************************************************************************)
+(*                                                                         *)
+(***************************************************************************)
+LOCAL INSTANCE Naturals   \* a + b
+LOCAL INSTANCE FiniteSets \* Cardinality
+LOCAL INSTANCE TLC        \* TLCGet
+
+-----------------------------------------------------------------------------
+
+(* Trace Exploration *)
+
+_TETrace ==
+    CHOOSE f \in [ (Nat \ {0}) -> STRING ] : TRUE
+
+LOCAL _TETraceLength == Cardinality(DOMAIN _TETrace)
+
+_TEPosition == 
+    IF TLCGet("level") >= _TETraceLength
+       THEN _TETraceLength
+       ELSE TLCGet("level") + 1
+
+=============================================================================
diff --git a/tlatools/src/tla2sany/configuration/ASCII_CharStream.java b/tlatools/src/tla2sany/configuration/ASCII_CharStream.java
index 3bbbdc33c62fe04d8607cddbcb981a66c6ae4c7a..71e03c7b21f9c766d9afad93ec142b73cc8f1bb7 100644
--- a/tlatools/src/tla2sany/configuration/ASCII_CharStream.java
+++ b/tlatools/src/tla2sany/configuration/ASCII_CharStream.java
@@ -284,7 +284,7 @@ public final class ASCII_CharStream
   public ASCII_CharStream(java.io.InputStream dstream, int startline,
   int startcolumn, int buffersize)
   {
-     this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096);
+     this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
   }
 
   public ASCII_CharStream(java.io.InputStream dstream, int startline,
@@ -296,7 +296,7 @@ public final class ASCII_CharStream
   static public void ReInit(java.io.InputStream dstream, int startline,
   int startcolumn, int buffersize)
   {
-     ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096);
+     ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
   }
 
   static public void ReInit(java.io.InputStream dstream, int startline,
diff --git a/tlatools/src/tla2sany/drivers/FrontEndException.java b/tlatools/src/tla2sany/drivers/FrontEndException.java
index 609ab4413c130db9e1ee0fbc3d9e198f6cb03426..e8804206b23545f562a602697c3e97c05b8cffdf 100644
--- a/tlatools/src/tla2sany/drivers/FrontEndException.java
+++ b/tlatools/src/tla2sany/drivers/FrontEndException.java
@@ -4,16 +4,17 @@ package tla2sany.drivers;
 /**
  * Exception thrown by SANY on errors
  * @author Leslie Lamport
- * @version $Id$
  */
 public class FrontEndException extends Exception {
-
     // SZ Feb 20, 2009: fixed the exception chaining
     // instead of having the cause as a member, the 
     // exception now has it in trace
     // also changed the visibility to public
-    public FrontEndException(Exception cause) 
-    { 
-        super(cause);
+	public FrontEndException(final Exception cause) {
+		super(cause);
+    }
+    
+    public FrontEndException(final String msg) {
+    	super(msg);
     }
 }
diff --git a/tlatools/src/tla2sany/drivers/SANY.java b/tlatools/src/tla2sany/drivers/SANY.java
index 9ce89d8765e1d429b7a557732907bc11ec47af8e..2ae22d90d45f955d61217e07cf3752cc9cfe06fc 100644
--- a/tlatools/src/tla2sany/drivers/SANY.java
+++ b/tlatools/src/tla2sany/drivers/SANY.java
@@ -78,7 +78,7 @@ public class SANY {
     //          should be reported
 
   /**
-   * The TLAFrontEnd.frontEndMain method Processes an entire TLA+ spec
+   * The SANY.frontEndMain method Processes an entire TLA+ spec
    * that starts in the file named in "filename", including all files
    * it refers to directly or indirectly in EXTENDS or INSTANCE
    * constructs.  "fileName" is relative to the current directory.  It
@@ -114,7 +114,7 @@ public class SANY {
    * processing--those are reported in the returned specification
    * object, which must be checked as described above.
    */
-  public static final void frontEndMain(
+  public static final int frontEndMain(
                              SpecObj spec, 
                              String fileName, 
                              PrintStream syserr) throws FrontEndException {
@@ -130,20 +130,20 @@ public class SANY {
             {frontEndSemanticAnalysis(spec, syserr, doLevelChecking);} ;
     }
     catch (InitException ie) {
-      return;
+      return -1;
     }
     catch (ParseException pe) {
-      return;
+      return -1;
     }
     catch (SemanticException se) {
-      return;
+      return -1;
     }
     catch (Exception e) {
       // e.printStackTrace(syserr);
       syserr.println(e.toString());
       throw new FrontEndException(e);
     }
-    return;
+    return 0;
   }
 
   /** 
@@ -197,6 +197,10 @@ public class SANY {
 
   // Parse all of the files referred to by the top-level file in specification
   public static void frontEndParse(SpecObj spec, PrintStream syserr) 
+  throws ParseException {
+	  frontEndParse(spec, syserr, true);
+  }
+  public static void frontEndParse(SpecObj spec, PrintStream syserr, boolean validatePCalTranslation) 
   throws ParseException {
       /***********************************************************************
        * Modified on 12 May 2008 by LL to remove "throws AbortException",     *
@@ -206,7 +210,7 @@ public class SANY {
       try 
       {
           // Actual parsing method called from inside loadSpec()
-          if (!spec.loadSpec(spec.getFileName(), spec.parseErrors)) 
+          if (!spec.loadSpec(spec.getFileName(), spec.parseErrors, validatePCalTranslation)) 
           {
               // dead code SZ 02. Aug 2009
               /*
@@ -389,7 +393,7 @@ public class SANY {
   }
 
   /**
-   * Main driver method for maintainers and debuggers of the TLAFrontEnd.
+   * Main driver method for maintainers and debuggers of SANY.
    * 
    * Calls frontEndMain for each file named on the command line and then, 
    * barring errors too severe, calls the Explorer tool with the resulting 
@@ -436,13 +440,16 @@ public class SANY {
       if (FileUtil.createNamedInputStream(args[i], spec.getResolver()) != null) 
       {
           try {
-              frontEndMain(spec, args[i], ToolIO.out);
+              int ret = frontEndMain(spec, args[i], ToolIO.out);
+			  if (ret != 0) {
+            	  System.exit(ret);
+              }
             }
             catch (FrontEndException fe) {
               // For debugging
               fe.printStackTrace();   
               ToolIO.out.println(fe);
-              return;
+              System.exit(-1);
             }
 
             // Compile operator usage stats
@@ -452,13 +459,14 @@ public class SANY {
               // Run the Semantic Graph Exploration tool
               Explorer explorer = new Explorer(spec.getExternalModuleTable());
               try {
-                explorer.main();
+                explorer.main(args);
               }
               catch (ExplorerQuitException e) { /*do nothing*/ }
             }
       } else 
       {
           ToolIO.out.println("Cannot find the specified file " + args[i] + ".");
+          System.exit(-1);
       }
     }
   }
diff --git a/tlatools/src/tla2sany/explorer/DotExplorerVisitor.java b/tlatools/src/tla2sany/explorer/DotExplorerVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfd828dc4732f5ed78ed3630fe8fb22e58543041
--- /dev/null
+++ b/tlatools/src/tla2sany/explorer/DotExplorerVisitor.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tla2sany.explorer;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import tla2sany.semantic.Context;
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.LetInNode;
+import tla2sany.semantic.ModuleNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.OpDeclNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.Subst;
+import util.FileUtil;
+
+public class DotExplorerVisitor extends ExplorerVisitor {
+
+	private static final Map<Class<? extends SemanticNode>, String> type2format = new HashMap<>();
+	
+	static {
+		type2format.put(OpDefNode.class, " [style=filled,shape=diamond,fillcolor=\"red\",");
+		type2format.put(OpApplNode.class, " [color=\"green\",");
+		type2format.put(OpDeclNode.class, " [shape=square,color=\"yellow\",");
+		type2format.put(LetInNode.class, " [color=\"orange\",");
+	}
+	
+	private final ModuleNode rootModule;
+	private final Hashtable<Integer, ExploreNode> table;
+	private final PrintWriter writer;
+	private final Deque<ExploreNode> stack = new ArrayDeque<>();
+	private final boolean includeLineNumbers = Boolean.getBoolean(DotExplorerVisitor.class.getName() + ".includeLineNumbers");
+
+	public DotExplorerVisitor(final ModuleNode rootModule) {
+		this.rootModule = rootModule;
+		this.table = new NoopTable<>();
+		try {
+			this.writer = new PrintWriter(FileUtil.newBFOS(rootModule.getName() + ".dot"));
+		} catch (FileNotFoundException e) {
+			throw new RuntimeException(e);
+		}
+		this.writer.append("strict digraph DiskGraph {\n"); // strict removes redundant edges
+	}
+	
+	@Override
+	public void preVisit(ExploreNode exploreNode) {
+		if (skipNode(exploreNode)) {
+			return;
+		}
+		
+		final ExploreNode parent = stack.peek();
+		if (exploreNode == this.rootModule) {
+			assert parent == null;
+			
+			final ModuleNode mn = (ModuleNode) exploreNode;
+			this.writer.append(Integer.toString(mn.hashCode()));
+			this.writer.append(" [label=\"");
+			this.writer.append(mn.getName().toString());
+			this.writer.append("\",style = filled]");
+			this.writer.append(";\n");
+			
+			stack.push(exploreNode);
+		} else {
+			final SemanticNode sn = (SemanticNode) exploreNode;
+			this.writer.append(Integer.toString(sn.hashCode()));
+			this.writer.append(type2format.getOrDefault(exploreNode.getClass(), " [") + "label=\"");
+			if (exploreNode instanceof OpDefNode) {
+				this.writer.append(toDot(((OpDefNode) sn).getName().toString()));
+			} else {
+				this.writer.append(toDot(sn.getTreeNode().getHumanReadableImage()));
+			}
+			if (includeLineNumbers) {
+				// Wrap location for more compact nodes in dot output.
+				final String loc = sn.getLocation().toString();
+				this.writer.append("\n");
+				this.writer.append(loc.replace("of module", "\n"));
+			}
+			this.writer.append("\"]");
+			this.writer.append(";\n");
+
+			this.writer.append(Integer.toString(parent.hashCode()));
+			this.writer.append(" -> ");
+			this.writer.append(Integer.toString(sn.hashCode()));
+			this.writer.append("\n");
+
+			stack.push(sn);
+		}
+	}
+
+	@Override
+	public void postVisit(final ExploreNode exploreNode) {
+		if (skipNode(exploreNode)) {
+			return;
+		}
+		final ExploreNode pop = stack.pop();
+		assert pop == exploreNode;
+	}
+
+	public void done() {
+		this.writer.append("}");
+		this.writer.close();
+	}
+
+	public Hashtable<Integer, ExploreNode> getTable() {
+		return table;
+	}
+	
+	private static String toDot(final String sn) {
+		return sn.replace("\\", "\\\\").replace("\"", "\\\"").trim().replace("\n", "\\n");
+	}
+	
+	private static boolean skipNode(final ExploreNode exploreNode) {
+		if (exploreNode instanceof Context || exploreNode instanceof FormalParamNode) {
+			return true;
+		}
+		if (exploreNode instanceof Subst) {
+			return true;
+		}
+		if (Context.isBuiltIn(exploreNode)) {
+			return true;
+		}
+		if (exploreNode instanceof SemanticNode) {
+			return ((SemanticNode) exploreNode).isStandardModule();
+		}
+		return false;
+	}
+	
+	@SuppressWarnings("serial")
+	private class NoopTable<K, V> extends Hashtable<K, V> {
+		@Override
+		public V get(Object key) {
+			// Return null here to visit an OpDefNode D multiple times if D is "called" from
+			// multiple OpApplNodes. However, stop endless recursion if D is a RECURSIVE
+			// operator.
+			final V v = super.get(key);
+			if (v instanceof OpDefNode) {
+				final OpDefNode odn = (OpDefNode) v;
+				if (odn.getInRecursive()) {
+					if (stack.contains(odn)) {
+						// RECURSIVE operators
+						return v;
+					}
+				}
+			}
+			return null;
+		}
+	}
+}
diff --git a/tlatools/src/tla2sany/explorer/ExploreNode.java b/tlatools/src/tla2sany/explorer/ExploreNode.java
index 81df5d3aa8594d943c39ab8434fb432b3b61331e..f3eb734631ebc0ed9ae9f6cf46a91a6b88cfdc74 100644
--- a/tlatools/src/tla2sany/explorer/ExploreNode.java
+++ b/tlatools/src/tla2sany/explorer/ExploreNode.java
@@ -21,13 +21,14 @@ public interface ExploreNode {
     * descendant foo.                                                      *
     ***********************************************************************/
   public String levelDataToString();
-  public void   walkGraph(Hashtable semNodesTable);
+
+  public void   walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, final ExplorerVisitor visitor);
     /***********************************************************************
     * This method is apparently supposed to insert an entry in             *
     * semNodesTable for itself and every descendant in the semantic tree   *
     * by executing                                                         *
     *                                                                      *
-    *     Integer uid = new Integer(myUID);                                *
+    *     Integer uid = Integer.valueOf(myUID);                                *
     *     if (semNodesTable.get(uid) != null) return;                      *
     *     semNodesTable.put(uid, this);                                    *
     *                                                                      *
diff --git a/tlatools/src/tla2sany/explorer/Explorer.java b/tlatools/src/tla2sany/explorer/Explorer.java
index 6fec0b6fc9946f0c3c467203c97f56f121b94885..04d2e7a28585ac79ad14213d2853417f9a6da7f1 100644
--- a/tlatools/src/tla2sany/explorer/Explorer.java
+++ b/tlatools/src/tla2sany/explorer/Explorer.java
@@ -4,15 +4,16 @@
 * 2 Mar 2007: enum <- Enum                                                 *
 ***************************************************************************/
 
-
 package tla2sany.explorer;
 
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.StringTokenizer;
 
 import tla2sany.semantic.ExternalModuleTable;
@@ -38,383 +39,399 @@ import util.UniqueString;
 
 public class Explorer {
 
-    public  Generator         generator;
-
-    // Next three vars used in reading commands from keyboard
-    private InputStreamReader inStream = new InputStreamReader(System.in);
-    private final int         inCapacity = 100;
-    private StringBuffer      input = new StringBuffer(inCapacity);
-    private int               lineLength;
-
-    // semNodesTable contains various nodes in the semantic graph keyed
-    // by their UIDs
-    private Hashtable         semNodesTable = new Hashtable();
-
-    // variables used in parsing commands
-    private int               ntokens;
-    private StringTokenizer   inputTokens;
-    private String            inputString;
-    private String            firstToken,secondToken;
-    private Integer           icmd,icmd2 = null;
-    private ExploreNode       obj;
-
-    private ExternalModuleTable       mt;
+	public Generator generator;
 
+	// Next three vars used in reading commands from keyboard
+	private final InputStreamReader inStream = new InputStreamReader(System.in);
+	private final int inCapacity = 100;
+	private StringBuffer input = new StringBuffer(inCapacity);
+	private int lineLength;
 
-    // Constructor
-    public Explorer (ExternalModuleTable mtarg) {
+	// semNodesTable contains various nodes in the semantic graph keyed
+	// by their UIDs
+	private final Hashtable<Integer, ExploreNode> semNodesTable = new Hashtable<>();
 
-      mt = mtarg;
+	// variables used in parsing commands
+	private int ntokens;
+	private StringTokenizer inputTokens;
+	private String firstToken, secondToken;
+	private Integer icmd, icmd2 = null;
+	private ExploreNode obj;
 
-    }
+	private ExternalModuleTable mt;
 
+	// Constructor
+	public Explorer(ExternalModuleTable mtarg) {
 
-    /* Reads one line from "inStream" into "input".
-       Returns "false" if there is an EOF; "true" otherwise
-    */
+		mt = mtarg;
 
-    private boolean getLine() {
-      try {
-        lineLength=0;
-        input.setLength(inCapacity);
-	do {
-          input.setCharAt(lineLength,(char)inStream.read());
-          lineLength++;
 	}
-	while (input.charAt(lineLength-1) != '\n' & lineLength < inCapacity );
-        input.setLength(lineLength);
-      }
-      catch (EOFException e) {
-	  return false;
-      }
-      catch (IOException e) {
-	System.out.println("***I/O exception on keyboard input; " + e);
-	System.exit(-1);
-      }
-      if (lineLength >= inCapacity) {
-        System.out.println("Input line too long; line limited to " + inCapacity
-                           + " chars.  Line ignored.");
-        input.setLength(0);
-      }
-
-      return true;
-    }
-
-
-
-    // Integer command
-    private void printNode(int depth) {
-
-      // See if the object requested is already in the table
-      if ((obj = (ExploreNode)semNodesTable.get(icmd)) != null) {
-	//Print tree to depth of icmd2
-        System.out.println(((ExploreNode)obj).toString(depth));
-        System.out.print("\n" + ((ExploreNode)obj).levelDataToString());
-      } else {
-        // object requested is not in semNodesTable
-        System.out.println("No such node encountered yet");
-      }
-    } // end method
-
-
-    private void lookUpAndPrintSyntaxTree(String symbName) {
-
-      Vector symbolVect = new Vector(8);  // Initial room for 8 symbols with same name
-
-
-      // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == symbName
-
-      for ( Enumeration Enum = semNodesTable.elements(); Enum.hasMoreElements(); ) {
-
-        Object semNode = Enum.nextElement();
-
-        if ( semNode instanceof SymbolNode &&
-             ((SymbolNode)semNode).getName() == UniqueString.uniqueStringOf(symbName) ) {
-
-          symbolVect.addElement(semNode);
 
-        }
-      }
-
-      // Print them all
-      for (int i = 0; i < symbolVect.size(); i++ ) {
-        SymbolNode sym = (SymbolNode)(symbolVect.elementAt(i));
-        ((SemanticNode)(sym)).getTreeNode().printST(0);
-        System.out.println();
-      }
+	/*
+	 * Reads one line from "inStream" into "input". Returns "false" if there is an
+	 * EOF; "true" otherwise
+	 */
+
+	private boolean getLine() {
+		try {
+			lineLength = 0;
+			input.setLength(inCapacity);
+			do {
+				input.setCharAt(lineLength, (char) inStream.read());
+				lineLength++;
+			} while (input.charAt(lineLength - 1) != '\n' & lineLength < inCapacity);
+			input.setLength(lineLength);
+		} catch (EOFException e) {
+			return false;
+		} catch (IOException e) {
+			System.out.println("***I/O exception on keyboard input; " + e);
+			System.exit(-1);
+		}
+		if (lineLength >= inCapacity) {
+			System.out.println("Input line too long; line limited to " + inCapacity + " chars.  Line ignored.");
+			input.setLength(0);
+		}
+
+		return true;
+	}
 
-    }
+	// Integer command
+	private void printNode(int depth) {
 
+		// See if the object requested is already in the table
+		if ((obj = (ExploreNode) semNodesTable.get(icmd)) != null) {
+			// Print tree to depth of icmd2
+			System.out.println(((ExploreNode) obj).toString(depth));
+			System.out.print("\n" + ((ExploreNode) obj).levelDataToString());
+		} else {
+			// object requested is not in semNodesTable
+			System.out.println("No such node encountered yet");
+		}
+	} // end method
 
-    private void lookUpAndPrintDef(String symbName) {
+	private void lookUpAndPrintSyntaxTree(String symbName) {
 
-      Vector symbolVect = new Vector(8);  // Initial room for 8 symbols with same name
+		Vector<SymbolNode> symbolVect = new Vector<>(8); // Initial room for 8 symbols with same name
 
+		// Collect in Vector symbols all SymbolNodes in the semNodesTable whose name ==
+		// symbName
 
-      // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == symbName
+		for (Enumeration<ExploreNode> Enum = semNodesTable.elements(); Enum.hasMoreElements();) {
 
-      for ( Enumeration Enum = semNodesTable.elements(); Enum.hasMoreElements(); ) {
+			ExploreNode semNode = Enum.nextElement();
 
-        Object semNode = Enum.nextElement();
+			if (semNode instanceof SymbolNode
+					&& ((SymbolNode) semNode).getName() == UniqueString.uniqueStringOf(symbName)) {
 
-        if ( semNode instanceof SymbolNode &&
-             ((SymbolNode)semNode).getName() == UniqueString.uniqueStringOf(symbName) ) {
+				symbolVect.addElement((SymbolNode) semNode);
 
-          symbolVect.addElement(semNode);
+			}
+		}
 
-        }
-      }
+		// Print them all
+		for (int i = 0; i < symbolVect.size(); i++) {
+			SymbolNode sym = (SymbolNode) (symbolVect.elementAt(i));
+			((SemanticNode) (sym)).getTreeNode().printST(0);
+			System.out.println();
+		}
 
-      // Print them all
-      for (int i = 0; i < symbolVect.size(); i++ ) {
-        SymbolNode sym = (SymbolNode)(symbolVect.elementAt(i));
-        if (sym instanceof OpDefOrDeclNode) {
-	  if ( ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode() != null ) {
-            System.out.print( "Module: " + ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode().getName() + "\n" );
-	  } else {
-            System.out.print( "Module: " + "null" + "\n");
-	  }
-	} else if (sym instanceof FormalParamNode) {
-          System.out.print( "Module: " + ((FormalParamNode)sym).getModuleNode().getName() );
 	}
-        System.out.println( ((ExploreNode)(symbolVect.elementAt(i))).toString(100) );
-        System.out.println();
-      }
-
-    }
 
-    private void levelDataPrint(String symbName) {
+	private void lookUpAndPrintDef(String symbName) {
 
-      Vector symbolVect = new Vector(8);  // Initial room for 8 symbols with same name
+		Vector<SymbolNode> symbolVect = new Vector<>(8); // Initial room for 8 symbols with same name
 
+		// Collect in Vector symbols all SymbolNodes in the semNodesTable whose name ==
+		// symbName
 
-      // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == symbName
+		for (Enumeration<ExploreNode> Enum = semNodesTable.elements(); Enum.hasMoreElements();) {
 
-      for ( Enumeration Enum = semNodesTable.elements(); Enum.hasMoreElements(); ) {
+			ExploreNode semNode = Enum.nextElement();
 
-        Object semNode = Enum.nextElement();
+			if (semNode instanceof SymbolNode
+					&& ((SymbolNode) semNode).getName() == UniqueString.uniqueStringOf(symbName)) {
 
-        if ( semNode instanceof SymbolNode &&
-             ((SymbolNode)semNode).getName() == UniqueString.uniqueStringOf(symbName) ) {
+				symbolVect.addElement((SymbolNode) semNode);
 
-          symbolVect.addElement(semNode);
+			}
+		}
 
-        }
-      }
+		// Print them all
+		for (int i = 0; i < symbolVect.size(); i++) {
+			SymbolNode sym = (SymbolNode) (symbolVect.elementAt(i));
+			if (sym instanceof OpDefOrDeclNode) {
+				if (((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode() != null) {
+					System.out.print(
+							"Module: " + ((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode().getName() + "\n");
+				} else {
+					System.out.print("Module: " + "null" + "\n");
+				}
+			} else if (sym instanceof FormalParamNode) {
+				System.out.print("Module: " + ((FormalParamNode) sym).getModuleNode().getName());
+			}
+			System.out.println(((ExploreNode) (symbolVect.elementAt(i))).toString(100));
+			System.out.println();
+		}
 
-      // Print them all
-      for (int i = 0; i < symbolVect.size(); i++ ) {
-        SymbolNode sym = (SymbolNode)(symbolVect.elementAt(i));
-        if (sym instanceof OpDefOrDeclNode) {
-	  if ( ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode() != null ) {
-            System.out.print( "Module: " + ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode().getName() + "\n" );
-	  } else {
-            System.out.print( "Module: " + "null" + "\n" );
-	  }
-	} else if (sym instanceof FormalParamNode) {
-          System.out.print( "Module: " + ((FormalParamNode)sym).getModuleNode().getName() + "\n" );
 	}
-        System.out.println( ((ExploreNode)(sym)).levelDataToString() );
-        System.out.println();
-      }
-
-    }
-
-
-    private void executeCommand() throws ExplorerQuitException {
-
-      // At this point icmd (firsToken) may be null, but icmd2
-      // (second token) is always non-null
-
-      // Integers as commands start printing at the node having icmd == UID;
-      // non-integer commands do something else
-
-      if (icmd != null) { // first token is an integer
 
-        printNode(icmd2.intValue());
+	private void levelDataPrint(String symbName) {
 
-      } else {      // the first token is not an integer
-
-        // non-integer commands
-        if (firstToken.toLowerCase().startsWith("qu")) {
-          // "quit" command
-          throw new ExplorerQuitException();
-
-        } else if (firstToken.toLowerCase().equals("mt")) {
-
-          // Print the semantic graph, rooted in the Module Table
-	  // excluding built-ins and ops defined in module Naturals
-	  if (icmd2 != null) {
-            mt.printExternalModuleTable(icmd2.intValue(),false);
-          } else {
-            mt.printExternalModuleTable(2, false);
-          }
+		Vector<SymbolNode> symbolVect = new Vector<>(8); // Initial room for 8 symbols with same name
 
-        } else if (firstToken.toLowerCase().equals("mt*")) {
+		// Collect in Vector symbols all SymbolNodes in the semNodesTable whose name ==
+		// symbName
 
-          // Print the semantic graph, rooted in the Module Table
-	  // including builtins and ops defined in Naturals
-	  if (icmd2 != null) {
-            mt.printExternalModuleTable(icmd2.intValue(),true);
-          } else {
-            mt.printExternalModuleTable(2,true);
-          }
+		for (Enumeration<ExploreNode> Enum = semNodesTable.elements(); Enum.hasMoreElements();) {
 
-        } else if (firstToken.toLowerCase().startsWith("cst")) {
-          printSyntaxTree();
+			ExploreNode semNode = Enum.nextElement();
 
-        } else if (firstToken.toLowerCase().startsWith("s")) {
-	  if (secondToken != null) {
-            lookUpAndPrintSyntaxTree(secondToken);
-	  } else {
-            System.out.println("***Error: You must indicate what name you want to print the syntax tree of.");
-	  }
+			if (semNode instanceof SymbolNode
+					&& ((SymbolNode) semNode).getName() == UniqueString.uniqueStringOf(symbName)) {
 
-        } else if (firstToken.toLowerCase().startsWith("d")) {
-	  if (secondToken != null) {
-            lookUpAndPrintDef(secondToken);
-	  } else {
-            System.out.println("***Error: You must indicate what name you want to print the definition of.");
-	  }
+				symbolVect.addElement((SymbolNode) semNode);
 
-        } else if (firstToken.toLowerCase().startsWith("l")) {
-	  if (secondToken != null) {
-            levelDataPrint(secondToken);
-	  } else {
-            System.out.println("***Error: You must indicate what name you want to print the level data of.");
-	  }
+			}
+		}
 
-        } else {
-	  // unknown command
-          System.out.println("Unknown command: " + firstToken.toString());
-          return;
-        }
+		// Print them all
+		for (int i = 0; i < symbolVect.size(); i++) {
+			SymbolNode sym = (SymbolNode) (symbolVect.elementAt(i));
+			if (sym instanceof OpDefOrDeclNode) {
+				if (((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode() != null) {
+					System.out.print(
+							"Module: " + ((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode().getName() + "\n");
+				} else {
+					System.out.print("Module: " + "null" + "\n");
+				}
+			} else if (sym instanceof FormalParamNode) {
+				System.out.print("Module: " + ((FormalParamNode) sym).getModuleNode().getName() + "\n");
+			}
+			System.out.println(((ExploreNode) (sym)).levelDataToString());
+			System.out.println();
+		}
 
-      } // end else
-
-    }
-
-
-    private void parseAndExecuteCommand() throws ExplorerQuitException {
-
-      icmd = null;
-      icmd2 = null;
-      ntokens = 0;
-
-      // Do nothing if cmd line contains no tokens
-      if (!inputTokens.hasMoreElements()) return;
+	}
 
-      // Process first token
-      ntokens++;
-      firstToken = (String)(inputTokens.nextElement());
+	private void executeCommand() throws ExplorerQuitException {
 
-      // Try parsing first token as an Integer
-      try {
-        icmd = Integer.valueOf(firstToken);
-      }
-      catch (Exception e) { }
+		// At this point icmd (firsToken) may be null, but icmd2
+		// (second token) is always non-null
 
-      //Process second token (if present)
-      if (inputTokens.hasMoreElements()) {
-        ntokens++;
-        secondToken = (String)(inputTokens.nextElement());
+		// Integers as commands start printing at the node having icmd == UID;
+		// non-integer commands do something else
 
-        // Try parsing second token as an Integer
-        try {
-          icmd2 = Integer.valueOf(secondToken);
-        }
-        catch (Exception e) { }
-      }
+		if (icmd != null) { // first token is an integer
 
-      // A single token command defaults the depth to 20, except for
-      // "mt" command, which defaults to 2
-      if (ntokens < 2 || (icmd2 != null && icmd2.intValue() < 0)) {
-	if (firstToken.toLowerCase().startsWith("mt")) {
-          icmd2 = new Integer(2);
-        } else {
-          icmd2 = new Integer(4);
-        }
-      }
+			printNode(icmd2.intValue());
 
-      if (inputTokens.hasMoreElements()) {
-        System.out.println("Command has too many tokens");
-        return;
-      }
+		} else { // the first token is not an integer
 
-      executeCommand();
+			// non-integer commands
+			if (firstToken.toLowerCase().startsWith("qu")) {
+				// "quit" command
+				throw new ExplorerQuitException();
 
-    } // end method
+			} else if (firstToken.toLowerCase().equals("mt")) {
 
+				// Print the semantic graph, rooted in the Module Table
+				// excluding built-ins and ops defined in module Naturals
+				if (icmd2 != null) {
+					mt.printExternalModuleTable(icmd2.intValue(), false);
+				} else {
+					mt.printExternalModuleTable(2, false);
+				}
 
-    public void printSyntaxTree () {
+			} else if (firstToken.toLowerCase().equals("mt*")) {
 
-      Integer key;
+				// Print the semantic graph, rooted in the Module Table
+				// including builtins and ops defined in Naturals
+				if (icmd2 != null) {
+					mt.printExternalModuleTable(icmd2.intValue(), true);
+				} else {
+					mt.printExternalModuleTable(2, true);
+				}
 
-      // Prepare to iterate over ExternalModuleTable entries
-      Iterator modules = mt.moduleHashTable.values().iterator();
-      ExternalModuleTable.ExternalModuleTableEntry mte;
+			} else if (firstToken.equalsIgnoreCase("dot")) {
+				dotSemanticGraph();
+			} else if (firstToken.toLowerCase().startsWith("cst")) {
+				printSyntaxTree();
 
-      // For each entry ExternalModuleTableEntry mte in the ExternalModuleTable mt ...
-      while (modules.hasNext()) {
-        key = new Integer(-1);
+			} else if (firstToken.toLowerCase().startsWith("s")) {
+				if (secondToken != null) {
+					lookUpAndPrintSyntaxTree(secondToken);
+				} else {
+					System.out.println("***Error: You must indicate what name you want to print the syntax tree of.");
+				}
 
-        mte = (ExternalModuleTable.ExternalModuleTableEntry)(modules.next());
+			} else if (firstToken.toLowerCase().startsWith("d")) {
+				if (secondToken != null) {
+					lookUpAndPrintDef(secondToken);
+				} else {
+					System.out.println("***Error: You must indicate what name you want to print the definition of.");
+				}
 
-        // Did the module parse correctly?
-        if (mte != null) {
-          if (mte.getModuleNode() != null ) {
-            key = new Integer(mte.getModuleNode().getUid());
+			} else if (firstToken.toLowerCase().startsWith("l")) {
+				if (secondToken != null) {
+					levelDataPrint(secondToken);
+				} else {
+					System.out.println("***Error: You must indicate what name you want to print the level data of.");
+				}
 
-            // Make an entry in the semNodesTable for this ModuleNode
-            semNodesTable.put(key,mte.getModuleNode());
+			} else {
+				// unknown command
+				System.out.println("Unknown command: " + firstToken.toString());
+				return;
+			}
 
-            // Print the concrete syntax tree for this ExternalModuleTableEntry
-            System.out.println("\n*** Concrete Syntax Tree for Module " + key);
+		} // end else
 
-            tla2sany.st.TreeNode stn = mte.getModuleNode().getTreeNode();
-            stn.printST(0);    // Zero indentation level
+	}
 
-            System.out.println("\n*** End of concrete syntax tree for Module "
-                               + key);
-          } else {
-            System.out.println("\n*** Null ExternalModuleTableEntry.  " +
-                      "\n*** Next module did not parse, and cannot be printed.");
-          }
-        } else {
-          System.out.println("*** Null SemanticNode in ExternalModuleTableEntry.  " +
-                      "/n*** Next module did not parse, and cannot be printed.");
-        }
+	private void parseAndExecuteCommand() throws ExplorerQuitException {
+
+		icmd = null;
+		icmd2 = null;
+		ntokens = 0;
+
+		// Do nothing if cmd line contains no tokens
+		if (!inputTokens.hasMoreElements())
+			return;
+
+		// Process first token
+		ntokens++;
+		firstToken = (String) (inputTokens.nextElement());
+
+		// Try parsing first token as an Integer
+		try {
+			icmd = Integer.valueOf(firstToken);
+		} catch (Exception e) {
+		}
+
+		// Process second token (if present)
+		if (inputTokens.hasMoreElements()) {
+			ntokens++;
+			secondToken = (String) (inputTokens.nextElement());
+
+			// Try parsing second token as an Integer
+			try {
+				icmd2 = Integer.valueOf(secondToken);
+			} catch (Exception e) {
+			}
+		}
+
+		// A single token command defaults the depth to 20, except for
+		// "mt" command, which defaults to 2
+		if (ntokens < 2 || (icmd2 != null && icmd2.intValue() < 0)) {
+			if (firstToken.toLowerCase().startsWith("mt")) {
+				icmd2 = new Integer(2);
+			} else {
+				icmd2 = new Integer(4);
+			}
+		}
+
+		if (inputTokens.hasMoreElements()) {
+			System.out.println("Command has too many tokens");
+			return;
+		}
+
+		executeCommand();
+
+	} // end method
+	
+	public void dotSemanticGraph() {
+		final DotExplorerVisitor visitor = new DotExplorerVisitor(mt.getRootModule());
+		mt.getRootModule().walkGraph(visitor.getTable(), visitor);
+		visitor.done();
+	}
 
-      }
-    }
+	public void printSyntaxTree() {
 
+		int key;
 
-    public void main() throws ExplorerQuitException {
+		// Prepare to iterate over ExternalModuleTable entries
+		Iterator<ExternalModuleTable.ExternalModuleTableEntry> modules = mt.moduleHashTable.values().iterator();
+		ExternalModuleTable.ExternalModuleTableEntry mte;
 
-      if (mt==null) {
-        System.out.println("*** module table == null in Explorer.main() ***");
-        return;
-      }
+		// For each entry ExternalModuleTableEntry mte in the ExternalModuleTable mt ...
+		while (modules.hasNext()) {
+			key = -1;
 
-      // Get all semNodes in semNodeTable
-      mt.walkGraph(semNodesTable);
+			mte = modules.next();
 
-      // Print initial user input prompt
-      System.out.println("\n\n*** TLA+ semantic graph exploration tool v 1.0 (DRJ)");
-      System.out.print("\n>>");
+			// Did the module parse correctly?
+			if (mte != null) {
+				if (mte.getModuleNode() != null) {
+					key = mte.getModuleNode().getUid();
 
-      // Main command interpreter loop
-      while (getLine()) {
+					// Make an entry in the semNodesTable for this ModuleNode
+					semNodesTable.put(key, mte.getModuleNode());
 
-        inputTokens = new StringTokenizer(input.toString());
+					// Print the concrete syntax tree for this ExternalModuleTableEntry
+					System.out.println("\n*** Concrete Syntax Tree for Module " + key);
 
-        parseAndExecuteCommand();
+					tla2sany.st.TreeNode stn = mte.getModuleNode().getTreeNode();
+					stn.printST(0); // Zero indentation level
 
-        // Print next user prompt
-        System.out.print("\n>>");
+					System.out.println("\n*** End of concrete syntax tree for Module " + key);
+				} else {
+					System.out.println("\n*** Null ExternalModuleTableEntry.  "
+							+ "\n*** Next module did not parse, and cannot be printed.");
+				}
+			} else {
+				System.out.println("*** Null SemanticNode in ExternalModuleTableEntry.  "
+						+ "/n*** Next module did not parse, and cannot be printed.");
+			}
 
-      } // end while
+		}
+	}
 
-   } // end main() method
+	public void main(final String[] args) throws ExplorerQuitException {
+
+		if (mt == null) {
+			System.out.println("*** module table == null in Explorer.main() ***");
+			return;
+		}
+
+		final List<String> asList = Arrays.asList(args);
+		// Passing single token commands as command line parameter skips Explorer's
+		// interpreter mode.
+		if (asList.contains("cst")) {
+			inputTokens = new StringTokenizer("cst");
+			parseAndExecuteCommand();
+			System.exit(0);
+		} else if (asList.contains("dot")) {
+			inputTokens = new StringTokenizer("dot");
+			parseAndExecuteCommand();
+			System.exit(0);
+		} else  if (asList.contains("mt")) {
+			inputTokens = new StringTokenizer("mt");
+			parseAndExecuteCommand();
+			System.exit(0);
+		} else if (asList.contains("mt*")) {
+			inputTokens = new StringTokenizer("mt*");
+			parseAndExecuteCommand();
+			System.exit(0);
+		} else {
+			// Get all semNodes in semNodeTable
+			mt.walkGraph(semNodesTable);
+
+			// Print initial user input prompt
+			System.out.println("\n\n*** TLA+ semantic graph exploration tool v 1.0 (DRJ)");
+			System.out.print("\n>>");
+			// Main command interpreter loop
+			while (getLine()) {
+				
+				inputTokens = new StringTokenizer(input.toString());
+				
+				parseAndExecuteCommand();
+				
+				// Print next user prompt
+				System.out.print("\n>>");
+				
+			} // end while
+		}
+
+	} // end main() method
 
 } // end class
diff --git a/tlatools/src/tla2sany/explorer/ExplorerVisitor.java b/tlatools/src/tla2sany/explorer/ExplorerVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc714a92f944fe791136e405db4a4470d0c44e00
--- /dev/null
+++ b/tlatools/src/tla2sany/explorer/ExplorerVisitor.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tla2sany.explorer;
+
+public class ExplorerVisitor {
+
+	public static final ExplorerVisitor NoopVisitor = new ExplorerVisitor();
+
+	public void preVisit(final ExploreNode exploreNode) {
+	}
+	
+	public void postVisit(final ExploreNode exploreNode) {
+	}
+}
diff --git a/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java b/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java
index 5dea4c75f4de4d7c2739d3a40f17111496bdd2fb..b96fac62d33823fc170219f1df8f3b2760ae151c 100644
--- a/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java
+++ b/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java
@@ -33,7 +33,7 @@ class ModuleRelationships {
   } // end getContext()
 
 
-  Enumeration getKeys() { return modRelHashtable.keys(); }  
+  Enumeration<ModulePointer> getKeys() { return modRelHashtable.keys(); }  
 
 
   // Add the entries from otherMR into THIS; they are assumed not to overlap.
diff --git a/tlatools/src/tla2sany/modanalyzer/ParseUnit.java b/tlatools/src/tla2sany/modanalyzer/ParseUnit.java
index 994692db3d331d98bbd6915ec5d87e04c79d9b36..9d8fa747a2979d11cb7eaff3929961068ae418b2 100644
--- a/tlatools/src/tla2sany/modanalyzer/ParseUnit.java
+++ b/tlatools/src/tla2sany/modanalyzer/ParseUnit.java
@@ -24,6 +24,7 @@ package tla2sany.modanalyzer;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.Path;
 
 import tla2sany.semantic.AbortException;
 import tla2sany.semantic.Errors;
@@ -32,7 +33,10 @@ import tla2sany.st.ParseTree;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Vector;
 import util.FileUtil;
+import util.FilenameToStream.TLAFile;
 import util.NamedInputStream;
+import util.TLAConstants;
+import util.TLAFlightRecorder;
 import util.ToolIO;
 
 /**
@@ -88,6 +92,13 @@ public class ParseUnit {
 	    parseStamp > nis.sourceFile().lastModified());
   }
 
+  public boolean isLibraryModule() {
+	  if (nis == null || !(nis.sourceFile() instanceof TLAFile)) {
+		  return false;
+	  }
+	  return ((TLAFile) nis.sourceFile()).isLibraryModule();
+  }
+
   // Get-methods
   public final String        getName()        { return rootModuleName; } 
 
@@ -234,20 +245,30 @@ public class ParseUnit {
         }
 
         // Print user feedback
+		// MAK 01/2018: Always print the absolute path. Useful if e.g. users symlink
+		// common TLA files and want to verify that symlinking worked.
+        Path absoluteResolvedPath;
+		try {
+			absoluteResolvedPath = nis.getAbsoluteResolvedPath();
+		} catch (IOException e1) {
+			// Fall back to to relative path if resolving the path to an absolute one fails.
+			// Chance of failure should be fairly low because existence is checked above.
+			absoluteResolvedPath = nis.sourceFile().toPath();
+		}
         /***********************************************************************
         * This is a bug.  The higher-level parsing methods have a PrintStream  *
         * argument to which such output gets written.  That argument should    *
         * have been passed down to this method.  (LL: 11 Mar 08)               *
         *                                                                      *
-        * The following statement modified by LL on 13 Mary 08 to produce      *
+        * The following statement modified by LL on 13 Mar 08 to produce       *
         * more useful output for the GUI.                                      *
         ***********************************************************************/
         if (ToolIO.getMode() == ToolIO.SYSTEM)
         {
-            ToolIO.out.println("Parsing file " + nis.sourceFile());
+			ToolIO.out.println(TLAFlightRecorder.message(String.format("%s %s", TLAConstants.LoggingAtoms.PARSING_FILE, absoluteResolvedPath)));
         } else
         {
-            ToolIO.out.println("Parsing module " + nis.getModuleName() + " in file " + nis.sourceFile());
+            ToolIO.out.println(TLAFlightRecorder.message(String.format("Parsing module %s in file %s", nis.getModuleName(), absoluteResolvedPath)));
         }
 
         boolean parseSuccess; 
diff --git a/tlatools/src/tla2sany/modanalyzer/SpecObj.java b/tlatools/src/tla2sany/modanalyzer/SpecObj.java
index fb9a0da5c5c924164f86e370d042efdd13a0b5cb..307699145876438d0d922b141336d115b3f7208a 100644
--- a/tlatools/src/tla2sany/modanalyzer/SpecObj.java
+++ b/tlatools/src/tla2sany/modanalyzer/SpecObj.java
@@ -2,13 +2,20 @@
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
 package tla2sany.modanalyzer;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 
+import pcal.Validator;
+import pcal.Validator.ValidationResult;
 import tla2sany.semantic.AbortException;
 import tla2sany.semantic.Errors;
 import tla2sany.semantic.ExternalModuleTable;
+import tla2sany.semantic.ModuleNode;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Vector;
 import util.FileUtil;
@@ -28,7 +35,7 @@ public class SpecObj
     // The raw file name for the root (top) module, unprocessed by adding
     // ".tla" or by prepending the full file system path to it)
 
-    ExternalModuleTable externalModuleTable = new ExternalModuleTable();
+    final ExternalModuleTable externalModuleTable = new ExternalModuleTable();
     // This is the one ExternalModuleTable for the entire specification;
     // it includes ModuleNode's for the root module, and for all modules
     // that it depends on directly or indirectly by EXTENDS or INSTANCE
@@ -38,7 +45,7 @@ public class SpecObj
     // must be done, i.e. if MODULE A references B, A is lower
     // on the stack. The same module name can occur multiple times.
 
-    public Hashtable parseUnitContext = new Hashtable();
+    public Hashtable<String, ParseUnit> parseUnitContext = new Hashtable<String, ParseUnit>();
     // Holds all known ParseUnit objects, i.e external, top-level
     // modules that have so far been encountered, keyed by module
     // (parseUnit) string name
@@ -159,6 +166,10 @@ public class SpecObj
     {
         return primaryFileName;
     }
+    
+    public final ModuleNode getRootModule() {
+    	return getExternalModuleTable().getRootModule();
+    }
 
     /**
      * Returns the ExternalModuleTable object that contains all of the
@@ -215,10 +226,19 @@ public class SpecObj
         return semanticErrors;
     }
 
+	public Set<String> getModuleNames() {
+		final Set<String> s = new HashSet<>();
+		final Enumeration<ModulePointer> modules = getModules();
+		while (modules.hasMoreElements()) {
+			s.add(modules.nextElement().getName());
+		}
+		return s;
+	}
+
     // Returns enumeration of the modules so far included in the spec.
     // As whoever wrote this documentation didn't think was worth mentioning,
     // it appears that the "modules" being returned are ModulePointer objects.
-    public final Enumeration getModules()
+    public final Enumeration<ModulePointer> getModules()
     {
         return moduleRelationshipsSpec.getKeys();
     }
@@ -232,12 +252,12 @@ public class SpecObj
     // Prints the context of one ParseUnit
     public final void printParseUnitContext()
     {
-        Enumeration enumerate = parseUnitContext.keys();
+        Enumeration<String> enumerate = parseUnitContext.keys();
 
         ToolIO.out.println("parseUnitContext =");
         while (enumerate.hasMoreElements())
         {
-            String key = (String) enumerate.nextElement();
+            String key = enumerate.nextElement();
             ToolIO.out.println("  " + key + "-->" + ((ParseUnit) parseUnitContext.get(key)).getName());
         }
     }
@@ -348,7 +368,7 @@ public class SpecObj
     // parseUnitName. If there is, cause an abort; otherwise return.
     private void nonCircularityTest(ParseUnit parseUnit, Errors errors) throws AbortException
     {
-        HashSet alreadyVisited = new HashSet();
+        Set<ParseUnit> alreadyVisited = new HashSet<ParseUnit>();
         Vector circularPath = new Vector();
 
         circularPath.addElement(parseUnit);
@@ -361,7 +381,7 @@ public class SpecObj
     // errors, and the method aborts. The set alreadyVisited is
     // used to prevent searching paths through the same candidate
     // multiple times.
-    private void nonCircularityBody(ParseUnit parseUnit, ParseUnit candidate, Errors errors, HashSet alreadyVisited,
+    private void nonCircularityBody(ParseUnit parseUnit, ParseUnit candidate, Errors errors, Set<ParseUnit> alreadyVisited,
             Vector circularPath) throws AbortException
     {
         // If we have already checked for circularities through this
@@ -874,18 +894,33 @@ public class SpecObj
     }
 
     /**
-     * This method "loads" an entire specification, starting with the
-     * top-level rootExternalModule and followed by all of the external
-     * modules it references via EXTENDS and INSTANCE statements.
+     * This invokes {@code loadSpec(rootExternalModuleName, errors, false);}
      */
-    public boolean loadSpec(String rootExternalModuleName, Errors errors) throws AbortException
-    {
+    public boolean loadSpec(final String rootExternalModuleName, final Errors errors) throws AbortException {
+    	return loadSpec(rootExternalModuleName, errors, false);
+    }
+    
+    /**
+	 * This method "loads" an entire specification, starting with the top-level
+	 * rootExternalModule and followed by all of the external modules it references
+	 * via EXTENDS and INSTANCE statements.
+	 * 
+	 * @param validateParseUnits if true, each parseable unit will be checked for
+	 *                           PlusCal and its accompanied TLA translation block,
+	 *                           followed by a validation of them if they exist
+	 * @see Validator
+	 */
+	public boolean loadSpec(final String rootExternalModuleName, final Errors errors, final boolean validateParseUnits)
+			throws AbortException {
         // If rootExternalModuleName" has *not* already been parsed, then
         // go to the file system and find the file containing it, create a
         // ParseUnit for it, and parse it. Parsing includes determining
         // module relationships. Aborts if not found in file system
         rootParseUnit = findOrCreateParsedUnit(rootExternalModuleName, errors, true /* first call */);
         rootModule = rootParseUnit.getRootModule();
+        if (validateParseUnits) {
+        	validateParseUnit(rootParseUnit);
+        }
 
         // Retrieve and parse all module extentions: As long as there is
         // another unresolved module name...
@@ -924,6 +959,10 @@ public class SpecObj
 
             if (extentionFound)
             {
+                if (validateParseUnits) {
+                	validateParseUnit(nextExtentionOrInstantiationParseUnit);
+                }
+
                 extenderOrInstancerParseUnit.addExtendee(nextExtentionOrInstantiationParseUnit);
                 nextExtentionOrInstantiationParseUnit.addExtendedBy(extenderOrInstancerParseUnit);
             }
@@ -963,6 +1002,54 @@ public class SpecObj
         return true;
         // loadUnresolvedRelatives(moduleRelationshipsSpec, rootModule, errors);
     }
+	
+	private void validateParseUnit(final ParseUnit parseUnit) {
+    	final File f = parseUnit.getNis().sourceFile();
+    	
+    	try (final FileInputStream fis = new FileInputStream(f)) {
+    		final Set<ValidationResult> results = Validator.validate(parseUnit, fis);
+    		
+    		if (results.contains(ValidationResult.TLA_DIVERGENCE_EXISTS) && results.contains(ValidationResult.PCAL_DIVERGENCE_EXISTS)) {
+    		/* Both hashes are invalid.
+    	       This is probably a sequel to Case 3, in which the user has decided either
+    	       (1) she has fixed the bug or (2) wants to change the spec and will later
+    	       modify the translation.
+    	       TLC called: By default, a warning should be raised.  It should be considered
+    	          the same as Case 2. */
+				ToolIO.out.println(String.format(
+						"!! WARNING: The PlusCal algorithm and its TLA+ translation in "
+								+ "module %s filename since the last translation.",
+						parseUnit.getName()));
+    		} else if (results.contains(ValidationResult.TLA_DIVERGENCE_EXISTS)) {
+      	      /* The algorithm hash is valid and the translation hash is invalid.
+     	       There are two reasons: (1) The user is debugging the spec, or
+     	       (2) She needed to modify the translation because she wants a spec that can't
+     	       be produced by PlusCal.  
+     	       TLC called: In both cases, no warning should be needed.  However,
+     	          in (1), she might have finished debugging and forgotten to run the 
+     	          translator.  To handle case (1), I suggest the default should be to run
+     	          TLC but raise a transient window with a warning that is easily ignored.  
+     	          For case (2), it should be possible to put something in a translation 
+     	          comment to disable the warning. */
+				ToolIO.out.println(String.format("!! WARNING: The TLA+ translation in "
+						+ "module %s has changed since its last translation.", parseUnit.getName()));
+    		} else if (results.contains(ValidationResult.PCAL_DIVERGENCE_EXISTS)) {
+       	      /* The algorithm hash is invalid and the translation hash is valid.
+     	       TLC called: By default, a warning should be generated.  I see little reason 
+     	         for not generating the warning.  So, it doesn't matter if its inconvenient
+     	         to turn off the warning, but turning it off should affect only the current
+     	         spec; and it should be easy to turn back on. */
+				ToolIO.out.println(String.format("!! WARNING: The PlusCal algorithm in "
+						+ "module %s has changed since its last translation.", parseUnit.getName()));
+    		} else if (results.contains(ValidationResult.ERROR_ENCOUNTERED)) {
+				ToolIO.err.println("A unexpected problem was encountered attempting to validate the specification for "
+						+ parseUnit.getName());
+    		}
+    	} catch (final IOException e) {
+    		ToolIO.err.println("Encountered an exception while attempt to validate " + f.getAbsolutePath() + " - "
+    				+ e.getMessage());
+    	}
+	}
 
     /**
      * Getter for the name resolver used in the Spec
@@ -981,4 +1068,8 @@ public class SpecObj
         this.globalContextErrors = globalContextErrors;
     }
 
+	public ParseUnit getRootParseUnit() {
+		return this.parseUnitContext.get(this.getName());
+	}
+
 }
\ No newline at end of file
diff --git a/tlatools/src/tla2sany/parser/BracketStack.java b/tlatools/src/tla2sany/parser/BracketStack.java
index 74a8713cc6e1204476b208e60b96640b00ade945..8ace9df9febc740f5fc32094e3c7f20c0a93a73c 100644
--- a/tlatools/src/tla2sany/parser/BracketStack.java
+++ b/tlatools/src/tla2sany/parser/BracketStack.java
@@ -132,8 +132,8 @@ public class BracketStack implements //LogCategories,
                           Offset is the end column of the token ...\/ ou .../\
                           on utilise - 1 pour comparer au de'but de la partie
                           significative du symbole.
-                          De cette mani€re, le comportement ne change pas si
-                          on utilise uniquement la forme non pr€fix€e des
+                          De cette manire, le comportement ne change pas si
+                          on utilise uniquement la forme non prafixe des
                          symboles */
   }
 }
diff --git a/tlatools/src/tla2sany/parser/ParseException.java b/tlatools/src/tla2sany/parser/ParseException.java
index 614bc91e5ea1008f25b66bab19a6a9bbd02bfe03..9eb892e8598c6300e5ba6e0ab6087c4f669e676f 100644
--- a/tlatools/src/tla2sany/parser/ParseException.java
+++ b/tlatools/src/tla2sany/parser/ParseException.java
@@ -111,8 +111,9 @@ public class ParseException extends Exception {
       //      retval += add_escapes(tok.image);
       tok = tok.next;
     }
-    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
-    return retval;
+		retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn
+				+ " and token \"" + add_escapes(currentToken.image) + "\" ";
+	    return retval;
   }
 
   /**
diff --git a/tlatools/src/tla2sany/parser/TLAplusParser.java b/tlatools/src/tla2sany/parser/TLAplusParser.java
index eea79546f5945da3ca22bf3847c2d39db4587ef6..e5e0b1c895a901dd4bfc5b58f221e39b5639390c 100644
--- a/tlatools/src/tla2sany/parser/TLAplusParser.java
+++ b/tlatools/src/tla2sany/parser/TLAplusParser.java
@@ -3,13 +3,13 @@ package tla2sany.parser;
 
 import tla2sany.st.ParseTree;
 import tla2sany.st.TreeNode;
-import tlc2.output.EC;
-
-import tla2sany.utilities.Vector;
 import tla2sany.utilities.Stack;
+import tla2sany.utilities.Vector;
+import tlc2.output.EC;
 import util.Assert;
-import util.UniqueString;
+import util.TLAConstants;
 import util.ToolIO;
+import util.UniqueString;
 
 public class TLAplusParser implements tla2sany.st.SyntaxTreeConstants, ParseTree, TLAplusParserConstants {
 
@@ -1656,7 +1656,7 @@ Token t;
   SyntaxTreeNode tn, sn[];
   Token t;
   bpa("Parameter declaration");
-  expecting = "CONSTANT";
+  expecting = TLAConstants.KeyWords.CONSTANT;
     tn = ParamSubDecl();
                          addHeir(tn);
  expecting = "Identifier, operator or _";
diff --git a/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java b/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java
index 2e9e7763c596838edbaa4b65b4c16fada72aad62..fadad65d8564ceaea2f5c189115325a3d2a433ac 100644
--- a/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java
+++ b/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java
@@ -1,13 +1,5 @@
 /* Generated By:23&JavaCC: Do not edit this line. TLAplusParserTokenManager.java */
 package tla2sany.parser;
-import tla2sany.st.ParseTree;
-import tla2sany.st.TreeNode;
-import tlc2.output.EC;
-import tla2sany.utilities.Vector;
-import tla2sany.utilities.Stack;
-import util.Assert;
-import util.UniqueString;
-import util.ToolIO;
 
 public class TLAplusParserTokenManager implements TLAplusParserConstants
 {
diff --git a/tlatools/src/tla2sany/semantic/APSubstInNode.java b/tlatools/src/tla2sany/semantic/APSubstInNode.java
index e14ac1582babfe825512c878dea78f77e369bd71..4b804c6f8ff96b671e44e19cd90a29793c11589a 100644
--- a/tlatools/src/tla2sany/semantic/APSubstInNode.java
+++ b/tlatools/src/tla2sany/semantic/APSubstInNode.java
@@ -21,14 +21,17 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
+import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 public class APSubstInNode extends LevelNode {
   /**
    * For a APSubstInNode object s that has the WITH clause
@@ -193,7 +196,8 @@ public class APSubstInNode extends LevelNode {
    * to this method can contain a mixture of explicit and implicit
    * substitutions
    */
-  final void addExplicitSubstitute(Context instanceeCtxt, UniqueString lhs,
+  @SuppressWarnings("unused")	// TODO final else block is dead code 
+  final void addExplicitSubstitute(Context instanceCtxt, UniqueString lhs,
                                    TreeNode stn, ExprOrOpArgNode sub) {
     int index;
     for (index = 0; index < this.substs.length; index++) {
@@ -221,7 +225,7 @@ public class APSubstInNode extends LevelNode {
       // the instancee context
 
       // look up the lhs symbol in the instancee context
-      SymbolNode lhsSymbol = instanceeCtxt.getSymbol(lhs);
+      SymbolNode lhsSymbol = instanceCtxt.getSymbol(lhs);
 
       // lhs must be an OpDeclNode; if not just return, as this error
       // will have been earlier, though semantic analysis was allowed
@@ -288,6 +292,7 @@ public class APSubstInNode extends LevelNode {
 //  private SetOfArgLevelConstraints argLevelConstraints;
 //  private HashSet argLevelParams;
 
+  @Override
   public final boolean levelCheck(int itr) {
     if (this.levelChecked >= itr) return this.levelCorrect;
     this.levelChecked = itr ;
@@ -308,7 +313,7 @@ public class APSubstInNode extends LevelNode {
 
     // Calculate the level information
     this.level = this.body.getLevel();
-    HashSet lpSet = this.body.getLevelParams();
+    HashSet<SymbolNode> lpSet = this.body.getLevelParams();
     for (int i = 0; i < this.substs.length; i++) {
       if (lpSet.contains(this.getSubFor(i))) {
 	this.level = Math.max(level, this.getSubWith(i).getLevel());
@@ -316,10 +321,9 @@ public class APSubstInNode extends LevelNode {
     }
 
 //    this.levelParams = new HashSet();
-    Iterator iter = lpSet.iterator();
+    Iterator<SymbolNode> iter = lpSet.iterator();
     while (iter.hasNext()) {
-      Object param = iter.next();
-      this.levelParams.addAll(Subst.paramSet(param, this.substs));
+      this.levelParams.addAll(Subst.paramSet(iter.next(), this.substs));
         /*******************************************************************
         * At this point, levelCheck(itr) has been invoked on              *
         * this.substs[i].getExpr() (which equals this.getSubWith(i)).      *
@@ -331,11 +335,10 @@ public class APSubstInNode extends LevelNode {
     * this.levelParams to compute this.allParams, except using             *
     * Subst.allParamSet instead of Subst.paramSet.                         *
     ***********************************************************************/
-    HashSet apSet = this.body.getAllParams();
+    HashSet<SymbolNode> apSet = this.body.getAllParams();
     iter = apSet.iterator();
     while (iter.hasNext()) {
-      Object param = iter.next();
-      this.allParams.addAll(Subst.allParamSet(param, this.substs));
+      this.allParams.addAll(Subst.allParamSet(iter.next(), this.substs));
         /*******************************************************************
         * At this point, levelCheck(itr) has been invoked on              *
         * this.substs[i].getExpr() (which equals this.getSubWith(i)).      *
@@ -392,6 +395,7 @@ public class APSubstInNode extends LevelNode {
 //           "ArgLevelParams: "      + this.argLevelParams      + "\n" ;
 //  }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -421,6 +425,7 @@ public class APSubstInNode extends LevelNode {
    * The children of this node are the body and the expressions
    * being substituted for symbols.
    */
+  @Override
   public SemanticNode[] getChildren() {
      SemanticNode[] res = new SemanticNode[this.substs.length + 1];
      res[0] = this.body;
@@ -430,31 +435,47 @@ public class APSubstInNode extends LevelNode {
      return res;
   }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
 
     if (this.substs != null) {
       for (int i = 0; i < this.substs.length; i++) {
-        if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable);
+        if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable, visitor);
       }
     }
-    if (this.body != null) this.body.walkGraph(semNodesTable);
+    if (this.body != null) this.body.walkGraph(semNodesTable, visitor);
+    visitor.postVisit(this);
     return;
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
       Element sbts = doc.createElement("substs");
       for (int i=0; i<substs.length; i++) {
         sbts.appendChild(substs[i].export(doc, context));
       }
       Element bdy = doc.createElement("body");
       bdy.appendChild(body.export(doc,context));
+
+      Element from = doc.createElement("instFrom");
+      Element fromchild = this.instantiatingModule.export(doc, context);
+      from.appendChild(fromchild);
+
+      Element to = doc.createElement("instTo");
+      Element tochild = instantiatedModule.export(doc,context);
+      to.appendChild(tochild);
+
+
       Element ret = doc.createElement("APSubstInNode");
       ret.appendChild(sbts);
       ret.appendChild(bdy);
+      ret.appendChild(from);
+      ret.appendChild(to);
 
       // at the end, we append the context of the symbols used in this node
       //ret.appendChild(context.export(doc));
@@ -462,8 +483,3 @@ public class APSubstInNode extends LevelNode {
       return ret;
     }
 }
-
-
-
-
-
diff --git a/tlatools/src/tla2sany/semantic/AnyDefNode.java b/tlatools/src/tla2sany/semantic/AnyDefNode.java
index 64b159da6ad1e4730e668221220e7f3de135c465..629e4e0b332d36d7cfa1523ae4ebe86320b16e5a 100644
--- a/tlatools/src/tla2sany/semantic/AnyDefNode.java
+++ b/tlatools/src/tla2sany/semantic/AnyDefNode.java
@@ -1,52 +1,53 @@
-/**
- * The AnyDefNode interface is implemented by OpDefNode and ThmOrAssumpDefNode.
- * It was added by LL on 24 Oct 2012 to fix a bug in the level-checking of
- * ThmOrAssumpDefNode objects.  When a module containing a theorem or assumption 
- * such as 
- * 
- *    THEOREM Fab == ...
- *    
- * is instantiated with parameters in a statement like
- * 
- *     I(x, y) == INSTANCE M WITH ...
- *     
- * then level checking of the expression I(e1, e2)!Fab did not check that
- * the levels of e1 and e2 were appropriate for the instantiation.  This was
- * because the levelCheck method of OpApplNode treated an operator like Fab
- * defined with a ThmOrAssumpDefNode differently from one defined with an 
- * OpDefNode.  To solve this problem, level checking of ThmOrAssumpDefNode was 
- * changed to be essentially the same as for OpDefNode.  The levelCheck method
- * of OpApplNode was changed to handle ThmOrAssumpDefNode and OpDefNode operators
- * the same by changing OpDefNode to AnyDefNode in a couple of places, and by 
- * replacing one access to an OpDefNode field by a call of a new method that
- * returns the value of that field.    
- */
-package tla2sany.semantic;
-
-import java.util.HashSet;
-
-import util.UniqueString;
-
-/**
- * @author lamport
- *
- */
-public interface AnyDefNode {
-    public boolean levelCheck(int itr) ;
-    public int getMaxLevel(int i) ;
-    public UniqueString getName() ;
-    public int getMinMaxLevel(int i, int j) ;
-    public boolean getOpLevelCond(int i, int j, int k) ;
-    public int getLevel() ;
-    public int getWeight(int i) ;
-    public HashSet getLevelParams() ;
-    public HashSet getAllParams() ;
-    public HashSet getNonLeibnizParams() ;
-    public int getArity() ;
-    public boolean[] getIsLeibnizArg() ;
-    public boolean getIsLeibniz() ;
-    public SetOfLevelConstraints getLevelConstraints() ;
-    public HashSet getArgLevelParams() ;
-    public SetOfArgLevelConstraints getArgLevelConstraints() ;
-    public FormalParamNode[] getParams() ;
-}
+/**
+ * The AnyDefNode interface is implemented by OpDefNode and ThmOrAssumpDefNode.
+ * It was added by LL on 24 Oct 2012 to fix a bug in the level-checking of
+ * ThmOrAssumpDefNode objects.  When a module containing a theorem or assumption 
+ * such as 
+ * 
+ *    THEOREM Fab == ...
+ *    
+ * is instantiated with parameters in a statement like
+ * 
+ *     I(x, y) == INSTANCE M WITH ...
+ *     
+ * then level checking of the expression I(e1, e2)!Fab did not check that
+ * the levels of e1 and e2 were appropriate for the instantiation.  This was
+ * because the levelCheck method of OpApplNode treated an operator like Fab
+ * defined with a ThmOrAssumpDefNode differently from one defined with an 
+ * OpDefNode.  To solve this problem, level checking of ThmOrAssumpDefNode was 
+ * changed to be essentially the same as for OpDefNode.  The levelCheck method
+ * of OpApplNode was changed to handle ThmOrAssumpDefNode and OpDefNode operators
+ * the same by changing OpDefNode to AnyDefNode in a couple of places, and by 
+ * replacing one access to an OpDefNode field by a call of a new method that
+ * returns the value of that field.    
+ */
+package tla2sany.semantic;
+
+import java.util.HashSet;
+
+import util.UniqueString;
+
+/**
+ * @author lamport
+ *
+ */
+public interface AnyDefNode {
+    public boolean levelCheck(int itr) ;
+    public int getMaxLevel(int i) ;
+    public UniqueString getName() ;
+    public int getMinMaxLevel(int i, int j) ;
+    public boolean getOpLevelCond(int i, int j, int k) ;
+    public int getLevel() ;
+    public int getWeight(int i) ;
+    public HashSet<SymbolNode> getLevelParams() ;
+    public HashSet<SymbolNode> getAllParams() ;
+    public HashSet<SymbolNode> getNonLeibnizParams() ;
+    public int getArity() ;
+    public boolean[] getIsLeibnizArg() ;
+    public boolean getIsLeibniz() ;
+    public SetOfLevelConstraints getLevelConstraints() ;
+    public HashSet<ArgLevelParam> getArgLevelParams() ;
+    public SetOfArgLevelConstraints getArgLevelConstraints() ;
+    public FormalParamNode[] getParams() ;
+    public AnyDefNode getSource();
+}
diff --git a/tlatools/src/tla2sany/semantic/AssumeNode.java b/tlatools/src/tla2sany/semantic/AssumeNode.java
index 961dacea486bbe3b894dc8ef743074a7a4170b4b..66aa37736f0c48c7b971dfd345beb60cd8f70cf0 100644
--- a/tlatools/src/tla2sany/semantic/AssumeNode.java
+++ b/tlatools/src/tla2sany/semantic/AssumeNode.java
@@ -5,13 +5,15 @@ package tla2sany.semantic;
 import java.util.HashSet;
 import java.util.Hashtable;
 
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-
-import tla2sany.xml.XMLExportable;
-import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
 
 /**
  * This class represents an assumption about the constants in a module.
@@ -80,6 +82,7 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn,
 
   /* Level checking */
   int levelChecked = 0 ;
+  @Override
   public final boolean levelCheck(int iter) {
     if (levelChecked >= iter) {return true ;} ;
     levelChecked = iter;
@@ -102,33 +105,40 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn,
     return res;
   }
 
+  @Override
   public final int getLevel() {
     return this.assumeExpr.getLevel();
   }
 
-  public final HashSet getLevelParams() {
+  @Override
+  public final HashSet<SymbolNode> getLevelParams() {
     return this.assumeExpr.getLevelParams();
   }
 
-  public final HashSet getAllParams() {
+  @Override
+  public final HashSet<SymbolNode> getAllParams() {
     return this.assumeExpr.getAllParams();
   }
 
+  @Override
   public final SetOfLevelConstraints getLevelConstraints() {
     return this.assumeExpr.getLevelConstraints();
   }
 
+  @Override
   public final SetOfArgLevelConstraints getArgLevelConstraints() {
     return this.assumeExpr.getArgLevelConstraints();
   }
 
-  public final HashSet getArgLevelParams() {
+  @Override
+  public final HashSet<ArgLevelParam> getArgLevelParams() {
     return this.assumeExpr.getArgLevelParams();
   }
 
   /**
    * toString(), levelDataToString(), and walkGraph() methods
    */
+  @Override
   public final String levelDataToString() {
     return "Level: "               + getLevel()               + "\n" +
            "LevelParameters: "     + getLevelParams()         + "\n" +
@@ -142,6 +152,7 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn,
    * interface; depth parameter is a bound on the depth of the portion
    * of the tree that is displayed.
    */
+  @Override
   public final String toString (int depth) {
     if (depth <= 0) return "";
     String res =
@@ -164,6 +175,7 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn,
    * The assume expression is the node's only child.
    */
 
+  @Override
   public SemanticNode[] getChildren() {
     return new SemanticNode[] {this.assumeExpr};
   }
@@ -173,26 +185,84 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn,
    * inserts them in the Hashtable semNodesTable for use by the
    * Explorer tool.
    */
-  public final void walkGraph (Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph (Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
 
     if (semNodesTable.get(uid) != null) return;
 
     semNodesTable.put(uid, this);
-    if (assumeExpr != null) {assumeExpr.walkGraph(semNodesTable);} ;
+    visitor.preVisit(this);
+    if (assumeExpr != null) {assumeExpr.walkGraph(semNodesTable, visitor);} ;
+    visitor.postVisit(this);
   }
 
-  public Element export(Document doc, tla2sany.xml.SymbolContext context) {
-    if (getDef() == null)
-      // we export the definition of the assumption
-      return super.export(doc,context);
-    else
-      // we export its name only, named assumptions will be exported through the ThmOrAss..
-      return getDef().export(doc,context);
+  /* MR: This is the same as SymbolNode.exportDefinition. Exports the actual theorem content, not only a reference.
+   */
+  public Element exportDefinition(Document doc, SymbolContext context) {
+    if (!context.isTop_level_entry())
+      throw new IllegalArgumentException("Exporting theorem ref "+getNodeRef()+" twice!");
+    context.resetTop_level_entry();
+    try {
+      Element e = getLevelElement(doc, context);
+      // level
+      try {
+        Element l = appendText(doc,"level",Integer.toString(getLevel()));
+        e.insertBefore(l,e.getFirstChild());
+      } catch (RuntimeException ee) {
+        // not sure it is legal for a LevelNode not to have level, debug it!
+      }
+      //location
+      try {
+        Element loc = getLocationElement(doc);
+        e.insertBefore(loc,e.getFirstChild());
+      } catch (RuntimeException ee) {
+        // do nothing if no location
+      }
+      return e;
+    } catch (RuntimeException ee) {
+      System.err.println("failed for node.toString(): " + toString() + "\n with error ");
+      ee.printStackTrace();
+      throw ee;
+    }
   }
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+
+  protected String getNodeRef() {
+    return "AssumeNodeRef";
+  }
+
+//  public Element export(Document doc, tla2sany.xml.SymbolContext context) {
+//    if (getDef() == null)
+//      // we export the definition of the assumption
+//      return super.export(doc,context);
+//    else
+//      // we export its name only, named assumptions will be exported through the ThmOrAss..
+//      return getDef().export(doc,context);
+//  }
+
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("AssumeNode");
-    e.appendChild(getAssume().export(doc,context));
+    if (def != null) {
+      //if there is a definition, export it too
+      Node d = doc.createElement("definition");
+      d.appendChild(def.export(doc, context));
+      e.appendChild(d);
+      assert( def.getBody() == this.assumeExpr ); //make sure theorem and definition body agree before export
+    }
+    Node n = doc.createElement("body");
+    n.appendChild(getAssume().export(doc,context));
+    e.appendChild(n);
+    return e;
+  }
+
+  /* overrides LevelNode.export and exports a UID reference instad of the full version*/
+  @Override
+  public Element export(Document doc, SymbolContext context) {
+    // first add symbol to context
+    context.put(this, doc);
+    Element e = doc.createElement(getNodeRef());
+    e.appendChild(appendText(doc,"UID",Integer.toString(myUID)));
     return e;
   }
 }
diff --git a/tlatools/src/tla2sany/semantic/AssumeProveNode.java b/tlatools/src/tla2sany/semantic/AssumeProveNode.java
index d2fd8dda8d60b187a9cdc605566843412d6abec3..28d0f6541b6641fab7bcf54c6beddeda7c34f1b7 100644
--- a/tlatools/src/tla2sany/semantic/AssumeProveNode.java
+++ b/tlatools/src/tla2sany/semantic/AssumeProveNode.java
@@ -6,12 +6,15 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+
 /***************************************************************************
 * An assume prove node represents something like                           *
 *                                                                          *
@@ -191,6 +194,7 @@ public class AssumeProveNode extends LevelNode {
   * compute the level-related fields for an OpApplNode.  This seems        *
   * reasonable, but I don't know if it's really correct.  -   LL           *
   *************************************************************************/
+  @Override
   public boolean levelCheck(int iter) {
     /***********************************************************************
     * Return immediately if this this.levelCheck(i) has already been       *
@@ -337,6 +341,7 @@ public class AssumeProveNode extends LevelNode {
   /**
    * The children of this node are the assumes and prove expressions.
    */
+  @Override
   public SemanticNode[] getChildren() {
      SemanticNode[] res = new SemanticNode[this.assumes.length + 1];
      res[assumes.length] = this.prove;
@@ -346,16 +351,19 @@ public class AssumeProveNode extends LevelNode {
      return res;
   }
 
-  public final void walkGraph(Hashtable h) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> h, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (h.get(uid) != null) return;
     h.put(uid, this);
+    visitor.preVisit(this);
     int i = 0 ;
     while (i <  assumes.length) {
-      assumes[i].walkGraph(h) ;
+      assumes[i].walkGraph(h, visitor) ;
       i = i+1;
      } ;
-    prove.walkGraph(h) ;
+    prove.walkGraph(h, visitor) ;
+    visitor.postVisit(this);
   } // end walkGraph()
 
 
@@ -363,6 +371,7 @@ public class AssumeProveNode extends LevelNode {
    * Displays this node as a String, implementing ExploreNode interface; depth
    * parameter is a bound on the depth of the portion of the tree that is displayed.
    */
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     String assumeStr = "" ;
@@ -385,7 +394,8 @@ public class AssumeProveNode extends LevelNode {
   * End fields and methods implementing the ExplorerNode interface:        *
   *************************************************************************/
 
-  public Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  public Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("AssumeProveNode");
     Element antecedent = doc.createElement("assumes");
     Element succedent = doc.createElement("prove");
diff --git a/tlatools/src/tla2sany/semantic/AtNode.java b/tlatools/src/tla2sany/semantic/AtNode.java
index 8c9d253caa9c63ec715a356ba0e78584b9365821..1d7f522ee9954272d71e926b0e7fa85b58efbc14 100644
--- a/tlatools/src/tla2sany/semantic/AtNode.java
+++ b/tlatools/src/tla2sany/semantic/AtNode.java
@@ -4,11 +4,14 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
-import tla2sany.utilities.Strings;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+
 public class AtNode extends ExprNode {
 
   private OpApplNode exceptRef;           // reference to OpApplNode for the innermost
@@ -72,6 +75,7 @@ public class AtNode extends ExprNode {
 //  private SetOfArgLevelConstraints argLevelConstraints;
 //  private HashSet argLevelParams;
 
+  @Override
   public final boolean levelCheck(int iter) {
     if (this.levelChecked >= iter) return true;
     this.levelChecked = iter;
@@ -147,9 +151,12 @@ public class AtNode extends ExprNode {
    * walkGraph finds all reachable nodes in the semantic graph
    * and inserts them in the Hashtable semNodesTable for use by the Explorer tool.
    */
-  public final void walkGraph(Hashtable h) {
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> h, ExplorerVisitor visitor) {
+	    visitor.preVisit(this);
   // Empty because there are no nodes reachable through an AtNode that are not
   // reachable by other paths through the semantic graph.
+	    visitor.postVisit(this);
   } // end walkGraph()
 
 
@@ -157,6 +164,7 @@ public class AtNode extends ExprNode {
    * Displays this node as a String, implementing ExploreNode interface; depth
    * parameter is a bound on the depth of the portion of the tree that is displayed.
    */
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     return "\n*AtNode: " + super.toString(depth) +
@@ -164,7 +172,8 @@ public class AtNode extends ExprNode {
                              "\nExceptComponent: " + exceptComponentRef.getUid());
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("AtNode");
     SemanticNode exceptObj = exceptRef.getArgs()[0];
     SemanticNode exceptComponents = exceptComponentRef.getArgs()[0];
diff --git a/tlatools/src/tla2sany/semantic/Context.java b/tlatools/src/tla2sany/semantic/Context.java
index c0a321ad7f5c5cb721add5ec47e89bcd43fb0f73..2181c5ce6096a1d550e4b748e33f8ecd87af5df7 100644
--- a/tlatools/src/tla2sany/semantic/Context.java
+++ b/tlatools/src/tla2sany/semantic/Context.java
@@ -8,18 +8,17 @@
 
 package tla2sany.semantic;
 
+import java.util.Collection;
 import java.util.Enumeration;
 import java.util.Hashtable;
 
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.Location;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 // A context contains def/declNodes only.
 // Implements a simple context for symbol decls and defs. Also
 // supports the intial (or global) context of built-in operators.
@@ -90,23 +89,23 @@ public class Context implements ExploreNode {
 
   public class InitialSymbolEnumeration {
 
-    Enumeration e = initialContext.content();
+    Enumeration<Pair> e = initialContext.content();
 
     public boolean hasMoreElements() { return e.hasMoreElements(); }
 
     public SymbolNode nextElement() {
-      return (SymbolNode)(((Pair)(e.nextElement())).getSymbol());
+      return e.nextElement().getSymbol();
     }
   }
 
   public class ContextSymbolEnumeration {
 
-    Enumeration e = Context.this.content();
+    Enumeration<Pair> e = Context.this.content();
 
     public boolean hasMoreElements() { return e.hasMoreElements(); }
 
     public SymbolNode nextElement() {
-      return ((Pair)(e.nextElement())).getSymbol();
+      return e.nextElement().getSymbol();
     }
 
    /* public Element export(Document doc) {
@@ -132,7 +131,7 @@ public class Context implements ExploreNode {
                                       // belongs to is null for global contex shared by all modules.
 
   private Errors         errors;      // Object in which to register errors
-  private Hashtable      table;       // Mapping from symbol name to Pair's that include SymbolNode's
+  private Hashtable<Object, Pair>      table;       // Mapping from symbol name to Pair's that include SymbolNode's
   private Pair           lastPair;    // Pair added last to the this.table
 
   /**
@@ -140,7 +139,7 @@ public class Context implements ExploreNode {
    * SymbolTable this Context is part of (or null).
    */
   public Context(ExternalModuleTable mt, Errors errs) {
-    table = new Hashtable();
+    table = new Hashtable<>();
     this.exMT = mt;
     this.errors = errs;
     this.lastPair = null;
@@ -163,6 +162,16 @@ public class Context implements ExploreNode {
   public static Context getGlobalContext() {
     return initialContext;
   }
+  
+	public static boolean isBuiltIn(final ExploreNode exploreNode) {
+		final Collection<Pair> pairs = initialContext.table.values();
+		for (Pair p : pairs) {
+			if (exploreNode == p.info) {
+				return true;
+			}
+		}
+		return false;
+	}
 
   public Errors getErrors() { return errors; }
 
@@ -216,7 +225,7 @@ public class Context implements ExploreNode {
    * Returns Enumeration of the elements of the Hashtable "Table",
    * which are pair of the form (Pair link, SymbolNode sn)
    */
-  public Enumeration content() {
+  public Enumeration<Pair> content() {
     return table.elements();
   }
 
@@ -232,11 +241,11 @@ public class Context implements ExploreNode {
    * Returns a Vector of those SymbolNodes in this Context that are
    * instances of class "template" (or one of its subclasses)
    */
-  public Vector getByClass( Class template ) {
-    Vector result = new Vector();
-    Enumeration list = table.elements();
+  public Vector<SymbolNode> getByClass( Class<?> template ) {
+    Vector<SymbolNode> result = new Vector<>();
+    Enumeration<Pair> list = table.elements();
     while (list.hasMoreElements()) {
-      Pair elt = (Pair)list.nextElement();
+      Pair elt = list.nextElement();
       if (template.isInstance(elt.info)) {
         result.addElement( elt.info );
       }
@@ -249,12 +258,12 @@ public class Context implements ExploreNode {
    * instances of class OpDefNode and that are NOT of kind BuiltInKind
    * or ModuleInstanceKind
    */
-  public Vector getOpDefs() {
+  public Vector<OpDefNode> getOpDefs() {
       // SZ Apr 21, 2009: not used instance
       // Class template = OpDefNode.class;
     Pair nextPair = lastPair;
 
-    Vector result = new Vector();
+    Vector<OpDefNode> result = new Vector<>();
     while (nextPair != null) {
       if ( nextPair.info instanceof OpDefNode &&     // true for superclasses too.
            ((OpDefNode)nextPair.info).getKind() != ASTConstants.ModuleInstanceKind &&
@@ -270,12 +279,12 @@ public class Context implements ExploreNode {
   * instances of class ThmOrAssumpDefNode or ModuleInstanceKind            *
   * Code copied from getOpDefs().                                          *
   *************************************************************************/
-  public Vector getThmOrAssDefs() {
+  public Vector<ThmOrAssumpDefNode> getThmOrAssDefs() {
       // SZ Apr 21, 2009: not used instance
       // Class template = ThmOrAssumpDefNode.class;
     Pair nextPair = lastPair;
 
-    Vector result = new Vector();
+    Vector<ThmOrAssumpDefNode> result = new Vector<>();
     while (nextPair != null) {
       if ( nextPair.info instanceof ThmOrAssumpDefNode)
         { result.addElement( (ThmOrAssumpDefNode)(nextPair.info) );} ;
@@ -287,13 +296,13 @@ public class Context implements ExploreNode {
   /**
    * Returns vector of OpDeclNodes that represent CONSTANT declarations
    */
-  public Vector getConstantDecls() {
-    Class templateClass = OpDeclNode.class;
-    Enumeration list = table.elements();
+  public Vector<SemanticNode> getConstantDecls() {
+    Class<? extends SemanticNode> templateClass = OpDeclNode.class;
+    Enumeration<Pair> list = table.elements();
 
-    Vector result = new Vector();
+    Vector<SemanticNode> result = new Vector<>();
     while (list.hasMoreElements()) {
-      Pair elt = (Pair)list.nextElement();
+      Pair elt = list.nextElement();
       if (templateClass.isInstance(elt.info) &&     // true for superclasses too.
          ((OpDeclNode)elt.info).getKind() == ASTConstants.ConstantDeclKind  )
         result.addElement( (SemanticNode)(elt.info) );
@@ -303,13 +312,13 @@ public class Context implements ExploreNode {
   }
 
   /* Returns vector of OpDeclNodes that represent CONSTANT declarations  */
-  public Vector getVariableDecls() {
-    Class templateClass = OpDeclNode.class;
-    Enumeration list = table.elements();
+  public Vector<SemanticNode> getVariableDecls() {
+    Class<? extends SemanticNode> templateClass = OpDeclNode.class;
+    Enumeration<Pair> list = table.elements();
 
-    Vector result = new Vector();
+    Vector<SemanticNode> result = new Vector<>();
     while (list.hasMoreElements()) {
-      Pair elt = (Pair)list.nextElement();
+      Pair elt = list.nextElement();
       if (templateClass.isInstance(elt.info) &&     // true for superclasses too.
            ((OpDeclNode)elt.info).getKind() == ASTConstants.VariableDeclKind  )
         result.addElement( (SemanticNode)(elt.info) );
@@ -321,13 +330,13 @@ public class Context implements ExploreNode {
    * Returns a Vector of those SymbolNodes in this Context that are
    * instances of class ModuleNode
    */
-  public Vector getModDefs() {
-    Class template = ModuleNode.class;
-    Enumeration list = table.elements();
+  public Vector<SemanticNode> getModDefs() {
+    Class<? extends SemanticNode> template = ModuleNode.class;
+    Enumeration<Pair> list = table.elements();
 
-    Vector result = new Vector();
+    Vector<SemanticNode> result = new Vector<>();
     while (list.hasMoreElements()) {
-      Pair elt = (Pair)list.nextElement();
+      Pair elt = list.nextElement();
       if (template.isInstance(elt.info))    // true for superclasses too.
         result.addElement( (SemanticNode)(elt.info) );
     }
@@ -351,72 +360,72 @@ public class Context implements ExploreNode {
    * The original implementation added the elements of Context ct to
    * this context in the inverse order as they appear in ct.  It was
    * changed on 12 Mar 2013 by LL to add them in the same order.
+   * 
+   * Note that the return value is never used in our code base. (2020.03.06)
    */
-  public boolean mergeExtendContext(Context ct) {
-    boolean erc = true;
-
-    // check locality, and multiplicity
-    // the .reversePairList was added to the following statement
-    // by LL on 12 Mar 2013
-    Pair p = ct.lastPair.reversePairList();
-    while (p != null) {
-      // Walk back along the list of pairs added to Context "ct"
-      SymbolNode sn = p.info;
-
-      // Ignore local symbols in Context "ct"
-      if (!sn.isLocal()) {
-	Object sName;
-	if (sn instanceof ModuleNode) {
-	  sName = new SymbolTable.ModuleName(sn.getName());
-	}
-	else {
-	  sName = sn.getName();
-	}
-
-        if (!table.containsKey(sName)) {
-	  // If this context DOES NOT contain this name, add it:
-	  table.put(sName, new Pair(sn));
-        }
-	else {
-	  // If this Context DOES contain this name
-          SymbolNode symbol = ((Pair)table.get(sName)).info;
-          if (symbol != sn) {
-	    // if the two SymbolNodes with the same name are distinct nodes,
-	    // We issue a warning or do nothing if they are instances of the same Java
-        // class--i.e. FormalParamNode, OpDeclNode, OpDefNode, or ModuleNode--doing
-        // nothing if they are both definitions coming from the same module.
-	    // otherwise, it is considered to be an error.
-        // Option of doing nothing if from same module added by LL on 31 Oct 2012 to
-        // fix problem caused by the same module being both EXTENDed and imported with
-        // a LOCAL INSTANCE.  Previously, it always added the warning.
-	    if (symbol.getClass() == sn.getClass()) {
-	        if (! symbol.sameOriginallyDefinedInModule(sn)) {
-              errors.addWarning( sn.getTreeNode().getLocation(),
-                                 "Warning: the " + kindOfNode(symbol) +  " of '" +
-                                 sName.toString() +
-                                 "' conflicts with \nits " + kindOfNode(symbol) + " at " +
-                                 symbol.getTreeNode().getLocation() + ".");
-	        }
-         }
-	    else {
-              errors.addError( sn.getTreeNode().getLocation(),
-                               "The " + kindOfNode(symbol) +  " of '" +
-                               sName.toString() +
-                               "' conflicts with \nits " + kindOfNode(symbol) + " at " +
-                               symbol.getTreeNode().getLocation() + ".");
+	public boolean mergeExtendContext(final Context ct) {
+		if (ct.lastPair == null) {
+			// If the context represents an inner module that defines no EXTENDS, ct.lastPair will be null
+			return true;
+		}
+		
+		boolean erc = true;
+		// check locality, and multiplicity
+		// the .reversePairList was added to the following statement
+		// by LL on 12 Mar 2013
+		Pair p = ct.lastPair.reversePairList();
+		while (p != null) {
+			// Walk back along the list of pairs added to Context "ct"
+			SymbolNode sn = p.info;
+
+			// Ignore local symbols in Context "ct"
+			if (!sn.isLocal()) {
+				Object sName;
+				if (sn instanceof ModuleNode) {
+					sName = new SymbolTable.ModuleName(sn.getName());
+				} else {
+					sName = sn.getName();
+				}
+
+				if (!table.containsKey(sName)) {
+					// If this context DOES NOT contain this name, add it:
+					table.put(sName, new Pair(sn));
+				} else {
+					// If this Context DOES contain this name
+					SymbolNode symbol = ((Pair) table.get(sName)).info;
+					if (symbol != sn) {
+						// if the two SymbolNodes with the same name are distinct nodes,
+						// We issue a warning or do nothing if they are instances of the same Java
+						// class--i.e. FormalParamNode, OpDeclNode, OpDefNode, or ModuleNode--doing
+						// nothing if they are both definitions coming from the same module.
+						// otherwise, it is considered to be an error.
+						// Option of doing nothing if from same module added by LL on 31 Oct 2012 to
+						// fix problem caused by the same module being both EXTENDed and imported with
+						// a LOCAL INSTANCE. Previously, it always added the warning.
+						if (symbol.getClass() == sn.getClass()) {
+							if (!symbol.sameOriginallyDefinedInModule(sn)) {
+								errors.addWarning(sn.getTreeNode().getLocation(),
+										"Warning: the " + kindOfNode(symbol) + " of '" + sName.toString()
+												+ "' conflicts with \nits " + kindOfNode(symbol) + " at "
+												+ symbol.getTreeNode().getLocation() + ".");
+							}
+						} else {
+							errors.addError(sn.getTreeNode().getLocation(),
+									"The " + kindOfNode(symbol) + " of '" + sName.toString() + "' conflicts with \nits "
+											+ kindOfNode(symbol) + " at " + symbol.getTreeNode().getLocation() + ".");
 
 //                               "Incompatible multiple definitions of symbol '" +
 //                               sName.toString() +
 //                               "'; \nthe conflicting declaration is at " +
 //                               symbol.getTreeNode().getLocation()+ ".");
-              erc = false;
-            } //end else
-          } // end if
-        } // end else
-      }
-      p  = p.link;
-    }
-    return erc;
+							erc = false;
+						} // end else
+					} // end if
+				} // end else
+			}
+			p = p.link;
+		}
+		return erc;
   }
 
   private static String kindOfNode(SymbolNode symbol) {
@@ -471,8 +480,8 @@ public class Context implements ExploreNode {
   * comment in the walkGraph method of this file for a bit more            *
   * information.                                                           *
   *************************************************************************/
-  public Vector getContextEntryStringVector(int depth, boolean b) {
-    Vector ctxtEntries = new Vector(100);  // vector of Strings
+  public Vector<String> getContextEntryStringVector(int depth, boolean b) {
+    Vector<String> ctxtEntries = new Vector<>(100);  // vector of Strings
     Context naturalsContext =
                exMT.getContext(UniqueString.uniqueStringOf("Naturals"));
 
@@ -496,7 +505,7 @@ public class Context implements ExploreNode {
     }
 
     // Reverse the order of elements in the vector so they print properly
-    Object obj;
+    String obj;
     int n = ctxtEntries.size();
     for (int i = 0; i < n/2; i++) {
       obj = ctxtEntries.elementAt(i);
@@ -506,11 +515,12 @@ public class Context implements ExploreNode {
     return ctxtEntries;
   }
 
-  public void walkGraph(Hashtable semNodesTable) {
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+	  visitor.preVisit(this);
     UniqueString key;
-    Enumeration  Enum = table.keys();
+    Enumeration<?>  e = table.keys();
 
-    while (Enum.hasMoreElements()) {
+    while (e.hasMoreElements()) {
       /*********************************************************************
       * Bug fix attempted by LL on 19 Apr 2007.                            *
       *                                                                    *
@@ -524,7 +534,7 @@ public class Context implements ExploreNode {
       * getContextEntryStringVector later on.  I decided to stop wasting   *
       * time on this.                                                      *
       *********************************************************************/
-      Object next = Enum.nextElement();
+      Object next = e.nextElement();
       if (next instanceof SymbolTable.ModuleName) {
          key = ((SymbolTable.ModuleName) next).name ;
          System.out.println("Bug in debugging caused by inner module " +
@@ -533,8 +543,9 @@ public class Context implements ExploreNode {
         }
       else {
         key = (UniqueString) next;
-        ((Pair)table.get(key)).info.walkGraph(semNodesTable);
+        ((Pair)table.get(key)).info.walkGraph(semNodesTable, visitor);
        } ;
+       visitor.postVisit(this);
     }
 
   }
diff --git a/tlatools/src/tla2sany/semantic/DecimalNode.java b/tlatools/src/tla2sany/semantic/DecimalNode.java
index 61995d1146a709ba0e0ae972a5ef84c906465120..22db04ecd4b140c17acb9ff17d0c2c9a67046eec 100644
--- a/tlatools/src/tla2sany/semantic/DecimalNode.java
+++ b/tlatools/src/tla2sany/semantic/DecimalNode.java
@@ -5,11 +5,14 @@ package tla2sany.semantic;
 import java.math.BigDecimal;
 import java.util.Hashtable;
 
-import tla2sany.st.TreeNode;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
+
 /**
  * Describes a decimal like 1347.052.  This number is represented by the
  * values
@@ -67,9 +70,11 @@ public class DecimalNode extends ExprNode {
    * Returns the value as a string, exactly the way the user typed it--e.g.,
    * without any normalization, removal of leading or trailing zero's, etc.
    */
+  @Override
   public final String toString() { return this.image; }
 
   /* Level checking */
+  @Override
   public final boolean levelCheck(int iter) {
     levelChecked = iter;
       /*********************************************************************
@@ -109,11 +114,14 @@ public class DecimalNode extends ExprNode {
    * inserts them in the Hashtable semNodesTable for use by the
    * Explorer tool.
    */
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    visitor.postVisit(this);
   }
 
   /**
@@ -121,6 +129,7 @@ public class DecimalNode extends ExprNode {
    * interface; depth parameter is a bound on the depth of the portion
    * of the tree that is displayed.
    */
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     return( "\n*DecimalNode" + super.toString(depth) + "Mantissa: "
@@ -130,7 +139,8 @@ public class DecimalNode extends ExprNode {
           );
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("DecimalNode");
     if (bigVal != null) {
       e.appendChild(appendText(doc,"mantissa",bigVal.unscaledValue().toString()));
diff --git a/tlatools/src/tla2sany/semantic/DefStepNode.java b/tlatools/src/tla2sany/semantic/DefStepNode.java
index 20f4de3b6ca80d1e73bf7b3db92a76f098c15cd2..2bba6d019525992375be96767c3a6d688c1ea385 100644
--- a/tlatools/src/tla2sany/semantic/DefStepNode.java
+++ b/tlatools/src/tla2sany/semantic/DefStepNode.java
@@ -1,92 +1,103 @@
-// Portions Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
-package tla2sany.semantic;
-
-import java.util.Hashtable;
-
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-import util.UniqueString;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/***************************************************************************
-* This class represents definition step of a proof, which consists of a    *
-* sequence of operator-, function-, or module-definition steps.  (A        *
-* module definition is something like "Id == INSTANCE...".)                *
-***************************************************************************/
-
-public class DefStepNode extends LevelNode {
-
-  /*************************************************************************
-  * The fields.                                                            *
-  *************************************************************************/
-  private UniqueString stepNumber ;
-    /***********************************************************************
-    * The step number of the step if it has one, otherwise null if it's    *
-    * not a numbered step.                                                 *
-    ***********************************************************************/
-
-  private OpDefNode[] defs ;
-    /***********************************************************************
-    * The sequence of definitions.                                         *
-    ***********************************************************************/
-
-  /*************************************************************************
-  * The constructor.                                                       *
-  *************************************************************************/
-  public DefStepNode(TreeNode stn, UniqueString stepNum, OpDefNode[] theDefs){
-    super(DefStepKind, stn);
-    this.stepNumber = stepNum;
-    this.defs = theDefs;
-   }
-
-  /*************************************************************************
-  * The methods just return the field values.                              *
-  *************************************************************************/
-  public UniqueString getStepNumber() {return stepNumber ;}
-  public OpDefNode[] getDefs() {return defs;}
-
-  public boolean levelCheck(int iter) {
-    /***********************************************************************
-    * Level check the steps and the instantiated modules coming from       *
-    * module definitions.                                                  *
-    ***********************************************************************/
-    return this.levelCheckSubnodes(iter, defs) ;
-   }
-
-  public void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
-    if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
-    for (int  i = 0; i < defs.length; i++) {
-      defs[i].walkGraph(semNodesTable);
-      } ;
-   }
-
-  public SemanticNode[] getChildren() {
-      SemanticNode[] res = new SemanticNode[defs.length];
-      for (int i = 0; i < defs.length; i++) {
-          res[i] = defs[i];
-      }
-      return res;
-   }
-  public String toString(int depth) {
-    if (depth <= 0) return "";
-    String ret = "\n*DefStepNode:\n"
-                  + super.toString(depth)
-                  + Strings.indent(2, "\ndefs:") ;
-    for (int i = 0 ; i < this.defs.length; i++) {
-        ret += Strings.indent(4, this.defs[i].toString(depth-1)) ;
-      } ;
-    return ret;
-   }
-
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
-      Element e = doc.createElement("DefStepNode");
-      for (int i=0; i<defs.length;i++) {
-        e.appendChild(defs[i].export(doc,context));
-      }
-      return e;
-    }
-}
+// Portions Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
+package tla2sany.semantic;
+
+import java.util.Hashtable;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+import util.UniqueString;
+
+/***************************************************************************
+* This class represents definition step of a proof, which consists of a    *
+* sequence of operator-, function-, or module-definition steps.  (A        *
+* module definition is something like "Id == INSTANCE...".)                *
+***************************************************************************/
+
+public class DefStepNode extends LevelNode {
+
+  /*************************************************************************
+  * The fields.                                                            *
+  *************************************************************************/
+  private UniqueString stepNumber ;
+    /***********************************************************************
+    * The step number of the step if it has one, otherwise null if it's    *
+    * not a numbered step.                                                 *
+    ***********************************************************************/
+
+  private OpDefNode[] defs ;
+    /***********************************************************************
+    * The sequence of definitions.                                         *
+    ***********************************************************************/
+
+  /*************************************************************************
+  * The constructor.                                                       *
+  *************************************************************************/
+  public DefStepNode(TreeNode stn, UniqueString stepNum, OpDefNode[] theDefs){
+    super(DefStepKind, stn);
+    this.stepNumber = stepNum;
+    this.defs = theDefs;
+   }
+
+  /*************************************************************************
+  * The methods just return the field values.                              *
+  *************************************************************************/
+  public UniqueString getStepNumber() {return stepNumber ;}
+  public OpDefNode[] getDefs() {return defs;}
+
+  @Override
+  public boolean levelCheck(int iter) {
+    /***********************************************************************
+    * Level check the steps and the instantiated modules coming from       *
+    * module definitions.                                                  *
+    ***********************************************************************/
+    return this.levelCheckSubnodes(iter, defs) ;
+   }
+
+  @Override
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
+    if (semNodesTable.get(uid) != null) return;
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    for (int  i = 0; i < defs.length; i++) {
+      defs[i].walkGraph(semNodesTable, visitor);
+      } ;
+      visitor.postVisit(this);
+   }
+
+  @Override
+  public SemanticNode[] getChildren() {
+      SemanticNode[] res = new SemanticNode[defs.length];
+      for (int i = 0; i < defs.length; i++) {
+          res[i] = defs[i];
+      }
+      return res;
+   }
+  
+  @Override
+  public String toString(int depth) {
+    if (depth <= 0) return "";
+    String ret = "\n*DefStepNode:\n"
+                  + super.toString(depth)
+                  + Strings.indent(2, "\ndefs:") ;
+    for (int i = 0 ; i < this.defs.length; i++) {
+        ret += Strings.indent(4, this.defs[i].toString(depth-1)) ;
+      } ;
+    return ret;
+   }
+
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
+      Element e = doc.createElement("DefStepNode");
+      for (int i=0; i<defs.length;i++) {
+        e.appendChild(defs[i].export(doc,context));
+      }
+      return e;
+    }
+}
diff --git a/tlatools/src/tla2sany/semantic/ExternalModuleTable.java b/tlatools/src/tla2sany/semantic/ExternalModuleTable.java
index 4a208f93b33d48482cf21d56aee097a5bfdbd56b..10835c4c439706e2ff39d34f57acffb205e95021 100644
--- a/tlatools/src/tla2sany/semantic/ExternalModuleTable.java
+++ b/tlatools/src/tla2sany/semantic/ExternalModuleTable.java
@@ -12,6 +12,7 @@ import java.util.Enumeration;
 import java.util.Hashtable;
 
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
 import util.UniqueString;
@@ -49,9 +50,9 @@ public class ExternalModuleTable implements ExploreNode {
      */
     public String levelDataToString() { return "Dummy level string"; }
 
-    public void walkGraph(Hashtable moduleNodesTable) {
-      if (moduleNode != null)   moduleNode.walkGraph(moduleNodesTable);
-      if (ctxt != null)      ctxt.walkGraph(moduleNodesTable);
+    public void walkGraph(Hashtable<Integer, ExploreNode> moduleNodesTable, ExplorerVisitor visitor) {
+      if (moduleNode != null)   moduleNode.walkGraph(moduleNodesTable, visitor);
+      if (ctxt != null)      ctxt.walkGraph(moduleNodesTable, visitor);
     } // end walkGraph()
 
     public String toString(int depth) {
@@ -80,21 +81,21 @@ public class ExternalModuleTable implements ExploreNode {
   * moduleHashTable, and that each of its entries has a moduleName as the  *
   * key and a value that's an ExternalModuleTableEntry object.             *
   *************************************************************************/
-  public Hashtable moduleHashTable;
+  public Hashtable<UniqueString, ExternalModuleTableEntry> moduleHashTable;
 
   // Vector moduleVector contains ModuleNodes (the same ones as
   // moduleHashTable), but preserves the order in which they were
   // inserted.  If module A depends on module B, then A has a HIGHER
   // index than B.
-  public Vector    moduleNodeVector;
+  public Vector<ModuleNode>    moduleNodeVector;
 
   // The nodule node of the root module
   public ModuleNode rootModule;
 
   // Constructor
   public ExternalModuleTable() {
-    moduleHashTable  = new Hashtable();
-    moduleNodeVector = new Vector();
+    moduleHashTable  = new Hashtable<>();
+    moduleNodeVector = new Vector<>();
   }
 
   // Set and get the rootModule field
@@ -102,11 +103,15 @@ public class ExternalModuleTable implements ExploreNode {
   public void       setRootModule(ModuleNode mn) { rootModule = mn; }
 
   public final Context getContext( UniqueString key ) {
-    ExternalModuleTableEntry p = (ExternalModuleTableEntry)moduleHashTable.get(key);
+    ExternalModuleTableEntry p = moduleHashTable.get(key);
     if (p == null) return null;
     return p.ctxt;
   }
 
+  public final Context getContextForRootModule() {
+	  return getContext(getRootModule().getName());
+  }
+  
   /**
    *  Returns a vector of ModuleNodes, one for each outer module (i.e. not
    *  inner modules) in the specification.  InnerModules can be obtained 
@@ -124,25 +129,26 @@ public class ExternalModuleTable implements ExploreNode {
   }
 
   public final ModuleNode getModuleNode( UniqueString key ) {
-    ExternalModuleTableEntry p = (ExternalModuleTableEntry)moduleHashTable.get(key);
+    ExternalModuleTableEntry p = moduleHashTable.get(key);
     if (p == null) return null;
     return p.moduleNode;
   }
 
   public final void put( UniqueString key, Context ctxt, ModuleNode moduleNode ) {
-    ExternalModuleTableEntry c = (ExternalModuleTableEntry)moduleHashTable.get( key );
+    ExternalModuleTableEntry c = moduleHashTable.get( key );
     if (c == null) {
       moduleHashTable.put( key, new ExternalModuleTableEntry(ctxt, moduleNode) );
       moduleNodeVector.addElement(moduleNode);
     }
   }
 
+  @Override
   public String toString() {
-    Enumeration Enum = moduleHashTable.elements();
+    Enumeration<ExternalModuleTableEntry> Enum = moduleHashTable.elements();
     String ret = "";
 
-    for (int i=1; Enum.hasMoreElements(); i++) {
-      ExternalModuleTableEntry mte = (ExternalModuleTableEntry)Enum.nextElement();
+    while (Enum.hasMoreElements()) {
+      ExternalModuleTableEntry mte = Enum.nextElement();
       ret = ret + mte.toString();
     }
     return "\nModule Table:" + Strings.indent(2,ret);
@@ -152,7 +158,7 @@ public class ExternalModuleTable implements ExploreNode {
     System.out.print("\nExternal Module Table:");
 
     for (int i = 0; i < moduleNodeVector.size(); i++) {
-      ModuleNode mn = (ModuleNode)moduleNodeVector.elementAt(i);
+      ModuleNode mn = moduleNodeVector.elementAt(i);
 
       if (mn != null) {
         System.out.print(Strings.indent(2, "\nModule: ")); 
@@ -186,12 +192,16 @@ public class ExternalModuleTable implements ExploreNode {
     return ret;
   }
 
-  public void walkGraph(Hashtable moduleNodesTable) {
-    Enumeration Enum = moduleHashTable.elements();
+  public void walkGraph(Hashtable<Integer, ExploreNode> moduleNodesTable) {
+	  walkGraph(moduleNodesTable, ExplorerVisitor.NoopVisitor);
+  }
+
+  public void walkGraph(Hashtable<Integer, ExploreNode> moduleNodesTable, ExplorerVisitor visitor) {
+    Enumeration<ExternalModuleTableEntry> Enum = moduleHashTable.elements();
 
     while ( Enum.hasMoreElements() ) {
-	ExternalModuleTableEntry mte = (ExternalModuleTableEntry)Enum.nextElement();
-	mte.walkGraph(moduleNodesTable);
+	ExternalModuleTableEntry mte = Enum.nextElement();
+	mte.walkGraph(moduleNodesTable, visitor);
     }
   }
 
diff --git a/tlatools/src/tla2sany/semantic/FormalParamNode.java b/tlatools/src/tla2sany/semantic/FormalParamNode.java
index 262db8715291eb8d6af6fda8daf56c58e00112ba..46f0756e9073d9fe90172350d7fc37c8028de0a7 100644
--- a/tlatools/src/tla2sany/semantic/FormalParamNode.java
+++ b/tlatools/src/tla2sany/semantic/FormalParamNode.java
@@ -4,12 +4,15 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
-import tla2sany.st.TreeNode;
-import util.UniqueString;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
+import util.UniqueString;
+
 /**
  * A FormalParamNode represents a formal parameter in a user
  * definition--for example, p and q in
@@ -67,6 +70,7 @@ public class FormalParamNode extends SymbolNode {
   /* Level checking */
 //  private HashSet levelParams;
 
+  @Override
   public final boolean levelCheck(int iter) {
     if (levelChecked == 0) {
       /*********************************************************************
@@ -111,13 +115,17 @@ public class FormalParamNode extends SymbolNode {
 //           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n" ;
 //  }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    visitor.postVisit(this);
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     return ("\n*FormalParamNode: " + this.getName().toString() +
@@ -128,7 +136,7 @@ public class FormalParamNode extends SymbolNode {
     return "FormalParamNodeRef";
   }
 
-  protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) {
+  protected Element getSymbolElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("FormalParamNode");
     e.appendChild(appendText(doc,"uniquename",getName().toString()));
     e.appendChild(appendText(doc,"arity",Integer.toString(getArity())));
diff --git a/tlatools/src/tla2sany/semantic/Generator.java b/tlatools/src/tla2sany/semantic/Generator.java
index d5917ac6bc0282566037012cb4221f6a14b3a6e8..9561f00c6b68eb2c329b7b3313d5ced9d66fcde2 100644
--- a/tlatools/src/tla2sany/semantic/Generator.java
+++ b/tlatools/src/tla2sany/semantic/Generator.java
@@ -33,8 +33,6 @@ import tlc2.output.MP;
 import util.UniqueString;
 import util.WrongInvocationException;
 
-
-
 // This class generates a semantic graph from a parse tree. It also uses
 // the list of modules to access contexts to instantiate or extend.
 
@@ -49,469 +47,435 @@ import util.WrongInvocationException;
 // are working from the inside.
 
 /***************************************************************************
-* Only one object of class Generator is ever created--namely, the object   *
-* gen in drivers/SANY.java.  So it seems like all fields and methods of    *
-* this class could have been made static.                                  *
-***************************************************************************/
-
-public class Generator implements ASTConstants, SyntaxTreeConstants,
-                                   LevelConstants, TLAplusParserConstants {
-
-  private Context     context;        // current context, not very useful.
-  private SymbolTable symbolTable;    // symbol table used throughout the spec.
-                                      //   except for embedded modules
-    /***********************************************************************
-    * A SymbolTable is actually a stack of contexts, where a context is a  *
-    * mapping from symbols to their meanings.  Elements are looked up in   *
-    * a SymbolTable by going through the stack of contexts from the top    *
-    * down.                                                                *
-    *                                                                      *
-    * symbolTable appears to contain the definitions and declarations of   *
-    * all symbols that are currently defined or declared.  When a          *
-    * construct containing local definitions or declarations is            *
-    * entered--for example, when constructing the ExprNode for a LET/IN,   *
-    * a new context is pushed onto symbolTable to hold those local defs    *
-    * or decls.  It appears that after the complete semantic ModuleNode    *
-    * has been created, symbolTable should contain only a single context,  *
-    * which becomes the ModuleNode's context.                              *
-    *                                                                      *
-    * symbolTable entries are made for SymbolNode objects (objects         *
-    * belonging to a subclass of SymbolNode).  It appears that all of      *
-    * those objects are added (to the context at the top of the stack)     *
-    * symbolTable by the SymbolNode object's constructor.                  *
-    ***********************************************************************/
-
-  private ExternalModuleTable moduleTable;
-  public  Errors      errors;
-  private Stack       excStack;       // Holds stack of OpApplNodes for $Except
-                                      //   operators; used for @
-  private Stack       excSpecStack;   // Holds stack of OpApplNode for @Pair
-                                      //   operators representing ExceptSpecs;
-                                      //   also used for @
-
-  // dummy definitions; used during the creation of the "-- TLA+ BUILTINS --" phony module,
-  // before real modules are processed; also used somewhat inconsistently to avoid returning
-  // null values, and to allow semantic analysis to proceed when an error is detected
-  private SubstInNode       nullSubstIn;
-  private FormalParamNode[] nullParam;
-  private OpDefNode         nullODN;
-  protected OpApplNode      nullOAN;
-  protected LabelNode       nullLabelNode;
-    /***********************************************************************
-    * This is a special OpApplNode that gets returned in various places    *
-    * when an error is detected.  This allows semantic analysis to         *
-    * continue, presumably by preventing a NullPointerException.           *
-    ***********************************************************************/
-  protected OpArgNode       nullOpArg;
-
-  private final static UniqueString S_e      = UniqueString.uniqueStringOf("\\E");
-  private final static UniqueString S_ex     = UniqueString.uniqueStringOf("\\exists");
-  private final static UniqueString S_f      = UniqueString.uniqueStringOf("\\A");
-  private final static UniqueString S_fx     = UniqueString.uniqueStringOf("\\always");
-  private final static UniqueString S_te     = UniqueString.uniqueStringOf("\\EE");
-  private final static UniqueString S_tf     = UniqueString.uniqueStringOf("\\AA");
-  private final static UniqueString S_a      = UniqueString.uniqueStringOf("<<");
-  private final static UniqueString S_brack  = UniqueString.uniqueStringOf("[");
-  private final static UniqueString S_sf     = UniqueString.uniqueStringOf("SF_");
-  private final static UniqueString S_wf     = UniqueString.uniqueStringOf("WF_");
-  private final static UniqueString S_at     = UniqueString.uniqueStringOf("@");
-  private final static UniqueString S_lambda = 
-                           UniqueString.uniqueStringOf("LAMBDA");
-  private final static UniqueString S_subexpression = 
-                           UniqueString.uniqueStringOf("$Subexpression");
-
-  class Function {
-    /***********************************************************************
-    * The only object of this class ever created is `functions', declared  *
-    * right after the class definition.  So, I don't know why this         *
-    * class's methods weren't just made ordinary methods of the Generator  *
-    * class.  Perhaps this was done so the subclass `pair' could be        *
-    * defined within it without interfering with any other subclass by     *
-    * that name.  However, there is no other subclass by that name in the  *
-    * Generator class.                                                     *
-    *                                                                      *
-    * See the comments attached to the `functions' object for the class's  *
-    * explanation.                                                         *
-    ***********************************************************************/
-    class pair{
-      UniqueString a;
-      OpApplNode   b;
-
-      pair(UniqueString uniqueString, OpApplNode oan) { a = uniqueString; b = oan; }
-      UniqueString uniqueString() { return a; }
-      OpApplNode   oan() { return b; }
-    }
-
-    Stack funcStack = new Stack();
-
-    void push(UniqueString uniqueString, OpApplNode oan) {
-      funcStack.push( new pair(uniqueString, oan) );
-    }
-
-    void pop() { funcStack.pop(); }
-
-    // If same function found farther down on stack, then this is a recursive
-    // function definition--change the operator to indicate so.
-    boolean recursionCheck(UniqueString uniqueString) {
-      for (int lvi = funcStack.size()-1; lvi>=0; lvi-- ) { 
-        if (uniqueString.equals( ((pair)funcStack.elementAt( lvi )).uniqueString())) {
-           // OA-rfs = recursive func spec
-           ((pair)funcStack.elementAt(lvi)).oan().resetOperator(OP_rfs); 
-           return true;
-        }
-      }
-      return false;
-    }
-
-  } // end class Function
-
-  // This is the only instance of class Function
-
-  Function functions = new Function();
-    /***********************************************************************
-    * This object is used to detect that a function definition is          *
-    * recursive.  It is a stack of <<UniqueString, OpApplNode>> pairs.     *
-    * An element <<f, opAp>> in the stack indicates that the current node  *
-    * being processed is inside the body of a function definition of the   *
-    * form f[...] == body.  The current context assigns to f an OpDefNode  *
-    * whose body is the OpApplNode opAp whose operator is either           *
-    * $RecursiveFcnSpec or $NonRecursiveFcnSpec having (an OpDeclNode      *
-    * for) the bound identifier f and having the body of the definition    *
-    * as its operand.  The following methods can be applied:               *
-    *                                                                      *
-    *   functions.push(UniqueString us, OpApplNode oan)                    *
-    *      Pushes <<us, oan>> onto the stack.                              *
-    *                                                                      *
-    *   functions.pop()                                                    *
-    *      Pops the last element off the stack.                            *
-    *                                                                      *
-    *   recursionCheck(UniqueString us)                                    *
-    *      If there is a pair <<us, oan>> on the stack, then the           *
-    *      operator of oan is set to $RecursiveFcnSpec.                    *
-    *                                                                      *
-    * An entry for f is pushed on the stack before processing the body     *
-    * f's definition, and popped upon return.                              *
-    *                                                                      *
-    * When processing a FcnAppl syntax nodefor an expression f[...],       *
-    * functions.recursionCheck is called to see the node occurs in the     *
-    * body of the definition of f, in which case the definition body's     *
-    * OpApplNode's operator is changed to indicate that f's definition is  *
-    * recursive.                                                           *
-    ***********************************************************************/
-    
-
-
-  // This class represents a generalized identifier, e.g. a syntactic 
-  // phrase such as A(2)!B(x,y)!C!D(u,v,w)  In this case the compoundID 
-  // would be A!B!C!D and the args array would contain [2,x,y]  
-  // (i.e. not including u,v,and w, because they are 
-  // args to the main operator, and not part of the GenID
-  private class GenID {
-
-    private TreeNode          treeNode;      
-      // The syntax tree node holding this GenID
-    private StringBuffer      compoundID;    
-      // The string name of the compound op, with "!"'s, if any
-    private Vector            argsVector = new Vector();        
-      // Vector of arguments (ExprNodes and OpArgNodes)
-      // that are embedded in the generalized identifier
-
-    // The next three fields are null until the finalAppend 
-    // method has been called
-    private UniqueString      compoundIDUS;      
-       // UniqueString version of compoundID
-    private SymbolNode        fullyQualifiedOp;  
-       // SymbolNode for compoundID
-    private ExprOrOpArgNode[] args;              
-      // Array with same contents as argsVector
-
-    // Constructor
-    public GenID(TreeNode node) {
-      treeNode         = node;
-      compoundID       = new StringBuffer("");
-      compoundIDUS     = null;
-      fullyQualifiedOp = null;
-      args             = null;
-    }
-
-    public final UniqueString getCompoundIDUS() { return compoundIDUS;}
-
-    public final SymbolNode getFullyQualifiedOp() { return fullyQualifiedOp; }
-
-    public final ExprOrOpArgNode[] getArgs() { return args; }
-
-    public final Vector getArgsVector() { return argsVector; }
-
-    /** Append a new segment to the compound name of the operator */
-    public final void append(String s) { 
-      compoundID.append(s);
-    }
-
-    /** Add a new argument to the vector of arguments being constructed */
-    public final void addArg(ExprOrOpArgNode arg) {
-      argsVector.addElement(arg);
-    }
-
-    /**
-     * Appends the final element of the fully-qualified name to the
-     * GenID that has been being built using addArg() and
-     * append(). Since it signals the completion of the construction
-     * of the name, this method converts the name from StringBuffer to
-     * UniqueString, resolves it to a SymbolNode, and converts the
-     * argument list from vector to array form.
-     */
-    public final void finalAppend(String s, boolean unaryNegKludge) {
-      // append the final segment of the compound name
-      if (unaryNegKludge && s.equals("-")) {
-        compoundID.append("-.");
-      }
-      else {
-        compoundID.append(s);
-      }
-
-      // convert the full name to a UniqueString
-      compoundIDUS = UniqueString.uniqueStringOf(compoundID.toString());
-
-      // look up the full name in the SymbolTable (may return null)
-      fullyQualifiedOp = symbolTable.resolveSymbol(Operators.resolveSynonym(compoundIDUS));
-
-      if (fullyQualifiedOp == null && compoundIDUS != S_at) {
-        // if not in the symbol table and not "@", then it is an unresolved symbol
-        errors.addError(treeNode.getLocation(),
-                        "Could not find declaration or definition of symbol '" +
-                        UniqueString.uniqueStringOf(compoundID.toString()) + "'.");
-      }
-    }
-
-    public final void finalizeID() {
-      // copy argsVector contents into args array
-      args = new ExprOrOpArgNode[argsVector.size()];
-
-      for (int i = 0; i < args.length; i++) {
-        args[i] = (ExprOrOpArgNode)argsVector.elementAt(i);
-      }
-    }
-
-    /**
-     * Special kluge to append a "." to the name of this ID; 
-     * should be used ONLY to change unary "-" to "-."
-     */
-    public final void appendDot() {
-      compoundIDUS = UniqueString.uniqueStringOf(compoundIDUS.toString() + ".");
-    }
-
-    public final String toString(int n) {
-      String ret = "compound ID: " + compoundID.toString() + "\nargs: " + args.length + "\n";
-      for (int i = 0; i < args.length; i++) {
-        ret += Strings.indent(2,args[i].toString(n));
-      }
-      return ret;
-    }
-
-  } // end GenID
-
-
-  final UniqueString AtUS    = UniqueString.uniqueStringOf("@") ;
-    /***********************************************************************
-    * This is needed in a couple of places.  (I didn't notice that this    *
-    * is already defined to equal S_at.)                                   *
-    ***********************************************************************/
-
-  /*************************************************************************
-  * An object of subclass Selector is used to represent a the translation  *
-  * of a GeneralId or an OpApplication whose operator is a GeneralId.  It  *
-  * contains the data represented in the Subexpression +cal algorithm by   *
-  * the arrays ops and args.  For example, the user expression             *
-  *                                                                        *
-  *   Foo(a,b)!:!(c)                                                       *
-  *                                                                        *
-  * produces the following in Subexpression algorithm:                     *
-  *                                                                        *
-  *   ops  = << "Foo",    ":",   "null"    >>                              *
-  *   args = << <<a, b>>, << >>,  << <<c>> >>,                             *
-  *                                                                        *
-  * This is represented by a Selector object with four arrays of           *
-  * length 3.  The first is:                                               *
-  *                                                                        *
-  *   int[] ops with  ops[0] = NameSel                                     *
-  *                   ops[1] = ColonSel                                    *
-  *                   ops[2] = NullSel                                     *
-  *                                                                        *
-  * The set of all possible values of ops[i] are:                          *
-  *                                                                        *
-  *   An integer i >= 0 // Represents the i of  "!i".                      *
-  *                                                                        *
-  *   One of the following integers < 0.                                   *
-  *************************************************************************/
-
-  final int NameSel  = -1 ; // A name like "Foo"  ;
-  final int NullSel  = -2 ; // A "(...)" selector ;
-  final int GGSel    = -3 ; // ">>" ;
-  final int LLSel    = -4 ; // "<<" ;
-  final int ColonSel = -5 ; // ":"  ;
-  final int AtSel    = -6 ; // "@"  ;
-   
-  /*************************************************************************
-  * The remaining three arrays are:                                        *
-  *                                                                        *
-  *   SyntaxTreeNode[] opsSTN with values equal to the syntax tree         *
-  *                                nodes for the corresponding element     *
-  *                                of ops.                                 *
-  *                                                                        *
-  *   UniqueString[] opNames with opNames[0] = "Foo"                       *
-  *                               opNames[1] = ":"                         *
-  *                               opNames[2] = null                        *
-  *                                                                        *
-  *   SyntaxTreeNode[] args with args[0] = "(a, b)"                        *
-  *                              args[1] = null                            *
-  *                              args[2] = "(c)"                           *
-  *                                                                        *
-  * Note: we can't turn the arguments into semantic nodes yet because we   *
-  * want to use generateExprOrOpArg to do that, which requires that we     *
-  * know the operator of which they're the arguments.  Hence, that         *
-  * computation has to be folded into the Subexpression algorithm.         *
-  *                                                                        *
-  * These arrays are initialized by the finish method, which is called     *
-  * after all calls of addSelector have been issued.                       *
-  *************************************************************************/
-  private class Selector {
-     /**********************************************************************
-     * A single field for the syntax tree node of the entire selector.     *
-     **********************************************************************/
-     SyntaxTreeNode selSTN ;
-          
-     /**********************************************************************
-     * The fields modified by addSelector:                                 *
-     **********************************************************************/
-     private Vector opVec    = new Vector(); // of SyntaxTreeNode    ;
-     private Vector argVec   = new Vector(); // of SyntaxTreeNode ;
-
-     /**********************************************************************
-     * The fields set from opVec and argVec by finalize.                   *
-     **********************************************************************/
-     int[] ops                = null ;
-     UniqueString[] opNames   = null ;
-     SyntaxTreeNode[] opsSTN  = null ;    
-     SyntaxTreeNode[] args    = null ;
-
-     /**********************************************************************
-     * Constants not needed elsewhere.                                     *
-     **********************************************************************/
-     final UniqueString GGUS    = UniqueString.uniqueStringOf(">>") ;
-     final UniqueString LLUS    = UniqueString.uniqueStringOf("<<") ;
-     final UniqueString ColonUS = UniqueString.uniqueStringOf(":") ;
-
-     /**********************************************************************
-     * The constructor.                                                    *
-     **********************************************************************/
-
-     Selector(SyntaxTreeNode tn) {
-       selSTN = tn ;
-       }     
-     void addSelector(SyntaxTreeNode stn, SyntaxTreeNode opargs) {
-       /********************************************************************
-       * opargs is the args entry.                                         *
-       *                                                                   *
-       * stn is the syntax tree node representing the ops entry.  If       *
-       * there are no arguments, it should be null.  For a NullSel entry   *
-       * it should be the OpArgs node (the "(arg1, ...  , argN)") that is  *
-       * used as the second argument.                                      *
-       ********************************************************************/
-       opVec.addElement(stn) ;
-       argVec.addElement(opargs) ;
-      } // addSelector
-
-     void finish() throws AbortException {
-       int arrayLen = opVec.size() ;
-       ops     = new int[arrayLen] ;
-       opNames = new UniqueString[arrayLen]  ;
-       opsSTN  = new SyntaxTreeNode[arrayLen] ;    
-       args    = new SyntaxTreeNode[arrayLen] ;    
-       for (int i = 0; i < arrayLen; i++) {
-         args[i] = (SyntaxTreeNode) argVec.elementAt(i) ;
-         SyntaxTreeNode stn = (SyntaxTreeNode) opVec.elementAt(i) ;
-         opsSTN[i] = stn ;
-         opNames[i] = stn.getUS() ;
-         switch (stn.getKind()) {
-           case  IDENTIFIER:
-           case  N_InfixOp:
-           case  N_NonExpPrefixOp:
-           case  N_PostfixOp:
-           case  N_PrefixOp:
-           case  ProofStepLexeme:
+ * Only one object of class Generator is ever created--namely, the object * gen
+ * in drivers/SANY.java. So it seems like all fields and methods of * this class
+ * could have been made static. *
+ ***************************************************************************/
+
+public class Generator implements ASTConstants, SyntaxTreeConstants, LevelConstants, TLAplusParserConstants {
+
+	private Context context; // current context, not very useful.
+	private SymbolTable symbolTable; // symbol table used throughout the spec.
+										// except for embedded modules
+	/***********************************************************************
+	 * A SymbolTable is actually a stack of contexts, where a context is a * mapping
+	 * from symbols to their meanings. Elements are looked up in * a SymbolTable by
+	 * going through the stack of contexts from the top * down. * * symbolTable
+	 * appears to contain the definitions and declarations of * all symbols that are
+	 * currently defined or declared. When a * construct containing local
+	 * definitions or declarations is * entered--for example, when constructing the
+	 * ExprNode for a LET/IN, * a new context is pushed onto symbolTable to hold
+	 * those local defs * or decls. It appears that after the complete semantic
+	 * ModuleNode * has been created, symbolTable should contain only a single
+	 * context, * which becomes the ModuleNode's context. * * symbolTable entries
+	 * are made for SymbolNode objects (objects * belonging to a subclass of
+	 * SymbolNode). It appears that all of * those objects are added (to the context
+	 * at the top of the stack) * symbolTable by the SymbolNode object's
+	 * constructor. *
+	 ***********************************************************************/
+
+	private ExternalModuleTable moduleTable;
+	public Errors errors;
+	private Stack excStack; // Holds stack of OpApplNodes for $Except
+							// operators; used for @
+	private Stack excSpecStack; // Holds stack of OpApplNode for @Pair
+								// operators representing ExceptSpecs;
+								// also used for @
+
+	// dummy definitions; used during the creation of the "-- TLA+ BUILTINS --"
+	// phony module,
+	// before real modules are processed; also used somewhat inconsistently to avoid
+	// returning
+	// null values, and to allow semantic analysis to proceed when an error is
+	// detected
+	private SubstInNode nullSubstIn;
+	private FormalParamNode[] nullParam;
+	private OpDefNode nullODN;
+	protected OpApplNode nullOAN;
+	protected LabelNode nullLabelNode;
+	/***********************************************************************
+	 * This is a special OpApplNode that gets returned in various places * when an
+	 * error is detected. This allows semantic analysis to * continue, presumably by
+	 * preventing a NullPointerException. *
+	 ***********************************************************************/
+	protected OpArgNode nullOpArg;
+
+	private final static UniqueString S_e = UniqueString.uniqueStringOf("\\E");
+	private final static UniqueString S_ex = UniqueString.uniqueStringOf("\\exists");
+	private final static UniqueString S_f = UniqueString.uniqueStringOf("\\A");
+	private final static UniqueString S_fx = UniqueString.uniqueStringOf("\\always");
+	private final static UniqueString S_te = UniqueString.uniqueStringOf("\\EE");
+	private final static UniqueString S_tf = UniqueString.uniqueStringOf("\\AA");
+	private final static UniqueString S_a = UniqueString.uniqueStringOf("<<");
+	private final static UniqueString S_brack = UniqueString.uniqueStringOf("[");
+	private final static UniqueString S_sf = UniqueString.uniqueStringOf("SF_");
+	private final static UniqueString S_wf = UniqueString.uniqueStringOf("WF_");
+	private final static UniqueString S_at = UniqueString.uniqueStringOf("@");
+	private final static UniqueString S_lambda = UniqueString.uniqueStringOf("LAMBDA");
+	private final static UniqueString S_subexpression = UniqueString.uniqueStringOf("$Subexpression");
+
+	class Function {
+		/***********************************************************************
+		 * The only object of this class ever created is `functions', declared * right
+		 * after the class definition. So, I don't know why this * class's methods
+		 * weren't just made ordinary methods of the Generator * class. Perhaps this was
+		 * done so the subclass `pair' could be * defined within it without interfering
+		 * with any other subclass by * that name. However, there is no other subclass
+		 * by that name in the * Generator class. * * See the comments attached to the
+		 * `functions' object for the class's * explanation. *
+		 ***********************************************************************/
+		class pair {
+			UniqueString a;
+			OpApplNode b;
+
+			pair(UniqueString uniqueString, OpApplNode oan) {
+				a = uniqueString;
+				b = oan;
+			}
+
+			UniqueString uniqueString() {
+				return a;
+			}
+
+			OpApplNode oan() {
+				return b;
+			}
+		}
+
+		Stack funcStack = new Stack();
+
+		void push(UniqueString uniqueString, OpApplNode oan) {
+			funcStack.push(new pair(uniqueString, oan));
+		}
+
+		void pop() {
+			funcStack.pop();
+		}
+
+		// If same function found farther down on stack, then this is a recursive
+		// function definition--change the operator to indicate so.
+		boolean recursionCheck(UniqueString uniqueString) {
+			for (int lvi = funcStack.size() - 1; lvi >= 0; lvi--) {
+				if (uniqueString.equals(((pair) funcStack.elementAt(lvi)).uniqueString())) {
+					// OA-rfs = recursive func spec
+					((pair) funcStack.elementAt(lvi)).oan().resetOperator(OP_rfs);
+					return true;
+				}
+			}
+			return false;
+		}
+
+	} // end class Function
+
+	// This is the only instance of class Function
+
+	Function functions = new Function();
+
+	/***********************************************************************
+	 * This object is used to detect that a function definition is * recursive. It
+	 * is a stack of <<UniqueString, OpApplNode>> pairs. * An element <<f, opAp>> in
+	 * the stack indicates that the current node * being processed is inside the
+	 * body of a function definition of the * form f[...] == body. The current
+	 * context assigns to f an OpDefNode * whose body is the OpApplNode opAp whose
+	 * operator is either * $RecursiveFcnSpec or $NonRecursiveFcnSpec having (an
+	 * OpDeclNode * for) the bound identifier f and having the body of the
+	 * definition * as its operand. The following methods can be applied: * *
+	 * functions.push(UniqueString us, OpApplNode oan) * Pushes <<us, oan>> onto the
+	 * stack. * * functions.pop() * Pops the last element off the stack. * *
+	 * recursionCheck(UniqueString us) * If there is a pair <<us, oan>> on the
+	 * stack, then the * operator of oan is set to $RecursiveFcnSpec. * * An entry
+	 * for f is pushed on the stack before processing the body * f's definition, and
+	 * popped upon return. * * When processing a FcnAppl syntax nodefor an
+	 * expression f[...], * functions.recursionCheck is called to see the node
+	 * occurs in the * body of the definition of f, in which case the definition
+	 * body's * OpApplNode's operator is changed to indicate that f's definition is
+	 * * recursive. *
+	 ***********************************************************************/
+
+	// This class represents a generalized identifier, e.g. a syntactic
+	// phrase such as A(2)!B(x,y)!C!D(u,v,w) In this case the compoundID
+	// would be A!B!C!D and the args array would contain [2,x,y]
+	// (i.e. not including u,v,and w, because they are
+	// args to the main operator, and not part of the GenID
+	private class GenID {
+
+		private TreeNode treeNode;
+		// The syntax tree node holding this GenID
+		private StringBuffer compoundID;
+		// The string name of the compound op, with "!"'s, if any
+		private Vector argsVector = new Vector();
+		// Vector of arguments (ExprNodes and OpArgNodes)
+		// that are embedded in the generalized identifier
+
+		// The next three fields are null until the finalAppend
+		// method has been called
+		private UniqueString compoundIDUS;
+		// UniqueString version of compoundID
+		private SymbolNode fullyQualifiedOp;
+		// SymbolNode for compoundID
+		private ExprOrOpArgNode[] args;
+		// Array with same contents as argsVector
+
+		// Constructor
+		public GenID(TreeNode node) {
+			treeNode = node;
+			compoundID = new StringBuffer("");
+			compoundIDUS = null;
+			fullyQualifiedOp = null;
+			args = null;
+		}
+
+		public final UniqueString getCompoundIDUS() {
+			return compoundIDUS;
+		}
+
+		public final SymbolNode getFullyQualifiedOp() {
+			return fullyQualifiedOp;
+		}
+
+		public final ExprOrOpArgNode[] getArgs() {
+			return args;
+		}
+
+		public final Vector getArgsVector() {
+			return argsVector;
+		}
+
+		/** Append a new segment to the compound name of the operator */
+		public final void append(String s) {
+			compoundID.append(s);
+		}
+
+		/** Add a new argument to the vector of arguments being constructed */
+		public final void addArg(ExprOrOpArgNode arg) {
+			argsVector.addElement(arg);
+		}
+
+		/**
+		 * Appends the final element of the fully-qualified name to the GenID that has
+		 * been being built using addArg() and append(). Since it signals the completion
+		 * of the construction of the name, this method converts the name from
+		 * StringBuffer to UniqueString, resolves it to a SymbolNode, and converts the
+		 * argument list from vector to array form.
+		 */
+		public final void finalAppend(String s, boolean unaryNegKludge) {
+			// append the final segment of the compound name
+			if (unaryNegKludge && s.equals("-")) {
+				compoundID.append("-.");
+			} else {
+				compoundID.append(s);
+			}
+
+			// convert the full name to a UniqueString
+			compoundIDUS = UniqueString.uniqueStringOf(compoundID.toString());
+
+			// look up the full name in the SymbolTable (may return null)
+			fullyQualifiedOp = symbolTable.resolveSymbol(Operators.resolveSynonym(compoundIDUS));
+
+			if (fullyQualifiedOp == null && compoundIDUS != S_at) {
+				// if not in the symbol table and not "@", then it is an unresolved symbol
+				errors.addError(treeNode.getLocation(), "Could not find declaration or definition of symbol '"
+						+ UniqueString.uniqueStringOf(compoundID.toString()) + "'.");
+			}
+		}
+
+		public final void finalizeID() {
+			// copy argsVector contents into args array
+			args = new ExprOrOpArgNode[argsVector.size()];
+
+			for (int i = 0; i < args.length; i++) {
+				args[i] = (ExprOrOpArgNode) argsVector.elementAt(i);
+			}
+		}
+
+		/**
+		 * Special kluge to append a "." to the name of this ID; should be used ONLY to
+		 * change unary "-" to "-."
+		 */
+		public final void appendDot() {
+			compoundIDUS = UniqueString.uniqueStringOf(compoundIDUS.toString() + ".");
+		}
+
+		public final String toString(int n) {
+			String ret = "compound ID: " + compoundID.toString() + "\nargs: " + args.length + "\n";
+			for (int i = 0; i < args.length; i++) {
+				ret += Strings.indent(2, args[i].toString(n));
+			}
+			return ret;
+		}
+
+	} // end GenID
+
+	final UniqueString AtUS = UniqueString.uniqueStringOf("@");
+	/***********************************************************************
+	 * This is needed in a couple of places. (I didn't notice that this * is already
+	 * defined to equal S_at.) *
+	 ***********************************************************************/
+
+	/*************************************************************************
+	 * An object of subclass Selector is used to represent a the translation * of a
+	 * GeneralId or an OpApplication whose operator is a GeneralId. It * contains
+	 * the data represented in the Subexpression +cal algorithm by * the arrays ops
+	 * and args. For example, the user expression * * Foo(a,b)!:!(c) * * produces
+	 * the following in Subexpression algorithm: * * ops = << "Foo", ":", "null" >>
+	 * * args = << <<a, b>>, << >>, << <<c>> >>, * * This is represented by a
+	 * Selector object with four arrays of * length 3. The first is: * * int[] ops
+	 * with ops[0] = NameSel * ops[1] = ColonSel * ops[2] = NullSel * * The set of
+	 * all possible values of ops[i] are: * * An integer i >= 0 // Represents the i
+	 * of "!i". * * One of the following integers < 0. *
+	 *************************************************************************/
+
+	final int NameSel = -1; // A name like "Foo" ;
+	final int NullSel = -2; // A "(...)" selector ;
+	final int GGSel = -3; // ">>" ;
+	final int LLSel = -4; // "<<" ;
+	final int ColonSel = -5; // ":" ;
+	final int AtSel = -6; // "@" ;
+
+	/*************************************************************************
+	 * The remaining three arrays are: * * SyntaxTreeNode[] opsSTN with values equal
+	 * to the syntax tree * nodes for the corresponding element * of ops. * *
+	 * UniqueString[] opNames with opNames[0] = "Foo" * opNames[1] = ":" *
+	 * opNames[2] = null * * SyntaxTreeNode[] args with args[0] = "(a, b)" * args[1]
+	 * = null * args[2] = "(c)" * * Note: we can't turn the arguments into semantic
+	 * nodes yet because we * want to use generateExprOrOpArg to do that, which
+	 * requires that we * know the operator of which they're the arguments. Hence,
+	 * that * computation has to be folded into the Subexpression algorithm. * *
+	 * These arrays are initialized by the finish method, which is called * after
+	 * all calls of addSelector have been issued. *
+	 *************************************************************************/
+	private class Selector {
+		/**********************************************************************
+		 * A single field for the syntax tree node of the entire selector. *
+		 **********************************************************************/
+		SyntaxTreeNode selSTN;
+
+		/**********************************************************************
+		 * The fields modified by addSelector: *
+		 **********************************************************************/
+		private Vector opVec = new Vector(); // of SyntaxTreeNode ;
+		private Vector argVec = new Vector(); // of SyntaxTreeNode ;
+
+		/**********************************************************************
+		 * The fields set from opVec and argVec by finalize. *
+		 **********************************************************************/
+		int[] ops = null;
+		UniqueString[] opNames = null;
+		SyntaxTreeNode[] opsSTN = null;
+		SyntaxTreeNode[] args = null;
+
+		/**********************************************************************
+		 * Constants not needed elsewhere. *
+		 **********************************************************************/
+		final UniqueString GGUS = UniqueString.uniqueStringOf(">>");
+		final UniqueString LLUS = UniqueString.uniqueStringOf("<<");
+		final UniqueString ColonUS = UniqueString.uniqueStringOf(":");
+
+		/**********************************************************************
+		 * The constructor. *
+		 **********************************************************************/
+
+		Selector(SyntaxTreeNode tn) {
+			selSTN = tn;
+		}
+
+		void addSelector(SyntaxTreeNode stn, SyntaxTreeNode opargs) {
+			/********************************************************************
+			 * opargs is the args entry. * * stn is the syntax tree node representing the
+			 * ops entry. If * there are no arguments, it should be null. For a NullSel
+			 * entry * it should be the OpArgs node (the "(arg1, ... , argN)") that is *
+			 * used as the second argument. *
+			 ********************************************************************/
+			opVec.addElement(stn);
+			argVec.addElement(opargs);
+		} // addSelector
+
+		void finish() throws AbortException {
+			int arrayLen = opVec.size();
+			ops = new int[arrayLen];
+			opNames = new UniqueString[arrayLen];
+			opsSTN = new SyntaxTreeNode[arrayLen];
+			args = new SyntaxTreeNode[arrayLen];
+			for (int i = 0; i < arrayLen; i++) {
+				args[i] = (SyntaxTreeNode) argVec.elementAt(i);
+				SyntaxTreeNode stn = (SyntaxTreeNode) opVec.elementAt(i);
+				opsSTN[i] = stn;
+				opNames[i] = stn.getUS();
+				switch (stn.getKind()) {
+				case IDENTIFIER:
+				case N_InfixOp:
+				case N_NonExpPrefixOp:
+				case N_PostfixOp:
+				case N_PrefixOp:
+				case ProofStepLexeme:
 // xyz: following case added by LL on 13 Oct 2007
 //           
-           case  ProofImplicitStepLexeme:
-             ops[i] = NameSel ;
-           break ;
-
-
-
-           case N_StructOp:
-             if (stn.heirs().length > 0) {
-               /************************************************************
-               * This is a number.                                         *
-               ************************************************************/
-               TreeNode numNode =  stn.heirs()[0].heirs()[0] ;
-               ops[i] = Integer.parseInt(numNode.getImage()) ;
-               }
-             else {
-               /************************************************************
-               * This is not a number, so it is ">>", "<<", "@", or ":".   *
-               ************************************************************/
-               UniqueString us = stn.getUS() ;
-                 if (us == GGUS)         {ops[i] = GGSel ;}
-                 else if (us == LLUS)    {ops[i] = LLSel ;}
-                 else if (us == ColonUS) {ops[i] = ColonSel ;}
-                 else if (us == AtUS)    {ops[i] = AtSel ; }
-                 else { errors.addAbort(
-                         stn.getLocation(),
-                         "Internal error: Unexpected selector `" + 
-                         stn.getImage() + "'.") ;} 
-              } // if stn.heirs().length > 0
-           break ;
-
-           case N_OpArgs:
-              ops[i] = NullSel ;
-           break ;
-
-           default:
-             /***************************************************************
-             * This error occurs on silly input like                        *
-             *                                                              *
-             *        USE DEF <<1, 2>>                                      *
-             *                                                              *
-             * It therefore seems better to report a mysterious error and   *
-             * let processing continue in the hopes that it will generate   *
-             * a later, more useful error.                                  *
-             ***************************************************************/
+				case ProofImplicitStepLexeme:
+					ops[i] = NameSel;
+					break;
+
+				case N_StructOp:
+					if (stn.heirs().length > 0) {
+						/************************************************************
+						 * This is a number. *
+						 ************************************************************/
+						TreeNode numNode = stn.heirs()[0].heirs()[0];
+						ops[i] = Integer.parseInt(numNode.getImage());
+					} else {
+						/************************************************************
+						 * This is not a number, so it is ">>", "<<", "@", or ":". *
+						 ************************************************************/
+						UniqueString us = stn.getUS();
+						if (us == GGUS) {
+							ops[i] = GGSel;
+						} else if (us == LLUS) {
+							ops[i] = LLSel;
+						} else if (us == ColonUS) {
+							ops[i] = ColonSel;
+						} else if (us == AtUS) {
+							ops[i] = AtSel;
+						} else {
+							errors.addAbort(stn.getLocation(),
+									"Internal error: Unexpected selector `" + stn.getImage() + "'.");
+						}
+					} // if stn.heirs().length > 0
+					break;
+
+				case N_OpArgs:
+					ops[i] = NullSel;
+					break;
+
+				default:
+					/***************************************************************
+					 * This error occurs on silly input like * * USE DEF <<1, 2>> * * It therefore
+					 * seems better to report a mysterious error and * let processing continue in
+					 * the hopes that it will generate * a later, more useful error. *
+					 ***************************************************************/
 //             errors.addAbort(
-             errors.addError(
-               stn.getLocation(),
+					errors.addError(stn.getLocation(),
 //               "Internal error: Selector had unexpected node kind " + 
 //               stn.getKind()) ;
-              "Unexpected token found." ) ;
-            break ;
-          } ;
-        } // for
-      }
-
-     public String toString() {
-       /********************************************************************
-       * For debugging.                                                    *
-       ********************************************************************/
-       String retval = "Selector object:\n" ;
-       for (int i = 0; i < ops.length; i++) {
-        retval = retval + " elt " + i + " : ops = " + ops[i] + 
-                   ", opNames = " + opNames[i].toString() + 
-                   ", opsSTN.kind = " + opsSTN[i].getKind() + 
-                   ", args.kind = " + 
-                      ((args[i] == null)?"null":(args[i].getKind()+" ")) + "\n" ;
-        }
-       return retval ;
-      }
-
-  } // class Selector 
+							"Unexpected token found.");
+					break;
+				}
+				;
+			} // for
+		}
+
+		public String toString() {
+			/********************************************************************
+			 * For debugging. *
+			 ********************************************************************/
+			String retval = "Selector object:\n";
+			for (int i = 0; i < ops.length; i++) {
+				retval = retval + " elt " + i + " : ops = " + ops[i] + ", opNames = " + opNames[i].toString()
+						+ ", opsSTN.kind = " + opsSTN[i].getKind() + ", args.kind = "
+						+ ((args[i] == null) ? "null" : (args[i].getKind() + " ")) + "\n";
+			}
+			return retval;
+		}
+
+	} // class Selector
 
 //  private ExprOrOpArgNode[] opArgsToArray(SyntaxTreeNode opArgNode) 
 //    /***********************************************************************
@@ -530,3993 +494,3743 @@ public class Generator implements ASTConstants, SyntaxTreeConstants,
 //       } // for      
 //   } //  opArgsToArray
 
-  private Selector genIdToSelector(SyntaxTreeNode genId)
-                        throws AbortException {
-    /***********************************************************************
-    * Constructs a selector in which all the addSelector calls to          *
-    * translate the GeneralId node genId have been made and finish has     *
-    * been called.                                                         *
-    *                                                                      *
-    * See the commments in tla+.jj before the OpOrExpr() production to     *
-    * see what the tree structure of a GeneralId node looks like.          *
-    ***********************************************************************/
-    Selector retval = new Selector(genId) ;
-    TreeNode prefix = genId.heirs()[0] ;
-    TreeNode[] prefixElts = prefix.heirs() ;
-    SyntaxTreeNode lastOp = (SyntaxTreeNode) genId.heirs()[1] ;
-    for (int i = 0 ; i < prefixElts.length; i++) {
-      TreeNode[] pe = prefixElts[i].heirs();
-      if (pe.length == 0) { 
-        /*******************************************************************
-        * We reach this point when processing the nonsensical input        *
-        *   HIDE DEF X'                                                    *
-        * So we report a not very helpful error in the hopes that further  *
-        * processing will produce a more useful error message.             *
-        *******************************************************************/
-        errors.addError(genId.getLocation(), 
-                "Was expecting a GeneralId."); 
-        break ;} ;
-      SyntaxTreeNode thisPrefix = (SyntaxTreeNode) pe[0] ;
-      switch (thisPrefix.getKind()) {
-        case N_OpArgs: 
-          retval.addSelector(thisPrefix, thisPrefix) ;
-        break;   
-        case N_StructOp:
-          retval.addSelector(thisPrefix, null) ;
-        break;   
-        default:
-          /*****************************************************************
-          * This must be an identifier or operator.  It may or may not     *
-          * have arguments.                                                *
-          *****************************************************************/
-          if (prefixElts[i].heirs().length == 2) {
-            /***************************************************************
-            * There are no arguments (the 2nd heir is the "!").            *
-            ***************************************************************/
-             retval.addSelector(thisPrefix, null) ;
-           }
-          else {
-            /***************************************************************
-            * There are arguments, which are heirs()[1].                   *
-            ***************************************************************/
-            if (prefixElts[i].heirs().length != 3) {
-              // Note added 13 April 2015 by LL:
-              // This error is caused by the spurious "(x)" in the leaf proof
-              //    BY ... DEF A!foo(x)
-              // It would be nice if this produced a more helpful error
-              // message, but I have no idea if there are other bad inputs
-              // that can cause it.
-              errors.addAbort(
-                prefixElts[i].getLocation(),
-                "Internal error: " + 
-                  "IdPrefixElement has other than 2 or 3 heirs.") ;
-              } ;
-            retval.addSelector(thisPrefix, 
-                               (SyntaxTreeNode) prefixElts[i].heirs()[1]) ;
-           } // if}           
-        break ;
-       } // switch
-     }; // for i
-
-    if (lastOp.getKind() == N_OpArgs) {retval.addSelector(lastOp, lastOp) ;} 
-      else                            {retval.addSelector(lastOp, null) ;} ;
-
-    retval.finish() ;
-    return retval ;            
-  } // genIdToSelector
-
-
-  /***********************************************************************
-  * Constants for use in selectorToNode.                                 *
-  ***********************************************************************/
-  private final int FindingOpName   = 11 ;
-  private final int FollowingLabels = 22 ;
-  private final int FindingSubExpr  = 33 ;
-
-  private final int ArgNum(int op, int arity) {
-    /***********************************************************************
-    * This is the implementation of ArgNum in Subexpression.tla.  Beware   *
-    * that this is the human argument number (where the first argument is  *
-    * argument 1), not the Java argument number.                           *
-    ***********************************************************************/
-    if (op > 0)      {if (op <= arity) {return op;} ;
-                      return -1 ;
-                      } ;
-    if (op == LLSel) {return (arity > 0) ? 1 : -1 ;} ;
-    if (op == GGSel) {// if (arity == 1) {return 1;} ;
-                      /**************************************************
-                      * Commented out on 9 Mar 2010 by LL. Apparently,  *
-                      * I once thought it was a good idea to let !>>    *
-                      * refer to the argument of a unary operator.  I   *
-                      * no longer think so.                             *
-                      **************************************************/
-                      if (arity == 2) {return 2;} ;
-                      } ;
-    return -1;
-    } // ArgNum
-
-  LevelNode selectorToNode(Selector sel,      // The selector.
-                           int expectedArity,
-                           boolean isFact,    // true if looking for a fact
-                           boolean isDef,     // true if looking for a DEF
-                                              //    clause item.
-                           ModuleNode cm )    // The current module.
-    throws AbortException {
-    /***********************************************************************
-    * This is an implementation of the +cal algorithm in module            *
-    * Subexpression.tla, which is attached as a comment to the end of      *
-    * this file.  If expectedArity >= 0, then it returns an                *
-    * ExprOrOpArgNode, otherwise it returns an OpDefNode or a              *
-    * ThmOrAssumpDefNode.  On an error, it returns nullOAN which will      *
-    * allow semantic analysis to continue.                                 *
-    *                                                                      *
-    * The +cal algorithm describes only the case isFact = false and isDef  *
-    * = false.  It should be updated to include the other case as well.    *
-    * It also doesn't mention the case of a NumberedProofStepKind OpDef    *
-    * node.                                                                *
-    *                                                                      *
-    * This code was modified on 15 Oct 2007 by LL to allow the use of a    *
-    * ModuleInstanceKind node (the M in "M == INSTANCE ...") as a fact or  *
-    * a DEF in a BY, USE, or HIDE. Parameterized instances can be used in  *
-    * a DEF (without the parameters), but only a non-parameterized module  *
-    * instance can be used as a fact.                                      *
-    *                                                                      *
-    * It appears that, if an error has occurred, a call with               *
-    * expectedArity>0 can return something other than an OpArgNode         *
-    * (probably an OpApplNode).  This caused a problem in one              *
-    * place--namely, in generateOpArg.  I didn't try figuring out whether  *
-    * this is actually a bug in selectorToNode.  Instead, I just kludged   *
-    * something to handle that case.                                       *
-    ***********************************************************************/
-    Vector substInPrefix = new Vector() ; // of SubstInNode
-    Vector params        = new Vector() ; // of FormalParamNode
-    Vector allArgs       = new Vector() ; // of ExprOrOpArgNode
-
-    /***********************************************************************
-    * Local algorithm variables.                                           *
-    ***********************************************************************/
-    UniqueString curName = null ;
-    UniqueString newName = null ; // Initial value set to make Eclipse happy.
-    SemanticNode curNode = null ;
-    SemanticNode newNode ;
-
-    int idx = 0 ;
-      /*********************************************************************
-      * We use the Java convention of counting from 0 instead of the       *
-      * human convention of couunting from 1.                              *
-      *********************************************************************/
-    int mode = FindingOpName ;
-    int prevMode = -999 ; 
-      /*********************************************************************
-      * The compiler is too dumb to figure out that it always gets set     *
-      * before it's used.                                                  *
-      *********************************************************************/
-    Context letInContext = null ;
-      /*********************************************************************
-      * This implements the algorithm's curContext variable when that      *
-      * context is a LET/IN node's context.  When mode = FindingOpName,    *
-      * letInContext is null iff this is the first time that mode is       *
-      * entered--i.e., for the name that is the initial part of the        *
-      * selector.  In this case, the algorithm's curContext is             *
-      * represented by the method's "global variable" symbolTable, since   *
-      * any name legal in that context can be referred to.  When mode is   *
-      * subsequently set to FindingOpName, it means that the name being    *
-      * sought is from a LET/IN node.  In that case, letInContext will     *
-      * contain the Context that implements the algorithm's curContext     *
-      * variable.                                                          *
-      *********************************************************************/
-    int opDefArityFound = 0 ;
-    Vector opDefArgs = new Vector() ; // of ExprOrOpArgNode objects
-    boolean firstFindingOpName = true;
-    SymbolNode subExprOf = null;
-
-    boolean inAPsuffices = false ;
-      /*********************************************************************
-      * Added 16 Feb 2009.  An AssumeProveNode with suffices field true    *
-      * represents SUFFICE ASSUME / PROVE, and the SUFFICES is treated as  *
-      * if it were a 1-argument operator.  This flag is set true when the  *
-      * node is first encountered.                                         *
-      *********************************************************************/
-
-    while (idx < sel.args.length) {
-      /*********************************************************************
-      * Check the one part of the algorithm's assert that should not       *
-      * automatically hold -- the first conjunct of the last conjunct --   *
-      * for i = idx:                                                       *
-      *********************************************************************/
-      if (   (   (    (sel.ops[idx] != NameSel)
-                  &&  (sel.ops[idx] != NullSel))
-              || (expectedArity != 0) )
-          && (sel.args[idx] != null)) {
-        errors.addError(
-           sel.opsSTN[idx].getLocation(),
-           "Selector `" + selectorItemToString(sel, idx) + 
-           "' should not have argument(s).") ;
-        return nullOAN ;
-        } // if (sel.ops[idx] == NameSel) ...  ;
-
-      /*********************************************************************
-      * Check that, if sel.args[idx] != null, then it is an OpArgs node.   *
-      *********************************************************************/
-      if (   (sel.args[idx] != null)
-          && (sel.args[idx].getKind() != N_OpArgs) ) {
-        errors.addAbort(
-         sel.args[idx].getLocation(),
-         "Internal error: Unexpected syntax node kind.");
-        } ;
-
-      switch (mode) {
-       case FindingOpName :
-         // Following code changed on 23 Sep 2009 to fix bug, and corrected
-         // on 9 Nov 2009.  See description in Subexpression.tla.
-         SymbolNode newSymbolNode = null;
-         Vector tempArgs = new Vector() ; 
-           // a vector of SyntaxTreeNode objects, one for each argument
-           // found for an undefined operator name in the following loop
-                   
-         while (newSymbolNode == null && idx < sel.args.length) {
-         /******************************************************************
-         * +cal: newName := IF IsName(ops[idx]) ... ELSE null ;            *
-         ******************************************************************/
-         if (sel.ops[idx] == NameSel) {
-           if (curName == null) { 
-             newName = Operators.resolveSynonym(sel.opNames[idx]) ; 
-               /************************************************************
-               * Need to call resolveSynonym so things like (+), aka       *
-               * \oplus, are handled properly.                             *
-               ************************************************************/
-            }  
-           else { newName = 
-                   UniqueString.uniqueStringOf(
-                    curName.toString() + "!" + 
-                      Operators.resolveSynonym(sel.opNames[idx]).toString()) ;
-            } ;
-          } // if (sel.ops[idx] = NameSel) 
-         else { newName = null ; } ;
-
-         if (    (curName == null)
-              && (sel.ops[idx] != NameSel)) {
-           if (idx == 0) { errors.addError(
-                             sel.opsSTN[idx].getLocation(),
-                             "Need name or step number here, not `" +
-                              sel.opNames[idx] + "'.") ;
-                           return nullOAN ; }
-           else {errors.addAbort(sel.opsSTN[idx].getLocation(),
-                         "Internal error: should have name here.") ;
-            } ;
-           } ; // if (curName == null) ...  ;
-           if (newName != null) {
-               if (letInContext == null) {
-                 /***************************************************************
-                 * See the comments for the declaration of letInContext.        *
-                 ***************************************************************/
-                 newSymbolNode = symbolTable.resolveSymbol(newName) ;
-                }
-               else {
-                 newSymbolNode = letInContext.getSymbol(newName) ;
-                } ;
-               } ; // if (newName != null) 
-          if (newSymbolNode == null) {
-              curName = newName;
-              if (sel.args[idx] != null) {
-                  // sel.args[idx].heirs() is the array of SyntaxTreeNode objects
-                  // representing:
-                  //   "("   "arg1"   ","  ... ","  "arg_n"  ")"
-                  // We add these SyntaxTreeNode objects to tempArgs.
-                  int numOfOpArgs = (sel.args[idx].heirs().length - 1) / 2 ;
-                  for (int i = 0 ; i < numOfOpArgs; i++) {
-                      tempArgs.addElement(sel.args[idx].heirs()[2*i+1]);
-                  }
-              }
-              idx++;
-          }
-         } // while (newNode == null)
-
-         if (newSymbolNode == null) {
-           int eidx = (idx < sel.args.length) ? idx : (sel.args.length-1);
-           errors.addError(
-               sel.opsSTN[eidx].getLocation(),
-               "Unknown operator: `" + 
-               selectorItemToString(sel, eidx) + "'.") ;
-           return nullOAN ; 
-          } ; 
-        
-         if (newSymbolNode.getKind() == ModuleKind) {
-             errors.addError(
-               sel.opsSTN[idx].getLocation(),
-               "Module name (" + sel.opNames[idx].toString() +
-               ") not allowed here.") ;
-             return nullOAN ; 
-           } ; // if 
-            
-         curNode = newSymbolNode ;
-         SymbolNode curSymbolNode = newSymbolNode ;
-           /****************************************************************
-           * A version of curNode "cast" as a SymbolNode.                  *
-           ****************************************************************/
-         curName = newName ;
-         switch (curSymbolNode.getKind()) {
-           case ConstantDeclKind: 
-           case VariableDeclKind:
-           case FormalParamKind:
-           case BuiltInKind:
-           case NewConstantKind:
-           case NewVariableKind:
-           case NewStateKind:
-           case NewActionKind:
-           case NewTemporalKind:
-           case NumberedProofStepKind:
-             /**************************************************************
-             * This last case added by LL on 17 Aug 2007.  I think it      *
-             * fits in here nicely, but I wouldn't swear to it.            *
-             **************************************************************/
-             if (idx != 0) {
-               errors.addAbort(
-                 sel.selSTN.getLocation(),
-                 "Internal error: impossible naming of declaration.") ;
-               }
-             else if (sel.ops.length != 1) {
-               errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "Cannot take subexpression of `" + curName.toString() 
-                  + "'.") ;
-               return nullOAN ; 
-            } //  ;
-
-           /****************************************************************
-           * Warning: horrible Java programming hack.  There is            *
-           * deliberately no break here, so this case "joins" the          *
-           * following case.                                               *
-           ****************************************************************/
-           //$FALL-THROUGH$
-           case UserDefinedOpKind:
-           case ThmOrAssumpDefKind:
-           case ModuleInstanceKind:
-             /**************************************************************
-             * +cal: nodeArity := Arity(curNode) ;                         *
-             **************************************************************/
-             int nodeArity = curSymbolNode.getArity() ;
-             /**************************************************************
-             * +cal: if expectedArity = 0                                  *
-             **************************************************************/
-             if (expectedArity == 0) {
-               SyntaxTreeNode opArgs = sel.args[idx] ;
-               int numOfOpArgs = 0 ;
-               if (opArgs != null) { 
-                   // if there are arguments, add them to tempArgs. 
-                   /**********************************************************
-                   * The heirs of an oparg node are                          *
-                   *                                                         *
-                   *     "("   "arg1"   ","  ... ","  "arg_n"  ")"           *
-                   **********************************************************/
-                   numOfOpArgs = (opArgs.heirs().length - 1) / 2 ;
-                    for (int i = 0 ; i < numOfOpArgs; i++) {
-                        tempArgs.addElement(sel.args[idx].heirs()[2*i+1]);
-                 } ;
-
-               };
-               /**************************************************************
-               * +cal  then if opDefArityFound + Len(tempArgs) # nodeArity  *
-               **************************************************************/
-               if (opDefArityFound + tempArgs.size() != nodeArity) {
-                 errors.addError(
-                    (opArgs == null)? sel.selSTN.getLocation() :
-                                      sel.args[idx].getLocation(),
-                    "The operator " + curName.toString() + " requires " + 
-                    (nodeArity - opDefArityFound) + " arguments.") ;
-                 return nullOAN ;
-                 } ; // if
-               ExprOrOpArgNode[] opArgNodes = 
-                                   new ExprOrOpArgNode[tempArgs.size()] ;
-                 /**********************************************************
-                 * The array of semantic nodes generated by the arguments. *
-                 **********************************************************/
-               for (int i = 0 ; i < tempArgs.size(); i++) {
-                 /**********************************************************
-                 * Call generateExprOrOpArg to generate the semantic       *
-                 * nodes for the arguments and add them to the vector      *
-                 * opDefArgs.  Note that curSymbolNode is used as the      *
-                 * argument to generateExprOrOpArg that specifies the      *
-                 * operator to which the arguments are being given.  (The  *
-                 * comments for that method assumes that the operator      *
-                 * arguments appear in an OpApplication node, but the      *
-                 * method doesn't really care; it just uses that operator  *
-                 * to check the arity of the arguments.)  Note that        *
-                 * argument number i of sel.args[idx] represents argument  *
-                 * number i + opDefArityFound of the operator described    *
-                 * by curSymbolNode.                                       *
-                 **********************************************************/
-                 opDefArgs.addElement( 
-                   generateExprOrOpArg(
-                     curSymbolNode,
-                     sel.opsSTN[idx],
-                     i + opDefArityFound,
-                     (TreeNode) tempArgs.elementAt(i),
-                     cm));
-                } ; // end for
-  
-              } // if expectedArity = 0
-             /**************************************************************
-             * +cal: else if expectedArity > 0)                            *
-             **************************************************************/
-             else {
-               if (expectedArity > 0) {
-                 /**********************************************************
-                 * Subexpression.tla has a test to report an error if a    *
-                 * higher-order operator is specified, since such an       *
-                 * operator can't be used as an operator argument.  That   *
-                 * test is eliminated here because this error should be    *
-                 * caught by the caller of the selectorToNode method.      *
-                 **********************************************************/
-                } 
-              } ; // else [expectedArity != 0]
-           opDefArityFound = nodeArity ;
-  
-           if (curNode.getKind() == ModuleInstanceKind) {
-             if ((idx == sel.ops.length - 1) && ! (isDef || isFact)) {
-               errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "Operator name " + curName.toString()  + " is incomplete.");
-               return nullOAN ; 
-               };
-            }
-           else { 
-             /**************************************************************
-             * curNode.getKind() \in                                       *
-             *      {UserDefinedOpKind, ThmOrAssumpDefKind,                *
-             *       ConstantDeclKind, VariableDeclKind, FormalParamKind,  *
-             *       BuiltInKind, BoundSymbolKind, NumberedProofStepKind}  *
-             **************************************************************/
-             /**************************************************************
-             * +cal: then if /\ curNode.kind = UserDefinedOpKind ...       *
-             **************************************************************/
-             if (   (curNode.getKind() == UserDefinedOpKind)
-                 && ( ! ((OpDefNode) curNode).isDefined )
-                 && (sel.ops.length != 1)) {
-               errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "Subexpression of  `" + curName.toString()  + 
-                  "' used inside the operator's definition.");
-               return nullOAN ; 
-               } ;
-
-             /**************************************************************
-             * +cal: if /\ firstFindingOpName ...                          *
-             **************************************************************/
-             if (   firstFindingOpName
-                 && (   (curNode.getKind() == UserDefinedOpKind)
-                     || (curNode.getKind() == ThmOrAssumpDefKind))) {
-               subExprOf = (SymbolNode) curNode;
-              }
-             /**************************************************************
-             * +cal: idx # Len(ops)                                        *
-             **************************************************************/
-             if (idx != sel.ops.length - 1) {
-               FormalParamNode[] opParams ;
-               if (curNode.getKind() == UserDefinedOpKind) {
-                 opParams = ((OpDefNode) curNode).getParams() ;
-                 newNode = ((OpDefNode) curNode).getBody() ;
-                 } // if
-                else { // curNode.getKind() == ThmOrAssumpDefKind
-                 opParams = ((ThmOrAssumpDefNode) curNode).getParams() ;
-                 newNode = ((ThmOrAssumpDefNode) curNode).getBody() ;
-                } ;
-               for (int i = 0 ; i < opParams.length ; i++) {
-                 params.addElement(opParams[i]) ;
-                 } ; // for
-               curName = null ;
-               if (sel.ops[idx+1] == NameSel) {mode = FollowingLabels; }
-                else {mode = FindingSubExpr ;} ;
-               for (int i = 0 ; i < opDefArgs.size() ; i++) {
-                 allArgs.addElement(opDefArgs.elementAt(i)) ;
-                 } ; // for
-               opDefArityFound = 0;
-               opDefArgs = new Vector() ;
-
-               /************************************************************
-               * If newNode = null, then I think there's an error.  It     *
-               * should be caught later.                                   *
-               ************************************************************/
-               if (newNode != null) {
-                 while (newNode.getKind() == SubstInKind) {
-                   substInPrefix.addElement(newNode) ;
-                   newNode = ((SubstInNode) newNode).getBody() ;
-                   }; // while
-                   while (newNode.getKind() == APSubstInKind) {
-                       substInPrefix.addElement(newNode) ;
-                       newNode = ((APSubstInNode) newNode).getBody() ;
-                       }; // while
-                } ;
-               if (mode == FindingSubExpr) {
-                 curNode = newNode ;
-                } ;
-              } // if (idx != sel.ops.length - 1) 
-             } // else curNode.getKind() \in {UserDefinedOpKind, ...}
-            prevMode = FindingOpName ;
-         break ; 
-
-         default:
-           errors.addAbort(sel.opsSTN[idx].getLocation(),
-                          "Internal error: unexpected node kind.") ;
-         break ;
-        } ; // switch (curSymbolNode.getKind())
-
-       break ; // case FindingOpName 
-
-
-       case FollowingLabels :
-         /******************************************************************
-         * Invariant: sel.ops[idx] = NameSel                               *
-         ******************************************************************/
-         if (   (   (prevMode == FindingOpName) 
-                 && (curNode.getKind() != UserDefinedOpKind)
-                 && (curNode.getKind() != ThmOrAssumpDefKind))
-             || (   (prevMode != FindingOpName) 
-                 && (curNode.getKind() != LabelKind))) {
-             errors.addAbort(sel.selSTN.getLocation(),
-                            "Unexpected node kind in FollowingLabels mode.") ;
-          } ;
-
-         LabelNode newLabelNode = 
-            ((OpDefOrLabelNode) curNode).getLabel(sel.opNames[idx]) ;
-
-         if (newLabelNode == null) {
-           errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "Cannot find label `" + sel.opNames[idx].toString() + "'.");
-           return nullOAN ; 
-          } ;
-
-         curNode = newLabelNode ;
- 
-         if (illegalLabelRef(newLabelNode, sel.opsSTN[idx])) {
-           errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "Accessing subexpression labeled `" + 
-                 sel.opNames[idx].toString() + 
-                 "' of ASSUME/PROVE clause within the scope of " +
-                 "a declaration\n from outside that declaration's scope.");
-           return nullOAN ; 
-          } ;
-
-         if (expectedArity == 0) {
-
-           /****************************************************************
-           * Check that label has right number of arguments.               *
-           ****************************************************************/
-           if (newLabelNode.getArity() !=
-                ((sel.args[idx] == null) 
-                   ? 0 
-                   : (sel.args[idx].heirs().length-1)/2)) {
-             errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "Label `" + sel.opNames[idx].toString() 
-                   + "' used with wrong number of arguments.");
-             return nullOAN ; 
-            } ;      
-          for (int i = 0; i < newLabelNode.getArity(); i++) {
-            allArgs.addElement(generateExpression(
-                                 sel.args[idx].heirs()[2 * i + 1] , cm)) ;
-           } ;
-          }; // if (expectedArity == 0)
-
-         for (int i = 0; i < newLabelNode.getArity(); i++) {
-            FormalParamNode pdecl = newLabelNode.params[i] ;
-            params.addElement(pdecl);
-           } ;
-
-         if ((idx < sel.ops.length - 1) && (sel.ops[idx+1] != NameSel)) {
-           mode = FindingSubExpr ;
-           } ; 
-
-         if ((mode == FindingSubExpr) || (idx == sel.ops.length)) {
-           curNode = newLabelNode.getBody() ;
-           } ;
-
-         prevMode = FollowingLabels ;
-       break ; // case FollowingLabels
-
-       case FindingSubExpr:
-         if (sel.ops[idx] == ColonSel) {
-           if ( (prevMode == FindingSubExpr)
-               || ! (   (   (idx == sel.ops.length - 1)
-                         && (prevMode == FindingOpName))
-                     || (   (idx < sel.ops.length - 1)
-                         && (sel.ops[idx+1] == NameSel)))) {
-             errors.addError(
-                sel.opsSTN[idx].getLocation(),
-                "`!:' can be used only after a name and either at the " +
-                   "end after an\noperator name or before an operator name.");
-             return nullOAN ; 
-            } // if ( (prevMode == FindingSubExpr) ...)
-          } // if (sel.ops[idx] == ColonSel)
-         else if (curNode == null) {
-           errors.addError(sel.opsSTN[idx].getLocation(),
-                "Subexpression selection failed, probably due to " +
-                "an error in the selected expression.");
-           return nullOAN ; 
-           }
-         else if (curNode.getKind() == LetInKind) {
-           if (ArgNum(sel.ops[idx], 1) == 1) {
-             curNode = ((LetInNode) curNode).getBody() ;
-            } else {
-             errors.addError(sel.opsSTN[idx].getLocation(),
-                             "A LET/IN expression has only one operand.");
-             return nullOAN ; 
-            } ;
-          } // else if (curNode.getKind() == LetInKind) 
-
-         else if (curNode.getKind() == OpApplKind) {         
-           OpApplNode curOpApplNode = (OpApplNode) curNode ;
-           ExprOrOpArgNode[] curArgs = curOpApplNode.getArgs() ;
-           SymbolNode opNode = curOpApplNode.getOperator();
-           if (   (opNode.getKind() == FormalParamKind)
-               || (opNode.getKind() == ConstantDeclKind)
-               || (opNode.getKind() == UserDefinedOpKind)) {
-             int temp = ArgNum(sel.ops[idx], opNode.getArity()) ;
-             if (temp == -1) {
-                 reportSelectorError(sel, idx) ;
+	private Selector genIdToSelector(SyntaxTreeNode genId) throws AbortException {
+		/***********************************************************************
+		 * Constructs a selector in which all the addSelector calls to * translate the
+		 * GeneralId node genId have been made and finish has * been called. * * See the
+		 * commments in tla+.jj before the OpOrExpr() production to * see what the tree
+		 * structure of a GeneralId node looks like. *
+		 ***********************************************************************/
+		Selector retval = new Selector(genId);
+		TreeNode prefix = genId.heirs()[0];
+		TreeNode[] prefixElts = prefix.heirs();
+		SyntaxTreeNode lastOp = (SyntaxTreeNode) genId.heirs()[1];
+		for (int i = 0; i < prefixElts.length; i++) {
+			TreeNode[] pe = prefixElts[i].heirs();
+			if (pe.length == 0) {
+				/*******************************************************************
+				 * We reach this point when processing the nonsensical input * HIDE DEF X' * So
+				 * we report a not very helpful error in the hopes that further * processing
+				 * will produce a more useful error message. *
+				 *******************************************************************/
+				errors.addError(genId.getLocation(), "Was expecting a GeneralId.");
+				break;
+			}
+			;
+			SyntaxTreeNode thisPrefix = (SyntaxTreeNode) pe[0];
+			switch (thisPrefix.getKind()) {
+			case N_OpArgs:
+				retval.addSelector(thisPrefix, thisPrefix);
+				break;
+			case N_StructOp:
+				retval.addSelector(thisPrefix, null);
+				break;
+			default:
+				/*****************************************************************
+				 * This must be an identifier or operator. It may or may not * have arguments. *
+				 *****************************************************************/
+				if (prefixElts[i].heirs().length == 2) {
+					/***************************************************************
+					 * There are no arguments (the 2nd heir is the "!"). *
+					 ***************************************************************/
+					retval.addSelector(thisPrefix, null);
+				} else {
+					/***************************************************************
+					 * There are arguments, which are heirs()[1]. *
+					 ***************************************************************/
+					if (prefixElts[i].heirs().length != 3) {
+						// Note added 13 April 2015 by LL:
+						// This error is caused by the spurious "(x)" in the leaf proof
+						// BY ... DEF A!foo(x)
+						// It would be nice if this produced a more helpful error
+						// message, but I have no idea if there are other bad inputs
+						// that can cause it.
+						errors.addAbort(prefixElts[i].getLocation(),
+								"Internal error: " + "IdPrefixElement has other than 2 or 3 heirs.");
+					}
+					;
+					retval.addSelector(thisPrefix, (SyntaxTreeNode) prefixElts[i].heirs()[1]);
+				} // if}
+				break;
+			} // switch
+		}
+		; // for i
+
+		if (lastOp.getKind() == N_OpArgs) {
+			retval.addSelector(lastOp, lastOp);
+		} else {
+			retval.addSelector(lastOp, null);
+		}
+		;
+
+		retval.finish();
+		return retval;
+	} // genIdToSelector
+
+	/***********************************************************************
+	 * Constants for use in selectorToNode. *
+	 ***********************************************************************/
+	private final int FindingOpName = 11;
+	private final int FollowingLabels = 22;
+	private final int FindingSubExpr = 33;
+
+	private final int ArgNum(int op, int arity) {
+		/***********************************************************************
+		 * This is the implementation of ArgNum in Subexpression.tla. Beware * that this
+		 * is the human argument number (where the first argument is * argument 1), not
+		 * the Java argument number. *
+		 ***********************************************************************/
+		if (op > 0) {
+			if (op <= arity) {
+				return op;
+			}
+			;
+			return -1;
+		}
+		;
+		if (op == LLSel) {
+			return (arity > 0) ? 1 : -1;
+		}
+		;
+		if (op == GGSel) {// if (arity == 1) {return 1;} ;
+			/**************************************************
+			 * Commented out on 9 Mar 2010 by LL. Apparently, * I once thought it was a good
+			 * idea to let !>> * refer to the argument of a unary operator. I * no longer
+			 * think so. *
+			 **************************************************/
+			if (arity == 2) {
+				return 2;
+			}
+			;
+		}
+		;
+		return -1;
+	} // ArgNum
+
+	LevelNode selectorToNode(Selector sel, // The selector.
+			int expectedArity, boolean isFact, // true if looking for a fact
+			boolean isDef, // true if looking for a DEF
+							// clause item.
+			ModuleNode cm) // The current module.
+			throws AbortException {
+		/***********************************************************************
+		 * This is an implementation of the +cal algorithm in module *
+		 * Subexpression.tla, which is attached as a comment to the end of * this file.
+		 * If expectedArity >= 0, then it returns an * ExprOrOpArgNode, otherwise it
+		 * returns an OpDefNode or a * ThmOrAssumpDefNode. On an error, it returns
+		 * nullOAN which will * allow semantic analysis to continue. * * The +cal
+		 * algorithm describes only the case isFact = false and isDef * = false. It
+		 * should be updated to include the other case as well. * It also doesn't
+		 * mention the case of a NumberedProofStepKind OpDef * node. * * This code was
+		 * modified on 15 Oct 2007 by LL to allow the use of a * ModuleInstanceKind node
+		 * (the M in "M == INSTANCE ...") as a fact or * a DEF in a BY, USE, or HIDE.
+		 * Parameterized instances can be used in * a DEF (without the parameters), but
+		 * only a non-parameterized module * instance can be used as a fact. * * It
+		 * appears that, if an error has occurred, a call with * expectedArity>0 can
+		 * return something other than an OpArgNode * (probably an OpApplNode). This
+		 * caused a problem in one * place--namely, in generateOpArg. I didn't try
+		 * figuring out whether * this is actually a bug in selectorToNode. Instead, I
+		 * just kludged * something to handle that case. *
+		 ***********************************************************************/
+		Vector substInPrefix = new Vector(); // of SubstInNode
+		Vector params = new Vector(); // of FormalParamNode
+		Vector allArgs = new Vector(); // of ExprOrOpArgNode
+
+		/***********************************************************************
+		 * Local algorithm variables. *
+		 ***********************************************************************/
+		UniqueString curName = null;
+		UniqueString newName = null; // Initial value set to make Eclipse happy.
+		SemanticNode curNode = null;
+		SemanticNode newNode;
+
+		int idx = 0;
+		/*********************************************************************
+		 * We use the Java convention of counting from 0 instead of the * human
+		 * convention of couunting from 1. *
+		 *********************************************************************/
+		int mode = FindingOpName;
+		int prevMode = -999;
+		/*********************************************************************
+		 * The compiler is too dumb to figure out that it always gets set * before it's
+		 * used. *
+		 *********************************************************************/
+		Context letInContext = null;
+		/*********************************************************************
+		 * This implements the algorithm's curContext variable when that * context is a
+		 * LET/IN node's context. When mode = FindingOpName, * letInContext is null iff
+		 * this is the first time that mode is * entered--i.e., for the name that is the
+		 * initial part of the * selector. In this case, the algorithm's curContext is *
+		 * represented by the method's "global variable" symbolTable, since * any name
+		 * legal in that context can be referred to. When mode is * subsequently set to
+		 * FindingOpName, it means that the name being * sought is from a LET/IN node.
+		 * In that case, letInContext will * contain the Context that implements the
+		 * algorithm's curContext * variable. *
+		 *********************************************************************/
+		int opDefArityFound = 0;
+		Vector opDefArgs = new Vector(); // of ExprOrOpArgNode objects
+		boolean firstFindingOpName = true;
+		SymbolNode subExprOf = null;
+
+		boolean inAPsuffices = false;
+		/*********************************************************************
+		 * Added 16 Feb 2009. An AssumeProveNode with suffices field true * represents
+		 * SUFFICE ASSUME / PROVE, and the SUFFICES is treated as * if it were a
+		 * 1-argument operator. This flag is set true when the * node is first
+		 * encountered. *
+		 *********************************************************************/
+
+		while (idx < sel.args.length) {
+			/*********************************************************************
+			 * Check the one part of the algorithm's assert that should not * automatically
+			 * hold -- the first conjunct of the last conjunct -- * for i = idx: *
+			 *********************************************************************/
+			if ((((sel.ops[idx] != NameSel) && (sel.ops[idx] != NullSel)) || (expectedArity != 0))
+					&& (sel.args[idx] != null)) {
+				errors.addError(sel.opsSTN[idx].getLocation(),
+						"Selector `" + selectorItemToString(sel, idx) + "' should not have argument(s).");
+				return nullOAN;
+			} // if (sel.ops[idx] == NameSel) ... ;
+
+			/*********************************************************************
+			 * Check that, if sel.args[idx] != null, then it is an OpArgs node. *
+			 *********************************************************************/
+			if ((sel.args[idx] != null) && (sel.args[idx].getKind() != N_OpArgs)) {
+				errors.addAbort(sel.args[idx].getLocation(), "Internal error: Unexpected syntax node kind.");
+			}
+			;
+
+			switch (mode) {
+			case FindingOpName:
+				// Following code changed on 23 Sep 2009 to fix bug, and corrected
+				// on 9 Nov 2009. See description in Subexpression.tla.
+				SymbolNode newSymbolNode = null;
+				Vector tempArgs = new Vector();
+				// a vector of SyntaxTreeNode objects, one for each argument
+				// found for an undefined operator name in the following loop
+
+				while (newSymbolNode == null && idx < sel.args.length) {
+					/******************************************************************
+					 * +cal: newName := IF IsName(ops[idx]) ... ELSE null ; *
+					 ******************************************************************/
+					if (sel.ops[idx] == NameSel) {
+						if (curName == null) {
+							newName = Operators.resolveSynonym(sel.opNames[idx]);
+							/************************************************************
+							 * Need to call resolveSynonym so things like (+), aka * \oplus, are handled
+							 * properly. *
+							 ************************************************************/
+						} else {
+							newName = UniqueString.uniqueStringOf(
+									curName.toString() + "!" + Operators.resolveSynonym(sel.opNames[idx]).toString());
+						}
+						;
+					} // if (sel.ops[idx] = NameSel)
+					else {
+						newName = null;
+					}
+					;
+
+					if ((curName == null) && (sel.ops[idx] != NameSel)) {
+						if (idx == 0) {
+							errors.addError(sel.opsSTN[idx].getLocation(),
+									"Need name or step number here, not `" + sel.opNames[idx] + "'.");
+							return nullOAN;
+						} else {
+							errors.addAbort(sel.opsSTN[idx].getLocation(), "Internal error: should have name here.");
+						}
+						;
+					}
+					; // if (curName == null) ... ;
+					if (newName != null) {
+						if (letInContext == null) {
+							/***************************************************************
+							 * See the comments for the declaration of letInContext. *
+							 ***************************************************************/
+							newSymbolNode = symbolTable.resolveSymbol(newName);
+						} else {
+							newSymbolNode = letInContext.getSymbol(newName);
+						}
+						;
+					}
+					; // if (newName != null)
+					if (newSymbolNode == null) {
+						curName = newName;
+						if (sel.args[idx] != null) {
+							// sel.args[idx].heirs() is the array of SyntaxTreeNode objects
+							// representing:
+							// "(" "arg1" "," ... "," "arg_n" ")"
+							// We add these SyntaxTreeNode objects to tempArgs.
+							int numOfOpArgs = (sel.args[idx].heirs().length - 1) / 2;
+							for (int i = 0; i < numOfOpArgs; i++) {
+								tempArgs.addElement(sel.args[idx].heirs()[2 * i + 1]);
+							}
+						}
+						idx++;
+					}
+				} // while (newNode == null)
+
+				if (newSymbolNode == null) {
+					int eidx = (idx < sel.args.length) ? idx : (sel.args.length - 1);
+					errors.addError(sel.opsSTN[eidx].getLocation(),
+							"Unknown operator: `" + selectorItemToString(sel, eidx) + "'.");
+					return nullOAN;
+				}
+				;
+
+				if (newSymbolNode.getKind() == ModuleKind) {
+					errors.addError(sel.opsSTN[idx].getLocation(),
+							"Module name (" + sel.opNames[idx].toString() + ") not allowed here.");
+					return nullOAN;
+				}
+				; // if
+
+				curNode = newSymbolNode;
+				SymbolNode curSymbolNode = newSymbolNode;
+				/****************************************************************
+				 * A version of curNode "cast" as a SymbolNode. *
+				 ****************************************************************/
+				curName = newName;
+				switch (curSymbolNode.getKind()) {
+				case ConstantDeclKind:
+				case VariableDeclKind:
+				case FormalParamKind:
+				case BuiltInKind:
+				case NewConstantKind:
+				case NewVariableKind:
+				case NewStateKind:
+				case NewActionKind:
+				case NewTemporalKind:
+				case NumberedProofStepKind:
+					/**************************************************************
+					 * This last case added by LL on 17 Aug 2007. I think it * fits in here nicely,
+					 * but I wouldn't swear to it. *
+					 **************************************************************/
+					if (idx != 0) {
+						errors.addAbort(sel.selSTN.getLocation(), "Internal error: impossible naming of declaration.");
+					} else if (sel.ops.length != 1) {
+						errors.addError(sel.opsSTN[idx].getLocation(),
+								"Cannot take subexpression of `" + curName.toString() + "'.");
+						return nullOAN;
+					} // ;
+
+					/****************************************************************
+					 * Warning: horrible Java programming hack. There is * deliberately no break
+					 * here, so this case "joins" the * following case. *
+					 ****************************************************************/
+					//$FALL-THROUGH$
+				case UserDefinedOpKind:
+				case ThmOrAssumpDefKind:
+				case ModuleInstanceKind:
+					/**************************************************************
+					 * +cal: nodeArity := Arity(curNode) ; *
+					 **************************************************************/
+					int nodeArity = curSymbolNode.getArity();
+					/**************************************************************
+					 * +cal: if expectedArity = 0 *
+					 **************************************************************/
+					if (expectedArity == 0) {
+						SyntaxTreeNode opArgs = sel.args[idx];
+						int numOfOpArgs = 0;
+						if (opArgs != null) {
+							// if there are arguments, add them to tempArgs.
+							/**********************************************************
+							 * The heirs of an oparg node are * * "(" "arg1" "," ... "," "arg_n" ")" *
+							 **********************************************************/
+							numOfOpArgs = (opArgs.heirs().length - 1) / 2;
+							for (int i = 0; i < numOfOpArgs; i++) {
+								tempArgs.addElement(sel.args[idx].heirs()[2 * i + 1]);
+							}
+							;
+
+						}
+						;
+						/**************************************************************
+						 * +cal then if opDefArityFound + Len(tempArgs) # nodeArity *
+						 **************************************************************/
+						if (opDefArityFound + tempArgs.size() != nodeArity) {
+							errors.addError((opArgs == null) ? sel.selSTN.getLocation() : sel.args[idx].getLocation(),
+									"The operator " + curName.toString() + " requires " + (nodeArity - opDefArityFound)
+											+ " arguments.");
+							return nullOAN;
+						}
+						; // if
+						ExprOrOpArgNode[] opArgNodes = new ExprOrOpArgNode[tempArgs.size()];
+						/**********************************************************
+						 * The array of semantic nodes generated by the arguments. *
+						 **********************************************************/
+						for (int i = 0; i < tempArgs.size(); i++) {
+							/**********************************************************
+							 * Call generateExprOrOpArg to generate the semantic * nodes for the arguments
+							 * and add them to the vector * opDefArgs. Note that curSymbolNode is used as
+							 * the * argument to generateExprOrOpArg that specifies the * operator to which
+							 * the arguments are being given. (The * comments for that method assumes that
+							 * the operator * arguments appear in an OpApplication node, but the * method
+							 * doesn't really care; it just uses that operator * to check the arity of the
+							 * arguments.) Note that * argument number i of sel.args[idx] represents
+							 * argument * number i + opDefArityFound of the operator described * by
+							 * curSymbolNode. *
+							 **********************************************************/
+							opDefArgs.addElement(generateExprOrOpArg(curSymbolNode, sel.opsSTN[idx],
+									i + opDefArityFound, (TreeNode) tempArgs.elementAt(i), cm));
+						}
+						; // end for
+
+					} // if expectedArity = 0
+					/**************************************************************
+					 * +cal: else if expectedArity > 0) *
+					 **************************************************************/
+					else {
+						if (expectedArity > 0) {
+							/**********************************************************
+							 * Subexpression.tla has a test to report an error if a * higher-order operator
+							 * is specified, since such an * operator can't be used as an operator argument.
+							 * That * test is eliminated here because this error should be * caught by the
+							 * caller of the selectorToNode method. *
+							 **********************************************************/
+						}
+					}
+					; // else [expectedArity != 0]
+					opDefArityFound = nodeArity;
+
+					if (curNode.getKind() == ModuleInstanceKind) {
+						if ((idx == sel.ops.length - 1) && !(isDef || isFact)) {
+							errors.addError(sel.opsSTN[idx].getLocation(),
+									"Operator name " + curName.toString() + " is incomplete.");
+							return nullOAN;
+						}
+						;
+					} else {
+						/**************************************************************
+						 * curNode.getKind() \in * {UserDefinedOpKind, ThmOrAssumpDefKind, *
+						 * ConstantDeclKind, VariableDeclKind, FormalParamKind, * BuiltInKind,
+						 * BoundSymbolKind, NumberedProofStepKind} *
+						 **************************************************************/
+						/**************************************************************
+						 * +cal: then if /\ curNode.kind = UserDefinedOpKind ... *
+						 **************************************************************/
+						if ((curNode.getKind() == UserDefinedOpKind) && (!((OpDefNode) curNode).isDefined)
+								&& (sel.ops.length != 1)) {
+							errors.addError(sel.opsSTN[idx].getLocation(), "Subexpression of  `" + curName.toString()
+									+ "' used inside the operator's definition.");
+							return nullOAN;
+						}
+						;
+
+						/**************************************************************
+						 * +cal: if /\ firstFindingOpName ... *
+						 **************************************************************/
+						if (firstFindingOpName && ((curNode.getKind() == UserDefinedOpKind)
+								|| (curNode.getKind() == ThmOrAssumpDefKind))) {
+							subExprOf = (SymbolNode) curNode;
+						}
+						/**************************************************************
+						 * +cal: idx # Len(ops) *
+						 **************************************************************/
+						if (idx != sel.ops.length - 1) {
+							FormalParamNode[] opParams;
+							if (curNode.getKind() == UserDefinedOpKind) {
+								opParams = ((OpDefNode) curNode).getParams();
+								newNode = ((OpDefNode) curNode).getBody();
+							} // if
+							else { // curNode.getKind() == ThmOrAssumpDefKind
+								opParams = ((ThmOrAssumpDefNode) curNode).getParams();
+								newNode = ((ThmOrAssumpDefNode) curNode).getBody();
+							}
+							;
+							for (int i = 0; i < opParams.length; i++) {
+								params.addElement(opParams[i]);
+							}
+							; // for
+							curName = null;
+							if (sel.ops[idx + 1] == NameSel) {
+								mode = FollowingLabels;
+							} else {
+								mode = FindingSubExpr;
+							}
+							;
+							for (int i = 0; i < opDefArgs.size(); i++) {
+								allArgs.addElement(opDefArgs.elementAt(i));
+							}
+							; // for
+							opDefArityFound = 0;
+							opDefArgs = new Vector();
+
+							/************************************************************
+							 * If newNode = null, then I think there's an error. It * should be caught
+							 * later. *
+							 ************************************************************/
+							if (newNode != null) {
+								while (newNode.getKind() == SubstInKind) {
+									substInPrefix.addElement(newNode);
+									newNode = ((SubstInNode) newNode).getBody();
+								}
+								; // while
+								while (newNode.getKind() == APSubstInKind) {
+									substInPrefix.addElement(newNode);
+									newNode = ((APSubstInNode) newNode).getBody();
+								}
+								; // while
+							}
+							;
+							if (mode == FindingSubExpr) {
+								curNode = newNode;
+							}
+							;
+						} // if (idx != sel.ops.length - 1)
+					} // else curNode.getKind() \in {UserDefinedOpKind, ...}
+					prevMode = FindingOpName;
+					break;
+
+				default:
+					errors.addAbort(sel.opsSTN[idx].getLocation(), "Internal error: unexpected node kind.");
+					break;
+				}
+				; // switch (curSymbolNode.getKind())
+
+				break; // case FindingOpName
+
+			case FollowingLabels:
+				/******************************************************************
+				 * Invariant: sel.ops[idx] = NameSel *
+				 ******************************************************************/
+				if (((prevMode == FindingOpName) && (curNode.getKind() != UserDefinedOpKind)
+						&& (curNode.getKind() != ThmOrAssumpDefKind))
+						|| ((prevMode != FindingOpName) && (curNode.getKind() != LabelKind))) {
+					errors.addAbort(sel.selSTN.getLocation(), "Unexpected node kind in FollowingLabels mode.");
+				}
+				;
+
+				LabelNode newLabelNode = ((OpDefOrLabelNode) curNode).getLabel(sel.opNames[idx]);
+
+				if (newLabelNode == null) {
+					errors.addError(sel.opsSTN[idx].getLocation(),
+							"Cannot find label `" + sel.opNames[idx].toString() + "'.");
+					return nullOAN;
+				}
+				;
+
+				curNode = newLabelNode;
+
+				if (illegalLabelRef(newLabelNode, sel.opsSTN[idx])) {
+					errors.addError(sel.opsSTN[idx].getLocation(),
+							"Accessing subexpression labeled `" + sel.opNames[idx].toString()
+									+ "' of ASSUME/PROVE clause within the scope of "
+									+ "a declaration\n from outside that declaration's scope.");
+					return nullOAN;
+				}
+				;
+
+				if (expectedArity == 0) {
+
+					/****************************************************************
+					 * Check that label has right number of arguments. *
+					 ****************************************************************/
+					if (newLabelNode
+							.getArity() != ((sel.args[idx] == null) ? 0 : (sel.args[idx].heirs().length - 1) / 2)) {
+						errors.addError(sel.opsSTN[idx].getLocation(),
+								"Label `" + sel.opNames[idx].toString() + "' used with wrong number of arguments.");
+						return nullOAN;
+					}
+					;
+					for (int i = 0; i < newLabelNode.getArity(); i++) {
+						allArgs.addElement(generateExpression(sel.args[idx].heirs()[2 * i + 1], cm));
+					}
+					;
+				}
+				; // if (expectedArity == 0)
+
+				for (int i = 0; i < newLabelNode.getArity(); i++) {
+					FormalParamNode pdecl = newLabelNode.params[i];
+					params.addElement(pdecl);
+				}
+				;
+
+				if ((idx < sel.ops.length - 1) && (sel.ops[idx + 1] != NameSel)) {
+					mode = FindingSubExpr;
+				}
+				;
+
+				if ((mode == FindingSubExpr) || (idx == sel.ops.length)) {
+					curNode = newLabelNode.getBody();
+				}
+				;
+
+				prevMode = FollowingLabels;
+				break; // case FollowingLabels
+
+			case FindingSubExpr:
+				if (sel.ops[idx] == ColonSel) {
+					if ((prevMode == FindingSubExpr) || !(((idx == sel.ops.length - 1) && (prevMode == FindingOpName))
+							|| ((idx < sel.ops.length - 1) && (sel.ops[idx + 1] == NameSel)))) {
+						errors.addError(sel.opsSTN[idx].getLocation(),
+								"`!:' can be used only after a name and either at the "
+										+ "end after an\noperator name or before an operator name.");
+						return nullOAN;
+					} // if ( (prevMode == FindingSubExpr) ...)
+				} // if (sel.ops[idx] == ColonSel)
+				else if (curNode == null) {
+					errors.addError(sel.opsSTN[idx].getLocation(), "Subexpression selection failed, probably due to "
+							+ "an error in the selected expression.");
+					return nullOAN;
+				} else if (curNode.getKind() == LetInKind) {
+					if (ArgNum(sel.ops[idx], 1) == 1) {
+						curNode = ((LetInNode) curNode).getBody();
+					} else {
+						errors.addError(sel.opsSTN[idx].getLocation(), "A LET/IN expression has only one operand.");
+						return nullOAN;
+					}
+					;
+				} // else if (curNode.getKind() == LetInKind)
+
+				else if (curNode.getKind() == OpApplKind) {
+					OpApplNode curOpApplNode = (OpApplNode) curNode;
+					ExprOrOpArgNode[] curArgs = curOpApplNode.getArgs();
+					SymbolNode opNode = curOpApplNode.getOperator();
+					if ((opNode.getKind() == FormalParamKind) || (opNode.getKind() == ConstantDeclKind)
+							|| (opNode.getKind() == UserDefinedOpKind)) {
+						int temp = ArgNum(sel.ops[idx], opNode.getArity());
+						if (temp == -1) {
+							reportSelectorError(sel, idx);
 //               errors.addError(sel.opsSTN[idx].getLocation(),
 //                               "NoNexistent operand specified by `"
 //                                + sel.opNames[idx].toString() + "'.");
-               return nullOAN ; 
-               } ;
-             curNode = curArgs[temp-1] ;
-             } // if (opNode.getKind() == FormalParamKind) || ...
-
-           else if (opNode.getKind() == BuiltInKind) {
-             if (   (opNode.getName() == OP_rc)      // $RcdConstructor
-                 || (opNode.getName() == OP_sor)){   // $SetOfRcds
-               
-               int temp = ArgNum(sel.ops[idx], curArgs.length) ;
-               if (temp == -1) {
+							return nullOAN;
+						}
+						;
+						curNode = curArgs[temp - 1];
+					} // if (opNode.getKind() == FormalParamKind) || ...
+
+					else if (opNode.getKind() == BuiltInKind) {
+						if ((opNode.getName() == OP_rc) // $RcdConstructor
+								|| (opNode.getName() == OP_sor)) { // $SetOfRcds
+
+							int temp = ArgNum(sel.ops[idx], curArgs.length);
+							if (temp == -1) {
 //                 errors.addError(sel.opsSTN[idx].getLocation(),
 //                                 "NonExistent operand specified by `"
 //                                  + sel.opNames[idx].toString() + "'.");
-                 reportSelectorError(sel, idx) ;
-                 return nullOAN ; 
-                } ;
-               curOpApplNode = (OpApplNode) curArgs[temp-1] ;
-               if (curOpApplNode.getOperator().getName() != OP_pair) {
-                 errors.addAbort(
-                   sel.opsSTN[idx].getLocation(),
-                   "Internal error: Expecting $Pair and didn't find it.") ;
-                  } ;
-               curNode = curOpApplNode.getArgs()[1] ;
-              } // if opNode.name == $RcdConstructor or $SetOfRcds
-
-             else if (opNode.getName() == OP_case) { //$Case 
-               if (idx == sel.ops.length - 1) {
-                 errors.addError(
-                    sel.opsSTN[idx].getLocation(),
-                    "Subexpression of CASE must have form !i!j.");
-                 return nullOAN ; 
-                } ;
-               int temp = ArgNum(sel.ops[idx], curArgs.length) ;
-               if (temp == -1) {
-                 reportSelectorError(sel, idx) ;
-                 return nullOAN ; 
-                } ;
-               curOpApplNode = (OpApplNode) curArgs[temp-1] ;
-               if (curOpApplNode.getOperator().getName() != OP_pair) {
-                 errors.addAbort(
-                   sel.opsSTN[idx].getLocation(),
-                   "Internal error: Expecting $Pair and didn't find it.") ;
-                  } ;
-               idx = idx + 1;
-               temp = ArgNum(sel.ops[idx], 2) ;
-               if (temp == -1) {
-                 errors.addError(
-                   sel.opsSTN[idx].getLocation(),
-                   "Second selector for CASE subexpression must specify "
-                    + " one of two operands.");
-                 return nullOAN ; 
-                } ;
-               curNode = curOpApplNode.getArgs()[temp-1] ;
-               if (curNode == null) {
-                 errors.addError(
-                   sel.opsSTN[idx].getLocation(),
-                   "Selecting OTHER in a CASE statement.");
-                 return nullOAN ; 
-                 } ;
-              }  // if opNode.name = $Case
-
-             else if (opNode.getName() == OP_exc) { //$Except
-               /************************************************************
-               * In an $Except node representing                           *
-               *                                                           *
-               *   [exp_1 ELSE !... = exp_2, ..., !... = exp_n]            *
-               *                                                           *
-               * operand i names exp_i.                                    *
-               ************************************************************/
-               int temp = ArgNum(sel.ops[idx], curArgs.length) ;
-               if (temp == -1) {
-                 reportSelectorError(sel, idx) ;
-                 return nullOAN ; 
-                } ;
-               /************************************************************
-               * Selection of subexpressions of EXCEPT have been outlawed  *
-               * because they may contain a dangling "@".  This doesn't    *
-               * seem to be worth fixing because it's unlikely that        *
-               * anyone will actually want to refer to such a              *
-               * subexpression.                                            *
-               * Change made 25 Oct 2007.                                  *
-               ************************************************************/
-               if (temp > 1) {
-                 errors.addError(
-                   sel.opsSTN[idx].getLocation(),
-                   "Selecting subexpression of an " + 
-                     "EXCEPT not yet implemented.");
-                 return nullOAN ; 
-                 } ;
-               curNode = curArgs[temp-1];
-               if (isNullSelection(curNode, sel, idx))
-                 { return nullOAN ; } ;
-               if (temp > 1) {
-                 curOpApplNode = (OpApplNode) curNode ;
-                 if (curOpApplNode.getOperator().getName() != OP_pair) {
-                   errors.addAbort(
-                     sel.opsSTN[idx].getLocation(),
-                     "Internal error: Expecting $Pair and didn't find it.") ;
-                    } ;
-                 curNode = curOpApplNode.getArgs()[1] ;
-                }; // if (temp > 1) 
-              }  // if opNode.name = $Except
-
-             else { // Handled by standard procedure 
-               if (   (curOpApplNode.getNumberOfBoundedBoundSymbols() == 0)
-                   && (   (curOpApplNode.getUnbdedQuantSymbols() == null) 
-                       || (curOpApplNode.getUnbdedQuantSymbols().length 
-                            == 0) )  
-                      /*****************************************************
-                      * I'm not sure getUnbdedQuantSymbols always returns  *
-                      * null if there are no such symbols.                 *
-                      *****************************************************/
-                   ) {
-                 /**********************************************************
-                 * Current subexpression has no bound variables.           *
-                 **********************************************************/
-                 int temp = ArgNum(sel.ops[idx],
-                                   curOpApplNode.getArgs().length) ;
-                 if (temp == -1) {
-                 reportSelectorError(sel, idx) ;
-                 return nullOAN ; 
-                  } ;
-                 curNode = curOpApplNode.getArgs()[temp-1] ;
-                } // if current subexpression has no bound variables
-               else {
-                 /**********************************************************
-                 * Current subexpression has bound variables.              *
-                 **********************************************************/
-                 if (   (sel.ops[idx] == NullSel)
-                     || (sel.ops[idx] == AtSel))  {
-                   /********************************************************
-                   * Set temp to the array of FormalParamNodes for the     *
-                   * parameters.                                           *
-                   ********************************************************/
-                   FormalParamNode[] temp ;
-                   if (curOpApplNode.getNumberOfBoundedBoundSymbols() > 0) {
-                     FormalParamNode[][] symbs = 
-                             curOpApplNode.getBdedQuantSymbolLists() ;
-                     int numSymbs = 0 ;
-                     for (int i = 0 ; i < symbs.length ; i++) {
-                       numSymbs = numSymbs + symbs[i].length ;
-                      }; // for
-                     temp = new FormalParamNode[numSymbs] ;
-                     int k = 0 ;
-                     for (int i = 0 ; i < symbs.length ; i++) {
-                       for (int j = 0 ; j < symbs[i].length ; j++) {
-                         temp[k] = symbs[i][j] ;
-                         k++;
-                        }; // for j
-                      }; // for i
-                    } // if (curOpApplNode.getNumberOf... > 0) 
-                   else {
-                     temp = curOpApplNode.getUnbdedQuantSymbols() ;
-                    } ; // else not (curOpApplNode.getNumberOf... > 0) 
-
-                    /*******************************************************
-                    * Add the elements of temp to the params vector.       *
-                    *******************************************************/
-                    for (int i = 0 ; i < temp.length ; i++) {
-                      params.addElement(temp[i]) ;
-                     } ; // for i
-
-                    if (sel.ops[idx] == NullSel) {
-                      /*****************************************************
-                      * Add the arguments to allArgs, checking if there    *
-                      * are the right number.                              *
-                      *****************************************************/
-                      int numOfArgs = (sel.args[idx].heirs().length-1)/2 ;
-                      if ( temp.length != numOfArgs) {
-                        errors.addError(
-                           sel.opsSTN[idx].getLocation(),
-                           "Selector with " + numOfArgs + 
-                            " argument(s) used for quantifier with "  +
-                            temp.length + " bound identifier(s).");
-                        return nullOAN ; 
-                       } ;
-                      for (int i = 0; i < numOfArgs; i++) {
-                        allArgs.addElement(
-                                  generateExpression(
-                                    sel.args[idx].heirs()[2 * i + 1] , cm)) ;
-                       } ; // for i
-                     } ; // if (sel.ops[idx] == NullSel)
-                    curNode = curOpApplNode.getArgs()[0] ;
-                  } // if (sel.ops[idx] == NullSel) || ...
-                 else {
-                   int temp = ArgNum(
-                                sel.ops[idx], 
-                                curOpApplNode.getBdedQuantBounds().length);
-                   if (temp == -1) {
-                     reportSelectorError(sel, idx) ;
-                     return nullOAN ; 
-                    } ;
-                   curNode = curOpApplNode.getBdedQuantBounds()[temp-1] ;
-                  }; // else
-                }; // else Current subexpression has bound variables
-              }; // Handled by standard procedure 
-             } // else if (opNode.getKind() == BuiltInKind)
-
-           else {
-             errors.addError(
-                sel.opsSTN[idx].getLocation(),
-                "Choosing operand `" + selectorItemToString(sel, idx) + 
-                "' of subexpression with no operands." );
-             return nullOAN ; 
-            } ;
-
-          } // else if ((curNode.getKind() == OpApplKind) 
-
-         else if (curNode.getKind() == AssumeProveKind) { 
-           AssumeProveNode curAPNode = (AssumeProveNode) curNode;
-
-           /****************************************************************
-           * 16 Feb 2009: Case of curAPNode.suffices true added.           *
-           ****************************************************************/
-           if (   (curAPNode.isSuffices()) 
-               && (! inAPsuffices) )   {
-             /**************************************************************
-             * In this case, the selector must be 1, and the curNode is    *
-             * left equal to the AssumeProve, but with inAPsuffices set    *
-             * to true.                                                    *
-             **************************************************************/
-             if (ArgNum(sel.ops[idx], 1) != 1) {
-               errors.addError(sel.opsSTN[idx].getLocation(),
-                    "Accessing non-existent subexpression of " +
-                     "a SUFFICES");
-               return nullOAN ; 
-              };
-             inAPsuffices = true ;             
-             } // if (curAPNode.isSuffices())
-           else {
-             inAPsuffices = false;
-               /************************************************************
-               * Added 16 Feb 2009 by LL.                                  *
-               ************************************************************/
-             int temp = ArgNum(sel.ops[idx], 
-                               1 + curAPNode.getAssumes().length);
-             if (temp == -1) {
-               reportSelectorError(sel, idx) ;
-               return nullOAN ; 
-              } ;
-  
-             if (illegalAPPosRef(curAPNode, temp)){
-               errors.addError(sel.opsSTN[idx].getLocation(),
-                    "Accessing ASSUME/PROVE clause within the scope of " +
-                   "a declaration\n from outside that declaration's scope.");
-               return nullOAN ; 
-               } ;
-             if (temp <= curAPNode.getAssumes().length) {
-               curNode = curAPNode.getAssumes()[temp-1] ;                 
-               if (isNullSelection(curNode, sel, idx)) { return nullOAN; } ;
-               if (   (curNode.getKind() == NewSymbKind)
-                   && (idx != sel.args.length-1)) {
-                 /************************************************************
-                 * Extra conjunct added to if test to allow selection of a   *
-                 * NEW clause as a fact.                                     *
-                 ************************************************************/
-                 errors.addError(
-                    sel.opsSTN[idx].getLocation(),
-                    "Selected a subexpression of a NEW clause of an ASSUME.");
-                 return nullOAN ; 
-                }
-              }
-             else {
-               curNode = curAPNode.getProve() ;
-             };
-           } // else if (curAPNode.isSuffices())
-          } // else if (curNode.getKind() == AssumeProveKind) 
-
-         else if (curNode.getKind() == OpArgKind) { 
-           SymbolNode opNode = ((OpArgNode) curNode).getOp() ;
-           if (   (opNode.getKind() != UserDefinedOpKind)
-               || (opNode.getName() !=S_lambda)) {
-             errors.addError(
-                sel.opsSTN[idx].getLocation(),
-                "Trying to select subexpression of an operator argument.");
-             return nullOAN ; 
-            } ;
-           OpDefNode opDefOpNode = (OpDefNode) opNode ;
-           if (   (sel.ops[idx] != NullSel)
-               && (sel.ops[idx] != AtSel)) {
-             errors.addError(
-                sel.opsSTN[idx].getLocation(),
-                "Cannot use !" + sel.opNames[idx].toString() +
-                " to select subexpression of a LAMBDA.");
-             return nullOAN ; 
-             } ;
-           if (sel.ops[idx] == NullSel) {
-             int numOfArgs = (sel.args[idx].heirs().length-1)/2 ;
-             if (opDefOpNode.getArity() != numOfArgs) {
-               errors.addError(
-                  sel.opsSTN[idx].getLocation(),
-                  "Selector with " + numOfArgs + 
-                   "arguments used for LAMBDA expression taking "  +
-                   opDefOpNode.getArity() + " arguments.");
-               return nullOAN ; 
-              } ;
-             for (int i = 0; i < numOfArgs; i++) {
-               allArgs.addElement(generateExpression(
-                                  sel.args[idx].heirs()[2 * i + 1] , cm)) ;
-              } ;
-             } ; // if (sel.ops[idx] == NullSel)
-            for (int i = 0; i < opDefOpNode.getArity(); i++) {
-              params.addElement(opDefOpNode.getParams()[i]) ;
-             } ;
-            curNode = opDefOpNode.getBody() ;
-          } // else if (curNode.getKind() == OpArgKind) 
-
-         else if (   (curNode.getKind() == UserDefinedOpKind) 
-                  || (curNode.getKind() == BuiltInKind)
-                  || (curNode.getKind() == NumberedProofStepKind)) { 
-              errors.addAbort(
-                sel.opsSTN[idx].getLocation(),
-                "Internal error: " +
-                " Should not have been able to select this node.") ;
-          } // else if (curNode.getKind() == UserDefinedOpKind) || ...
-
-         else if (   (curNode.getKind() == AtNodeKind) 
-                  || (curNode.getKind() == DecimalKind) 
-                  || (curNode.getKind() == NumeralKind) 
-                  || (curNode.getKind() == StringKind) 
-                  || (curNode.getKind() == FormalParamKind) 
-                  || (curNode.getKind() == ConstantDeclKind) 
-                  || (curNode.getKind() == VariableDeclKind) 
-                  || (curNode.getKind() == BoundSymbolKind) ) { 
-              errors.addError(
-                sel.opsSTN[idx].getLocation(),
-                "Selecting subexpression of expression that has none.") ;
-              return nullOAN ;
-          } // else if (curNode.getKind() == AtNodeKind) || ...
-
-         else if (curNode.getKind() == LabelKind) { 
-           curNode = ((LabelNode) curNode).getBody() ;
-           idx = idx - 1;
-          } // else if (curNode.getKind() == LabelKind) 
-
-         else { 
-           errors.addAbort(
-             sel.opsSTN[idx].getLocation(),
-             "Internal error: " +
-              " Unknown node kind.") ;
-          } ; // end last else of if sel.ops[idx] != ColonSel
-
-         if (isNullSelection(curNode, sel, idx)) { return nullOAN; } ;
-
-         if (idx != sel.ops.length - 1) {
-           if (sel.ops[idx+1] == NameSel) {
-             while (curNode.getKind() == LabelKind) {
-               curNode = ((LabelNode) curNode).getBody();
-               if (isNullSelection(curNode, sel, idx)) { return nullOAN; } ;
-              }; // while
-             if (curNode.getKind() == LetInKind) {
-               letInContext = ((LetInNode) curNode).context ;
-               mode = FindingOpName ;
-               firstFindingOpName = false;
-              } 
-             else {
-               errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "A name selector here must be from a LET clause.") ;
-               return nullOAN ;
-              }
-            } // if (sel.ops[idx+1] == NameSel) 
-           else if (sel.ops[idx+1] == ColonSel) {
-             errors.addError(
-                 sel.opsSTN[idx].getLocation(),
-                 "!: should not follow an operand selector.") ;
-             return nullOAN ;
-              }
-          } ; // if (idx != sel.ops.length - 1)
-
-         prevMode = FindingSubExpr ;
-       break ; // case FindingSubExpr
-
-       default:
-         errors.addAbort(sel.selSTN.getLocation(),
-                         "Internal error: Unexpected mode") ;
-       } // switch (mode)
-      idx++ ;
-     } // while idx < 
-     /**********************************************************************
-     * +cal: end while ;                                                   *
-     **********************************************************************/
-
-    /***********************************************************************
-    * +cal: if curNode.kind = AssumeProveKind ... end if                   *
-    *                                                                      *
-    * Modified 17 Feb 2009 by LL to allow ASSUME/PROVE to be used as a     *
-    * fact.  This seems to have been a bug in the original.                *
-    *                                                                      *
-    * Not corrected in the PlusCal code because that code doesn't talk     *
-    * about the isFact case.                                               *
-    ***********************************************************************/
-    if (curNode.getKind() == AssumeProveKind) {
-      if (isFact) { return (AssumeProveNode) curNode ; };
-      errors.addError(
-               sel.selSTN.getLocation(),
-               "Selected ASSUME/PROVE instead of expression.") ;
-             return nullOAN ; 
-     } ;
-
-    /***********************************************************************
-    * The following added to allow naming of NEW constructs of an          *
-    * ASSUME/PROVE in facts.                                               *
-    ***********************************************************************/
-    if (curNode.getKind() == NewSymbKind) {
-      if (isFact) { return (LevelNode) curNode ; } ;
-      errors.addError(
-         sel.selSTN.getLocation(), 
-         "Selected a NEW declaration as an expression or operator.") ;
-      return nullOAN ;
-     } ;
-    
-    /***********************************************************************
-    * +cal: if expectedArity < 0  ... end if;                              *
-    ***********************************************************************/
-    if (expectedArity < 0) {
-      if (    (prevMode != FindingOpName)
-          || !(   (curNode.getKind() == UserDefinedOpKind)
-               || (curNode.getKind() == ThmOrAssumpDefKind)
-               || (curNode.getKind() == NumberedProofStepKind)
-               || // This clause added by LL 15 Oct 2007, not in +cal code
-                  (   (curNode.getKind() == ModuleInstanceKind)
-                   && isDef))) {
-            errors.addError(
-              sel.selSTN.getLocation(), 
-              "DEF clause entry should describe a defined operator.") ;
-            return nullOAN ;
-       } ; 
-      if (   (curNode.getKind() == NumberedProofStepKind)
-          && (((OpDefNode) curNode).getStepNode().getKind() != DefStepKind)) {
-         errors.addError(
-           sel.selSTN.getLocation(), 
-           "DEF clause entry refers to a non-definition step.") ;
-          return nullOAN ;
-       }
-      return (LevelNode) curNode;
-     }; // if (expectedArity < 0)
-
-    if (curNode.getKind() == NumberedProofStepKind) {
-         errors.addError(
-           sel.selSTN.getLocation(), 
-           isFact ? "Step number of non-fact used as a fact"
-             : "Step number of non-expression step used as an expression.") ;
-          return nullOAN ;
-      }
-
-    /***********************************************************************
-    * +cal: if expectedArity > 0 ... end if                                *
-    ***********************************************************************/
-    if (expectedArity > 0) {
-      int temp = params.size() ;
-      if (curNode.getKind() == OpArgKind) {
-        temp = temp + ((OpArgNode) curNode).getArity();
-       }
-      else if (prevMode == FindingOpName) {
-        temp = temp + ((SymbolNode) curNode).getArity();
-       } ;
-
-      if (expectedArity != temp) {
-            errors.addError(
-               sel.selSTN.getLocation(),
-               "Expected arity " + expectedArity + 
-               " but found operator of arity " + temp + ".") ;
-            return nullOAN ;
-        } ; 
-     }; // if (expectedArity > 0)
-
-    /***********************************************************************
-    * If opDefArgs is non-empty, we will need to convert it to an array,   *
-    * so we might as well do it here.                                      *
-    ***********************************************************************/
-    ExprOrOpArgNode[] opDefArgArray = 
-                            new ExprOrOpArgNode[opDefArgs.size()] ;
-    for (int i = 0 ; i < opDefArgs.size() ; i++) {
-      opDefArgArray[i] = (ExprOrOpArgNode) opDefArgs.elementAt(i) ;
-     } ; // for
-       
-    /***********************************************************************
-    * +cal: if /\ prevMode = "FindingOpName" ... end if;                   *
-    ***********************************************************************/
-    if (   (prevMode == FindingOpName)
-         && (params.size() + substInPrefix.size() > 0)) {
-
-      /*********************************************************************
-      * Set nodeParams to the +cal program's curNode.params value.         *
-      *********************************************************************/
-      FormalParamNode[] nodeParams = null ;
-      if (curNode.getKind() == UserDefinedOpKind) {
-        nodeParams = ((OpDefNode) curNode).getParams();
-       }
-      else {
-        if (curNode.getKind() != ThmOrAssumpDefKind) {
-              errors.addAbort(
-                sel.opsSTN[sel.opsSTN.length-1].getLocation(),
-                "Internal Error: " +
-                 " Found unexpected node kind after FindingOpName") ;
-         } ;
-        nodeParams = ((ThmOrAssumpDefNode) curNode).getParams();
-       } ;
-
-      for (int i = 0 ; i < opDefArgs.size() ; i++) {
-        allArgs.addElement(opDefArgArray[i]) ;
-       } ; // for
-
-      ExprOrOpArgNode[] temp = new ExprOrOpArgNode[nodeParams.length] ;
-      for (int i = 0; i < nodeParams.length; i++) {
-        FormalParamNode pm = nodeParams[i] ;
-        /*******************************************************************
-        * Set newpm to a "clone" of pm.                                    *
-        *******************************************************************/
-        FormalParamNode newpm = new FormalParamNode(pm.getName(),
-                                                    pm.getArity(), 
-                                                    pm.stn,
-                                                    null, 
-                                                    cm) ;
-
-        /*******************************************************************
-        * Set eoag to the ExprOrOpArgNode constructed from newpm.          *
-        *******************************************************************/
-        ExprOrOpArgNode eoag = null ;
-        if (pm.getArity() == 0) {
-          /*****************************************************************
-          * Formal parameter eoag is an ordinary (non-operator) parameter. *
-          *****************************************************************/
-          eoag = new OpApplNode(newpm,
-                                new ExprNode[0],
-                                sel.selSTN,
-                                cm) ;
-         }
-        else {
-          /*****************************************************************
-          * Formal parameter eoag is an operator parameter.                *
-          *****************************************************************/
-          eoag = new OpArgNode(newpm, sel.selSTN, cm) ;
-         } ;
-        temp[i] = eoag ;
-        params.addElement(newpm) ;
-       } ; // for
-      SymbolNode curSymNode = (SymbolNode) curNode ;
-      curNode = new OpApplNode(curSymNode,
-                               temp, // opDefArgArray,
-                               sel.selSTN, // TreeNode
-                               cm );
-
-     } ;// if (prevMode == FindingOpName) ...
-
-
-    /***********************************************************************
-    * +cal: if curNode.kind = OpArgKind                                    *
-    ***********************************************************************/
-    if (curNode.getKind() == OpArgKind) {
-      OpArgNode curOpArgNode = (OpArgNode) curNode ;
-
-      /*********************************************************************
-      * +cal: if expectedArity = 0 then ... elsif                         *
-      *********************************************************************/
-      if (expectedArity == 0) {
-        errors.addError(
-               sel.selSTN.getLocation(),
-               "Selected operator argument when expression expected.") ;
-        return nullOAN ;
-       } ;
-
-      /*********************************************************************
-      * +cal: elsif expectedArity # Len(params) + ... else                 *
-      *********************************************************************/
-      int temp = params.size() + curOpArgNode.getArity() ;
-      if (expectedArity != temp) {
-        errors.addError(
-               sel.selSTN.getLocation(),
-               "Expected operator of arity " + expectedArity +
-               " but selected operator has arity " + temp + ".") ;
-        return nullOAN ;
-       }; 
-
-      /*********************************************************************
-      * +cal: else ... end if                                              *
-      *********************************************************************/
-      if (params.size() + substInPrefix.size() > 0) {
-        FormalParamNode[] temp2 = 
-                            new FormalParamNode[curOpArgNode.getArity()];
-        for (int i = 0; i < temp2.length; i++) {
-          UniqueString temp3 = UniqueString.uniqueStringOf(
-                                 "NewParam" + i) ;
-          temp2[i] = new FormalParamNode(
-                           temp3,                      // name
-                           0,                          // arity
-                           new SyntaxTreeNode(temp3),  // syntax tree node
-                           null,                       // symbol table
-                           cm) ;                       // module
-          params.addElement(temp2[i]) ;
-         } ; // for
-        curNode = new OpApplNode(curOpArgNode.getOp(),
-                                 opDefArgArray,
-                                 sel.selSTN, // TreeNode
-                                 cm );
-       } ; // if (params.size() + ...)
-      
-     }; // if (curNode.getKind() == OpArgKind)
- 
-    /***********************************************************************
-    * This code doesn't seem to be present in the +cal code.  I forget     *
-    * why not; perhaps I added it without correcting the +cal.             *
-    *                                                                      *
-    * Modified 19 May 2008 by LL because of addition of labeled            *
-    * ASSUME/PROVE nodes.                                                  *
-    ***********************************************************************/
-    if (    ! isFact 
-        && (   (    (curNode.getKind() == ThmOrAssumpDefKind)
-                 && ( ((ThmOrAssumpDefNode) curNode).getBody().getKind()
-                         == AssumeProveKind))
-                /***********************************************************
-                * curNode is an ASSUME/PROVE theorem.                      *
-                ***********************************************************/
-            || (    (curNode.getKind() == LabelKind)
-                 && (((LabelNode) curNode).isAssumeProve)))
-                /***********************************************************
-                * curNode is a LabelNode naming an ASSUME/PROVE.           *
-                ***********************************************************/
-       ) { 
-      errors.addError(
-           sel.selSTN.getLocation(),
-           "ASSUME/PROVE used where an expression is required.") ;
-        return nullOAN ;
-     };
-
-    /***********************************************************************
-    * The following code added 18 Oct 2007 to check if a                   *
-    * pseudo-expression like "HAVE P" or "PICK ..." was used as an         *
-    * expression.                                                          *
-    ***********************************************************************/
-   if (   (! isFact)
-       && (curNode.getKind() == ThmOrAssumpDefKind)
-       && (((ThmOrAssumpDefNode) curNode).getBody().getKind() 
-                == OpApplKind)) {
-      UniqueString opName = 
-         ((OpApplNode) ((ThmOrAssumpDefNode) 
-                           curNode).getBody()).getOperator().getName() ;
-      String exprType = null ;
-      if (opName == OP_qed) { exprType = "QED step" ;}
-      else if (opName == OP_pfcase)  { exprType = "CASE" ;}
-      else if (opName == OP_have)    { exprType = "HAVE" ;}
-      else if (opName == OP_take)    { exprType = "TAKE" ;}
-      else if (opName == OP_pick)    { exprType = "PICK" ;}
-      else if (opName == OP_witness) { exprType = "WITNESS" ;}
-      else if (opName == OP_suffices) { exprType = "SUFFICES" ;};
-        /*******************************************************************
-        * OP_suffices added by LL 16 Feb 2009.                             *
-        *******************************************************************/
-      if (exprType != null) {
-         errors.addError(
-            sel.selSTN.getLocation(),
-            exprType + " proof step selected instead of expression.");
-        return nullOAN ;
-       } ;
-    } // if
-
-
-    /***********************************************************************
-    * +cal: if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind...    *
-    ***********************************************************************/
-    if (   (curNode.getKind() == UserDefinedOpKind) 
-        || (curNode.getKind() == ConstantDeclKind) 
-        || (curNode.getKind() == VariableDeclKind) 
-        || (curNode.getKind() == FormalParamKind) 
-        || (curNode.getKind() == BuiltInKind) 
-        || (curNode.getKind() == BoundSymbolKind) 
-        || (curNode.getKind() == ThmOrAssumpDefKind)
-        || (curNode.getKind() == NewConstantKind)
-        || (curNode.getKind() == NewVariableKind)
-        || (curNode.getKind() == NewStateKind)
-        || (curNode.getKind() == NewActionKind)
-        || (curNode.getKind() == NewTemporalKind)) {
-      SymbolNode curSymbolNode = (SymbolNode) curNode ;
-      if (expectedArity > 0) {
-        return new OpArgNode(curSymbolNode, sel.selSTN, cm) ;
-       } 
-
-      /*********************************************************************
-      * +cal: elsif expectedArity = 0 then                                 *
-      *********************************************************************/
-      else {
-        /*******************************************************************
-        * expectedArity = 0                                                *
-        *******************************************************************/
-        OpApplNode oan = new OpApplNode(curSymbolNode,
-                                        opDefArgArray,
-                                        sel.selSTN,
-                                        cm ) ;
-        oan.subExpressionOf = subExprOf;
-        return oan;
-       }  // if (expectedArity > 0)
-     } ; // if (curNode.getKind() == UserDefinedOpKind)
-
-    /***********************************************************************
-    * +cal: elsif curNode.kind = OpArgKind then                            *
-    ***********************************************************************/
-    if (curNode.getKind() == OpArgKind) { return (OpArgNode) curNode; } ;
-
-    /***********************************************************************
-    * +cal: else                                                           *
-    ***********************************************************************/
-
-    /***********************************************************************
-    * The following test is not in Subexpression.tla and should not be,    *
-    * having been added to allow the use of a ModuleInstanceKind as a      *
-    * Fact or Def.  The else case should not occur because if it would be  *
-    * the case, then the error should have been caught earlier (I think).  *
-    ***********************************************************************/
-    if (curNode.getKind() == ModuleInstanceKind) {
-      if (isFact || isDef) {
-        return (OpDefNode) curNode; 
-        } 
-      else {
-        errors.addError(
-          sel.selSTN.getLocation(),
-             "Module instantiation selected instead of expression.");
-        return nullOAN ;
-       }
-      } ;    
-    /***********************************************************************
-    * curNode should be an expression node.                                *
-    ***********************************************************************/
-    if (!(curNode instanceof ExprNode)) {
-      errors.addAbort(sel.selSTN.getLocation(),
-                      "Internal error: Expected expression node.") ;
-     } ;
-    ExprNode curExprNode = (ExprNode) curNode ;
-    int temp = substInPrefix.size() ;
-
-    /***********************************************************************
-    * +cal: while temp > 0 do ... end while                                *
-    ***********************************************************************/
-    while (temp > 0) {
-      // Modified on 13 Nov 2009 by LL to handle the case of a subexpression
-      // of a Theorem or Assumption. 
-      Object substOb = substInPrefix.elementAt(temp-1) ;
-      if (substOb instanceof SubstInNode) {
-          SubstInNode subst = (SubstInNode) substOb;
-          curExprNode = new SubstInNode(subst.stn,
-                                        subst.getSubsts(),
-                                        curExprNode,
-                                        subst.getInstantiatingModule(),
-                                        subst.getInstantiatedModule()
-                                       );
-      } else {
-          APSubstInNode subst = (APSubstInNode) substOb;
-          curExprNode = new SubstInNode(subst.stn,
-                                        subst.getSubsts(),
-                                        curExprNode,
-                                        subst.getInstantiatingModule(),
-                                        subst.getInstantiatedModule()
-                 );
-          
-      }
-      temp = temp - 1;
-     }; // while
-
-    /***********************************************************************
-    * Will need params as an array.                                        *
-    ***********************************************************************/
-    FormalParamNode[] paramsArray = new FormalParamNode[params.size()] ;
-    for (int i = 0; i < params.size(); i++) {
-      paramsArray[i] = (FormalParamNode) params.elementAt(i) ;
-      } ;
-
-    /***********************************************************************
-    * If there are params, will need a Lambda operator.                    *
-    ***********************************************************************/
-    OpDefNode newLambda = null ;
-    if (paramsArray.length > 0) {
-      newLambda =
-        new OpDefNode(S_lambda,          // The operator name is "LAMBDA"
-                      UserDefinedOpKind, // The node kind 
-                      paramsArray,       // the array of formal parameters
-                      false ,            // localness, which is meaningless
-                      curExprNode,       // the body 
-                      cm,                // the module
-                      null,              // the symbol table
-                      sel.selSTN,        // syntax-tree node
-                      true,              // Is defined.  
-                      null ) ;           // Source
-      } ;
-    
-    /***********************************************************************
-    * +cal: if expectedArity > 0                                           *
-    ***********************************************************************/
-    if (expectedArity > 0) {
-      if (paramsArray.length != expectedArity) {
-        errors.addError(
-          sel.selSTN.getLocation(),
-          "Expected operator argument with arity " + expectedArity + 
-          " but found one of arity " + paramsArray.length + ".") ;
-        return nullOAN ; 
-       } ;
-      return new OpArgNode(newLambda, sel.selSTN, cm) ;
-      } // if (expectedArity > 0) 
-
-    /***********************************************************************
-    * expectedArity = 0                                                    *
-    * +cal: else if Len(params) # Len(allArgs)                             *
-    ***********************************************************************/
-    if (paramsArray.length != allArgs.size()) {
-      errors.addError(
-        sel.selSTN.getLocation(),
-        "Expected " + paramsArray.length + " arguments but found " +
-          allArgs.size() + ".") ;
-      return nullOAN ; 
-     } ; 
-    /***********************************************************************
-    * +cal: if Len(params) = 0 then                                        *
-    ***********************************************************************/
-    if (paramsArray.length == 0) {
-      /*********************************************************************
-      * In this case, we don't have to construct a new LAMBDA or OpAppl    *
-      * node.  However, we want to have a new node to which to attach the  *
-      * SyntaxTree node.  So, we create an OpApplNode with the dummy       *
-      * builtin operator $Nop and return it.                               *
-      *********************************************************************/
-      ExprOrOpArgNode[] args = new ExprOrOpArgNode[1] ;
-      args[0] = curExprNode ;
-      OpApplNode ln = new OpApplNode(OP_nop, args,  sel.selSTN, cm) ;
-      ln.subExpressionOf = subExprOf ;
-      return ln;}
-
-    /***********************************************************************
-    * +cal: else                                                           *
-    ***********************************************************************/
-    /***********************************************************************
-    * Will need allArgs as an array.                                       *
-    ***********************************************************************/
-    ExprOrOpArgNode[] allArgsArray = new ExprOrOpArgNode[allArgs.size()] ;
-    for (int i = 0; i < allArgs.size(); i++) {
-      allArgsArray[i] = (ExprOrOpArgNode) allArgs.elementAt(i) ;
-      } ;
-    
-    OpApplNode oan = 
-          new OpApplNode (newLambda, allArgsArray, sel.selSTN, cm) ; 
-    oan.subExpressionOf = subExprOf ;
-    return oan ;
-       
-   } // selectorToNode
-
-  private static String selectorItemToString(Selector sel, int idx) {
-    String selStr = sel.opNames[idx].toString() ;
-    if (selStr.equals("N_StructOp")) {
-      selStr = "argument selector " + 
-               sel.opsSTN[idx].heirs()[0].heirs()[0].getImage() ;
-     } 
-    else if (selStr.equals("N_OpArgs")) {
-      selStr = "!(...)" ;
-     };
-    return selStr ;
-    }
-
-  private void reportSelectorError(Selector sel, int idx) {
-    /***********************************************************************
-    * Just a method to perform error reporting when an error is detected   *
-    * when executing selectorToNode.                                       *
-    ***********************************************************************/
-    errors.addError(sel.opsSTN[idx].getLocation(),
-                    "Nonexistent operand specified by `"
-                     +  selectorItemToString(sel, idx) + "'.");
-   }
-
-  private boolean isNullSelection(SemanticNode node, Selector sel, int idx) {
-    /***********************************************************************
-    * A method for use in selectorToNode to test if a a null node has      *
-    * been selected.  This should happen only if the null node was         *
-    * created as a result of a previous error.  If the node is null, then  *
-    * an error is reported and true is returned.                           *
-    ***********************************************************************/
-    boolean val = (node == null) ;
-    if (val) { 
-      errors.addError(sel.opsSTN[idx].getLocation(),
-                    "An unexpected null node specified by "
-                     +  selectorItemToString(sel, idx) + "'."
-                     + "\nThis is probably due to a previous error.");
-     };
-    return val ;
-   } 
-
-  // Constructor
-  public Generator(ExternalModuleTable moduleTable, Errors errs) {
-    nullParam = new FormalParamNode[0];
-    nullODN   = new OpDefNode(UniqueString.uniqueStringOf("nullODN"));
-    nullOAN   = new OpApplNode(nullODN);
-    nullOpArg = new OpArgNode(UniqueString.uniqueStringOf("nullOpArg"));
-    nullLabelNode = new LabelNode(nullOAN) ;
-    this.errors       = errs;
-    this.moduleTable  = moduleTable;
-    this.symbolTable  = new SymbolTable(moduleTable, errors);
-    this.excStack     = new Stack(); 
-    this.excSpecStack = new Stack();
-  }
-
-  public final SymbolTable getSymbolTable() { return symbolTable; }
-
-  public final ModuleNode generate(TreeNode treeNode) throws AbortException {
-    /*************************************************************************
-    * This is the method called to generate the semantic graph for a module. *
-    *************************************************************************/
+								reportSelectorError(sel, idx);
+								return nullOAN;
+							}
+							;
+							curOpApplNode = (OpApplNode) curArgs[temp - 1];
+							if (curOpApplNode.getOperator().getName() != OP_pair) {
+								errors.addAbort(sel.opsSTN[idx].getLocation(),
+										"Internal error: Expecting $Pair and didn't find it.");
+							}
+							;
+							curNode = curOpApplNode.getArgs()[1];
+						} // if opNode.name == $RcdConstructor or $SetOfRcds
+
+						else if (opNode.getName() == OP_case) { // $Case
+							if (idx == sel.ops.length - 1) {
+								errors.addError(sel.opsSTN[idx].getLocation(),
+										"Subexpression of CASE must have form !i!j.");
+								return nullOAN;
+							}
+							;
+							int temp = ArgNum(sel.ops[idx], curArgs.length);
+							if (temp == -1) {
+								reportSelectorError(sel, idx);
+								return nullOAN;
+							}
+							;
+							curOpApplNode = (OpApplNode) curArgs[temp - 1];
+							if (curOpApplNode.getOperator().getName() != OP_pair) {
+								errors.addAbort(sel.opsSTN[idx].getLocation(),
+										"Internal error: Expecting $Pair and didn't find it.");
+							}
+							;
+							idx = idx + 1;
+							temp = ArgNum(sel.ops[idx], 2);
+							if (temp == -1) {
+								errors.addError(sel.opsSTN[idx].getLocation(),
+										"Second selector for CASE subexpression must specify "
+												+ " one of two operands.");
+								return nullOAN;
+							}
+							;
+							curNode = curOpApplNode.getArgs()[temp - 1];
+							if (curNode == null) {
+								errors.addError(sel.opsSTN[idx].getLocation(), "Selecting OTHER in a CASE statement.");
+								return nullOAN;
+							}
+							;
+						} // if opNode.name = $Case
+
+						else if (opNode.getName() == OP_exc) { // $Except
+							/************************************************************
+							 * In an $Except node representing * * [exp_1 ELSE !... = exp_2, ..., !... =
+							 * exp_n] * * operand i names exp_i. *
+							 ************************************************************/
+							int temp = ArgNum(sel.ops[idx], curArgs.length);
+							if (temp == -1) {
+								reportSelectorError(sel, idx);
+								return nullOAN;
+							}
+							;
+							/************************************************************
+							 * Selection of subexpressions of EXCEPT have been outlawed * because they may
+							 * contain a dangling "@". This doesn't * seem to be worth fixing because it's
+							 * unlikely that * anyone will actually want to refer to such a * subexpression.
+							 * * Change made 25 Oct 2007. *
+							 ************************************************************/
+							if (temp > 1) {
+								errors.addError(sel.opsSTN[idx].getLocation(),
+										"Selecting subexpression of an " + "EXCEPT not yet implemented.");
+								return nullOAN;
+							}
+							;
+							curNode = curArgs[temp - 1];
+							if (isNullSelection(curNode, sel, idx)) {
+								return nullOAN;
+							}
+							;
+							if (temp > 1) {
+								curOpApplNode = (OpApplNode) curNode;
+								if (curOpApplNode.getOperator().getName() != OP_pair) {
+									errors.addAbort(sel.opsSTN[idx].getLocation(),
+											"Internal error: Expecting $Pair and didn't find it.");
+								}
+								;
+								curNode = curOpApplNode.getArgs()[1];
+							}
+							; // if (temp > 1)
+						} // if opNode.name = $Except
+
+						else { // Handled by standard procedure
+							if ((curOpApplNode.getNumberOfBoundedBoundSymbols() == 0)
+									&& ((curOpApplNode.getUnbdedQuantSymbols() == null)
+											|| (curOpApplNode.getUnbdedQuantSymbols().length == 0))
+							/*****************************************************
+							 * I'm not sure getUnbdedQuantSymbols always returns * null if there are no such
+							 * symbols. *
+							 *****************************************************/
+							) {
+								/**********************************************************
+								 * Current subexpression has no bound variables. *
+								 **********************************************************/
+								int temp = ArgNum(sel.ops[idx], curOpApplNode.getArgs().length);
+								if (temp == -1) {
+									reportSelectorError(sel, idx);
+									return nullOAN;
+								}
+								;
+								curNode = curOpApplNode.getArgs()[temp - 1];
+							} // if current subexpression has no bound variables
+							else {
+								/**********************************************************
+								 * Current subexpression has bound variables. *
+								 **********************************************************/
+								if ((sel.ops[idx] == NullSel) || (sel.ops[idx] == AtSel)) {
+									/********************************************************
+									 * Set temp to the array of FormalParamNodes for the * parameters. *
+									 ********************************************************/
+									FormalParamNode[] temp;
+									if (curOpApplNode.getNumberOfBoundedBoundSymbols() > 0) {
+										FormalParamNode[][] symbs = curOpApplNode.getBdedQuantSymbolLists();
+										int numSymbs = 0;
+										for (int i = 0; i < symbs.length; i++) {
+											numSymbs = numSymbs + symbs[i].length;
+										}
+										; // for
+										temp = new FormalParamNode[numSymbs];
+										int k = 0;
+										for (int i = 0; i < symbs.length; i++) {
+											for (int j = 0; j < symbs[i].length; j++) {
+												temp[k] = symbs[i][j];
+												k++;
+											}
+											; // for j
+										}
+										; // for i
+									} // if (curOpApplNode.getNumberOf... > 0)
+									else {
+										temp = curOpApplNode.getUnbdedQuantSymbols();
+									}
+									; // else not (curOpApplNode.getNumberOf... > 0)
+
+									/*******************************************************
+									 * Add the elements of temp to the params vector. *
+									 *******************************************************/
+									for (int i = 0; i < temp.length; i++) {
+										params.addElement(temp[i]);
+									}
+									; // for i
+
+									if (sel.ops[idx] == NullSel) {
+										/*****************************************************
+										 * Add the arguments to allArgs, checking if there * are the right number. *
+										 *****************************************************/
+										int numOfArgs = (sel.args[idx].heirs().length - 1) / 2;
+										if (temp.length != numOfArgs) {
+											errors.addError(sel.opsSTN[idx].getLocation(),
+													"Selector with " + numOfArgs
+															+ " argument(s) used for quantifier with " + temp.length
+															+ " bound identifier(s).");
+											return nullOAN;
+										}
+										;
+										for (int i = 0; i < numOfArgs; i++) {
+											allArgs.addElement(
+													generateExpression(sel.args[idx].heirs()[2 * i + 1], cm));
+										}
+										; // for i
+									}
+									; // if (sel.ops[idx] == NullSel)
+									curNode = curOpApplNode.getArgs()[0];
+								} // if (sel.ops[idx] == NullSel) || ...
+								else {
+									int temp = ArgNum(sel.ops[idx], curOpApplNode.getBdedQuantBounds().length);
+									if (temp == -1) {
+										reportSelectorError(sel, idx);
+										return nullOAN;
+									}
+									;
+									curNode = curOpApplNode.getBdedQuantBounds()[temp - 1];
+								}
+								; // else
+							}
+							; // else Current subexpression has bound variables
+						}
+						; // Handled by standard procedure
+					} // else if (opNode.getKind() == BuiltInKind)
+
+					else {
+						errors.addError(sel.opsSTN[idx].getLocation(), "Choosing operand `"
+								+ selectorItemToString(sel, idx) + "' of subexpression with no operands.");
+						return nullOAN;
+					}
+					;
+
+				} // else if ((curNode.getKind() == OpApplKind)
+
+				else if (curNode.getKind() == AssumeProveKind) {
+					AssumeProveNode curAPNode = (AssumeProveNode) curNode;
+
+					/****************************************************************
+					 * 16 Feb 2009: Case of curAPNode.suffices true added. *
+					 ****************************************************************/
+					if ((curAPNode.isSuffices()) && (!inAPsuffices)) {
+						/**************************************************************
+						 * In this case, the selector must be 1, and the curNode is * left equal to the
+						 * AssumeProve, but with inAPsuffices set * to true. *
+						 **************************************************************/
+						if (ArgNum(sel.ops[idx], 1) != 1) {
+							errors.addError(sel.opsSTN[idx].getLocation(),
+									"Accessing non-existent subexpression of " + "a SUFFICES");
+							return nullOAN;
+						}
+						;
+						inAPsuffices = true;
+					} // if (curAPNode.isSuffices())
+					else {
+						inAPsuffices = false;
+						/************************************************************
+						 * Added 16 Feb 2009 by LL. *
+						 ************************************************************/
+						int temp = ArgNum(sel.ops[idx], 1 + curAPNode.getAssumes().length);
+						if (temp == -1) {
+							reportSelectorError(sel, idx);
+							return nullOAN;
+						}
+						;
+
+						if (illegalAPPosRef(curAPNode, temp)) {
+							errors.addError(sel.opsSTN[idx].getLocation(),
+									"Accessing ASSUME/PROVE clause within the scope of "
+											+ "a declaration\n from outside that declaration's scope.");
+							return nullOAN;
+						}
+						;
+						if (temp <= curAPNode.getAssumes().length) {
+							curNode = curAPNode.getAssumes()[temp - 1];
+							if (isNullSelection(curNode, sel, idx)) {
+								return nullOAN;
+							}
+							;
+							if ((curNode.getKind() == NewSymbKind) && (idx != sel.args.length - 1)) {
+								/************************************************************
+								 * Extra conjunct added to if test to allow selection of a * NEW clause as a
+								 * fact. *
+								 ************************************************************/
+								errors.addError(sel.opsSTN[idx].getLocation(),
+										"Selected a subexpression of a NEW clause of an ASSUME.");
+								return nullOAN;
+							}
+						} else {
+							curNode = curAPNode.getProve();
+						}
+						;
+					} // else if (curAPNode.isSuffices())
+				} // else if (curNode.getKind() == AssumeProveKind)
+
+				else if (curNode.getKind() == OpArgKind) {
+					SymbolNode opNode = ((OpArgNode) curNode).getOp();
+					if ((opNode.getKind() != UserDefinedOpKind) || (opNode.getName() != S_lambda)) {
+						errors.addError(sel.opsSTN[idx].getLocation(),
+								"Trying to select subexpression of an operator argument.");
+						return nullOAN;
+					}
+					;
+					OpDefNode opDefOpNode = (OpDefNode) opNode;
+					if ((sel.ops[idx] != NullSel) && (sel.ops[idx] != AtSel)) {
+						errors.addError(sel.opsSTN[idx].getLocation(),
+								"Cannot use !" + sel.opNames[idx].toString() + " to select subexpression of a LAMBDA.");
+						return nullOAN;
+					}
+					;
+					if (sel.ops[idx] == NullSel) {
+						int numOfArgs = (sel.args[idx].heirs().length - 1) / 2;
+						if (opDefOpNode.getArity() != numOfArgs) {
+							errors.addError(sel.opsSTN[idx].getLocation(),
+									"Selector with " + numOfArgs + "arguments used for LAMBDA expression taking "
+											+ opDefOpNode.getArity() + " arguments.");
+							return nullOAN;
+						}
+						;
+						for (int i = 0; i < numOfArgs; i++) {
+							allArgs.addElement(generateExpression(sel.args[idx].heirs()[2 * i + 1], cm));
+						}
+						;
+					}
+					; // if (sel.ops[idx] == NullSel)
+					for (int i = 0; i < opDefOpNode.getArity(); i++) {
+						params.addElement(opDefOpNode.getParams()[i]);
+					}
+					;
+					curNode = opDefOpNode.getBody();
+				} // else if (curNode.getKind() == OpArgKind)
+
+				else if ((curNode.getKind() == UserDefinedOpKind) || (curNode.getKind() == BuiltInKind)
+						|| (curNode.getKind() == NumberedProofStepKind)) {
+					errors.addAbort(sel.opsSTN[idx].getLocation(),
+							"Internal error: " + " Should not have been able to select this node.");
+				} // else if (curNode.getKind() == UserDefinedOpKind) || ...
+
+				else if ((curNode.getKind() == AtNodeKind) || (curNode.getKind() == DecimalKind)
+						|| (curNode.getKind() == NumeralKind) || (curNode.getKind() == StringKind)
+						|| (curNode.getKind() == FormalParamKind) || (curNode.getKind() == ConstantDeclKind)
+						|| (curNode.getKind() == VariableDeclKind) || (curNode.getKind() == BoundSymbolKind)) {
+					errors.addError(sel.opsSTN[idx].getLocation(),
+							"Selecting subexpression of expression that has none.");
+					return nullOAN;
+				} // else if (curNode.getKind() == AtNodeKind) || ...
+
+				else if (curNode.getKind() == LabelKind) {
+					curNode = ((LabelNode) curNode).getBody();
+					idx = idx - 1;
+				} // else if (curNode.getKind() == LabelKind)
+
+				else {
+					errors.addAbort(sel.opsSTN[idx].getLocation(), "Internal error: " + " Unknown node kind.");
+				}
+				; // end last else of if sel.ops[idx] != ColonSel
+
+				if (isNullSelection(curNode, sel, idx)) {
+					return nullOAN;
+				}
+				;
+
+				if (idx != sel.ops.length - 1) {
+					if (sel.ops[idx + 1] == NameSel) {
+						while (curNode.getKind() == LabelKind) {
+							curNode = ((LabelNode) curNode).getBody();
+							if (isNullSelection(curNode, sel, idx)) {
+								return nullOAN;
+							}
+							;
+						}
+						; // while
+						if (curNode.getKind() == LetInKind) {
+							letInContext = ((LetInNode) curNode).context;
+							mode = FindingOpName;
+							firstFindingOpName = false;
+						} else {
+							errors.addError(sel.opsSTN[idx].getLocation(),
+									"A name selector here must be from a LET clause.");
+							return nullOAN;
+						}
+					} // if (sel.ops[idx+1] == NameSel)
+					else if (sel.ops[idx + 1] == ColonSel) {
+						errors.addError(sel.opsSTN[idx].getLocation(), "!: should not follow an operand selector.");
+						return nullOAN;
+					}
+				}
+				; // if (idx != sel.ops.length - 1)
+
+				prevMode = FindingSubExpr;
+				break; // case FindingSubExpr
+
+			default:
+				errors.addAbort(sel.selSTN.getLocation(), "Internal error: Unexpected mode");
+			} // switch (mode)
+			idx++;
+		} // while idx <
+		/**********************************************************************
+		 * +cal: end while ; *
+		 **********************************************************************/
+
+		/***********************************************************************
+		 * +cal: if curNode.kind = AssumeProveKind ... end if * * Modified 17 Feb 2009
+		 * by LL to allow ASSUME/PROVE to be used as a * fact. This seems to have been a
+		 * bug in the original. * * Not corrected in the PlusCal code because that code
+		 * doesn't talk * about the isFact case. *
+		 ***********************************************************************/
+		if (curNode.getKind() == AssumeProveKind) {
+			if (isFact) {
+				return (AssumeProveNode) curNode;
+			}
+			;
+			errors.addError(sel.selSTN.getLocation(), "Selected ASSUME/PROVE instead of expression.");
+			return nullOAN;
+		}
+		;
+
+		/***********************************************************************
+		 * The following added to allow naming of NEW constructs of an * ASSUME/PROVE in
+		 * facts. *
+		 ***********************************************************************/
+		if (curNode.getKind() == NewSymbKind) {
+			if (isFact) {
+				return (LevelNode) curNode;
+			}
+			;
+			errors.addError(sel.selSTN.getLocation(), "Selected a NEW declaration as an expression or operator.");
+			return nullOAN;
+		}
+		;
+
+		/***********************************************************************
+		 * +cal: if expectedArity < 0 ... end if; *
+		 ***********************************************************************/
+		if (expectedArity < 0) {
+			if ((prevMode != FindingOpName) || !((curNode.getKind() == UserDefinedOpKind)
+					|| (curNode.getKind() == ThmOrAssumpDefKind) || (curNode.getKind() == NumberedProofStepKind) || // This
+																													// clause
+																													// added
+																													// by
+																													// LL
+																													// 15
+																													// Oct
+																													// 2007,
+																													// not
+																													// in
+																													// +cal
+																													// code
+					((curNode.getKind() == ModuleInstanceKind) && isDef))) {
+				errors.addError(sel.selSTN.getLocation(), "DEF clause entry should describe a defined operator.");
+				return nullOAN;
+			}
+			;
+			if ((curNode.getKind() == NumberedProofStepKind)
+					&& (((OpDefNode) curNode).getStepNode().getKind() != DefStepKind)) {
+				errors.addError(sel.selSTN.getLocation(), "DEF clause entry refers to a non-definition step.");
+				return nullOAN;
+			}
+			return (LevelNode) curNode;
+		}
+		; // if (expectedArity < 0)
+
+		if (curNode.getKind() == NumberedProofStepKind) {
+			errors.addError(sel.selSTN.getLocation(), isFact ? "Step number of non-fact used as a fact"
+					: "Step number of non-expression step used as an expression.");
+			return nullOAN;
+		}
+
+		/***********************************************************************
+		 * +cal: if expectedArity > 0 ... end if *
+		 ***********************************************************************/
+		if (expectedArity > 0) {
+			int temp = params.size();
+			if (curNode.getKind() == OpArgKind) {
+				temp = temp + ((OpArgNode) curNode).getArity();
+			} else if (prevMode == FindingOpName) {
+				temp = temp + ((SymbolNode) curNode).getArity();
+			}
+			;
+
+			if (expectedArity != temp) {
+				errors.addError(sel.selSTN.getLocation(),
+						"Expected arity " + expectedArity + " but found operator of arity " + temp + ".");
+				return nullOAN;
+			}
+			;
+		}
+		; // if (expectedArity > 0)
+
+		/***********************************************************************
+		 * If opDefArgs is non-empty, we will need to convert it to an array, * so we
+		 * might as well do it here. *
+		 ***********************************************************************/
+		ExprOrOpArgNode[] opDefArgArray = new ExprOrOpArgNode[opDefArgs.size()];
+		for (int i = 0; i < opDefArgs.size(); i++) {
+			opDefArgArray[i] = (ExprOrOpArgNode) opDefArgs.elementAt(i);
+		}
+		; // for
+
+		/***********************************************************************
+		 * +cal: if /\ prevMode = "FindingOpName" ... end if; *
+		 ***********************************************************************/
+		if ((prevMode == FindingOpName) && (params.size() + substInPrefix.size() > 0)) {
+
+			/*********************************************************************
+			 * Set nodeParams to the +cal program's curNode.params value. *
+			 *********************************************************************/
+			FormalParamNode[] nodeParams = null;
+			if (curNode.getKind() == UserDefinedOpKind) {
+				nodeParams = ((OpDefNode) curNode).getParams();
+			} else {
+				if (curNode.getKind() != ThmOrAssumpDefKind) {
+					errors.addAbort(sel.opsSTN[sel.opsSTN.length - 1].getLocation(),
+							"Internal Error: " + " Found unexpected node kind after FindingOpName");
+				}
+				;
+				nodeParams = ((ThmOrAssumpDefNode) curNode).getParams();
+			}
+			;
+
+			for (int i = 0; i < opDefArgs.size(); i++) {
+				allArgs.addElement(opDefArgArray[i]);
+			}
+			; // for
+
+			ExprOrOpArgNode[] temp = new ExprOrOpArgNode[nodeParams.length];
+			for (int i = 0; i < nodeParams.length; i++) {
+				FormalParamNode pm = nodeParams[i];
+				/*******************************************************************
+				 * Set newpm to a "clone" of pm. *
+				 *******************************************************************/
+				FormalParamNode newpm = new FormalParamNode(pm.getName(), pm.getArity(), pm.stn, null, cm);
+
+				/*******************************************************************
+				 * Set eoag to the ExprOrOpArgNode constructed from newpm. *
+				 *******************************************************************/
+				ExprOrOpArgNode eoag = null;
+				if (pm.getArity() == 0) {
+					/*****************************************************************
+					 * Formal parameter eoag is an ordinary (non-operator) parameter. *
+					 *****************************************************************/
+					eoag = new OpApplNode(newpm, new ExprNode[0], sel.selSTN, cm);
+				} else {
+					/*****************************************************************
+					 * Formal parameter eoag is an operator parameter. *
+					 *****************************************************************/
+					eoag = new OpArgNode(newpm, sel.selSTN, cm);
+				}
+				;
+				temp[i] = eoag;
+				params.addElement(newpm);
+			}
+			; // for
+			SymbolNode curSymNode = (SymbolNode) curNode;
+			curNode = new OpApplNode(curSymNode, temp, // opDefArgArray,
+					sel.selSTN, // TreeNode
+					cm);
+
+		}
+		;// if (prevMode == FindingOpName) ...
+
+		/***********************************************************************
+		 * +cal: if curNode.kind = OpArgKind *
+		 ***********************************************************************/
+		if (curNode.getKind() == OpArgKind) {
+			OpArgNode curOpArgNode = (OpArgNode) curNode;
+
+			/*********************************************************************
+			 * +cal: if expectedArity = 0 then ... elsif *
+			 *********************************************************************/
+			if (expectedArity == 0) {
+				errors.addError(sel.selSTN.getLocation(), "Selected operator argument when expression expected.");
+				return nullOAN;
+			}
+			;
+
+			/*********************************************************************
+			 * +cal: elsif expectedArity # Len(params) + ... else *
+			 *********************************************************************/
+			int temp = params.size() + curOpArgNode.getArity();
+			if (expectedArity != temp) {
+				errors.addError(sel.selSTN.getLocation(), "Expected operator of arity " + expectedArity
+						+ " but selected operator has arity " + temp + ".");
+				return nullOAN;
+			}
+			;
+
+			/*********************************************************************
+			 * +cal: else ... end if *
+			 *********************************************************************/
+			if (params.size() + substInPrefix.size() > 0) {
+				FormalParamNode[] temp2 = new FormalParamNode[curOpArgNode.getArity()];
+				for (int i = 0; i < temp2.length; i++) {
+					UniqueString temp3 = UniqueString.uniqueStringOf("NewParam" + i);
+					temp2[i] = new FormalParamNode(temp3, // name
+							0, // arity
+							new SyntaxTreeNode(temp3), // syntax tree node
+							null, // symbol table
+							cm); // module
+					params.addElement(temp2[i]);
+				}
+				; // for
+				curNode = new OpApplNode(curOpArgNode.getOp(), opDefArgArray, sel.selSTN, // TreeNode
+						cm);
+			}
+			; // if (params.size() + ...)
+
+		}
+		; // if (curNode.getKind() == OpArgKind)
+
+		/***********************************************************************
+		 * This code doesn't seem to be present in the +cal code. I forget * why not;
+		 * perhaps I added it without correcting the +cal. * * Modified 19 May 2008 by
+		 * LL because of addition of labeled * ASSUME/PROVE nodes. *
+		 ***********************************************************************/
+		if (!isFact && (((curNode.getKind() == ThmOrAssumpDefKind)
+				&& (((ThmOrAssumpDefNode) curNode).getBody().getKind() == AssumeProveKind))
+				/***********************************************************
+				 * curNode is an ASSUME/PROVE theorem. *
+				 ***********************************************************/
+				|| ((curNode.getKind() == LabelKind) && (((LabelNode) curNode).isAssumeProve)))
+		/***********************************************************
+		 * curNode is a LabelNode naming an ASSUME/PROVE. *
+		 ***********************************************************/
+		) {
+			errors.addError(sel.selSTN.getLocation(), "ASSUME/PROVE used where an expression is required.");
+			return nullOAN;
+		}
+		;
+
+		/***********************************************************************
+		 * The following code added 18 Oct 2007 to check if a * pseudo-expression like
+		 * "HAVE P" or "PICK ..." was used as an * expression. *
+		 ***********************************************************************/
+		if ((!isFact) && (curNode.getKind() == ThmOrAssumpDefKind)
+				&& (((ThmOrAssumpDefNode) curNode).getBody().getKind() == OpApplKind)) {
+			UniqueString opName = ((OpApplNode) ((ThmOrAssumpDefNode) curNode).getBody()).getOperator().getName();
+			String exprType = null;
+			if (opName == OP_qed) {
+				exprType = "QED step";
+			} else if (opName == OP_pfcase) {
+				exprType = "CASE";
+			} else if (opName == OP_have) {
+				exprType = "HAVE";
+			} else if (opName == OP_take) {
+				exprType = "TAKE";
+			} else if (opName == OP_pick) {
+				exprType = "PICK";
+			} else if (opName == OP_witness) {
+				exprType = "WITNESS";
+			} else if (opName == OP_suffices) {
+				exprType = "SUFFICES";
+			}
+			;
+			/*******************************************************************
+			 * OP_suffices added by LL 16 Feb 2009. *
+			 *******************************************************************/
+			if (exprType != null) {
+				errors.addError(sel.selSTN.getLocation(), exprType + " proof step selected instead of expression.");
+				return nullOAN;
+			}
+			;
+		} // if
+
+		/***********************************************************************
+		 * +cal: if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind... *
+		 ***********************************************************************/
+		if ((curNode.getKind() == UserDefinedOpKind) || (curNode.getKind() == ConstantDeclKind)
+				|| (curNode.getKind() == VariableDeclKind) || (curNode.getKind() == FormalParamKind)
+				|| (curNode.getKind() == BuiltInKind) || (curNode.getKind() == BoundSymbolKind)
+				|| (curNode.getKind() == ThmOrAssumpDefKind) || (curNode.getKind() == NewConstantKind)
+				|| (curNode.getKind() == NewVariableKind) || (curNode.getKind() == NewStateKind)
+				|| (curNode.getKind() == NewActionKind) || (curNode.getKind() == NewTemporalKind)) {
+			SymbolNode curSymbolNode = (SymbolNode) curNode;
+			if (expectedArity > 0) {
+				return new OpArgNode(curSymbolNode, sel.selSTN, cm);
+			}
+
+			/*********************************************************************
+			 * +cal: elsif expectedArity = 0 then *
+			 *********************************************************************/
+			else {
+				/*******************************************************************
+				 * expectedArity = 0 *
+				 *******************************************************************/
+				OpApplNode oan = new OpApplNode(curSymbolNode, opDefArgArray, sel.selSTN, cm);
+				oan.subExpressionOf = subExprOf;
+				return oan;
+			} // if (expectedArity > 0)
+		}
+		; // if (curNode.getKind() == UserDefinedOpKind)
+
+		/***********************************************************************
+		 * +cal: elsif curNode.kind = OpArgKind then *
+		 ***********************************************************************/
+		if (curNode.getKind() == OpArgKind) {
+			return (OpArgNode) curNode;
+		}
+		;
+
+		/***********************************************************************
+		 * +cal: else *
+		 ***********************************************************************/
+
+		/***********************************************************************
+		 * The following test is not in Subexpression.tla and should not be, * having
+		 * been added to allow the use of a ModuleInstanceKind as a * Fact or Def. The
+		 * else case should not occur because if it would be * the case, then the error
+		 * should have been caught earlier (I think). *
+		 ***********************************************************************/
+		if (curNode.getKind() == ModuleInstanceKind) {
+			if (isFact || isDef) {
+				return (OpDefNode) curNode;
+			} else {
+				errors.addError(sel.selSTN.getLocation(), "Module instantiation selected instead of expression.");
+				return nullOAN;
+			}
+		}
+		;
+		/***********************************************************************
+		 * curNode should be an expression node. *
+		 ***********************************************************************/
+		if (!(curNode instanceof ExprNode)) {
+			errors.addAbort(sel.selSTN.getLocation(), "Internal error: Expected expression node.");
+		}
+		;
+		ExprNode curExprNode = (ExprNode) curNode;
+		int temp = substInPrefix.size();
+
+		/***********************************************************************
+		 * +cal: while temp > 0 do ... end while *
+		 ***********************************************************************/
+		while (temp > 0) {
+			// Modified on 13 Nov 2009 by LL to handle the case of a subexpression
+			// of a Theorem or Assumption.
+			Object substOb = substInPrefix.elementAt(temp - 1);
+			if (substOb instanceof SubstInNode) {
+				SubstInNode subst = (SubstInNode) substOb;
+				curExprNode = new SubstInNode(subst.stn, subst.getSubsts(), curExprNode, subst.getInstantiatingModule(),
+						subst.getInstantiatedModule());
+			} else {
+				APSubstInNode subst = (APSubstInNode) substOb;
+				curExprNode = new SubstInNode(subst.stn, subst.getSubsts(), curExprNode, subst.getInstantiatingModule(),
+						subst.getInstantiatedModule());
+
+			}
+			temp = temp - 1;
+		}
+		; // while
+
+		/***********************************************************************
+		 * Will need params as an array. *
+		 ***********************************************************************/
+		FormalParamNode[] paramsArray = new FormalParamNode[params.size()];
+		for (int i = 0; i < params.size(); i++) {
+			paramsArray[i] = (FormalParamNode) params.elementAt(i);
+		}
+		;
+
+		/***********************************************************************
+		 * If there are params, will need a Lambda operator. *
+		 ***********************************************************************/
+		OpDefNode newLambda = null;
+		if (paramsArray.length > 0) {
+			newLambda = new OpDefNode(S_lambda, // The operator name is "LAMBDA"
+					UserDefinedOpKind, // The node kind
+					paramsArray, // the array of formal parameters
+					false, // localness, which is meaningless
+					curExprNode, // the body
+					cm, // the module
+					null, // the symbol table
+					sel.selSTN, // syntax-tree node
+					true, // Is defined.
+					null); // Source
+		}
+		;
+
+		/***********************************************************************
+		 * +cal: if expectedArity > 0 *
+		 ***********************************************************************/
+		if (expectedArity > 0) {
+			if (paramsArray.length != expectedArity) {
+				errors.addError(sel.selSTN.getLocation(), "Expected operator argument with arity " + expectedArity
+						+ " but found one of arity " + paramsArray.length + ".");
+				return nullOAN;
+			}
+			;
+			return new OpArgNode(newLambda, sel.selSTN, cm);
+		} // if (expectedArity > 0)
+
+		/***********************************************************************
+		 * expectedArity = 0 * +cal: else if Len(params) # Len(allArgs) *
+		 ***********************************************************************/
+		if (paramsArray.length != allArgs.size()) {
+			errors.addError(sel.selSTN.getLocation(),
+					"Expected " + paramsArray.length + " arguments but found " + allArgs.size() + ".");
+			return nullOAN;
+		}
+		;
+		/***********************************************************************
+		 * +cal: if Len(params) = 0 then *
+		 ***********************************************************************/
+		if (paramsArray.length == 0) {
+			/*********************************************************************
+			 * In this case, we don't have to construct a new LAMBDA or OpAppl * node.
+			 * However, we want to have a new node to which to attach the * SyntaxTree node.
+			 * So, we create an OpApplNode with the dummy * builtin operator $Nop and return
+			 * it. *
+			 *********************************************************************/
+			ExprOrOpArgNode[] args = new ExprOrOpArgNode[1];
+			args[0] = curExprNode;
+			OpApplNode ln = new OpApplNode(OP_nop, args, sel.selSTN, cm);
+			ln.subExpressionOf = subExprOf;
+			return ln;
+		}
+
+		/***********************************************************************
+		 * +cal: else *
+		 ***********************************************************************/
+		/***********************************************************************
+		 * Will need allArgs as an array. *
+		 ***********************************************************************/
+		ExprOrOpArgNode[] allArgsArray = new ExprOrOpArgNode[allArgs.size()];
+		for (int i = 0; i < allArgs.size(); i++) {
+			allArgsArray[i] = (ExprOrOpArgNode) allArgs.elementAt(i);
+		}
+		;
+
+		OpApplNode oan = new OpApplNode(newLambda, allArgsArray, sel.selSTN, cm);
+		oan.subExpressionOf = subExprOf;
+		return oan;
+
+	} // selectorToNode
+
+	private static String selectorItemToString(Selector sel, int idx) {
+		String selStr = sel.opNames[idx].toString();
+		if (selStr.equals("N_StructOp")) {
+			selStr = "argument selector " + sel.opsSTN[idx].heirs()[0].heirs()[0].getImage();
+		} else if (selStr.equals("N_OpArgs")) {
+			selStr = "!(...)";
+		}
+		;
+		return selStr;
+	}
+
+	private void reportSelectorError(Selector sel, int idx) {
+		/***********************************************************************
+		 * Just a method to perform error reporting when an error is detected * when
+		 * executing selectorToNode. *
+		 ***********************************************************************/
+		errors.addError(sel.opsSTN[idx].getLocation(),
+				"Nonexistent operand specified by `" + selectorItemToString(sel, idx) + "'.");
+	}
+
+	private boolean isNullSelection(SemanticNode node, Selector sel, int idx) {
+		/***********************************************************************
+		 * A method for use in selectorToNode to test if a a null node has * been
+		 * selected. This should happen only if the null node was * created as a result
+		 * of a previous error. If the node is null, then * an error is reported and
+		 * true is returned. *
+		 ***********************************************************************/
+		boolean val = (node == null);
+		if (val) {
+			errors.addError(sel.opsSTN[idx].getLocation(), "An unexpected null node specified by "
+					+ selectorItemToString(sel, idx) + "'." + "\nThis is probably due to a previous error.");
+		}
+		;
+		return val;
+	}
+
+	// Constructor
+	public Generator(ExternalModuleTable moduleTable, Errors errs) {
+		nullParam = new FormalParamNode[0];
+		nullODN = new OpDefNode(UniqueString.uniqueStringOf("nullODN"));
+		nullOAN = new OpApplNode(nullODN);
+		nullOpArg = new OpArgNode(UniqueString.uniqueStringOf("nullOpArg"));
+		nullLabelNode = new LabelNode(nullOAN);
+		this.errors = errs;
+		this.moduleTable = moduleTable;
+		this.symbolTable = new SymbolTable(moduleTable, errors);
+		this.excStack = new Stack();
+		this.excSpecStack = new Stack();
+	}
+
+	public final SymbolTable getSymbolTable() {
+		return symbolTable;
+	}
+
+	public final ModuleNode generate(TreeNode treeNode) throws AbortException {
+		/*************************************************************************
+		 * This is the method called to generate the semantic graph for a module. *
+		 *************************************************************************/
 //System.out.println("TRUE = " + symbolTable.resolveSymbol(
 //                      UniqueString.uniqueStringOf("TRUE"))) ;
 //System.out.println("$SetEnumerate = " + symbolTable.resolveSymbol(
 //                      UniqueString.uniqueStringOf("$SetEnumerate"))) ;
 
-
-    if (treeNode.isKind( N_Module )) {
-      this.context = symbolTable.getContext();
-      return this.generateModule(treeNode, null);
-    } 
-    return null;
-  }
-
-  private final Context getContext(UniqueString us) {
-    ModuleNode symbolNode = symbolTable.resolveModule(us);
-
-    if (symbolNode == null) {
-      return moduleTable.getContext(us);
-    }
-    return symbolNode.getContext();
-  }
-
-  private final ModuleNode generateModule(TreeNode treeNode, ModuleNode parent)
-  throws AbortException {
-    moduleNestingLevel++ ;
-    TreeNode[] children    = treeNode.heirs();     // Array of heirs of the module root
-    TreeNode[] definitions = null;                 
-      // Array of definitions in the module
-      /*********************************************************************
-      * Actually, the array of all syntactic nodes that are heirs of the   *
-      * module's body node--that is, definitions, theorems, parameter      *
-      * declarations, etc.                                                 *
-      *********************************************************************/
-    TreeNode[] ss          = children[0].heirs();  // Array of heirs of the module header
-                                                   // ss[1] is always the module name
-    String  moduleName     = ss[1].getImage();     // the module name
-    ModuleNode currentModule = new ModuleNode(ss[1].getUS(), context, treeNode);
-    currentModule.nestingLevel = moduleNestingLevel ;
-    // if this is an internal module, add its ModuleNode to the end of
-    // the list of definitions for the parent
-    if (parent != null) {
-      parent.appendDef(currentModule);
-    }
-
-    symbolTable.setModuleNode( currentModule );
-    // children[1] == extends
-    // children[1] is always the Extend's list (even if none)
-    processExtendsList(children[1].heirs(), currentModule); 
-
-    // children[2] == body
-    // children[2] is always the module body note that this line
-    // redefines "children" to be the array of definitions in the
-    // module
-    definitions = children[2].heirs();
-
-    // for each declaration, op definition, etc. in the body...
-    for (int lvi = 0; lvi < definitions.length; lvi++) {
-      switch (definitions[lvi].getKind()) {
-      case N_VariableDeclaration :
-        checkIfInRecursiveSection(definitions[lvi], "A VARIABLE declaration") ;
-        processVariables(definitions[lvi].heirs(), currentModule);
-        break;
-
-      case N_ParamDeclaration :
-        checkIfInRecursiveSection(definitions[lvi], "A declaration") ;
-        processParameters(definitions[lvi].heirs(), currentModule);
-        break;
-
-      case N_OperatorDefinition :
-        processOperator(definitions[lvi], null, currentModule);
-        break;
-
-      case N_FunctionDefinition :
-        processFunction(definitions[lvi], null, currentModule);
-        break;
-
-      case N_ModuleDefinition :
+		if (treeNode.isKind(N_Module)) {
+			this.context = symbolTable.getContext();
+			return this.generateModule(treeNode, null);
+		}
+		return null;
+	}
+
+	private final Context getContext(UniqueString us) {
+		ModuleNode symbolNode = symbolTable.resolveModule(us);
+
+		if (symbolNode == null) {
+			return moduleTable.getContext(us);
+		}
+		return symbolNode.getContext();
+	}
+
+	private final ModuleNode generateModule(TreeNode treeNode, ModuleNode parent) throws AbortException {
+		moduleNestingLevel++;
+		TreeNode[] children = treeNode.heirs(); // Array of heirs of the module root
+		TreeNode[] definitions = null;
+		// Array of definitions in the module
+		/*********************************************************************
+		 * Actually, the array of all syntactic nodes that are heirs of the * module's
+		 * body node--that is, definitions, theorems, parameter * declarations, etc. *
+		 *********************************************************************/
+		TreeNode[] ss = children[0].heirs(); // Array of heirs of the module header
+												// ss[1] is always the module name
+		String moduleName = ss[1].getImage(); // the module name
+		ModuleNode currentModule = new ModuleNode(ss[1].getUS(), context, treeNode);
+		currentModule.nestingLevel = moduleNestingLevel;
+		// if this is an internal module, add its ModuleNode to the end of
+		// the list of definitions for the parent
+		if (parent != null) {
+			parent.appendDef(currentModule);
+		}
+
+		symbolTable.setModuleNode(currentModule);
+		// children[1] == extends
+		// children[1] is always the Extend's list (even if none)
+		processExtendsList(children[1].heirs(), currentModule);
+
+		// children[2] == body
+		// children[2] is always the module body note that this line
+		// redefines "children" to be the array of definitions in the
+		// module
+		definitions = children[2].heirs();
+
+		// for each declaration, op definition, etc. in the body...
+		for (int lvi = 0; lvi < definitions.length; lvi++) {
+			switch (definitions[lvi].getKind()) {
+			case N_VariableDeclaration:
+				checkIfInRecursiveSection(definitions[lvi], "A VARIABLE declaration");
+				processVariables(definitions[lvi].heirs(), currentModule);
+				break;
+
+			case N_ParamDeclaration:
+				checkIfInRecursiveSection(definitions[lvi], "A declaration");
+				processParameters(definitions[lvi].heirs(), currentModule);
+				break;
+
+			case N_OperatorDefinition:
+				processOperator(definitions[lvi], null, currentModule);
+				break;
+
+			case N_FunctionDefinition:
+				processFunction(definitions[lvi], null, currentModule);
+				break;
+
+			case N_ModuleDefinition:
 // We now allow INSTANCEs in recursive sections.
 //        checkIfInRecursiveSection(definitions[lvi], "An INSTANCE") ;
-        processModuleDefinition(definitions[lvi], null, null, currentModule);
-        break;
-
-      case N_Module :
-        // Modules can be nested, but inner ones need to keep a 
-        // separate SymbolTable of their own.
-        checkIfInRecursiveSection(definitions[lvi], "A MODULE ") ;
-        SymbolTable oldSt = symbolTable; 
-        symbolTable = new SymbolTable(moduleTable, errors, oldSt);
-        context = new Context(moduleTable, errors);
-        symbolTable.pushContext(context);
-        ModuleNode mn = generateModule(definitions[lvi], currentModule);
-        symbolTable.popContext();
-        symbolTable = oldSt;
-
-        // Add the inner module's name to the context of the outer module
-        symbolTable.addModule(mn.getName(), mn);
-
-        /*******************************************************************
-        * Append the opDefsInRecursiveSection field of the inner module    *
-        * to that of the current module.                                   *
-        *******************************************************************/
-        for (int i = 0; i < mn.opDefsInRecursiveSection.size(); i++) {
-        currentModule.opDefsInRecursiveSection.addElement(
-               mn.opDefsInRecursiveSection.elementAt(i));
-         } ;
-        // System.err.println(mn.getName() + " added to SymbolTable for " + currentModule.getName()); 
-        break;
-
-      case N_Instance :
+				processModuleDefinition(definitions[lvi], null, null, currentModule);
+				break;
+
+			case N_Module:
+				// Modules can be nested, but inner ones need to keep a
+				// separate SymbolTable of their own.
+				checkIfInRecursiveSection(definitions[lvi], "A MODULE ");
+				SymbolTable oldSt = symbolTable;
+				symbolTable = new SymbolTable(moduleTable, errors, oldSt);
+				context = new Context(moduleTable, errors);
+				symbolTable.pushContext(context);
+				ModuleNode mn = generateModule(definitions[lvi], currentModule);
+				symbolTable.popContext();
+				symbolTable = oldSt;
+
+				// Add the inner module's name to the context of the outer module
+				symbolTable.addModule(mn.getName(), mn);
+
+				/*******************************************************************
+				 * Append the opDefsInRecursiveSection field of the inner module * to that of
+				 * the current module. *
+				 *******************************************************************/
+				for (int i = 0; i < mn.opDefsInRecursiveSection.size(); i++) {
+					currentModule.opDefsInRecursiveSection.addElement(mn.opDefsInRecursiveSection.elementAt(i));
+				}
+				;
+				// System.err.println(mn.getName() + " added to SymbolTable for " +
+				// currentModule.getName());
+				break;
+
+			case N_Instance:
 // We now allow INSTANCEs in recursive sections.
 //        checkIfInRecursiveSection(definitions[lvi], "An INSTANCE") ;
-        generateInstance(definitions[lvi], currentModule, true);
-        break;
-
-      case N_Proof :
-        checkIfInRecursiveSection(definitions[lvi], "A proof") ;
-        break;
-
-      case N_Theorem : 
-        checkIfInRecursiveSection(definitions[lvi], "A THEOREM") ;
-        processTheorem(definitions[lvi], currentModule);
-        break; 
-
-      case N_Assumption : 
-        checkIfInRecursiveSection(definitions[lvi], "An ASSUME");
-        processAssumption(definitions[lvi], currentModule);
-        break;
-       
-      case N_UseOrHide : 
-        checkIfInRecursiveSection(definitions[lvi], "A USE or HIDE");
-        UseOrHideNode uohn = generateUseOrHide(definitions[lvi], 
-                                                currentModule) ;
-        uohn.factCheck();
-          // Added 4 Mar 2009.
-        if (uohn.facts.length + uohn.defs.length == 0) {
-          errors.addError(definitions[lvi].getLocation(),
-                          "Empty USE or HIDE statement.");
-         };
-        currentModule.addTopLevel(uohn) ;
-        break;
-       
-      case SEPARATOR :
-        /*******************************************************************
-        * This code was originally                                         *
-        *                                                                  *
-        *   case 35                                                        *
-        *   // Intended to handle "---------".  Kludge for a parser bug.   *
-        *                                                                  *
-        * I have no idea why 35 was used instead of SEPARATOR, and what    *
-        * kind of parser bug mentioned was encountered.  The original      *
-        * code did not import parser.TLAplusParserConstants where          *
-        * SEPARATOR was used, so perhaps there was some reason why this    *
-        * class could not be imported.                                     *
-        *******************************************************************/
-        break;  
-
-      case N_Recursive :
-        processRecursive(definitions[lvi], currentModule);
-        break; 
-      default :
-        errors.addAbort(
-          definitions[ lvi ].getLocation(),
-          "Internal error: Syntax node of kind " + 
-          definitions[ lvi ].getKind() + " unsupported " + 
-          definitions[ lvi ].getImage(), true);
-        break;
-      }
-    }
-
-    checkForUndefinedRecursiveOps(currentModule) ;
-
-Vector vec = currentModule.recursiveOpDefNodes ;
-for (int i = 0 ; i < vec.size() ; i++) {
-OpDefNode node = (OpDefNode) vec.elementAt(i);
+				generateInstance(definitions[lvi], currentModule, true);
+				break;
+
+			case N_Proof:
+				checkIfInRecursiveSection(definitions[lvi], "A proof");
+				break;
+
+			case N_Theorem:
+				checkIfInRecursiveSection(definitions[lvi], "A THEOREM");
+				processTheorem(definitions[lvi], currentModule);
+				break;
+
+			case N_Assumption:
+				checkIfInRecursiveSection(definitions[lvi], "An ASSUME");
+				processAssumption(definitions[lvi], currentModule);
+				break;
+
+			case N_UseOrHide:
+				checkIfInRecursiveSection(definitions[lvi], "A USE or HIDE");
+				UseOrHideNode uohn = generateUseOrHide(definitions[lvi], currentModule);
+				uohn.factCheck();
+				// Added 4 Mar 2009.
+				if (uohn.facts.length + uohn.defs.length == 0) {
+					errors.addError(definitions[lvi].getLocation(), "Empty USE or HIDE statement.");
+				}
+				;
+				currentModule.addTopLevel(uohn);
+				break;
+
+			case SEPARATOR:
+				/*******************************************************************
+				 * This code was originally * * case 35 * // Intended to handle "---------".
+				 * Kludge for a parser bug. * * I have no idea why 35 was used instead of
+				 * SEPARATOR, and what * kind of parser bug mentioned was encountered. The
+				 * original * code did not import parser.TLAplusParserConstants where *
+				 * SEPARATOR was used, so perhaps there was some reason why this * class could
+				 * not be imported. *
+				 *******************************************************************/
+				break;
+
+			case N_Recursive:
+				processRecursive(definitions[lvi], currentModule);
+				break;
+			default:
+				errors.addAbort(definitions[lvi].getLocation(), "Internal error: Syntax node of kind "
+						+ definitions[lvi].getKind() + " unsupported " + definitions[lvi].getImage(), true);
+				break;
+			}
+		}
+
+		checkForUndefinedRecursiveOps(currentModule);
+
+		Vector vec = currentModule.recursiveOpDefNodes;
+		for (int i = 0; i < vec.size(); i++) {
+			OpDefNode node = (OpDefNode) vec.elementAt(i);
 // System.out.println("symbol " + node.getName() + ": recSect = " 
 //                    + node.recursiveSection + ", inRecSect = " 
 //                    + node.inRecursiveSection + ", letInLevel = "
 //                    + node.letInLevel);
-  } ;
-
-    moduleNestingLevel -- ;
-    return currentModule;
-
-  } // generateModule
-
-  // This method must be extended so that the Extends list hangs off of 
-  // the ModuleNode to support a getExtends() method that might reasonably 
-  // be in the API.
-  private final void processExtendsList(TreeNode treeNodes[], ModuleNode cm)
-  throws AbortException{
-    Vector extendeeVector = new Vector(2);
-
-    if (treeNodes != null) {
-      // module names in the EXTENDS list are separated by commas; hence incr by 2
-      for (int lvi = 1; lvi < treeNodes.length; lvi += 2) {
-        // Try to find the ModuleNode for the module being EXTENDED in the symbolTable
-        UniqueString extendeeID = treeNodes[lvi].getUS();
-        ModuleNode extendee = symbolTable.resolveModule(extendeeID);
-
-        // It must be an external module if it isn't in the symbolTable;
-        // try to find it in moduleTable (it cannot be in both places)
-        if (extendee == null) {
-          extendee = moduleTable.getModuleNode(extendeeID);
-          if (extendee == null) {
-            errors.addAbort(treeNodes[lvi].getLocation(), 
-                            "Could not find module " + extendeeID,
-                            false);
-          }
-        }
-
-        extendeeVector.addElement(extendee);
-
-        Context context = this.getContext(extendeeID);
-        if (context != null) {
-          symbolTable.getContext().mergeExtendContext(context);
-        }
-        else {
-          errors.addError(treeNodes[lvi].getLocation(),
-                          "Couldn't find context for module `" + extendeeID
-                          + "'.");
-        }
-
-        // copy nonlocal Assumes and Theorems
-        cm.copyAssumes(extendee);
-        cm.copyTheorems(extendee);
-        cm.copyTopLevel(extendee);
-      }
-    }
-    cm.createExtendeeArray(extendeeVector);
-  } // processExtendsList
-
-  // This method must be extended so that the variable declarations list 
-  // hangs off of the module node to support the getVariableDecls method 
-  // of the API.
-  private final void processVariables(TreeNode treeNodes[], ModuleNode cm) {
-    for (int lvi = 1; lvi < treeNodes.length; lvi += 2) {
-      UniqueString us = treeNodes[ lvi ].getUS();
-
-      if (us == S_at) {
-        errors.addError(treeNodes[lvi].getLocation(),
-                        "Attempted to declare '@' as a variable.");
-      }
-
-      // The next line has its side-effects in the constructor; in particular,
-      // the new OpDeclNode is placed in the symbolTble there.
-      new OpDeclNode(us, VariableDeclKind, 1, 0, cm, symbolTable, treeNodes[lvi]);
-    }
-  }
-
-
-
-  private final OpDeclNode buildParameter(TreeNode treeNode, 
-                                          int declKind,
-                                          int declLevel,
-                                          ModuleNode cm,
-                                          boolean declare) {
-    /***********************************************************************
-    * This method was originally called only by the processParameters      *
-    * method to build an OpDeclNode for a module-level CONSTANT            *
-    * declaration.  For a declaration "CONSTANT foo(_), _+_" it would be   *
-    * called twice, once with treeNode the node for "foo(_)", and once     *
-    * with it equal to the node for "_+_".  The OpDeclNode is not returned *
-    * because its constructor adds the node to symbolTable.                *
-    *                                                                      *
-    * It was modified by LL on 22 Mar 2007 for use in the generateNewSymb  *
-    * method as well.  The modifications consisted of returning the        *
-    * OpDeclNode and adding arguments to specify the kind and level of     *
-    * the OpDeclNode.                                                      *
-    *                                                                      *
-    * Further modified by LL on 6 Apr 2007 for use in the                  *
-    * processRecursive method as well.  The modification consisted of      *
-    * adding the declare argument.  If true, it calls the OpDeclNode       *
-    * constructor with symbol table symbolTable, so the symbol is added    *
-    * to the current symbol table.  If false, it calls the constructor     *
-    * with a null symbol table, so nothing is declared.  The               *
-    * processRecursive method just uses the name and arity from the        *
-    * returned OpDeclNode.                                                 *
-    ***********************************************************************/
-    UniqueString us = null;
-    int arity = 0;
-    TreeNode[] ss = treeNode.heirs();
-
-    if      ( treeNode.isKind( N_IdentDecl ) ) {
-      us = ss[0].getUS();
-      arity = (ss.length - 1) / 2;
-    }
-    else if ( treeNode.isKind( N_PrefixDecl ) ) {
-      us = ss[0].getUS();
-      arity = 1;
-    }
-    else if ( treeNode.isKind( N_InfixDecl ) ) {
-      us = ss[1].getUS();
-      arity = 2;
-    }
-    else if ( treeNode.isKind( N_PostfixDecl ) ) {
-      us = ss[1].getUS();
-      arity = 1;
-    }
-    else {
-      errors.addError(treeNode.getLocation(),
-                      "Unknown parameter declaration `" + treeNode.getUS()
-                      + "'.");
-    }
+		}
+		;
+
+		moduleNestingLevel--;
+		return currentModule;
+
+	} // generateModule
+
+	// This method must be extended so that the Extends list hangs off of
+	// the ModuleNode to support a getExtends() method that might reasonably
+	// be in the API.
+	private final void processExtendsList(TreeNode treeNodes[], ModuleNode cm) throws AbortException {
+		Vector extendeeVector = new Vector(2);
+
+		if (treeNodes != null) {
+			// module names in the EXTENDS list are separated by commas; hence incr by 2
+			for (int lvi = 1; lvi < treeNodes.length; lvi += 2) {
+				// Try to find the ModuleNode for the module being EXTENDED in the symbolTable
+				UniqueString extendeeID = treeNodes[lvi].getUS();
+				ModuleNode extendee = symbolTable.resolveModule(extendeeID);
+
+				// It must be an external module if it isn't in the symbolTable;
+				// try to find it in moduleTable (it cannot be in both places)
+				if (extendee == null) {
+					extendee = moduleTable.getModuleNode(extendeeID);
+					if (extendee == null) {
+						errors.addAbort(treeNodes[lvi].getLocation(), "Could not find module " + extendeeID, false);
+					}
+				}
+
+				extendeeVector.addElement(extendee);
+
+				Context context = this.getContext(extendeeID);
+				if (context != null) {
+					symbolTable.getContext().mergeExtendContext(context);
+				} else {
+					errors.addError(treeNodes[lvi].getLocation(),
+							"Couldn't find context for module `" + extendeeID + "'.");
+				}
+
+				// copy nonlocal Assumes and Theorems
+				cm.copyAssumes(extendee);
+				cm.copyTheorems(extendee);
+				cm.copyTopLevel(extendee);
+			}
+		}
+		cm.createExtendeeArray(extendeeVector);
+	} // processExtendsList
+
+	// This method must be extended so that the variable declarations list
+	// hangs off of the module node to support the getVariableDecls method
+	// of the API.
+	private final void processVariables(TreeNode treeNodes[], ModuleNode cm) {
+		for (int lvi = 1; lvi < treeNodes.length; lvi += 2) {
+			UniqueString us = treeNodes[lvi].getUS();
+
+			if (us == S_at) {
+				errors.addError(treeNodes[lvi].getLocation(), "Attempted to declare '@' as a variable.");
+			}
+
+			// The next line has its side-effects in the constructor; in particular,
+			// the new OpDeclNode is placed in the symbolTble there.
+			new OpDeclNode(us, VariableDeclKind, 1, 0, cm, symbolTable, treeNodes[lvi]);
+		}
+	}
+
+	private final OpDeclNode buildParameter(TreeNode treeNode, int declKind, int declLevel, ModuleNode cm,
+			boolean declare) {
+		/***********************************************************************
+		 * This method was originally called only by the processParameters * method to
+		 * build an OpDeclNode for a module-level CONSTANT * declaration. For a
+		 * declaration "CONSTANT foo(_), _+_" it would be * called twice, once with
+		 * treeNode the node for "foo(_)", and once * with it equal to the node for
+		 * "_+_". The OpDeclNode is not returned * because its constructor adds the node
+		 * to symbolTable. * * It was modified by LL on 22 Mar 2007 for use in the
+		 * generateNewSymb * method as well. The modifications consisted of returning
+		 * the * OpDeclNode and adding arguments to specify the kind and level of * the
+		 * OpDeclNode. * * Further modified by LL on 6 Apr 2007 for use in the *
+		 * processRecursive method as well. The modification consisted of * adding the
+		 * declare argument. If true, it calls the OpDeclNode * constructor with symbol
+		 * table symbolTable, so the symbol is added * to the current symbol table. If
+		 * false, it calls the constructor * with a null symbol table, so nothing is
+		 * declared. The * processRecursive method just uses the name and arity from the
+		 * * returned OpDeclNode. *
+		 ***********************************************************************/
+		UniqueString us = null;
+		int arity = 0;
+		TreeNode[] ss = treeNode.heirs();
+
+		if (treeNode.isKind(N_IdentDecl)) {
+			us = ss[0].getUS();
+			arity = (ss.length - 1) / 2;
+		} else if (treeNode.isKind(N_PrefixDecl)) {
+			us = ss[0].getUS();
+			arity = 1;
+		} else if (treeNode.isKind(N_InfixDecl)) {
+			us = ss[1].getUS();
+			arity = 2;
+		} else if (treeNode.isKind(N_PostfixDecl)) {
+			us = ss[1].getUS();
+			arity = 1;
+		} else {
+			errors.addError(treeNode.getLocation(), "Unknown parameter declaration `" + treeNode.getUS() + "'.");
+		}
 //    SymbolNode symbolNode = 
-     SymbolTable st = null ;
-     if (declare) {st = symbolTable;};
-
-     /**********************************************************************
-     * Changed us to Operators.resolveSynonym(us) in the following so      *
-     * constant declarations work with any synonym for an operator--e.g.,  *
-     * with both (+) and \oplus.                                           *
-     **********************************************************************/
-     return new  OpDeclNode(Operators.resolveSynonym(us), declKind, declLevel, arity, cm, 
-                            st, treeNode);
-  }
-
-  private final void processParameters(TreeNode treeNodes[], ModuleNode cm) {
-    for (int lvi = 1; lvi < treeNodes.length; lvi +=2 ) {
-      if (treeNodes[lvi].getUS() == S_at) {
-        errors.addError(treeNodes[lvi].getLocation(),
-                        "Attempted to declare '@' as a constant.");
-       } ;
-      OpDeclNode odn = 
-         buildParameter(treeNodes[lvi], ConstantDeclKind, ConstantLevel, 
-                        cm, true);
-        /*******************************************************************
-        * We throw away the OpDeclNode because, when it was constructed,   *
-        * it was added to symbolTable, whose top context should be the     *
-        * top-level context of the module.                                 *
-        *******************************************************************/
-    }
-  }
-
-  /**
-   * Processes the LHS of an operator definition, in several ways,
-   * depending on whether it is a prefix, infix, postfix, or a
-   * parameter-free or parameter-ful function-notation operator. Also
-   * creates context entries for the operator and its parameters.
-   */
-   /************************************************************************
-   * Processes a syntactic node of type N_OperatorDefinition.  It is       *
-   * called by generateModule (with defs = null) when processing an        *
-   * outer-level definition and by processLetIn (with defs /= null) when   *
-   * processing a LET definition.  It creates an OpDef node and puts it    *
-   * in ModuleNode cm's set of definitions.                                *
-   ************************************************************************/
-  private final void processOperator(TreeNode treeNode, Vector defs, 
-                                     ModuleNode cm) throws AbortException {
-    TreeNode           syntaxTreeNode = treeNode;
-    UniqueString       name     = null;
-    int                arity    = 0;
-    boolean            local    = syntaxTreeNode.zero() != null;
-    TreeNode []        children = syntaxTreeNode.one();
-    TreeNode []        ss       = children[0].heirs();
-    FormalParamNode [] params   = null;
-    Context            ctxt     = new Context(moduleTable, errors);
-    boolean isRecursive = false ;
-      /*********************************************************************
-      * Will be set to true if this operator was declared in a RECURSIVE   *
-      * statement.                                                         *
-      *********************************************************************/
-    // New context needed because parameter symbols may have to be
-    // added if the operator being defined takes params
-    symbolTable.pushContext( ctxt );
-  
-    // If the operator is an identifier (possibly with params), as
-    // opposed to a prefix, infix, or postfix symbol
-    if ( children[ 0 ].isKind( N_IdentLHS ) ) {
-      if ( ss.length > 2 ) {
-        // If the operator has arguments
-        params = new FormalParamNode[ (ss.length-2) / 2 ];
-        for ( int lvi = 2; lvi < ss.length; lvi += 2 ) {
-          TreeNode sss[] = ss[ lvi ].heirs();
-          if        ( ss[ lvi ].isKind( N_IdentDecl ) ) {
-            // parameter is simple identifier
-            name = sss[0].getUS();
-            arity = (sss.length - 1 )/ 2;
-          }
-          else if ( ss[ lvi ].isKind( N_InfixDecl ) ) {
-            // parameter is infix operator
-            // Call of Operators.resolveSynonym added by LL on 27 Mar 2013
-            name = sss[1].getUS();
-            
-            // Following added by LL on 27 Mar 2013
-            // to handle parameters like _(+)_
-            name = Operators.resolveSynonym(name);
-            
-            arity = 2;
-          }
-          else if ( ss[ lvi ].isKind( N_PrefixDecl ) ) {
-            // parameter is prefix operator
-            name = sss[0].getUS();
-            arity = 1;
-          }
-          else {
-            // parameter must be postfix operator
-            // SZ Jul 13, 2009: added the message to the assert
-            if(! ss[ lvi ].isKind( N_PostfixDecl )) 
-            {
-                throw new WrongInvocationException(MP.getMessage(EC.TLC_PARAMETER_MUST_BE_POSTFIX)); 
-            }
-            name = sss[1].getUS();
-            arity = 1;
-          }
-          params[ (lvi-2)/2 ] = 
-            new FormalParamNode(name, arity, ss[lvi], symbolTable, cm);
-        }
-      }
-      else {
-        // The operator has no arguments
-        params = new FormalParamNode[0];
-      }
-      name = ss[0].getUS();
-    }
-    else if (children[ 0 ].isKind(N_PrefixLHS)) { // operator is a prefix operator
-      // Process the parameter
-      params = new FormalParamNode[1];
-      params[0] = new FormalParamNode(ss[1].getUS(), 0, ss[1], symbolTable, cm);
-  
-      // Process the operator
-      name = Operators.resolveSynonym(ss[0].getUS());
-    }
-    else if (children[ 0 ].isKind(N_InfixLHS)) { // operator is an infix operator
-      params = new FormalParamNode[2];
-      // Process the first param
-      params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm);
-  
-      // Process the second param
-      params[1] = new FormalParamNode(ss[2].getUS(), 0, ss[2], symbolTable, cm);
-  
-      // Process the operator
-      name = Operators.resolveSynonym(ss[1].getUS());
-    }
-    else if (children[ 0 ].isKind(N_PostfixLHS)) { // operator is a postfix operator
-      // Process the parameter
-      params = new FormalParamNode[1];
-      params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm);
-  
-      // Process the operator
-      name = Operators.resolveSynonym(ss[1].getUS());
-    }
-    else {
-      errors.addError(children[0].getLocation(),
-                      "Unknown parameter declaration `" + 
-                      children[0].getUS() + "'.");
-    }
-
-    /***********************************************************************
-    * The following code added by LL on 1 April 2007 to handle             *
-    * recursively defined operators.                                       *
-    ***********************************************************************/
-    OpDefNode odn = null ;
-    /***********************************************************************
-    * Check if this operator has already been declared or defined.  If     *
-    * so, if it was declared in a RECURSIVE statement at this let/in       *
-    * level and not yet defined, then set the OpDefNode's fields           *
-    * appropriately, otherwise report an error.                            *
-    ***********************************************************************/
-    SymbolNode symbolNode = symbolTable.resolveSymbol(name) ;
-
-    if (symbolNode != null) {
-      /*********************************************************************
-      * The symbol has already been defined or declared.  Check if it was  *
-      * declared in a RECURSIVE statement.                                 *
-      *********************************************************************/
-      if (symbolNode instanceof OpDefNode) {odn = (OpDefNode) symbolNode;};
-      if (   (odn != null) 
-          && odn.inRecursive 
-          && (! odn.isDefined) ) {
-        if (odn.letInLevel == curLevel) {
-          isRecursive = true;
-
-          /*****************************************************************
-          * Check that the parameters of the definition match the ones in  *
-          * the RECURSIVE declaration.                                     *
-          *****************************************************************/
-          boolean paramsMatch = (odn.getParams().length == params.length) ;
-          if (paramsMatch) {
-            for (int i = 0 ; i < params.length ; i++) {
-              paramsMatch = (params[i].getArity() == 0) ;
-             };
-           }; // if
-          if (!paramsMatch) { 
-            errors.addError(treeNode.getLocation(),
-                            "Definition of " + odn.getName() + 
-                            " has different arity than " + 
-                            "its RECURSIVE declaration.");
-           };
-
-          odn.setParams(params) ;
-            /***************************************************************
-            * Here's where the params field of the OpDefNode is set for an *
-            * operator declared in a RECURSIVE statement.                  *
-            ***************************************************************/
-          } // if (odn.letInLevel == curLevel)
-        else {errors.addError(treeNode.getLocation(),
-                              "Recursive operator " + name.toString() + 
-                              " defined at wrong LET/IN level.") ;
-              odn = null ;
-         } // else
-        } // if (odn != null) ...
-      else { errors.addError(treeNode.getLocation(),
-                             "Operator " + name.toString() + 
-                              " already defined or declared.") ;
-       }
-     } // if (symbolNode != null)
-
-    pushLS() ;
-      /*********************************************************************
-      * Start fresh processing of labels.                                  *
-      *********************************************************************/
-    // Generate expression that is the body of the operator
-    ExprNode exp = generateExpression( children[2], cm );
-  
-    // Restore old context, popping off the context containing the parameters
-    symbolTable.popContext();
-  
-    
-    if (isRecursive) {endOpDefNode(odn, exp, syntaxTreeNode) ; } 
-      else {
-      // Create OpDefNode using symbolTable whose top context contains the 
-      // parameter symbols
-       /********************************************************************
-       * This comment appears to be incorrect, because it looks like the   *
-       * parameter symbols were just popped off symbolTable.               *
-       ********************************************************************/
-      odn = new OpDefNode(name, UserDefinedOpKind, params, local, 
-                           exp, cm, symbolTable, syntaxTreeNode, true, null);
-      symbolNode = odn ;
-        /*******************************************************************
-        * Keeping symbolNode for historical reasons--I'm too lazy to       *
-        * rearrange the code to get rid of the redundant identifier.       *
-        *******************************************************************/
-
-      setOpDefNodeRecursionFields(odn, cm) ;
-
-     } // else
-
-    Hashtable ht = popLabelNodeSet() ;
-      /*********************************************************************
-      * Succeed or fail, we must execute popLabelNodeSet to match the      *
-      * previous pushLS.                                                   *
-      *********************************************************************/
-    if (odn != null) {odn.setLabels(ht);} ;
-      /*********************************************************************
-      * If there was no error, then odn is an OpDefNode and we must set    *
-      * it labels field.                                                   *
-      *********************************************************************/
-    cm.appendDef(symbolNode) ;
-        /*******************************************************************
-        * Add the new OpDefNode to the current module's set of             *
-        * definitions.                                                     *
-        *******************************************************************/
-    // defs is non-null iff this definition is in the Let part of a 
-    // Let-In expression
-    if (defs != null) defs.addElement(symbolNode);
-  } // processOperator   
-    
-  private final void processQuantBoundArgs(
-                      TreeNode[]treeNodeA, // node whose children include 
-                                           // the bounded quants
-                      int offset,          // nodes to skip to get to first 
-                                           // "quantifier"
-                      FormalParamNode[][] odna, 
-                      boolean []bt,        // set to true if arg is a tuple; 
-                                           // otherwise false
-                      ExprNode[] ena, 
-                      ModuleNode cm)
-    /***********************************************************************
-    * Despite the incoherent and/or incorrect comments, here's what I      *
-    * think is going on:                                                   *
-    *                                                                      *
-    *  - odna, ena, and bt have the same length N.                         *
-    *                                                                      *
-    *  - treeNodeA is an array of nodes containing a subsequence of        *
-    *    N_QuantBound nodes of length N that starts at                     *
-    *    treeNodeA[offset].                                                *
-    *                                                                      *
-    *  - odna, ena, and bt are set to the arguments for the OpApplNode     *
-    *    constructor that produces an OpApplNode for the operator          *
-    *    having this sequence of N_QuantBound nodes.                       *
-    ***********************************************************************/
-
-  throws AbortException {
-    // For each quantifier, evaluate the bound in the context of the
-    // current symbol table, i.e. before the quantified variables are
-    // added to the new context, since the quantified vars may not
-    // appear in the bounds.
-    for (int lvi = 0; lvi < bt.length; lvi++ ) {
-      // Make ss point to each N_QuantBound node in turn.
-      TreeNode[] ss = treeNodeA[offset + 2 * lvi].heirs();
-      // the last element in ss is expression for the quantifier bound
-      ena[lvi] = generateExpression( ss[ ss.length - 1 ], cm );
-    }
-
-    // Now for each quantifier, process the variable names
-    for (int lvi = 0; lvi < bt.length; lvi++ ) {
-      TreeNode treeNode = treeNodeA[offset + 2 * lvi];  
-
-      // The variable bound to the "quantifier"
-      TreeNode[] ss = treeNode.heirs();
-
-      if (ss[0].isKind(N_IdentifierTuple)) { // three elements only, go into node
-        bt[lvi] = true;
-        TreeNode[] sss = ss[0].heirs();
-        odna[lvi] = new FormalParamNode[ sss.length / 2 ];
-
-        for (int lvj = 0; lvj < sss.length / 2; lvj++ ) {
-          odna[lvi][lvj] = new FormalParamNode(
-                                sss[ 2*lvj+1 ].getUS(), 0, sss[ 2*lvj+1 ],
-                                symbolTable, cm) ;
-        }
-      }
-      else { // gotta be N_Identifier
-        bt[lvi] = false;
-        odna[lvi] = new FormalParamNode[ (ss.length - 1)/2 ];
-
-        for ( int lvj = 0; lvj <  (ss.length - 1)/2 ; lvj++ ) {
-          odna[lvi][lvj] = new FormalParamNode(
-                                  ss[ 2*lvj ].getUS(), 0, ss[ 2*lvj ],
-                                  symbolTable, cm);
-        }
-      }
-    }
-  }
-
-  // Process a function definition
-  private final void processFunction(TreeNode treeNode, Vector defs, ModuleNode cm)
-  throws AbortException {
-    TreeNode        syntaxTreeNode = treeNode;
-    boolean         local      = syntaxTreeNode.zero() != null;
-    TreeNode[]      ss         = syntaxTreeNode.one();  
-       // Heirs to N_FunctionDefinition node
-    int             ql         = (ss.length-4)/2;       
-       // number of QuantBound's
-    OpApplNode      oan;
-    OpDefNode       odn = null;
-    FormalParamNode[][] quants     = new FormalParamNode[ql][0];
-    FormalParamNode[]   fcnDeclForRecursion = new FormalParamNode[1];
-      /*********************************************************************
-      * This is an array of length 1 instead of just an OpDeclNode         *
-      * because it is needed in such an array to pass as an argument to    *
-      * new OpApplNode.                                                    *
-      *********************************************************************/
-    boolean []      tuples     = new boolean[ql];
-    ExprNode []     domains    = new ExprNode[ql];
-    ExprNode []     lhs        = new ExprNode[1];
-    Context         newContext = new Context(moduleTable, errors);
-    boolean isRecursive = false ;
-      /*********************************************************************
-      * Will be set to true if this operator was declared in a RECURSIVE   *
-      * statement.                                                         *
-      *********************************************************************/
-
-    // Fill arrays with quantifier-related information; must be called
-    // in scope of *new* context, since it adds parameter symbols to
-    // the context.
-    symbolTable.pushContext(newContext);
-    processQuantBoundArgs( ss, 2, quants, tuples, domains, cm ); 
-
-    UniqueString name = ss[0].getUS() ;
-    SymbolNode symbolNode = symbolTable.resolveSymbol(name) ;
-
-
-    // This is in anticipation of the possibility that the function is
-    // recursive.  We are creating a new bound symbol of the same name
-    // as the function to stand in the body for the function.  The
-    // arity of the bound symbol is 0 because a function formally has
-    // arity 0 as an operator, even if it has several arguments as a
-    // function.
-    /***********************************************************************
-    * If the function name is already defined (which should happen only    *
-    * if it was declared in a RECURSIVE statement, then there is no need   *
-    * to add this OpDeclNode to the symbol table.  As near as I can tell   *
-    * (which might not be near enough), this OpDeclNode's entry in the     *
-    * symbol table is needed only so the name will be declared in case     *
-    * of a recursive call.  The check for whether a function call in the   *
-    * body actually is recursive is made by calling the recursionCheck     *
-    * method of the Function subclass to see if the OpApplNode is in the   *
-    * Function object's funcStack.                                         *
-    ***********************************************************************/
-    SymbolTable st = null ;
-    if (symbolNode == null) {st = symbolTable; } ;
-      fcnDeclForRecursion[0] = 
-        new FormalParamNode(name, 0, treeNode, st, cm);
-    symbolTable.popContext();
-
-    // Create OpApplNode to hold the function body; type is assumed to
-    // be non-recursive function (OP_nrfs); if the body is recursive,
-    // this will be discovered during generateExpression() for the
-    // body
-    oan = new OpApplNode(OP_nrfs, fcnDeclForRecursion, new ExprNode[0], 
-                         quants, tuples, domains, syntaxTreeNode, cm);
-                 // constructor 5
-
-    /***********************************************************************
-    * The following code added by LL on 19 April 2007 to handle            *
-    * recursively defined operators.                                       *
-    ***********************************************************************/
-    if (symbolNode == null) {
-      odn = new OpDefNode(ss[0].getUS(), UserDefinedOpKind, nullParam, local, 
-                          oan, cm, symbolTable, syntaxTreeNode, true, null);
-      setOpDefNodeRecursionFields(odn, cm) ;
-     }
-    else 
-      {
-      /*********************************************************************
-      * The symbol has already been defined or declared.  Check if it was  *
-      * declared in a RECURSIVE statement.                                 *
-      *********************************************************************/
-      if (symbolNode instanceof OpDefNode) {odn = (OpDefNode) symbolNode;};
-      if (   (odn != null) 
-          && odn.inRecursive 
-          && (! odn.isDefined) ) {
-        if (odn.letInLevel == curLevel) {
-          isRecursive = true;
-          /*****************************************************************
-          * Check that the RECURSIVE declaration had no paramters.         *
-          *****************************************************************/
-          if (odn.getArity() == 0) {
-            endOpDefNode(odn, oan, syntaxTreeNode) ;
-            }
-           else {
-              /*************************************************************
-              * RECURSIVE declaration had parameters.                      *
-              *************************************************************/
-              errors.addError(treeNode.getLocation(),
-                              "Function " + odn.getName() + 
-                              " has operator arguments in " + 
-                              "its RECURSIVE declaration.");
-            } ;
-         } // if (odn.letInLevel == curLevel)
-        else {errors.addError(treeNode.getLocation(),
-                              "Recursive function " + name.toString() + 
-                              " defined at wrong LET/IN level.") ;
-              odn = null ;
-         } // else
-        } // if (odn != null) ...
-      else { errors.addError(treeNode.getLocation(),
-                             "Function name `" + name.toString() + 
-                              "' already defined or declared.") ;
-       } 
-     } // else (symbolNode != null)            
-
-    // Create OpDefNode to hold the function definition, including
-    // reference to the function body ("oan")
-    if (odn != null) {
-      /*********************************************************************
-      * There was no error, so the OpDefNode was created.                  *
-      *********************************************************************/
-      cm.appendDef(odn);
-
-       // defs is non-null iff this function definition is in the Let
-       // part of a Let-In expression.  If so, then we have to accumulate
-       // these defs in a vector.
-       if (defs != null) defs.addElement(odn);
-
-       // Function body must be processed in the scope of the new
-       // context, including the parms
-       symbolTable.pushContext( newContext );
-
-     }; // if (odn != null)
-
-    // Keep stack of nested function defs to enable detection of recursion
-    functions.push( ss[0].getUS(), oan );
-
-    
-    // Create semantic graph function body in the inner context including parameters
-    pushLS() ; 
-    pushFormalParams(flattenParams(quants)) ;
-      /*********************************************************************
-      * Push a new empty label set onto LS, and push the function          *
-      * definition's formal parameters onto Last(LS).paramSeq for          *
-      * processing the function body.                                      *
-      *********************************************************************/
-    lhs[0] = generateExpression( ss[ss.length - 1], cm ); 
-    popFormalParams() ;
-      /*********************************************************************
-      * Pop the formal params from Last(LS).paramSeq, which should now be  *
-      * empty.  This isn't really necessary, since we're going to pop the  *
-      * LS stack below, but I hate to have a push without a pop.           *
-      *********************************************************************/
-    Hashtable ht = popLabelNodeSet() ;
-      /*********************************************************************
-      * The matching pop for the pushLS above.                             *
-      *********************************************************************/
-    if (odn != null) { odn.setLabels(ht); } ;
-      /*********************************************************************
-      * If there was no error and we created an OpDefNode, then set its    *
-      * label set.                                                         *
-      *********************************************************************/
-    functions.pop();
-
-    oan.setArgs( lhs );
-    /***********************************************************************
-    *  Test for odn != null added 2 Jul 2009 to avoid bug that caused      *
-    *  popping of empty symbol table when the function name was            *
-    *  already declared to be a variable.                                  *
-    ***********************************************************************/
-    if (odn != null){
-    // Restore old context
-    symbolTable.popContext();
-    }
-    
-    // if the function body turned out to be non-recursive, then we
-    // should null-out the fcnDefForRecursion ref put in place above,
-    // since it is unnecessary for a nonrecursive func
-    if (oan.getOperator().getName() == OP_nrfs) {
-      oan.makeNonRecursive();
-    }
-  } // end processFunction() 
-
-  private final ExprNode processLetIn(TreeNode treeNode, TreeNode[] children, 
-                                      ModuleNode cm)
-  throws AbortException {
-    TreeNode[] syntaxTreeNode = children[1].heirs(); // extract LetDefinitions
-    Vector   defVec = new Vector(4);
-    Vector   instVec = new Vector(1);
-
-    Context letCtxt = new Context(moduleTable, errors) ;
-    symbolTable.pushContext(letCtxt);
-      /*********************************************************************
-      * Create a new sub-Context for the IN expression, containing the     *
-      * LET definitions                                                    *
-      *********************************************************************/
-
-    /***********************************************************************
-    * Increment curLevel.                                                  *
-    ***********************************************************************/
-    if (curLevel < MaxLetInLevel) { curLevel++ ; }
-     else { errors.addAbort(treeNode.getLocation(),
-                            "LETs nested more than " +  MaxLetInLevel + 
-                            " deep.") ;
-          } ;      
-    unresolvedCnt[curLevel] = 0 ;
-      /*********************************************************************
-      * Will not have been initialized if we haven't yet reached this      *
-      * LET/IN nesting depth.                                              *
-      *********************************************************************/
-      
-    for (int lvi = 0; lvi < syntaxTreeNode.length; lvi++) {
-      /*********************************************************************
-      * Note: LL changed from an "if" and a sequence of "elseif"s to a     *
-      * switch statement on 7 Apr 2007 when adding the N_Recursive case.   *
-      *********************************************************************/
-      switch (syntaxTreeNode[lvi].getKind()) {
-        case N_OperatorDefinition :
-          processOperator(syntaxTreeNode[ lvi ], defVec, cm);
-          break ;
-
-        case N_FunctionDefinition :
-          processFunction(syntaxTreeNode[ lvi ], defVec, cm );
-          break ;
-
-        case N_ModuleDefinition :
-          processModuleDefinition(syntaxTreeNode[lvi], defVec, instVec, cm);
-          break ;
-
-        case N_Recursive :
-          processRecursive(syntaxTreeNode[ lvi ], cm) ;
-          break ;
-
-        default :
-          errors.addAbort(syntaxTreeNode[ lvi ].getLocation(),
-                           "Internal error: found unexpected syntax " +
-                           "tree node in LET.") ;
-       } // switch
-     } // for
-
-    checkForUndefinedRecursiveOps(cm) ;
-
-    /***********************************************************************
-    * Decrement curLevel.                                                  *
-    ***********************************************************************/
-    curLevel -- ;
-    if (curLevel < 0) { curLevel = 0;} ;
-
-
-    ExprNode body = generateExpression(children[3], cm);
-
-    /***********************************************************************
-    * Convert from Vector to array of SymbolNode, whose elements may be    *
-    * OpDefNode or ThmOrAssumpDefNode objects.                             *
-    ***********************************************************************/
-    SymbolNode[] opDefs = new SymbolNode[defVec.size()];
-    for (int i = 0; i < opDefs.length; i++) {
-      opDefs[i] = (SymbolNode)defVec.elementAt(i);
-    }
-
-    InstanceNode[] insts = new InstanceNode[instVec.size()];
-    for (int i = 0; i < insts.length; i++) {
-      insts[i] = (InstanceNode)instVec.elementAt(i);
-    }
-    LetInNode letIn = new LetInNode(treeNode, opDefs, insts, body, letCtxt);
-    symbolTable.popContext();
-    return letIn;
-  } // end processLetIn
-
-  private final ExprNode generateExpression(TreeNode treeNode, ModuleNode cm)
-   throws AbortException {
-    /***********************************************************************
-    * Must return an ExprNode that represents an expression, and not a     *
-    * labeled ASSUME/PROVE                                                 *
-    ***********************************************************************/
-    return generateExpressionOrLAP(treeNode, cm, false) ;
-   }
-
-  private final ExprNode generateExpressionOrLAP(
-                 TreeNode treeNode, ModuleNode cm, boolean allowLabeledAP)
-    /***********************************************************************
-    * Can return a LabelNode labeling an ASSUME/PROVE iff allowLabeledAP   *
-    * = true.  Otherwise, it can return only an ExprNode that represents   *
-    * an expression.                                                       *
-    ***********************************************************************/
-  throws AbortException {
-    TreeNode[]        children = treeNode.heirs();
-    TreeNode[]        ss       = null; // grandchildren
-    SymbolNode        opn      = null;
-    TreeNode          op       = null;
-    GenID             genID;
-    ExprOrOpArgNode[] sns;             // a ExprNode list used for arguments
-   
-    switch (treeNode.getKind()) {
-
-    case N_Real :
-      return new DecimalNode(children[0].getImage(),children[2].getImage(), treeNode);
-
-    case N_Number :
-      return new NumeralNode( children[0].getImage(), treeNode);
-
-    case N_String :
-      return new StringNode( treeNode, true);
-
-    case N_ParenExpr :
-      return generateExpression( children[1], cm );
-
-    case N_InfixExpr :
-      genID = generateGenID(children[1], cm);
-
-      sns = new ExprOrOpArgNode[2];
-      opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
-      if ( opn == null ) {
-        errors.addError(treeNode.getLocation(),
-                        "Couldn't resolve infix operator symbol `" + 
-                        genID.getCompoundIDUS() + "'." );
-        return null;
-      }
-
-      sns[0] = generateExpression( children[0], cm );
-      sns[1] = generateExpression( children[2], cm );
-      return new OpApplNode(opn, sns, treeNode, cm);
-
-    case N_PrefixExpr :
-      // 1 get gen operator node
-      ss = children[0].heirs();
-
-      // 2 get rightmost part of the possibly compound Op itself;
-      op = ss[1];
-      genID = generateGenID(children[0], cm, true);
-      sns = new ExprOrOpArgNode[1];
-      opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
-
-      if ( opn == null ) {
-        errors.addError(treeNode.getLocation(), 
-                        "Couldn't resolve prefix operator symbol `" + 
-                        genID.getCompoundIDUS() + "'." );
-        return null;
-      } 
-
-      sns[0] = generateExpression( children[1], cm );
-      return new OpApplNode(opn, sns, treeNode, cm);    // constructor 2 
-
-    case N_PostfixExpr :
-      genID = generateGenID(children[1], cm);
-
-      sns = new ExprNode[1];
-      opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
-      if ( opn == null ) {
-        errors.addError(treeNode.getLocation(), "Couldn't resolve postfix " +
-                        "operator symbol `" + genID.getCompoundIDUS() + "'.");
-        return null;
-      }
-
-      sns[0] = generateExpression( children[0], cm );
-      return new OpApplNode(opn, sns, treeNode, cm);  // constructor 2 
-
-    case N_Times : // or cartesian product
-      sns = new ExprNode[ (children.length+1)/2 ];  
-
-      for (int lvi = 0; lvi < sns.length; lvi ++ ) {
-        sns[ lvi ]  = generateExpression( children[ 2 * lvi ], cm );
-      }
-      return new OpApplNode(OP_cp, sns, treeNode, cm);  // constructor 3 
-
-    case N_SetEnumerate :
-      int size = (children.length-1) / 2;
-      sns = new ExprNode [size];
-      for ( int lvi = 0; lvi < size; lvi++ ) {
-        sns[lvi] = generateExpression( children[ 2 * lvi + 1 ], cm );
-      }
-      return new OpApplNode(OP_se, sns, treeNode, cm);  // constructor 3 
-
-    case N_GeneralId :
-      // This is a zero-ary operator; it should show in the syntax
-      // tree as an OpApp, but it does not.  Hence, an OpApplication
-      // node with zero arguments must be constructed for it
-      // if we get here, the GeneralID really is an OpApplication with 0
-      // primary arguments, but with any number of prefix arguments
-
-      // process the generalized identifier, complete with its
-      // embedded argument lists (if any)
-
-      /*********************************************************************
-      * If the N_GeneralId represents the identifier "@", then check for   *
-      * errors and return an AtNode if none.                               *
-      *********************************************************************/
-      SyntaxTreeNode sTreeNode = (SyntaxTreeNode) treeNode ;
-      if (   (sTreeNode.heirs()[1].getKind() == IDENTIFIER)
-          && (sTreeNode.heirs()[1].getUS() == AtUS)
-          && (((SyntaxTreeNode) sTreeNode.heirs()[0]).heirs().length == 0) ) {
-        if (excStack.empty() || excSpecStack.empty()) {
-          // if either stack is empty, then @ used in improper EXCEPT context
-          errors.addError(sTreeNode.getLocation(),
-                          "@ used where its meaning is not defined.");
-          return nullOAN ;
-        }
-        else { 
-          // So, the context for @ is proper, then construct the 
-          // AtNode and return it
-          return new AtNode((OpApplNode)excStack.peek(),
-                            (OpApplNode)excSpecStack.peek());
-        }
-      } ;
-
-      ExprNode retVal = 
-         (ExprNode) selectorToNode(genIdToSelector(sTreeNode), 
-                                    0, false, false, cm) ;
-
-      /*********************************************************************
-      * A function definition generates an OpDefNode whose body is an      *
-      * OpApplNode whose operator is either $RecursiveFcnSpec or           *
-      * $NonRecursiveFcnSpec, the latter when the definition is            *
-      * recursive.  However, in SANY1 the definition                       *
-      *                                                                    *
-      *    f[x \in S] == ...                                               *
-      *                                                                    *
-      * was found to be recursive only if a subexpression f[...] occurs    *
-      * in the body.  It was not marked as recursive if another instance   *
-      * of f occurs, such as Foo(f).  To fix this, we need to check if     *
-      * this genID is actually the Identifier of a function currently      *
-      * being defined.  The following call to functions.recursionCheck     *
-      * does that.                                                         *
-      *********************************************************************/
-      if (retVal.getKind() == OpApplKind) {
-        functions.recursionCheck(
-           ((OpApplNode) retVal).getOperator().getName()) ;
-       } ;
-
-      return retVal ;
- /**************************  old version
-      genID = generateGenID(treeNode, cm);
-
-      // if the symbol is "@" then check for errors and 
-      // return an AtNode if none.
-      if (genID.getCompoundIDUS() == S_at) {
-        if (excStack.empty() || excSpecStack.empty()) {
-          // if either stack is empty, then @ used in improper EXCEPT context
-          errors.addError(treeNode.getLocation(),
-                          "@ used where its meaning is not defined.");
-        }
-        else { 
-          // So, the context for @ is proper, then construct the 
-          // AtNode and return it
-          return new AtNode((OpApplNode)excStack.peek(),
-                            (OpApplNode)excSpecStack.peek());
-        }
-      }
-      else if (genID.getFullyQualifiedOp() == null 
-                 || genID.getArgs() == null) {
-        // If it is not an "@" symbol, it may still be an unresolved symbol
-        return nullOAN;
-      }
-      else if (genID.getFullyQualifiedOp().getKind() == ModuleKind) {
-        errors.addError(
-          treeNode.getLocation(),
-          "Module name '" + genID.getFullyQualifiedOp().getName() +
-          "' used as operator.");
-        return nullOAN;
-      }
-      else {
-        // but if there are no problems then we are in a situation in
-        // which return the appropriate OpApplNode an N_GenID node in
-        // the syntax tree really stands for an OpApplication
-
-//      *********************************************************************
-//      * Modified on 20 Apr 2007 by LL to correct the following bug.        *
-//      *                                                                    *
-//      * A function definition generates an OpDefNode whose body is an      *
-//      * OpApplNode whose operator is either $RecursiveFcnSpec or           *
-//      * $NonRecursiveFcnSpec, the latter when the definition is            *
-//      * recursive.  However, the definition                                *
-//      *                                                                    *
-//      *    f[x \in S] == ...                                               *
-//      *                                                                    *
-//      * was found to be recursive only if a subexpression f[...] occurs    *
-//      * in the body.  It was not marked as recursive if another instance   *
-//      * of f occurs, such as Foo(f).  To fix this, we need to check if     *
-//      * this genID is actually the Identifier of a function currently      *
-//      * being defined.  The call to functions.recursionCheck was added     *
-//      * here to do that.                                                   *
-//      *********************************************************************
-      SymbolNode symNode = genID.getFullyQualifiedOp() ;
-
-      if (symNode.getKind() == ThmOrAssumpDefKind) {
-        errors.addError(treeNode.getLocation(),
-                        "Theorem or Assumption name used as expression.") ;
-       };
-      OpApplNode retVal = 
-       // return 
-         new OpApplNode(symNode, genID.getArgs(), 
-                              treeNode, cm);
-      functions.recursionCheck(symNode.getName());
-      return retVal ;
-      }
-
- ******************************* old version ******************/
-    case N_OpApplication:
-      // for an operator with arguments
-      // Note: in neither case can this be an operator passed as an argument; 
-      // the opAppl argument forces the return of an Operator application
-      // operators passed as arguments generate OpArg nodes, and that 
-      // can happen only in certain contexts, not in every context where an 
-      // expression can occur, which is the context we are in here
-
-      SyntaxTreeNode genIdNode  = (SyntaxTreeNode) treeNode.heirs()[0] ;
-      SyntaxTreeNode opApplNode = (SyntaxTreeNode) treeNode.heirs()[1] ;
-
-      /*********************************************************************
-      * First a check.  It appears that the children of an OpApplication   *
-      * node must be a GeneralId node and an OpArgs node, but let's be     *
-      * sure.                                                              *
-      *********************************************************************/
-      if (   (genIdNode.getKind() != N_GeneralId) 
-          || (opApplNode.getKind() != N_OpArgs)) {
-          errors.addAbort(
-            treeNode.getLocation(),
-            "Internal error: OpAppl node with unexpected children.",
-            true);
-        } ;
-
-      Selector sel = genIdToSelector(genIdNode) ;
-      sel.args[sel.args.length - 1] = opApplNode ;
-      sel.selSTN = (SyntaxTreeNode) treeNode ;
-        /*******************************************************************
-        * For error reporting, make the syntax tree node of the selector   *
-        * include both the general ID node and its argument.               *
-        *******************************************************************/
-      return (ExprNode) selectorToNode(sel, 0, false, false, cm) ;
-
-/***************** old version *********************
-      return generateOpAppl(treeNode, cm);
-************************/
-
-    case N_Tuple :
-      size = (children.length - 1) / 2;
-      sns = new ExprNode [size];
-      for ( int lvi = 0; lvi < size; lvi++ ) {
-        sns[lvi] = generateExpression( children[ 2 * lvi + 1 ], cm );
-      }
-      return new OpApplNode(OP_tup, sns, treeNode, cm); // Constructor 3 
-
-    case N_FcnAppl :  // Apparent function application
-      // Number of arguments to the apparent func app
-      int numArgs = (children.length - 2) / 2;
-
-      // Function appl involves two semantic nodes: 1 for function,
-      // and 1 for arg or args tuple.
-      sns = new ExprNode[2];
-
-      // Generate expression tree for the function itself
-      sns[0] = generateExpression( children[0], cm );  
-
-      if (sns[0] == null) {return null;} ;
-        /*******************************************************************
-        * sns[0] can be null if the parsing the function generates an      *
-        * error (which is the case if the function is missing).            *
-        * Added by LL on 29 Feb 2008                                       *
-        *******************************************************************/
-        
-      // If the function is an OpApplNode (and could it be otherwise?)
-      if (sns[0].getKind() == OpApplKind) { 
-        // Note if this is a recursive function, and change the top level
-        functions.recursionCheck(((OpApplNode)sns[0]).getOperator().getName());
-      }
-
-      // We next check that the number of arguments to a user-defined
-      // function is correct, if possible.
-
-      // Retrieve the expression that represents the function being
-      // applied, i.e. the 1st arg to $FcnApply
-      ExprOrOpArgNode fcn = sns[0];
-
-      // The entire next conditional is for one purpose: to make sure
-      // that a function symbol is applied to the right number of
-      // arguments, when it is possible to do that during semantic
-      // analysis.  This means that if a function is declared with,
-      // say, 3 parameters, e.g. "f[a,b,c] == {a,b,c}", then it is
-      // never used with 2 or 4 arguments.  However, it can appear
-      // with zero arguments as the expression "f" (not as "f[]"), and
-      // it can appear with one argument, as in "f[e]", because e
-      // might be a 3-tuple value.  (Whether it always is or not
-      // cannot be determined at the time of semantic analysis.)
-      // Furthermore a function declared with exactly one parameter
-      // can appear with any number of argument expressions because,
-      // e.g. f[1,2,3,4] is considered just an alternate way of
-      // writing f[<<1,2,3,4>>], so there is really just one argument
-      // value.
-
-      // If it is an OpApplNode (as opposed to, say, an OpDeclNode)
-      if ( fcn instanceof OpApplNode ) {
-        // Retrieve the function being applied
-        SymbolNode funcOperator = ((OpApplNode)fcn).getOperator();
-
-        // If the function being applied is a user-defined function
-        // (as opposed to OpDeclNode, FormalParamNode, builtin
-        // operator, or expression)
-        if (funcOperator instanceof OpDefNode &&  
-            funcOperator.getKind() == UserDefinedOpKind) {
-          // Retrieve the function body expression
-          ExprOrOpArgNode funcBody = ((OpDefNode)funcOperator).getBody();
-
-          // if the function body is an OpApplNode (as opposed to,
-          // say, NumeralNode, DecimalNode, etc.)
-          if (funcBody instanceof OpApplNode && 
-              (((OpApplNode)funcBody).getOperator().getName()==OP_nrfs  ||
-               ((OpApplNode)funcBody).getOperator().getName()==OP_rfs )) {
-
-            // find out how many arguments it is SUPPOSED to have
-            int numParms = ((OpApplNode)funcBody ).getNumberOfBoundedBoundSymbols();
-                                                  
-            // If the function appears with numArgs >= 2 argument
-            // expressions, it must be declared with exactly numArgs
-            // parameters; and a function with numParms parameters in
-            // its definition should be applied to exactly numParms
-            // expressions, or 1 expression (representing arguments in
-            // tuple form), or 0 expressions (representing the
-            // function itself).  Note: one cannot define a function
-            // with 0 arguments in TLA+.
-            if ( numArgs >= 2 && numParms != numArgs ) {
-              errors.addError(treeNode.getLocation(), 
-                              "Function '" + 
-                                ((OpApplNode)sns[0]).getOperator().getName() +
-                              "' is defined with " + numParms +
-                              " parameters, but is applied to " + 
-                              numArgs + " arguments.");
-              return nullOAN;
-            } // end if
-          } // end if
-        } // end if
-      } // end if
-
-      // Assert.check(numArgs > 0);
-      if (numArgs == 1) {
-        sns[1] = generateExpression(children[2], cm);
-      }
-      else {
-        // If there is more than one arg we have to create a tuple for the arguments.
-        ExprOrOpArgNode[] exprs = new ExprNode[ numArgs ];  // One for each of the arguments
-
-        // For each argument...
-        for (int lvi = 0; lvi < numArgs; lvi++) {
-          // Create the expression for that argument
-          exprs[lvi] = generateExpression( children[ 2+2*lvi ], cm );
-        }
-        // Create an application of $Tuple
-        sns[1] = new OpApplNode(OP_tup, exprs, treeNode, cm);
-      }
-      // Create the function application node.
-      return new OpApplNode(OP_fa, sns, treeNode, cm);
-
-    case N_UnboundOrBoundChoose :
-      return processChoose(treeNode, children, cm );
-
-    case N_BoundQuant :
-      return processBoundQuant(treeNode, children, cm);
-
-    case N_UnboundQuant :
-      return processUnboundQuant( treeNode, children, cm );
-
-    case N_IfThenElse :
-      sns = new ExprNode[3];
-      sns[0] = generateExpression( children[1], cm );
-      sns[1] = generateExpression( children[3], cm );
-      sns[2] = generateExpression( children[5], cm );
-      return new OpApplNode(OP_ite, sns, treeNode, cm);
-
-    case N_Case :
-      return processCase(treeNode, children, cm);
-
-    case N_DisjList:
-    case N_ConjList:
-      sns = new ExprNode[ children.length ];
-      for (int lvi = 0; lvi< sns.length; lvi++ ) {
-        sns[lvi] = generateExpression( children[lvi].heirs()[1], cm ); 
-      }
-      if ( treeNode.isKind(N_DisjList ) )
-        return new OpApplNode(OP_dl, sns, treeNode, cm);
-      else
-        return new OpApplNode(OP_cl, sns, treeNode, cm);
-
-    case N_RecordComponent : // really RcdSelect in the API
-      sns = new ExprNode[2];
-      sns[0] = generateExpression( children[0], cm );
-      sns[1] = new StringNode(children[2], false);
-      return new OpApplNode(OP_rs, sns, treeNode, cm);
-
-    case N_SetOfFcns:   /* [S -> T] */
-      sns = new ExprNode[2];
-      sns[0] = generateExpression( children[1], cm );
-      sns[1] = generateExpression( children[3], cm );
-
-      return new OpApplNode(OP_sof, sns, treeNode, cm);
-
-    case N_SubsetOf :
-      return processSubsetOf( treeNode, children, cm );
-
-    case N_SetOfAll :
-      return processSetOfAll( treeNode, children, cm );
-
-    case N_RcdConstructor:
-      return processRcdForms( OP_rc, treeNode, children, cm );
-
-    case N_SetOfRcds:
-      return processRcdForms( OP_sor, treeNode, children, cm );
-
-    case N_FcnConst:
-      return processFcnConst( treeNode, children, cm );
-
-    case N_ActionExpr:
-    case N_FairnessExpr:
-      return processAction( treeNode, children, cm );
-
-    case N_Except:
-      return processExcept( treeNode, children, cm );
-
-    case N_LetIn:
-      return processLetIn( treeNode, children, cm );
-
-    case N_Lambda:
-      errors.addError(
-        treeNode.getLocation(),
-        "LAMBDA expression used where an expression is required." );
-      return null;
-
-    case N_Label:
-      LabelNode ln = generateLabel(treeNode, cm);
-      if (ln.isAssumeProve && !allowLabeledAP) {
-        errors.addError(
-          treeNode.getLocation(),
-          "Labeled ASSUME/PROVE used where an expression is required." );
-       }
-      return ln ;
-
-    default:
-      errors.addError(treeNode.getLocation(),
-                      "Unsupported expression type `" + 
-                      treeNode.getImage() + "'.");
-      return null;
-
-    } // end switch
-
-  } // end generateExpression()
-
-/***************************************************************************
-* The following variables are used in the processing of ASSUME/PROVE       *
-* nodes, and the labels that lie within them.                              *
-***************************************************************************/
-  private int assumeProveDepth = 0 ;
-    /***********************************************************************
-    * The nesting depth of ASSUME/PROVEs within which the node corrently   *
-    * being processed lies.  In                                            *
-    *                                                                      *
-    *   THEOREM ASSUME P PROVE Q                                           *
-    *                                                                      *
-    * the nodes for P and Q are at depth 1 (not 0).                        *
-    ***********************************************************************/
-
-  private ThmOrAssumpDefNode currentGoal = null ;
-  private int currentGoalClause ;
-    /***********************************************************************
-    * currentGoal is the named theorem or proof-step node within           *
-    * which lies the current node that is being processed, or null if      *
-    * we're not inside such a step.  If currentGoal != null, and we're     *
-    * within an ASSUME/PROVE step (which is true iff assumeProveDepth >    *
-    * 0), then then currentGoalClause equals the clause of within which    *
-    * the node being processed lies.  (If we're processing Q in ASSUME P   *
-    * PROVE Q, then Q = 1.)                                                *
-    ***********************************************************************/
-
-  private static final int maxAPDepth = 100 ;
-    /***********************************************************************
-    * Maximum allowed nesting depth of ASSUME/PROVES. 100 ought to         *
-    * suffice.                                                             *
-    ***********************************************************************/
-
-  private boolean[] inScopeOfAPDecl = new boolean[maxAPDepth] ;
-    /***********************************************************************
-    * For all i \leq assumeProveDepth, inScopeOfAPDecl[i] = true iff the   *
-    * node currently being processed lies within a declaration made at     *
-    * assumeProveDepth = i.  Thus in                                       *
-    *                                                                      *
-    *   THEOREM ASSUME NEW x \in S,                                        *
-    *                  P,                                                  *
-    *                  ASSUME R , ACTION A PROVE T                         *
-    *           PROVE  Q                                                   *
-    *                                                                      *
-    * inScopeOfAPDecl[1] equals false when S is being processed and        *
-    * equals true when P, Q, R, and T are being processed.                 *
-    *                                                                      *
-    * inScopeOfAPDecl[2] equals false when R is being processed and true   *
-    * when T is being processed.                                           *
-    ***********************************************************************/
-
-
-  private boolean noLabelsAllowed() {
-    /***********************************************************************
-    * This returns true if we are inside an ASSUME/PROVE node where a      *
-    * label is not allowed because we are in the scope of a declaration    *
-    * from an inner ASSUME/PROVE.                                          *
-    ***********************************************************************/
-    for (int i = 2; i <= assumeProveDepth; i++) {
-      if (inScopeOfAPDecl[i]) { return true ; } ;
-      } ;
-    return false ;
-   }
-
-
-  private final boolean illegalLabelRef(LabelNode ln, SyntaxTreeNode stn) 
-    throws AbortException {
-    /***********************************************************************
-    * True iff we are currently in a point where a reference to this       *
-    * label is illegal because it lies inside an ASSUME/PROVE clause from  *
-    * outside the scope of a symbol it may contain.  See the comments in   *
-    * AssumeProveNode.java for an explanation of this method.              *
-    ***********************************************************************/
-    ThmOrAssumpDefNode goal = ln.goal ;
-    if (goal == null) { return false ; } ;
-    if (   (goal.getBody() == null)
-        || (goal.getBody().getKind() != AssumeProveKind)) {
-       errors.addAbort(
-         stn.getLocation(),
-         "Internal error: Expecting label to be in AssumeProveNode, " +
-         "but it's not.") ;
-      } ;
-    AssumeProveNode ap = (AssumeProveNode) goal.getBody() ;
-    return    ap.inScopeOfDecl[ln.goalClause]
-           && (goal.isSuffices() == ap.inProof) ;
-   } // illegalLabelRef
-
-  private final boolean illegalAPPosRef(AssumeProveNode ap, int pos) {
-    /***********************************************************************
-    * True iff it is illegal for the node currently being processed to     *
-    * access the clause number pos of AssumeProve node ap because it is    *
-    * not in the scope of symbols declared in previous ASSUME clauses of   *
-    * ap.  See the comments in AssumeProveNode.java for an explanation of  *
-    * this method.                                                         *
-    ***********************************************************************/
-    return    ap.inScopeOfDecl[pos-1]
-           && (   (ap.getGoal() == null)
-               || (ap.getGoal().isSuffices() == ap.inProof)) ;
-    }
-  
+		SymbolTable st = null;
+		if (declare) {
+			st = symbolTable;
+		}
+		;
+
+		/**********************************************************************
+		 * Changed us to Operators.resolveSynonym(us) in the following so * constant
+		 * declarations work with any synonym for an operator--e.g., * with both (+) and
+		 * \oplus. *
+		 **********************************************************************/
+		return new OpDeclNode(Operators.resolveSynonym(us), declKind, declLevel, arity, cm, st, treeNode);
+	}
+
+	private final void processParameters(TreeNode treeNodes[], ModuleNode cm) {
+		for (int lvi = 1; lvi < treeNodes.length; lvi += 2) {
+			if (treeNodes[lvi].getUS() == S_at) {
+				errors.addError(treeNodes[lvi].getLocation(), "Attempted to declare '@' as a constant.");
+			}
+			;
+			OpDeclNode odn = buildParameter(treeNodes[lvi], ConstantDeclKind, ConstantLevel, cm, true);
+			/*******************************************************************
+			 * We throw away the OpDeclNode because, when it was constructed, * it was added
+			 * to symbolTable, whose top context should be the * top-level context of the
+			 * module. *
+			 *******************************************************************/
+		}
+	}
+
+	/**
+	 * Processes the LHS of an operator definition, in several ways, depending on
+	 * whether it is a prefix, infix, postfix, or a parameter-free or parameter-ful
+	 * function-notation operator. Also creates context entries for the operator and
+	 * its parameters.
+	 */
+	/************************************************************************
+	 * Processes a syntactic node of type N_OperatorDefinition. It is * called by
+	 * generateModule (with defs = null) when processing an * outer-level definition
+	 * and by processLetIn (with defs /= null) when * processing a LET definition.
+	 * It creates an OpDef node and puts it * in ModuleNode cm's set of definitions.
+	 * *
+	 ************************************************************************/
+	private final void processOperator(TreeNode treeNode, Vector defs, ModuleNode cm) throws AbortException {
+		TreeNode syntaxTreeNode = treeNode;
+		UniqueString name = null;
+		int arity = 0;
+		boolean local = syntaxTreeNode.zero() != null;
+		TreeNode[] children = syntaxTreeNode.one();
+		TreeNode[] ss = children[0].heirs();
+		FormalParamNode[] params = null;
+		Context ctxt = new Context(moduleTable, errors);
+		boolean isRecursive = false;
+		/*********************************************************************
+		 * Will be set to true if this operator was declared in a RECURSIVE * statement.
+		 * *
+		 *********************************************************************/
+		// New context needed because parameter symbols may have to be
+		// added if the operator being defined takes params
+		symbolTable.pushContext(ctxt);
+
+		// If the operator is an identifier (possibly with params), as
+		// opposed to a prefix, infix, or postfix symbol
+		if (children[0].isKind(N_IdentLHS)) {
+			if (ss.length > 2) {
+				// If the operator has arguments
+				params = new FormalParamNode[(ss.length - 2) / 2];
+				for (int lvi = 2; lvi < ss.length; lvi += 2) {
+					TreeNode sss[] = ss[lvi].heirs();
+					if (ss[lvi].isKind(N_IdentDecl)) {
+						// parameter is simple identifier
+						name = sss[0].getUS();
+						arity = (sss.length - 1) / 2;
+					} else if (ss[lvi].isKind(N_InfixDecl)) {
+						// parameter is infix operator
+						// Call of Operators.resolveSynonym added by LL on 27 Mar 2013
+						name = sss[1].getUS();
+
+						// Following added by LL on 27 Mar 2013
+						// to handle parameters like _(+)_
+						name = Operators.resolveSynonym(name);
+
+						arity = 2;
+					} else if (ss[lvi].isKind(N_PrefixDecl)) {
+						// parameter is prefix operator
+						name = sss[0].getUS();
+						arity = 1;
+					} else {
+						// parameter must be postfix operator
+						// SZ Jul 13, 2009: added the message to the assert
+						if (!ss[lvi].isKind(N_PostfixDecl)) {
+							throw new WrongInvocationException(MP.getMessage(EC.TLC_PARAMETER_MUST_BE_POSTFIX));
+						}
+						name = sss[1].getUS();
+						arity = 1;
+					}
+					params[(lvi - 2) / 2] = new FormalParamNode(name, arity, ss[lvi], symbolTable, cm);
+				}
+			} else {
+				// The operator has no arguments
+				params = new FormalParamNode[0];
+			}
+			name = ss[0].getUS();
+		} else if (children[0].isKind(N_PrefixLHS)) { // operator is a prefix operator
+			// Process the parameter
+			params = new FormalParamNode[1];
+			params[0] = new FormalParamNode(ss[1].getUS(), 0, ss[1], symbolTable, cm);
+
+			// Process the operator
+			name = Operators.resolveSynonym(ss[0].getUS());
+		} else if (children[0].isKind(N_InfixLHS)) { // operator is an infix operator
+			params = new FormalParamNode[2];
+			// Process the first param
+			params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm);
+
+			// Process the second param
+			params[1] = new FormalParamNode(ss[2].getUS(), 0, ss[2], symbolTable, cm);
+
+			// Process the operator
+			name = Operators.resolveSynonym(ss[1].getUS());
+		} else if (children[0].isKind(N_PostfixLHS)) { // operator is a postfix operator
+			// Process the parameter
+			params = new FormalParamNode[1];
+			params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm);
+
+			// Process the operator
+			name = Operators.resolveSynonym(ss[1].getUS());
+		} else {
+			errors.addError(children[0].getLocation(), "Unknown parameter declaration `" + children[0].getUS() + "'.");
+		}
+
+		/***********************************************************************
+		 * The following code added by LL on 1 April 2007 to handle * recursively
+		 * defined operators. *
+		 ***********************************************************************/
+		OpDefNode odn = null;
+		/***********************************************************************
+		 * Check if this operator has already been declared or defined. If * so, if it
+		 * was declared in a RECURSIVE statement at this let/in * level and not yet
+		 * defined, then set the OpDefNode's fields * appropriately, otherwise report an
+		 * error. *
+		 ***********************************************************************/
+		SymbolNode symbolNode = symbolTable.resolveSymbol(name);
+
+		if (symbolNode != null) {
+			/*********************************************************************
+			 * The symbol has already been defined or declared. Check if it was * declared
+			 * in a RECURSIVE statement. *
+			 *********************************************************************/
+			if (symbolNode instanceof OpDefNode) {
+				odn = (OpDefNode) symbolNode;
+			}
+			;
+			if ((odn != null) && odn.inRecursive && (!odn.isDefined)) {
+				if (odn.letInLevel == curLevel) {
+					isRecursive = true;
+
+					/*****************************************************************
+					 * Check that the parameters of the definition match the ones in * the RECURSIVE
+					 * declaration. *
+					 *****************************************************************/
+					boolean paramsMatch = (odn.getParams().length == params.length);
+					if (paramsMatch) {
+						for (int i = 0; i < params.length; i++) {
+							paramsMatch = (params[i].getArity() == 0);
+						}
+						;
+					}
+					; // if
+					if (!paramsMatch) {
+						errors.addError(treeNode.getLocation(), "Definition of " + odn.getName()
+								+ " has different arity than " + "its RECURSIVE declaration.");
+					}
+					;
+
+					odn.setParams(params);
+					/***************************************************************
+					 * Here's where the params field of the OpDefNode is set for an * operator
+					 * declared in a RECURSIVE statement. *
+					 ***************************************************************/
+				} // if (odn.letInLevel == curLevel)
+				else {
+					errors.addError(treeNode.getLocation(),
+							"Recursive operator " + name.toString() + " defined at wrong LET/IN level.");
+					odn = null;
+				} // else
+			} // if (odn != null) ...
+			else {
+				errors.addError(treeNode.getLocation(),
+						"Operator " + name.toString() + " already defined or declared.");
+			}
+		} // if (symbolNode != null)
+
+		pushLS();
+		/*********************************************************************
+		 * Start fresh processing of labels. *
+		 *********************************************************************/
+		// Generate expression that is the body of the operator
+		ExprNode exp = generateExpression(children[2], cm);
+
+		// Restore old context, popping off the context containing the parameters
+		symbolTable.popContext();
+
+		if (isRecursive) {
+			endOpDefNode(odn, exp, syntaxTreeNode);
+		} else {
+			// Create OpDefNode using symbolTable whose top context contains the
+			// parameter symbols
+			/********************************************************************
+			 * This comment appears to be incorrect, because it looks like the * parameter
+			 * symbols were just popped off symbolTable. *
+			 ********************************************************************/
+			odn = new OpDefNode(name, UserDefinedOpKind, params, local, exp, cm, symbolTable, syntaxTreeNode, true,
+					null);
+			symbolNode = odn;
+			/*******************************************************************
+			 * Keeping symbolNode for historical reasons--I'm too lazy to * rearrange the
+			 * code to get rid of the redundant identifier. *
+			 *******************************************************************/
+
+			setOpDefNodeRecursionFields(odn, cm);
+
+		} // else
+
+		Hashtable ht = popLabelNodeSet();
+		/*********************************************************************
+		 * Succeed or fail, we must execute popLabelNodeSet to match the * previous
+		 * pushLS. *
+		 *********************************************************************/
+		if (odn != null) {
+			odn.setLabels(ht);
+		}
+		;
+		/*********************************************************************
+		 * If there was no error, then odn is an OpDefNode and we must set * it labels
+		 * field. *
+		 *********************************************************************/
+		cm.appendDef(symbolNode);
+		/*******************************************************************
+		 * Add the new OpDefNode to the current module's set of * definitions. *
+		 *******************************************************************/
+		// defs is non-null iff this definition is in the Let part of a
+		// Let-In expression
+		if (defs != null)
+			defs.addElement(symbolNode);
+	} // processOperator
+
+	private final void processQuantBoundArgs(TreeNode[] treeNodeA, // node whose children include
+																	// the bounded quants
+			int offset, // nodes to skip to get to first
+						// "quantifier"
+			FormalParamNode[][] odna, boolean[] bt, // set to true if arg is a tuple;
+													// otherwise false
+			ExprNode[] ena, ModuleNode cm)
+			/***********************************************************************
+			 * Despite the incoherent and/or incorrect comments, here's what I * think is
+			 * going on: * * - odna, ena, and bt have the same length N. * * - treeNodeA is
+			 * an array of nodes containing a subsequence of * N_QuantBound nodes of length
+			 * N that starts at * treeNodeA[offset]. * * - odna, ena, and bt are set to the
+			 * arguments for the OpApplNode * constructor that produces an OpApplNode for
+			 * the operator * having this sequence of N_QuantBound nodes. *
+			 ***********************************************************************/
+
+			throws AbortException {
+		// For each quantifier, evaluate the bound in the context of the
+		// current symbol table, i.e. before the quantified variables are
+		// added to the new context, since the quantified vars may not
+		// appear in the bounds.
+		for (int lvi = 0; lvi < bt.length; lvi++) {
+			// Make ss point to each N_QuantBound node in turn.
+			TreeNode[] ss = treeNodeA[offset + 2 * lvi].heirs();
+			// the last element in ss is expression for the quantifier bound
+			ena[lvi] = generateExpression(ss[ss.length - 1], cm);
+		}
+
+		// Now for each quantifier, process the variable names
+		for (int lvi = 0; lvi < bt.length; lvi++) {
+			TreeNode treeNode = treeNodeA[offset + 2 * lvi];
+
+			// The variable bound to the "quantifier"
+			TreeNode[] ss = treeNode.heirs();
+
+			if (ss[0].isKind(N_IdentifierTuple)) { // three elements only, go into node
+				bt[lvi] = true;
+				TreeNode[] sss = ss[0].heirs();
+				odna[lvi] = new FormalParamNode[sss.length / 2];
+
+				for (int lvj = 0; lvj < sss.length / 2; lvj++) {
+					odna[lvi][lvj] = new FormalParamNode(sss[2 * lvj + 1].getUS(), 0, sss[2 * lvj + 1], symbolTable,
+							cm);
+				}
+			} else { // gotta be N_Identifier
+				bt[lvi] = false;
+				odna[lvi] = new FormalParamNode[(ss.length - 1) / 2];
+
+				for (int lvj = 0; lvj < (ss.length - 1) / 2; lvj++) {
+					odna[lvi][lvj] = new FormalParamNode(ss[2 * lvj].getUS(), 0, ss[2 * lvj], symbolTable, cm);
+				}
+			}
+		}
+	}
+
+	// Process a function definition
+	private final void processFunction(TreeNode treeNode, Vector defs, ModuleNode cm) throws AbortException {
+		TreeNode syntaxTreeNode = treeNode;
+		boolean local = syntaxTreeNode.zero() != null;
+		TreeNode[] ss = syntaxTreeNode.one();
+		// Heirs to N_FunctionDefinition node
+		int ql = (ss.length - 4) / 2;
+		// number of QuantBound's
+		OpApplNode oan;
+		OpDefNode odn = null;
+		FormalParamNode[][] quants = new FormalParamNode[ql][0];
+		FormalParamNode[] fcnDeclForRecursion = new FormalParamNode[1];
+		/*********************************************************************
+		 * This is an array of length 1 instead of just an OpDeclNode * because it is
+		 * needed in such an array to pass as an argument to * new OpApplNode. *
+		 *********************************************************************/
+		boolean[] tuples = new boolean[ql];
+		ExprNode[] domains = new ExprNode[ql];
+		ExprNode[] lhs = new ExprNode[1];
+		Context newContext = new Context(moduleTable, errors);
+		boolean isRecursive = false;
+		/*********************************************************************
+		 * Will be set to true if this operator was declared in a RECURSIVE * statement.
+		 * *
+		 *********************************************************************/
+
+		// Fill arrays with quantifier-related information; must be called
+		// in scope of *new* context, since it adds parameter symbols to
+		// the context.
+		symbolTable.pushContext(newContext);
+		processQuantBoundArgs(ss, 2, quants, tuples, domains, cm);
+
+		UniqueString name = ss[0].getUS();
+		SymbolNode symbolNode = symbolTable.resolveSymbol(name);
+
+		// This is in anticipation of the possibility that the function is
+		// recursive. We are creating a new bound symbol of the same name
+		// as the function to stand in the body for the function. The
+		// arity of the bound symbol is 0 because a function formally has
+		// arity 0 as an operator, even if it has several arguments as a
+		// function.
+		/***********************************************************************
+		 * If the function name is already defined (which should happen only * if it was
+		 * declared in a RECURSIVE statement, then there is no need * to add this
+		 * OpDeclNode to the symbol table. As near as I can tell * (which might not be
+		 * near enough), this OpDeclNode's entry in the * symbol table is needed only so
+		 * the name will be declared in case * of a recursive call. The check for
+		 * whether a function call in the * body actually is recursive is made by
+		 * calling the recursionCheck * method of the Function subclass to see if the
+		 * OpApplNode is in the * Function object's funcStack. *
+		 ***********************************************************************/
+		SymbolTable st = null;
+		if (symbolNode == null) {
+			st = symbolTable;
+		}
+		;
+		fcnDeclForRecursion[0] = new FormalParamNode(name, 0, treeNode, st, cm);
+		symbolTable.popContext();
+
+		// Create OpApplNode to hold the function body; type is assumed to
+		// be non-recursive function (OP_nrfs); if the body is recursive,
+		// this will be discovered during generateExpression() for the
+		// body
+		oan = new OpApplNode(OP_nrfs, fcnDeclForRecursion, new ExprNode[0], quants, tuples, domains, syntaxTreeNode,
+				cm);
+		// constructor 5
+
+		/***********************************************************************
+		 * The following code added by LL on 19 April 2007 to handle * recursively
+		 * defined operators. *
+		 ***********************************************************************/
+		if (symbolNode == null) {
+			odn = new OpDefNode(ss[0].getUS(), UserDefinedOpKind, nullParam, local, oan, cm, symbolTable,
+					syntaxTreeNode, true, null);
+			setOpDefNodeRecursionFields(odn, cm);
+		} else {
+			/*********************************************************************
+			 * The symbol has already been defined or declared. Check if it was * declared
+			 * in a RECURSIVE statement. *
+			 *********************************************************************/
+			if (symbolNode instanceof OpDefNode) {
+				odn = (OpDefNode) symbolNode;
+			}
+			;
+			if ((odn != null) && odn.inRecursive && (!odn.isDefined)) {
+				if (odn.letInLevel == curLevel) {
+					isRecursive = true;
+					/*****************************************************************
+					 * Check that the RECURSIVE declaration had no paramters. *
+					 *****************************************************************/
+					if (odn.getArity() == 0) {
+						endOpDefNode(odn, oan, syntaxTreeNode);
+					} else {
+						/*************************************************************
+						 * RECURSIVE declaration had parameters. *
+						 *************************************************************/
+						errors.addError(treeNode.getLocation(), "Function " + odn.getName()
+								+ " has operator arguments in " + "its RECURSIVE declaration.");
+					}
+					;
+				} // if (odn.letInLevel == curLevel)
+				else {
+					errors.addError(treeNode.getLocation(),
+							"Recursive function " + name.toString() + " defined at wrong LET/IN level.");
+					odn = null;
+				} // else
+			} // if (odn != null) ...
+			else {
+				errors.addError(treeNode.getLocation(),
+						"Function name `" + name.toString() + "' already defined or declared.");
+			}
+		} // else (symbolNode != null)
+
+		// Create OpDefNode to hold the function definition, including
+		// reference to the function body ("oan")
+		if (odn != null) {
+			/*********************************************************************
+			 * There was no error, so the OpDefNode was created. *
+			 *********************************************************************/
+			cm.appendDef(odn);
+
+			// defs is non-null iff this function definition is in the Let
+			// part of a Let-In expression. If so, then we have to accumulate
+			// these defs in a vector.
+			if (defs != null)
+				defs.addElement(odn);
+
+			// Function body must be processed in the scope of the new
+			// context, including the parms
+			symbolTable.pushContext(newContext);
+
+		}
+		; // if (odn != null)
+
+		// Keep stack of nested function defs to enable detection of recursion
+		functions.push(ss[0].getUS(), oan);
+
+		// Create semantic graph function body in the inner context including parameters
+		pushLS();
+		pushFormalParams(flattenParams(quants));
+		/*********************************************************************
+		 * Push a new empty label set onto LS, and push the function * definition's
+		 * formal parameters onto Last(LS).paramSeq for * processing the function body.
+		 * *
+		 *********************************************************************/
+		lhs[0] = generateExpression(ss[ss.length - 1], cm);
+		popFormalParams();
+		/*********************************************************************
+		 * Pop the formal params from Last(LS).paramSeq, which should now be * empty.
+		 * This isn't really necessary, since we're going to pop the * LS stack below,
+		 * but I hate to have a push without a pop. *
+		 *********************************************************************/
+		Hashtable ht = popLabelNodeSet();
+		/*********************************************************************
+		 * The matching pop for the pushLS above. *
+		 *********************************************************************/
+		if (odn != null) {
+			odn.setLabels(ht);
+		}
+		;
+		/*********************************************************************
+		 * If there was no error and we created an OpDefNode, then set its * label set.
+		 * *
+		 *********************************************************************/
+		functions.pop();
+
+		oan.setArgs(lhs);
+		/***********************************************************************
+		 * Test for odn != null added 2 Jul 2009 to avoid bug that caused * popping of
+		 * empty symbol table when the function name was * already declared to be a
+		 * variable. *
+		 ***********************************************************************/
+		if (odn != null) {
+			// Restore old context
+			symbolTable.popContext();
+		}
+
+		// if the function body turned out to be non-recursive, then we
+		// should null-out the fcnDefForRecursion ref put in place above,
+		// since it is unnecessary for a nonrecursive func
+		if (oan.getOperator().getName() == OP_nrfs) {
+			oan.makeNonRecursive();
+		}
+	} // end processFunction()
+
+	private final ExprNode processLetIn(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException {
+		TreeNode[] syntaxTreeNode = children[1].heirs(); // extract LetDefinitions
+		Vector defVec = new Vector(4);
+		Vector instVec = new Vector(1);
+
+		Context letCtxt = new Context(moduleTable, errors);
+		symbolTable.pushContext(letCtxt);
+		/*********************************************************************
+		 * Create a new sub-Context for the IN expression, containing the * LET
+		 * definitions *
+		 *********************************************************************/
+
+		/***********************************************************************
+		 * Increment curLevel. *
+		 ***********************************************************************/
+		if (curLevel < MaxLetInLevel) {
+			curLevel++;
+		} else {
+			errors.addAbort(treeNode.getLocation(), "LETs nested more than " + MaxLetInLevel + " deep.");
+		}
+		;
+		unresolvedCnt[curLevel] = 0;
+		/*********************************************************************
+		 * Will not have been initialized if we haven't yet reached this * LET/IN
+		 * nesting depth. *
+		 *********************************************************************/
+
+		for (int lvi = 0; lvi < syntaxTreeNode.length; lvi++) {
+			/*********************************************************************
+			 * Note: LL changed from an "if" and a sequence of "elseif"s to a * switch
+			 * statement on 7 Apr 2007 when adding the N_Recursive case. *
+			 *********************************************************************/
+			switch (syntaxTreeNode[lvi].getKind()) {
+			case N_OperatorDefinition:
+				processOperator(syntaxTreeNode[lvi], defVec, cm);
+				break;
+
+			case N_FunctionDefinition:
+				processFunction(syntaxTreeNode[lvi], defVec, cm);
+				break;
+
+			case N_ModuleDefinition:
+				processModuleDefinition(syntaxTreeNode[lvi], defVec, instVec, cm);
+				break;
+
+			case N_Recursive:
+				processRecursive(syntaxTreeNode[lvi], cm);
+				break;
+
+			default:
+				errors.addAbort(syntaxTreeNode[lvi].getLocation(),
+						"Internal error: found unexpected syntax " + "tree node in LET.");
+			} // switch
+		} // for
+
+		checkForUndefinedRecursiveOps(cm);
+
+		/***********************************************************************
+		 * Decrement curLevel. *
+		 ***********************************************************************/
+		curLevel--;
+		if (curLevel < 0) {
+			curLevel = 0;
+		}
+		;
+
+		ExprNode body = generateExpression(children[3], cm);
+
+		/***********************************************************************
+		 * Convert from Vector to array of SymbolNode, whose elements may be * OpDefNode
+		 * or ThmOrAssumpDefNode objects. *
+		 ***********************************************************************/
+		SymbolNode[] opDefs = new SymbolNode[defVec.size()];
+		for (int i = 0; i < opDefs.length; i++) {
+			opDefs[i] = (SymbolNode) defVec.elementAt(i);
+		}
+
+		InstanceNode[] insts = new InstanceNode[instVec.size()];
+		for (int i = 0; i < insts.length; i++) {
+			insts[i] = (InstanceNode) instVec.elementAt(i);
+		}
+		LetInNode letIn = new LetInNode(treeNode, opDefs, insts, body, letCtxt);
+		symbolTable.popContext();
+		return letIn;
+	} // end processLetIn
+
+	private final ExprNode generateExpression(TreeNode treeNode, ModuleNode cm) throws AbortException {
+		/***********************************************************************
+		 * Must return an ExprNode that represents an expression, and not a * labeled
+		 * ASSUME/PROVE *
+		 ***********************************************************************/
+		return generateExpressionOrLAP(treeNode, cm, false);
+	}
+
+	private final ExprNode generateExpressionOrLAP(TreeNode treeNode, ModuleNode cm, boolean allowLabeledAP)
+			/***********************************************************************
+			 * Can return a LabelNode labeling an ASSUME/PROVE iff allowLabeledAP * = true.
+			 * Otherwise, it can return only an ExprNode that represents * an expression. *
+			 ***********************************************************************/
+			throws AbortException {
+		TreeNode[] children = treeNode.heirs();
+		TreeNode[] ss = null; // grandchildren
+		SymbolNode opn = null;
+		TreeNode op = null;
+		GenID genID;
+		ExprOrOpArgNode[] sns; // a ExprNode list used for arguments
+
+		switch (treeNode.getKind()) {
+
+		case N_Real:
+			return new DecimalNode(children[0].getImage(), children[2].getImage(), treeNode);
+
+		case N_Number:
+			return new NumeralNode(children[0].getImage(), treeNode);
+
+		case N_String:
+			return new StringNode(treeNode, true);
+
+		case N_ParenExpr:
+			return generateExpression(children[1], cm);
+
+		case N_InfixExpr:
+			genID = generateGenID(children[1], cm);
+
+			sns = new ExprOrOpArgNode[2];
+			opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
+			if (opn == null) {
+				errors.addError(treeNode.getLocation(),
+						"Couldn't resolve infix operator symbol `" + genID.getCompoundIDUS() + "'.");
+				return null;
+			}
+
+			sns[0] = generateExpression(children[0], cm);
+			sns[1] = generateExpression(children[2], cm);
+			return new OpApplNode(opn, sns, treeNode, cm);
+
+		case N_PrefixExpr:
+			// 1 get gen operator node
+			ss = children[0].heirs();
+
+			// 2 get rightmost part of the possibly compound Op itself;
+			op = ss[1];
+			genID = generateGenID(children[0], cm, true);
+			sns = new ExprOrOpArgNode[1];
+			opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
+
+			if (opn == null) {
+				errors.addError(treeNode.getLocation(),
+						"Couldn't resolve prefix operator symbol `" + genID.getCompoundIDUS() + "'.");
+				return null;
+			}
+
+			sns[0] = generateExpression(children[1], cm);
+			return new OpApplNode(opn, sns, treeNode, cm); // constructor 2
+
+		case N_PostfixExpr:
+			genID = generateGenID(children[1], cm);
+
+			sns = new ExprNode[1];
+			opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
+			if (opn == null) {
+				errors.addError(treeNode.getLocation(),
+						"Couldn't resolve postfix " + "operator symbol `" + genID.getCompoundIDUS() + "'.");
+				return null;
+			}
+
+			sns[0] = generateExpression(children[0], cm);
+			return new OpApplNode(opn, sns, treeNode, cm); // constructor 2
+
+		case N_Times: // or cartesian product
+			sns = new ExprNode[(children.length + 1) / 2];
+
+			for (int lvi = 0; lvi < sns.length; lvi++) {
+				sns[lvi] = generateExpression(children[2 * lvi], cm);
+			}
+			return new OpApplNode(OP_cp, sns, treeNode, cm); // constructor 3
+
+		case N_SetEnumerate:
+			int size = (children.length - 1) / 2;
+			sns = new ExprNode[size];
+			for (int lvi = 0; lvi < size; lvi++) {
+				sns[lvi] = generateExpression(children[2 * lvi + 1], cm);
+			}
+			return new OpApplNode(OP_se, sns, treeNode, cm); // constructor 3
+
+		case N_GeneralId:
+			// This is a zero-ary operator; it should show in the syntax
+			// tree as an OpApp, but it does not. Hence, an OpApplication
+			// node with zero arguments must be constructed for it
+			// if we get here, the GeneralID really is an OpApplication with 0
+			// primary arguments, but with any number of prefix arguments
+
+			// process the generalized identifier, complete with its
+			// embedded argument lists (if any)
+
+			/*********************************************************************
+			 * If the N_GeneralId represents the identifier "@", then check for * errors and
+			 * return an AtNode if none. *
+			 *********************************************************************/
+			SyntaxTreeNode sTreeNode = (SyntaxTreeNode) treeNode;
+			if ((sTreeNode.heirs()[1].getKind() == IDENTIFIER) && (sTreeNode.heirs()[1].getUS() == AtUS)
+					&& (((SyntaxTreeNode) sTreeNode.heirs()[0]).heirs().length == 0)) {
+				if (excStack.empty() || excSpecStack.empty()) {
+					// if either stack is empty, then @ used in improper EXCEPT context
+					errors.addError(sTreeNode.getLocation(), "@ used where its meaning is not defined.");
+					return nullOAN;
+				} else {
+					// So, the context for @ is proper, then construct the
+					// AtNode and return it
+					return new AtNode((OpApplNode) excStack.peek(), (OpApplNode) excSpecStack.peek());
+				}
+			}
+			;
+
+			ExprNode retVal = (ExprNode) selectorToNode(genIdToSelector(sTreeNode), 0, false, false, cm);
+
+			/*********************************************************************
+			 * A function definition generates an OpDefNode whose body is an * OpApplNode
+			 * whose operator is either $RecursiveFcnSpec or * $NonRecursiveFcnSpec, the
+			 * latter when the definition is * recursive. However, in SANY1 the definition *
+			 * * f[x \in S] == ... * * was found to be recursive only if a subexpression
+			 * f[...] occurs * in the body. It was not marked as recursive if another
+			 * instance * of f occurs, such as Foo(f). To fix this, we need to check if *
+			 * this genID is actually the Identifier of a function currently * being
+			 * defined. The following call to functions.recursionCheck * does that. *
+			 *********************************************************************/
+			if (retVal.getKind() == OpApplKind) {
+				functions.recursionCheck(((OpApplNode) retVal).getOperator().getName());
+			}
+			;
+
+			return retVal;
+		/**************************
+		 * old version genID = generateGenID(treeNode, cm);
+		 * 
+		 * // if the symbol is "@" then check for errors and // return an AtNode if
+		 * none. if (genID.getCompoundIDUS() == S_at) { if (excStack.empty() ||
+		 * excSpecStack.empty()) { // if either stack is empty, then @ used in improper
+		 * EXCEPT context errors.addError(treeNode.getLocation(), "@ used where its
+		 * meaning is not defined."); } else { // So, the context for @ is proper, then
+		 * construct the // AtNode and return it return new
+		 * AtNode((OpApplNode)excStack.peek(), (OpApplNode)excSpecStack.peek()); } }
+		 * else if (genID.getFullyQualifiedOp() == null || genID.getArgs() == null) { //
+		 * If it is not an "@" symbol, it may still be an unresolved symbol return
+		 * nullOAN; } else if (genID.getFullyQualifiedOp().getKind() == ModuleKind) {
+		 * errors.addError( treeNode.getLocation(), "Module name '" +
+		 * genID.getFullyQualifiedOp().getName() + "' used as operator."); return
+		 * nullOAN; } else { // but if there are no problems then we are in a situation
+		 * in // which return the appropriate OpApplNode an N_GenID node in // the
+		 * syntax tree really stands for an OpApplication
+		 * 
+		 * // ********************************************************************* // *
+		 * Modified on 20 Apr 2007 by LL to correct the following bug. * // * * // * A
+		 * function definition generates an OpDefNode whose body is an * // * OpApplNode
+		 * whose operator is either $RecursiveFcnSpec or * // * $NonRecursiveFcnSpec,
+		 * the latter when the definition is * // * recursive. However, the definition *
+		 * // * * // * f[x \in S] == ... * // * * // * was found to be recursive only if
+		 * a subexpression f[...] occurs * // * in the body. It was not marked as
+		 * recursive if another instance * // * of f occurs, such as Foo(f). To fix
+		 * this, we need to check if * // * this genID is actually the Identifier of a
+		 * function currently * // * being defined. The call to functions.recursionCheck
+		 * was added * // * here to do that. * //
+		 * *********************************************************************
+		 * SymbolNode symNode = genID.getFullyQualifiedOp() ;
+		 * 
+		 * if (symNode.getKind() == ThmOrAssumpDefKind) {
+		 * errors.addError(treeNode.getLocation(), "Theorem or Assumption name used as
+		 * expression.") ; }; OpApplNode retVal = // return new OpApplNode(symNode,
+		 * genID.getArgs(), treeNode, cm); functions.recursionCheck(symNode.getName());
+		 * return retVal ; }
+		 ******************************* 
+		 * old version
+		 ******************/
+		case N_OpApplication:
+			// for an operator with arguments
+			// Note: in neither case can this be an operator passed as an argument;
+			// the opAppl argument forces the return of an Operator application
+			// operators passed as arguments generate OpArg nodes, and that
+			// can happen only in certain contexts, not in every context where an
+			// expression can occur, which is the context we are in here
+
+			SyntaxTreeNode genIdNode = (SyntaxTreeNode) treeNode.heirs()[0];
+			SyntaxTreeNode opApplNode = (SyntaxTreeNode) treeNode.heirs()[1];
+
+			/*********************************************************************
+			 * First a check. It appears that the children of an OpApplication * node must
+			 * be a GeneralId node and an OpArgs node, but let's be * sure. *
+			 *********************************************************************/
+			if ((genIdNode.getKind() != N_GeneralId) || (opApplNode.getKind() != N_OpArgs)) {
+				errors.addAbort(treeNode.getLocation(), "Internal error: OpAppl node with unexpected children.", true);
+			}
+			;
+
+			Selector sel = genIdToSelector(genIdNode);
+			sel.args[sel.args.length - 1] = opApplNode;
+			sel.selSTN = (SyntaxTreeNode) treeNode;
+			/*******************************************************************
+			 * For error reporting, make the syntax tree node of the selector * include both
+			 * the general ID node and its argument. *
+			 *******************************************************************/
+			return (ExprNode) selectorToNode(sel, 0, false, false, cm);
+
+		/*****************
+		 * old version ********************* return generateOpAppl(treeNode, cm);
+		 ************************/
+
+		case N_Tuple:
+			size = (children.length - 1) / 2;
+			sns = new ExprNode[size];
+			for (int lvi = 0; lvi < size; lvi++) {
+				sns[lvi] = generateExpression(children[2 * lvi + 1], cm);
+			}
+			return new OpApplNode(OP_tup, sns, treeNode, cm); // Constructor 3
+
+		case N_FcnAppl: // Apparent function application
+			// Number of arguments to the apparent func app
+			int numArgs = (children.length - 2) / 2;
+
+			// Function appl involves two semantic nodes: 1 for function,
+			// and 1 for arg or args tuple.
+			sns = new ExprNode[2];
+
+			// Generate expression tree for the function itself
+			sns[0] = generateExpression(children[0], cm);
+
+			if (sns[0] == null) {
+				return null;
+			}
+			;
+			/*******************************************************************
+			 * sns[0] can be null if the parsing the function generates an * error (which is
+			 * the case if the function is missing). * Added by LL on 29 Feb 2008 *
+			 *******************************************************************/
+
+			// If the function is an OpApplNode (and could it be otherwise?)
+			if (sns[0].getKind() == OpApplKind) {
+				// Note if this is a recursive function, and change the top level
+				functions.recursionCheck(((OpApplNode) sns[0]).getOperator().getName());
+			}
+
+			// We next check that the number of arguments to a user-defined
+			// function is correct, if possible.
+
+			// Retrieve the expression that represents the function being
+			// applied, i.e. the 1st arg to $FcnApply
+			ExprOrOpArgNode fcn = sns[0];
+
+			// The entire next conditional is for one purpose: to make sure
+			// that a function symbol is applied to the right number of
+			// arguments, when it is possible to do that during semantic
+			// analysis. This means that if a function is declared with,
+			// say, 3 parameters, e.g. "f[a,b,c] == {a,b,c}", then it is
+			// never used with 2 or 4 arguments. However, it can appear
+			// with zero arguments as the expression "f" (not as "f[]"), and
+			// it can appear with one argument, as in "f[e]", because e
+			// might be a 3-tuple value. (Whether it always is or not
+			// cannot be determined at the time of semantic analysis.)
+			// Furthermore a function declared with exactly one parameter
+			// can appear with any number of argument expressions because,
+			// e.g. f[1,2,3,4] is considered just an alternate way of
+			// writing f[<<1,2,3,4>>], so there is really just one argument
+			// value.
+
+			// If it is an OpApplNode (as opposed to, say, an OpDeclNode)
+			if (fcn instanceof OpApplNode) {
+				// Retrieve the function being applied
+				SymbolNode funcOperator = ((OpApplNode) fcn).getOperator();
+
+				// If the function being applied is a user-defined function
+				// (as opposed to OpDeclNode, FormalParamNode, builtin
+				// operator, or expression)
+				if (funcOperator instanceof OpDefNode && funcOperator.getKind() == UserDefinedOpKind) {
+					// Retrieve the function body expression
+					ExprOrOpArgNode funcBody = ((OpDefNode) funcOperator).getBody();
+
+					// if the function body is an OpApplNode (as opposed to,
+					// say, NumeralNode, DecimalNode, etc.)
+					if (funcBody instanceof OpApplNode && (((OpApplNode) funcBody).getOperator().getName() == OP_nrfs
+							|| ((OpApplNode) funcBody).getOperator().getName() == OP_rfs)) {
+
+						// find out how many arguments it is SUPPOSED to have
+						int numParms = ((OpApplNode) funcBody).getNumberOfBoundedBoundSymbols();
+
+						// If the function appears with numArgs >= 2 argument
+						// expressions, it must be declared with exactly numArgs
+						// parameters; and a function with numParms parameters in
+						// its definition should be applied to exactly numParms
+						// expressions, or 1 expression (representing arguments in
+						// tuple form), or 0 expressions (representing the
+						// function itself). Note: one cannot define a function
+						// with 0 arguments in TLA+.
+						if (numArgs >= 2 && numParms != numArgs) {
+							errors.addError(treeNode.getLocation(),
+									"Function '" + ((OpApplNode) sns[0]).getOperator().getName() + "' is defined with "
+											+ numParms + " parameters, but is applied to " + numArgs + " arguments.");
+							return nullOAN;
+						} // end if
+					} // end if
+				} // end if
+			} // end if
+
+			// Assert.check(numArgs > 0);
+			if (numArgs == 1) {
+				sns[1] = generateExpression(children[2], cm);
+			} else {
+				// If there is more than one arg we have to create a tuple for the arguments.
+				ExprOrOpArgNode[] exprs = new ExprNode[numArgs]; // One for each of the arguments
+
+				// For each argument...
+				for (int lvi = 0; lvi < numArgs; lvi++) {
+					// Create the expression for that argument
+					exprs[lvi] = generateExpression(children[2 + 2 * lvi], cm);
+				}
+				// Create an application of $Tuple
+				sns[1] = new OpApplNode(OP_tup, exprs, treeNode, cm);
+			}
+			// Create the function application node.
+			return new OpApplNode(OP_fa, sns, treeNode, cm);
+
+		case N_UnboundOrBoundChoose:
+			return processChoose(treeNode, children, cm);
+
+		case N_BoundQuant:
+			return processBoundQuant(treeNode, children, cm);
+
+		case N_UnboundQuant:
+			return processUnboundQuant(treeNode, children, cm);
+
+		case N_IfThenElse:
+			sns = new ExprNode[3];
+			sns[0] = generateExpression(children[1], cm);
+			sns[1] = generateExpression(children[3], cm);
+			sns[2] = generateExpression(children[5], cm);
+			return new OpApplNode(OP_ite, sns, treeNode, cm);
+
+		case N_Case:
+			return processCase(treeNode, children, cm);
+
+		case N_DisjList:
+		case N_ConjList:
+			sns = new ExprNode[children.length];
+			for (int lvi = 0; lvi < sns.length; lvi++) {
+				sns[lvi] = generateExpression(children[lvi].heirs()[1], cm);
+			}
+			if (treeNode.isKind(N_DisjList))
+				return new OpApplNode(OP_dl, sns, treeNode, cm);
+			else
+				return new OpApplNode(OP_cl, sns, treeNode, cm);
+
+		case N_RecordComponent: // really RcdSelect in the API
+			sns = new ExprNode[2];
+			sns[0] = generateExpression(children[0], cm);
+			sns[1] = new StringNode(children[2], false);
+			return new OpApplNode(OP_rs, sns, treeNode, cm);
+
+		case N_SetOfFcns: /* [S -> T] */
+			sns = new ExprNode[2];
+			sns[0] = generateExpression(children[1], cm);
+			sns[1] = generateExpression(children[3], cm);
+
+			return new OpApplNode(OP_sof, sns, treeNode, cm);
+
+		case N_SubsetOf:
+			return processSubsetOf(treeNode, children, cm);
+
+		case N_SetOfAll:
+			return processSetOfAll(treeNode, children, cm);
+
+		case N_RcdConstructor:
+			return processRcdForms(OP_rc, treeNode, children, cm);
+
+		case N_SetOfRcds:
+			return processRcdForms(OP_sor, treeNode, children, cm);
+
+		case N_FcnConst:
+			return processFcnConst(treeNode, children, cm);
+
+		case N_ActionExpr:
+		case N_FairnessExpr:
+			return processAction(treeNode, children, cm);
+
+		case N_Except:
+			return processExcept(treeNode, children, cm);
+
+		case N_LetIn:
+			return processLetIn(treeNode, children, cm);
+
+		case N_Lambda:
+			errors.addError(treeNode.getLocation(), "LAMBDA expression used where an expression is required.");
+			return null;
+
+		case N_Label:
+			LabelNode ln = generateLabel(treeNode, cm);
+			if (ln.isAssumeProve && !allowLabeledAP) {
+				errors.addError(treeNode.getLocation(), "Labeled ASSUME/PROVE used where an expression is required.");
+			}
+			return ln;
+
+		default:
+			errors.addError(treeNode.getLocation(), "Unsupported expression type `" + treeNode.getImage() + "'.");
+			return null;
+
+		} // end switch
+
+	} // end generateExpression()
+
+	/***************************************************************************
+	 * The following variables are used in the processing of ASSUME/PROVE * nodes,
+	 * and the labels that lie within them. *
+	 ***************************************************************************/
+	private int assumeProveDepth = 0;
+	/***********************************************************************
+	 * The nesting depth of ASSUME/PROVEs within which the node corrently * being
+	 * processed lies. In * * THEOREM ASSUME P PROVE Q * * the nodes for P and Q are
+	 * at depth 1 (not 0). *
+	 ***********************************************************************/
+
+	private ThmOrAssumpDefNode currentGoal = null;
+	private int currentGoalClause;
+	/***********************************************************************
+	 * currentGoal is the named theorem or proof-step node within * which lies the
+	 * current node that is being processed, or null if * we're not inside such a
+	 * step. If currentGoal != null, and we're * within an ASSUME/PROVE step (which
+	 * is true iff assumeProveDepth > * 0), then then currentGoalClause equals the
+	 * clause of within which * the node being processed lies. (If we're processing
+	 * Q in ASSUME P * PROVE Q, then Q = 1.) *
+	 ***********************************************************************/
+
+	private static final int maxAPDepth = 100;
+	/***********************************************************************
+	 * Maximum allowed nesting depth of ASSUME/PROVES. 100 ought to * suffice. *
+	 ***********************************************************************/
+
+	private boolean[] inScopeOfAPDecl = new boolean[maxAPDepth];
+
+	/***********************************************************************
+	 * For all i \leq assumeProveDepth, inScopeOfAPDecl[i] = true iff the * node
+	 * currently being processed lies within a declaration made at *
+	 * assumeProveDepth = i. Thus in * * THEOREM ASSUME NEW x \in S, * P, * ASSUME R
+	 * , ACTION A PROVE T * PROVE Q * * inScopeOfAPDecl[1] equals false when S is
+	 * being processed and * equals true when P, Q, R, and T are being processed. *
+	 * * inScopeOfAPDecl[2] equals false when R is being processed and true * when T
+	 * is being processed. *
+	 ***********************************************************************/
+
+	private boolean noLabelsAllowed() {
+		/***********************************************************************
+		 * This returns true if we are inside an ASSUME/PROVE node where a * label is
+		 * not allowed because we are in the scope of a declaration * from an inner
+		 * ASSUME/PROVE. *
+		 ***********************************************************************/
+		for (int i = 2; i <= assumeProveDepth; i++) {
+			if (inScopeOfAPDecl[i]) {
+				return true;
+			}
+			;
+		}
+		;
+		return false;
+	}
+
+	private final boolean illegalLabelRef(LabelNode ln, SyntaxTreeNode stn) throws AbortException {
+		/***********************************************************************
+		 * True iff we are currently in a point where a reference to this * label is
+		 * illegal because it lies inside an ASSUME/PROVE clause from * outside the
+		 * scope of a symbol it may contain. See the comments in * AssumeProveNode.java
+		 * for an explanation of this method. *
+		 ***********************************************************************/
+		ThmOrAssumpDefNode goal = ln.goal;
+		if (goal == null) {
+			return false;
+		}
+		;
+		if ((goal.getBody() == null) || (goal.getBody().getKind() != AssumeProveKind)) {
+			errors.addAbort(stn.getLocation(),
+					"Internal error: Expecting label to be in AssumeProveNode, " + "but it's not.");
+		}
+		;
+		AssumeProveNode ap = (AssumeProveNode) goal.getBody();
+		return ap.inScopeOfDecl[ln.goalClause] && (goal.isSuffices() == ap.inProof);
+	} // illegalLabelRef
+
+	private final boolean illegalAPPosRef(AssumeProveNode ap, int pos) {
+		/***********************************************************************
+		 * True iff it is illegal for the node currently being processed to * access the
+		 * clause number pos of AssumeProve node ap because it is * not in the scope of
+		 * symbols declared in previous ASSUME clauses of * ap. See the comments in
+		 * AssumeProveNode.java for an explanation of * this method. *
+		 ***********************************************************************/
+		return ap.inScopeOfDecl[pos - 1] && ((ap.getGoal() == null) || (ap.getGoal().isSuffices() == ap.inProof));
+	}
 
 // XXXX Currently, this handles both label and positional subexpression
 // specifiers.  To handle positional ones, we need to know what
 // the position is.
 //  private final boolean withinScopeOf(SemanticNode goal) {
-  /*************************************************************************
-  * Returns true iff the node currently being processed lies within the    *
-  * scope of (the declarations made in the ASSUME clauses) of goal, which  *
-  * will be a TheoremNode or a proof-step node.                            *
-  *                                                                        *
-  * XXXXX Dummy implementation for now.                                    *
-  *************************************************************************/
+	/*************************************************************************
+	 * Returns true iff the node currently being processed lies within the * scope
+	 * of (the declarations made in the ASSUME clauses) of goal, which * will be a
+	 * TheoremNode or a proof-step node. * * XXXXX Dummy implementation for now. *
+	 *************************************************************************/
 //  return true;
 //    if (goal == null) {return true;} ;
 //    return false ;  
 //    }         
 
-  // The following objects added 10 Feb 2011 by LL are used to check that
-  // a []ASSUME does not lie within the scope of (the assumptions of)
-  // an ordinary ASSUME.  It does this by declaring the dummy OpDeclNode
-  // InAssumeDummyNode as if it were declared within the ASSUME and then
-  // checking if it's defined where the []ASSUME is used.
-  private final static UniqueString S_InAssume = UniqueString.uniqueStringOf("$$InAssume");
-  private final static OpDeclNode InAssumeDummyNode = 
-     new OpDeclNode(S_InAssume, 0, 0, 0, null, null, null) ;
-
-  private final AssumeProveNode 
-                   generateAssumeProve(TreeNode treeNode, ModuleNode cm)
-    /***********************************************************************
-    * Added by LL on 17 Mar 2007.                                          *
-    ***********************************************************************/
-     throws AbortException { 
-       // The following flag is used to record if this is a
-       // Following code added on 9 Nov 2009 so that the goal
-       // field of the AssumeProveNode is null unless this is
-       // a top-level Assume/Prove.
-       ThmOrAssumpDefNode cg = null ;
-       if (assumeProveDepth == 0) {
-           cg = currentGoal;
-       } ;
-       assumeProveDepth++ ;
-       AssumeProveNode apn = new AssumeProveNode(treeNode, cg) ;
-       TreeNode[] children = treeNode.heirs();
-       int numOfChildren = children.length;
-       if (numOfChildren % 2 != 0) { 
-           throw new WrongInvocationException("AssumeProve has odd number of children"); } ;
-       int numOfAssumptions = (numOfChildren - 2) / 2 ;
-       // Check if this is a []ASSUME and that the PROVE matches
-       // the ASSUME.  
-       boolean isBoxAssumeProve = false;
-       String proveString = children[children.length -2].getImage();
-       if (children[0].getImage().equals("[]ASSUME")) {
-           isBoxAssumeProve = true;
-           if (!proveString.equals("[]PROVE")) {
-               errors.addError(children[0].getLocation(), 
-               "[]ASSUME matched by PROVE instead of []PROVE");
-           } 
-       } else {
-           if (!proveString.equals("PROVE")) {
-               errors.addError(children[0].getLocation(), 
-               "ASSUME matched by []PROVE instead of PROVE");
-           } 
-       }
-       apn.setIsBoxAssumeProve(isBoxAssumeProve);
-       
-       apn.assumes = new LevelNode[numOfAssumptions] ;
-       boolean inDeclScope = false ;
-         /******************************************************************
-         * Set true when after we've processed a declaration.              *
-         ******************************************************************/
-       apn.inScopeOfDecl = new boolean[numOfAssumptions + 1] ;
-       apn.inScopeOfDecl[0] = false ; 
-       inScopeOfAPDecl[assumeProveDepth] = false ;
-       if (assumeProveDepth != 1) {
-         /******************************************************************
-         * The context is pushed by the top-level caller of                *
-         * generateAssumeProve, which is processTheorem.                   *
-         ******************************************************************/
-         symbolTable.pushContext(new Context(moduleTable, errors)) ;
-         /******************************************************************
-         * I don't understand exactly what's going on here, but this       *
-         * seems to be the magic incantation for starting a new context    *
-         * into which declarations among the assumptions should be put     *
-         * for interpreting expressions later in the assumption list and   *
-         * in the "prove" expression.                                      *
-         ******************************************************************/
-        } // if
-       
-       if (isBoxAssumeProve) {
-           if (symbolTable.resolveSymbol(S_InAssume) != null) {
-               errors.addError(children[0].getLocation(), 
-               "[]ASSUME used within the scope of an ordinary ASSUME's assumptions"); 
-           }
-       } else {
-           if (symbolTable.resolveSymbol(S_InAssume) == null) {
-               symbolTable.addSymbol(S_InAssume, InAssumeDummyNode); 
-           } 
-       }
-       
-       if (assumeProveDepth == 1) {currentGoalClause = 0 ; } ;
-       for (int i = 0 ; i < numOfAssumptions ; i++) {
-         /******************************************************************
-         * The current assumption is assumption number i+1 (in human       *
-         * numbering).                                                     *
-         ******************************************************************/
-         apn.inScopeOfDecl[i+1] = apn.inScopeOfDecl[i] ; 
-         TreeNode tn = children[2*i + 1] ;
-         switch (tn.getKind()) {
-           case N_AssumeProve :
-             apn.assumes[i] = generateAssumeProve(tn, cm) ;
-             break ;
-           case N_NewSymb :
-             apn.assumes[i] = generateNewSymb(tn, cm) ;
-             apn.inScopeOfDecl[i+1] = true ;
-             inScopeOfAPDecl[assumeProveDepth] = true ;
-             OpDeclNode[] odn = new OpDeclNode[1] ;
-             odn[0] = ((NewSymbNode) apn.assumes[i]).getOpDeclNode() ;
-             break ;
-           default :
-             /**************************************************************
-             * Should be an expression node or a labeled ASSUME/PROVE.     *
-             **************************************************************/
+	// The following objects added 10 Feb 2011 by LL are used to check that
+	// a []ASSUME does not lie within the scope of (the assumptions of)
+	// an ordinary ASSUME. It does this by declaring the dummy OpDeclNode
+	// InAssumeDummyNode as if it were declared within the ASSUME and then
+	// checking if it's defined where the []ASSUME is used.
+	private final static UniqueString S_InAssume = UniqueString.uniqueStringOf("$$InAssume");
+	private final static OpDeclNode InAssumeDummyNode = new OpDeclNode(S_InAssume, 0, 0, 0, null, null, null);
+
+	private final AssumeProveNode generateAssumeProve(TreeNode treeNode, ModuleNode cm)
+			/***********************************************************************
+			 * Added by LL on 17 Mar 2007. *
+			 ***********************************************************************/
+			throws AbortException {
+		// The following flag is used to record if this is a
+		// Following code added on 9 Nov 2009 so that the goal
+		// field of the AssumeProveNode is null unless this is
+		// a top-level Assume/Prove.
+		ThmOrAssumpDefNode cg = null;
+		if (assumeProveDepth == 0) {
+			cg = currentGoal;
+		}
+		;
+		assumeProveDepth++;
+		AssumeProveNode apn = new AssumeProveNode(treeNode, cg);
+		TreeNode[] children = treeNode.heirs();
+		int numOfChildren = children.length;
+		if (numOfChildren % 2 != 0) {
+			throw new WrongInvocationException("AssumeProve has odd number of children");
+		}
+		;
+		int numOfAssumptions = (numOfChildren - 2) / 2;
+		// Check if this is a []ASSUME and that the PROVE matches
+		// the ASSUME.
+		boolean isBoxAssumeProve = false;
+		String proveString = children[children.length - 2].getImage();
+		if (children[0].getImage().equals("[]ASSUME")) {
+			isBoxAssumeProve = true;
+			if (!proveString.equals("[]PROVE")) {
+				errors.addError(children[0].getLocation(), "[]ASSUME matched by PROVE instead of []PROVE");
+			}
+		} else {
+			if (!proveString.equals("PROVE")) {
+				errors.addError(children[0].getLocation(), "ASSUME matched by []PROVE instead of PROVE");
+			}
+		}
+		apn.setIsBoxAssumeProve(isBoxAssumeProve);
+
+		apn.assumes = new LevelNode[numOfAssumptions];
+		boolean inDeclScope = false;
+		/******************************************************************
+		 * Set true when after we've processed a declaration. *
+		 ******************************************************************/
+		apn.inScopeOfDecl = new boolean[numOfAssumptions + 1];
+		apn.inScopeOfDecl[0] = false;
+		inScopeOfAPDecl[assumeProveDepth] = false;
+		if (assumeProveDepth != 1) {
+			/******************************************************************
+			 * The context is pushed by the top-level caller of * generateAssumeProve, which
+			 * is processTheorem. *
+			 ******************************************************************/
+			symbolTable.pushContext(new Context(moduleTable, errors));
+			/******************************************************************
+			 * I don't understand exactly what's going on here, but this * seems to be the
+			 * magic incantation for starting a new context * into which declarations among
+			 * the assumptions should be put * for interpreting expressions later in the
+			 * assumption list and * in the "prove" expression. *
+			 ******************************************************************/
+		} // if
+
+		if (isBoxAssumeProve) {
+			if (symbolTable.resolveSymbol(S_InAssume) != null) {
+				errors.addError(children[0].getLocation(),
+						"[]ASSUME used within the scope of an ordinary ASSUME's assumptions");
+			}
+		} else {
+			if (symbolTable.resolveSymbol(S_InAssume) == null) {
+				symbolTable.addSymbol(S_InAssume, InAssumeDummyNode);
+			}
+		}
+
+		if (assumeProveDepth == 1) {
+			currentGoalClause = 0;
+		}
+		;
+		for (int i = 0; i < numOfAssumptions; i++) {
+			/******************************************************************
+			 * The current assumption is assumption number i+1 (in human * numbering). *
+			 ******************************************************************/
+			apn.inScopeOfDecl[i + 1] = apn.inScopeOfDecl[i];
+			TreeNode tn = children[2 * i + 1];
+			switch (tn.getKind()) {
+			case N_AssumeProve:
+				apn.assumes[i] = generateAssumeProve(tn, cm);
+				break;
+			case N_NewSymb:
+				apn.assumes[i] = generateNewSymb(tn, cm);
+				apn.inScopeOfDecl[i + 1] = true;
+				inScopeOfAPDecl[assumeProveDepth] = true;
+				OpDeclNode[] odn = new OpDeclNode[1];
+				odn[0] = ((NewSymbNode) apn.assumes[i]).getOpDeclNode();
+				break;
+			default:
+				/**************************************************************
+				 * Should be an expression node or a labeled ASSUME/PROVE. *
+				 **************************************************************/
 //             apn.assumes[i] = generateExpression(tn, cm) ;
-             apn.assumes[i] = generateExpressionOrLAP(tn, cm, true) ;
-             break ;
-           } ; // end switch
-         if (assumeProveDepth == 1) {
-           currentGoalClause = currentGoalClause + 1;
-          } ;
-        } ; // end for 
-
-       apn.prove = generateExpression(children[numOfChildren - 1], cm) ;
-
-       if (assumeProveDepth != 1) {
-         symbolTable.popContext();
-           /****************************************************************
-           * Restore the current context, removing the declarations by     *
-           * the assumptions.  assumption list.                            *
-           ****************************************************************/
-         } ;
-       assumeProveDepth-- ;
-       return apn ; }
-
-  private final NewSymbNode generateNewSymb(TreeNode treeNode, ModuleNode cm)
-    /***********************************************************************
-    * Added by LL on 21 Mar 2007.                                          *
-    ***********************************************************************/
-    throws AbortException { 
-      TreeNode[] children = treeNode.heirs();
-      int numOfChildren = children.length;
-
-      /*********************************************************************
-      * Determine if this node should have a non-null "set" field, which   *
-      * is the case iff the declaration ends with "\in S", in which case   *
-      * set the set field to the ExprNode for S.                           *
-      *********************************************************************/
-      ExprNode set = null ;
-      if (children[numOfChildren-2].getKind() == IN) {
-        set = generateExpression(children[numOfChildren-1], cm) ;} ;
-      
-      int declKind ;
-      int declLevel ;
-      /*********************************************************************
-      * Set declKind to the kind of the NewSymbNode object's OpDeclNode.   *
-      * Set declLevel to its level.                                        *
-      *                                                                    *
-      * Start by setting i to 1 or 0 depending on whether the declaration  *
-      * begins with a NEW token.                                           *
-      *********************************************************************/
-      int i = 0 ;
-      if (children[0].getKind() == NEW) {i = 1;};
-
-      /*********************************************************************
-      * Set declKind, and leave i so that children[i+1] is the syntax      *
-      * tree node describing the declared symbol and its arity.            *
-      *                                                                    *
-      * We do this by seeing if the next token is "CONSTANT", "VARIABLE",  *
-      * etc.  If it isn't, then the declaration begins "NEW id", so it's   *
-      * a CONSTANT declaration and i must be set to 0.                     *
-      *********************************************************************/
-      switch (children[i].getKind()) {
-        case CONSTANT :
-               declKind  = NewConstantKind ;
-               declLevel = ConstantLevel;
-               break ;
-        case VARIABLE :
-               declKind  = NewVariableKind ;
-               declLevel = VariableLevel;
-               break ;
-        case STATE :
-               declKind  = NewStateKind ;
-               declLevel = VariableLevel;
-               break ;
-        case ACTION :
-               declKind  = NewActionKind ;
-               declLevel = ActionLevel;
-               break ;
-        case TEMPORAL :
-               declKind  = NewTemporalKind ;
-               declLevel = TemporalLevel;
-               break ;
-        default :
-               declKind  = NewConstantKind ;
-               declLevel = ConstantLevel;
-               i = 0 ;
-       }; // switch
-      return new NewSymbNode(
-                   buildParameter(children[i+1], declKind, declLevel, cm, true),
-                   set,
-                   treeNode) ;
-    }   
-
-  private final ExprNode 
-       processChoose(TreeNode treeNode, TreeNode[] children, ModuleNode cm) 
-  throws AbortException {
-    ExprNode[]   semanticNode   = new ExprNode[1];
-    TreeNode[]   syntaxTreeNode = children[2].heirs();
-    OpApplNode   result;
-
-    symbolTable.pushContext( new Context(moduleTable, errors) );
-
-    if (syntaxTreeNode == null || syntaxTreeNode.length == 0) {
-      // unbounded case
-      FormalParamNode[] odn;
-      boolean      tuple;
-
-      // either Tuple or single identifier
-      if (children[1].isKind( N_IdentifierTuple)) {
-        syntaxTreeNode = children[1].heirs();
-        odn = new FormalParamNode[ syntaxTreeNode.length / 2 ];
-        for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++ ) {
-          odn[lvj] = new FormalParamNode(
-                          syntaxTreeNode[ 2*lvj+1 ].getUS(), 0, 
-                          syntaxTreeNode[ 2*lvj+1 ], symbolTable, cm);
-        }
-        tuple = true;
-      }
-      else {
-        odn = new FormalParamNode[1];
-        odn[0] = new FormalParamNode(children[1].getUS(), 0, children[0],
-                                     symbolTable, cm);
-        tuple = false;
-      } 
-      pushFormalParams(odn) ;
-        /*******************************************************************
-        * Push formal parameters on Last(LS).paramSeq for processing the   *
-        * body.                                                            *
-        *******************************************************************/
-      semanticNode[0] = generateExpression( children[4], cm );
-      popFormalParams() ;
-
-      result =  new OpApplNode(OP_uc, semanticNode, odn, treeNode, cm);
-    }
-    else {
-      // bounded case
-      FormalParamNode[][] odna   = new FormalParamNode[1][0];
-      boolean[]      tuples = new boolean[1];
-      ExprNode[]     exprs  = new ExprNode[1]; 
-
-      // syntaxTreeNode can be reused further down.
-      exprs[0] = generateExpression(syntaxTreeNode[1], cm);
-
-      if ( children[1].isKind( N_IdentifierTuple ) ) {
-        syntaxTreeNode = children[1].heirs();
-        odna[0] = new FormalParamNode[ syntaxTreeNode.length / 2 ];
-        for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++ ) {
-          odna[0][lvj] = 
-            new FormalParamNode(
-                  syntaxTreeNode[ 2*lvj+1 ].getUS(), 0,
-                  syntaxTreeNode[2*lvj+1], symbolTable, cm);
-        }
-        tuples[0] = true;
-      }
-      else {
-        odna[0] = new FormalParamNode[1];
-        odna[0][0] = new FormalParamNode(children[1].getUS(), 0,
-                                         children[1], symbolTable, cm);
-        tuples[0] = false;
-      } 
-      pushFormalParams(flattenParams(odna)) ;
-        /*******************************************************************
-        * Push the bound variables on Last(LS).paramSeq and process the    *
-        * body of the CHOOSE.                                              *
-        *******************************************************************/
-      semanticNode[0] = generateExpression( children[4], cm );
-      popFormalParams() ;
-
-      result = new OpApplNode(OP_bc, null, semanticNode, odna, 
-                              tuples, exprs, treeNode, cm);
-    }
-    symbolTable.popContext();
-    return result;
-  }
-
-  private final ExprNode processBoundQuant(TreeNode treeNode, 
-                                           TreeNode[] children,
-                                           ModuleNode cm) 
-  throws AbortException {
-    // Create data structures for all parameters
-    int            length = (children.length - 2) / 2;
-    FormalParamNode[][] odna   = new FormalParamNode[length][0];
-    boolean[]      bt     = new boolean[length];
-    ExprNode[]     ea     = new ExprNode[ length ];
-
-    // then process parameters
-    symbolTable.pushContext( new Context(moduleTable, errors) );
-    processQuantBoundArgs( children, 1, odna, bt, ea, cm );
-
-    // process expression
-    ExprNode semanticNode[] = new ExprNode[1];
-      pushFormalParams(flattenParams(odna)) ;
-        /*******************************************************************
-        * Push the bound variables on Last(LS).paramSeq and process the    *
-        * body of the quantified expression.                               *
-        *******************************************************************/
-    semanticNode[0] = generateExpression( children[ children.length - 1 ], cm );
-    popFormalParams() ;
-
-    symbolTable.popContext();
-
-    // then return new node.
-    // which variety? look under first child.
-    boolean isExists =    children[0].getUS().equals( S_e ) 
-                       || children[0].getUS().equals( S_ex );
-    if (isExists) {
-      return new OpApplNode(OP_be, null, semanticNode, odna, 
-                            bt, ea, treeNode, cm);
-    }
-    else {
-      return new OpApplNode(OP_bf, null, semanticNode, odna, 
-                            bt, ea, treeNode, cm);
-    }
-  }
-
-  private final ExprNode processUnboundQuant(TreeNode treeNode, 
-                                             TreeNode[] children,
-                                             ModuleNode cm)
-  throws AbortException  {
-   // which variety? look under first child.
-   UniqueString us = children[0].getUS();
-   UniqueString r_us;
-   int level;
-
-   if      ( us.equals (S_e ) ) { r_us = OP_ue; level = 0; } // \E
-   else if ( us.equals (S_ex) ) { r_us = OP_ue; level = 0; } // \exists
-   else if ( us.equals (S_f ) ) { r_us = OP_uf; level = 0; } // \A
-   else if ( us.equals (S_fx) ) { r_us = OP_uf; level = 0; } // \always
-   else if ( us.equals (S_te) ) { r_us = OP_te; level = 1; } // \EE
-   else                         { r_us = OP_tf; level = 1; } // \AA
-
-   // Process all identifiers bound by thus quantifier
-   int length = ( children.length - 2 ) / 2;
-   FormalParamNode odn[] = new FormalParamNode[ length ];
-   symbolTable.pushContext( new Context(moduleTable, errors) );
-
-   for ( int lvi = 0; lvi < length; lvi ++ ) {
-     odn[lvi] = new FormalParamNode(
-                      children[2*lvi +1].getUS(), 0, 
-                      children[2*lvi +1], symbolTable, cm);
-   }
-
-   // now the expression
-   ExprNode semanticNode[] = new ExprNode[1];
-   pushFormalParams(odn) ;
-     /**********************************************************************
-     * Push formal parameters on Last(LS).paramSeq for processing the      *
-     * body.                                                               *
-     **********************************************************************/
-   semanticNode[0] = generateExpression(children[children.length-1], cm);
-   popFormalParams() ;
-
-   // wrap up.
-   symbolTable.popContext();
-   return new OpApplNode(r_us, semanticNode, odn, treeNode, cm);
-  }
-
-  private final ExprNode processCase(TreeNode treeNode, TreeNode[] children, ModuleNode cm) 
-  throws AbortException {
-    // number of arms to CASE-expr, not counting the CASE nodse itself
-    // or the []-separators
-    int armCount = children.length/2;          
-    ExprNode[] casePairs = new ExprNode[armCount];
-    
-    for (int lvi = 0; lvi < armCount; lvi++) {
-      TreeNode caseArm = children[2*lvi+1];
-      TreeNode[] ss = caseArm.heirs();
-      ExprNode[] sops = new ExprNode[2];
-      if (!caseArm.isKind(N_OtherArm)) {
-        sops[0] = this.generateExpression(ss[0], cm);
-      }
-      sops[1] = this.generateExpression(ss[2], cm);
-      casePairs[lvi] = new OpApplNode(OP_pair, sops, caseArm, cm);
-    }
-    return new OpApplNode(OP_case, casePairs, treeNode, cm);
-  }
-
-  private final ExprNode processSubsetOf(TreeNode treeNode, 
-                                         TreeNode children[],
-                                         ModuleNode cm ) 
-  throws AbortException { 
-    // cfr. unbounded choose
-    ExprNode[]     ops    = new ExprNode[1];
-    FormalParamNode[][] odna   = new FormalParamNode[1][0];
-    boolean[]      tuples = new boolean[1];
-    ExprNode[]     exprs  = new ExprNode[1];
-
-    exprs[0] = generateExpression( children[3], cm  );
-
-    symbolTable.pushContext( new Context(moduleTable, errors) );
-
-    if ( children[1].isKind( N_IdentifierTuple ) ) {
-      TreeNode[] ss = children[1].heirs();
-      odna[0] = new FormalParamNode[ ss.length / 2 ];
-      for (int lvj = 0; lvj < ss.length / 2; lvj++ ) {
-        odna[0][lvj] = new FormalParamNode(
-                             ss[ 2*lvj+1 ].getUS(), 0, ss[ 2*lvj+1 ],
-                             symbolTable, cm);
-      }
-      tuples[0] = true;
-    }
-    else {
-      odna[0] = new FormalParamNode[1];
-      odna[0][0] = new FormalParamNode(
-                         children[1].getUS(), 0, children[1], 
-                         symbolTable, cm);
-      tuples[0] = false;
-    }
-
-    pushFormalParams(flattenParams(odna)) ;
-      /*********************************************************************
-      * Push the bound variables on Last(LS).paramSeq and process the      *
-      * body of the CHOOSE.                                                *
-      *********************************************************************/
-    ops[0] = generateExpression( children[5], cm  );
-    popFormalParams() ;
-
-    symbolTable.popContext();
-    return new OpApplNode(OP_sso, null, ops, odna, tuples, exprs, 
-                          treeNode, cm);
-  }
-
-  private final ExprNode processSetOfAll(TreeNode treeNode,
-                                         TreeNode children[],
-                                         ModuleNode cm) 
-  throws AbortException {
-    ExprNode[]     ops    = new ExprNode[1];
-    int            length = (children.length - 3) / 2;
-    FormalParamNode[][] odna   = new FormalParamNode[length][0];
-    boolean[]      tuples = new boolean[length];
-    ExprNode[]     exprs  = new ExprNode[length];
-
-    symbolTable.pushContext(new Context(moduleTable, errors));
-    processQuantBoundArgs(children, 3, odna, tuples, exprs, cm);
-
-    pushFormalParams(flattenParams(odna)) ;
-      /*********************************************************************
-      * Push the bound variables on Last(LS).paramSeq and process the      *
-      * body of the CHOOSE.                                                *
-      *********************************************************************/
-    ops[0] = generateExpression( children[1], cm  );
-    popFormalParams() ;
-
-    symbolTable.popContext();
-
-    return new OpApplNode(OP_soa, null, ops, odna, tuples, exprs,
-                          treeNode, cm);
-  }
-
-  private final ExprNode processFcnConst(TreeNode treeNode,
-                                         TreeNode children[], ModuleNode cm) 
-  throws AbortException { 
-    ExprNode[]     ops    = new ExprNode[1];
-    int            length = (children.length - 3) / 2;    // number of args to function constant
-    FormalParamNode[][] odna   = new FormalParamNode[length][0];
-    boolean[]      tuples = new boolean[length];
-    ExprNode[]     exprs  = new ExprNode[length];
-
-    symbolTable.pushContext( new Context(moduleTable, errors) );
-    processQuantBoundArgs( children, 1, odna, tuples, exprs, cm );
-
-    pushFormalParams(flattenParams(odna)) ;
-      /*********************************************************************
-      * Push the bound variables on Last(LS).paramSeq and process the      *
-      * body of the CHOOSE.                                                *
-      *********************************************************************/
-    ops[0] = generateExpression( children[children.length-2], cm  );
-    popFormalParams() ;
-
-   symbolTable.popContext();
-
-    return new OpApplNode(OP_fc, null, ops, odna, tuples, exprs,
-                          treeNode, cm);
-  }
-
-  /**
-   * This method processes both the RecordConstructor construct and the
-   * SetOfRecords operator.  The two are essentially identical except for 
-   * which builtin operator is used.
-   */
-  private final ExprNode processRcdForms(UniqueString operator,
-                                         TreeNode treeNode,
-                                         TreeNode children[],
-                                         ModuleNode cm) 
-  throws AbortException {
-    // handles RcdConstructor or SetOfRcds 
-    int length = (children.length - 1) / 2;
-
-    // Create an array of pairs to handle all of the fields mentioned in the form
-    ExprNode[] fieldPairs = new ExprNode[length];
-    // Create an array of Unique Strings to check uniqueness of fields. Not very efficient
-    // but a HashTable may be overkill most of the time.
-    // Remember a label is a string.
-    UniqueString [] labels = new UniqueString[length]; 
-
-    // For each field in the RcdConstructor or SetOfRecords
-    for ( int lvi = 0; lvi < length; lvi++ ) {
-      TreeNode syntaxTreeNode[] = children[ 2*lvi + 1].heirs();
-
-      // Create a pair of SemanticNodes to represent one record component
-      ExprNode sops[] = new ExprNode[2];
-
-      // The first one gets a new StringNode indicating the field name
-      sops[0] = new StringNode(syntaxTreeNode[0], false);
-      labels[ lvi ] = ((StringNode) sops[0]).getRep();
-      for ( int cmpIndex=0; cmpIndex < lvi; cmpIndex++) {
-        if ( labels[lvi].compareTo(labels[cmpIndex]) == 0) {
-          errors.addError(syntaxTreeNode[0].getLocation(),
-          "Non-unique fields in constructor.");
-        }
-      }
-
-      // The second one gets the expression indicating the field value (or set of values)
-      sops[1] = generateExpression( syntaxTreeNode[2], cm  );
-
-      // Put the $Pair OpApplNode into the fieldPairs array
-      fieldPairs[lvi] = new OpApplNode(OP_pair, sops, children[2*lvi+1], cm);
-    }
-    // Create the top-level OpApplNode, for either the SetOfRecords op
-    // or the RcdConstructor op.
-    return new OpApplNode(operator, fieldPairs, treeNode, cm);
-  }
-
-  private final ExprNode processAction(TreeNode treeNode, TreeNode children[], ModuleNode cm) 
-  throws AbortException {
-    UniqueString match = children[0].getUS();
-    if      ( match.equals( S_a ) )
-      match = OP_aa;
-    else if ( match.equals( S_brack ) )
-      match = OP_sa;
-    else if ( match.equals( S_sf) )
-      match = OP_sf;
-    else if ( match.equals( S_wf) )
-      match = OP_wf;
-
-    ExprNode ops[] = new ExprNode[2];
-    ops[0] = generateExpression( children[1], cm  );
-    ops[1] = generateExpression( children[3], cm  );
-    return new OpApplNode( match, ops, treeNode, cm);
-  }
-
-  private final ExprNode processExcept(TreeNode treeNode, TreeNode[] children, ModuleNode cm) 
-  throws AbortException {
-    int              numExcepts = (children.length-3)/2 ;     // number of ExceptSpec's;
-    ExprNode[]       operands   = new ExprNode[numExcepts+1]; // 1 for each ExceptionSpec +  first expr
-    OpApplNode       excNode;                                 // Holds OpApplNode for $Except operator
-    OpApplNode       excSpecNode;                             // Holds $Pair node for ExceptSpec
-
-    // The first operand of the $Except operator is the expression to
-    // which the exceptions apply Note this first operand is generated
-    // BEFORE the $Except node is stacked in the next couple of lines,
-    // because an @ in the first expression does NOT refer to the
-    // $Except node currently being generated, but to the next outer
-    // $Except node.
-    operands[0] = generateExpression( children[ 1 ], cm  );  
-
-    // Create the $Except OpApplNode that will be returned by this
-    // method.  We create it now, and fill out its contents later,
-    // because we need a reference to it in order to process @ properly.
-    excNode = new OpApplNode( OP_exc, operands, treeNode, cm);
-
-    // for each of the ExceptSpecs produce another element of the operands array
-    for ( int excSpecIx = 0; excSpecIx < numExcepts; excSpecIx++ ) {
-      TreeNode[] syntaxTreeNode = children[3 + 2*excSpecIx].heirs();  // extract ExceptSpec 
-      ExprNode[] sops           = new ExprNode[2];                    // Each ExceptionSpec is a $Pair
-      int        slength        = syntaxTreeNode.length - 3;          // # of ExceptComponents in ExceptSpec
-      ExprNode[] ssops          = new ExprNode[ slength ];            // to store ExceptComponents
-
-      // Process the LHS of the ExceptSpec
-
-      // for each ExceptComponent of the form .h or [i] or [i,j,k] add
-      // an arg to $SEQ node and add build up the syntax tree for
-      // exceptionTarget
-      for ( int excCompIx = 0; excCompIx < slength ; excCompIx++ ) {
-        // the heirs of an ExceptComponent
-        TreeNode subSyntaxTreeNode[] = syntaxTreeNode[ 1 + excCompIx ].heirs();
-
-        if (subSyntaxTreeNode[0].getUS().equals( S_brack ) ) {
-          // The first heir is "[" , indicates one or more fcn args;
-          // add expressions as function args
-          if ( subSyntaxTreeNode.length > 3 ) {
-            // if so, must generate a tuple for comma list of fcn args
-            int        sslength = (subSyntaxTreeNode.length-1)/2; // number of func args in comma list
-            ExprNode[] sssops   = new ExprNode[ sslength ];       // holds the comma list of fcn args
-
-            // for each of multiple function args in the ExceptComponent
-            for (int fArgIx=0; fArgIx < sslength; fArgIx++ ) {
-              sssops[ fArgIx ] = generateExpression( subSyntaxTreeNode[1+ 2*fArgIx], cm);
-            }
-
-            // add one ExceptComponent to vector of ExceptComponents
-            ssops[ excCompIx ] = 
-              new OpApplNode(OP_tup, sssops, syntaxTreeNode[2*excCompIx+1], cm);
-          }
-          else {
-            // add one ExceptComponent to vector of ExceptComponents
-            ssops[ excCompIx ] = generateExpression( subSyntaxTreeNode[1], cm );
-          }
-        }
-        else {
-          // otherwise a "." indicates record selection; add a
-          // StringNode operand as record selector add one
-          // ExceptComponent to vector of ExceptComponents
-          ssops[ excCompIx ] = new StringNode(subSyntaxTreeNode[1], false);
-        }
-      } // end for (each ExceptionComponent)
-
-      // Create OpAppl for $SEQ applied to array of ExceptComponents
-      // following record or func expr or !
-      // This is the LHS of an ExceptionSpec
-      sops[0] = new OpApplNode(OP_seq, ssops, children[3 + 2*excSpecIx], cm);
-
-      // Process the RHS of the ExceptionSpec
-
-      // Create exceptSpec node now, so that it can be available in
-      // case the RHS expression of this ExceptSpec contains an @
-      excSpecNode = new OpApplNode(OP_pair, sops, children[3+2*excSpecIx], cm);
-
-      // Push the except node and the except spec node on stacks so
-      // that the RHS of the ExceptSpec, which might contain an @, can
-      // be evaluated in their "context".
-      excSpecStack.push(excSpecNode);
-      excStack.push(excNode);
-
-      // Generate the expression constituting the RHS of the
-      // ExceptionSpec allow @ in the context of this expression.
-      sops[1] = generateExpression(syntaxTreeNode[syntaxTreeNode.length-1], cm );
-
-      // Pop them back off
-      excSpecStack.pop();
-      excStack.pop();
-
-      // Store excSpecNode as another operand of $Except
-      operands[ excSpecIx+1 ] = excSpecNode;
-    } // end for (each ExceptionSpec)
-
-    return excNode;
-  } // end processExcept()
-
-  /**
-   * This method generates an expression tree or an OpArgNode as an
-   * argument to an OpApplNode
-   * 
-   *    mainOp      is the operator under that node
-   *    mainSTN     is the "parent" OpApplNode, used in case an error message 
-   *                must be generated
-   *    argPosition is the argument position (counting from 0) that treeNode 
-   *                represents
-   *    argRoot     is the argument syntax tree that either becomes an 
-   *                ExprNode or an OpArgNode
-   *    mn          is the module these expressions are part of
-   */
-  private ExprOrOpArgNode generateExprOrOpArg(SymbolNode mainOp, 
-                                              TreeNode   mainSTN, 
-                                              int        argPosition,
-                                              TreeNode   argRoot, 
-                                              ModuleNode mn) 
-  throws AbortException {
-    SymbolNode argOp  = null;  
-      // the SymbolNode that heads the argRoot expression
-    int        argArity;           
-      // number of actual arguments under argRoot
-    int        arityExpected;      
-      // arity that mainOp expects of argument number <argPosition>
-
-    if ( mainOp == null) {
-      errors.addError(
-         mainSTN.getLocation(),
-         "Unable to generate expression or operator argument; " + 
-         "this is probably because of previously reported errors." );
-      return nullOAN;
-    }
-
-    // Are we sure the "operator" is not a ModuleNode?
-    if (mainOp instanceof ModuleNode) {
-      errors.addError(mainSTN.getLocation(), 
-                      "Module name '" + mainOp.getName() + 
-                      "' used as operator.");
-      return nullOAN;
-    }
-
-    // Are there too many arguments to mainOp?
-    if (argPosition+1 > mainOp.getArity()) {
-      errors.addError(mainSTN.getLocation(), 
-                      "Too many arguments for operator '" +
-                      mainOp.getName() + "'.  There should be only " + 
-                      mainOp.getArity() + "." ); 
-
-      return nullOAN;
-    }
-
-    // For user-defined mainOp check the FormalParamNodes array
-    // associated with the mainOp to find out whether it expects an
-    // operator argument or an ordinary expression argument in
-    // position number argPosition.  (Only UserDefined ops can have
-    // other multi-arg ops passed to them as params, so we can just
-    // assign arityExpected = 0 in other cases.)
-    /***********************************************************************
-    * In SANY2, mainOp can also be a ModuleInstanceKind, so the if test    *
-    * was modified appropriately.                                          *
-    ***********************************************************************/
-    if (   (mainOp.getKind() == UserDefinedOpKind)
-        || (mainOp.getKind() == ModuleInstanceKind)) {
-      arityExpected = 
-           ((OpDefNode)mainOp).getParams()[argPosition].getArity();
-     } else {
-      arityExpected = 0;
-     };
-
-    // if mainOp expects zero arguments, then it expects an ordinary
-    // expression arg in this position, so generate an expression.
-    // Any errors will be found in the generateExpression method.
-    if ( arityExpected == 0 ) {
-      return generateExpression( argRoot, mn );
-    }
-    else {
-      // otherwise, we are expecting an OpArg or Lambda
-      /*********************************************************************
-      * The following test was originally                                  *
-      *                                                                    *
-      *   if (argRoot.getImage().equals("N_OpApplication"))                *
-      *                                                                    *
-      * However, that was not sufficient, because there are lots of other  *
-      * nodes that represent expressions.  At best, these caused weird     *
-      * error messages.  At worst, as in the case of a number or string,   *
-      * they caused an array out of bounds exception when generateGenID    *
-      * was called a few lines later.  I hope that the following test      *
-      * covers all the cases in which this should be called with argRoot   *
-      * not an expression.                                                 *
-      *                                                                    *
-      * Change made by LL on 9 May 2007.                                   *
-      *********************************************************************/
-      if ( !(   argRoot.getImage().equals("N_GeneralId")
-             || argRoot.getImage().equals("N_GenInfixOp")
-             || argRoot.getImage().equals("N_GenNonExpPrefixOp")
-             || argRoot.getImage().equals("N_GenPostfixOp")
-             || argRoot.getImage().equals("N_GenPrefixOp")   
-             || argRoot.getImage().equals("N_Lambda")   )) {
-        errors.addError(
-          argRoot.getLocation(),
-          "An expression appears as argument number " + (argPosition+1) +
-          " (counting from 1) to operator '" + mainOp.getName() +
-          "', in a position an operator is required.");
-        return nullOAN;
-       } // end if
-      
-      if (argRoot.getKind() == N_Lambda) {
-        /*******************************************************************
-        * The argument is a lambda expression.                             *
-        *******************************************************************/
-        argOp = generateLambda(argRoot, mn) ;
-        if (arityExpected == argOp.getArity()) 
-           {return new OpArgNode(argOp, argRoot, mn);} // if
-        else { errors.addError(
-                mainSTN.getLocation(),
-                "Lambda expression with arity " + argOp.getArity() + 
-                " used as argument " + (argPosition+1) + 
-                " of operator `" + mainOp.getName() +
-                "', \nbut an operator of arity " + arityExpected 
-                                + " is required.");
-               return nullOpArg;
-        } // else
-       } // if (argRoot.kind == N_Lambda) 
-      else { 
-        /*******************************************************************
-        * The argument is not a lambda expression.                         *
-        *******************************************************************/
-
-        /*******************************************************************
-        * If the argument is a GeneralId node, then we use                 *
-        * genIdToSelector and selectorToNode to generate the OpArg node.   *
-        *******************************************************************/
-        if (argRoot.getKind() == N_GeneralId) {
-          return (ExprOrOpArgNode)
-                 selectorToNode(genIdToSelector((SyntaxTreeNode) argRoot), 
-                                 arityExpected, false, false, mn);
-          } ;
-       
-        /*******************************************************************
-        * If the argument is not a GeneralId node, we use the code from    *
-        * SANY1 to generate the OpArg node.                                *
-        *******************************************************************/
-        GenID genID = generateGenID(argRoot, mn);
-        argOp = genID.getFullyQualifiedOp(); ;
-  
-        // If the symbol has not been defined, then indicate an error and
-        // return a nullOAN, allowing semantic analysis to continue
-        if (argOp == null) 
-          return nullOAN;
-  
-        argArity = argOp.getArity();
-        if ( arityExpected == argArity && genID.getArgs().length == 0) {
-          return new OpArgNode(genID.getFullyQualifiedOp(), argRoot, mn);
-          }
-         else if (genID.getArgs().length > 0) {
-          // expression (with or without correct number of args) being 
-          // used where operator should be    
-          errors.addError(
-             mainSTN.getLocation(), 
-             "Expression used in argument position " + (argPosition+1) + 
-             " (counting from 1) of operator `" + mainOp.getName() +
-             "', whereas an operator of arity " + arityExpected + 
-             " is required.");
-          return nullOpArg;
-         }
-        else {
-        // operator of the wrong arity is being passed as argument
-          errors.addError(
-             mainSTN.getLocation(),
-             "Operator with incorrect arity passed as argument. " + 
-             "\nOperator '" + argOp.getName() + "' of arity " + argArity + 
-             " is argument number " + (argPosition+1) + 
-             " (counting from 1) to operator `" + mainOp + 
-             "', \nbut an operator of arity " + arityExpected + 
-             " was expected.");
-          return nullOpArg;
-        }
-     } // else of non-Lambda expression case
-   }  // end else of if (arityExpected == 0)
-  } // end generateExprOrOpArgOrLambda
-
-  /**
-   *  Process the General ID syntax tree, i.e. the part that contains
-   *  an expression of the form A(x,y)!B!C(u,v,w)!D (with no params to
-   *  D) and represents an operator.  A General ID occurs in the
-   *  syntax in only a few places: as an operator in an operator
-   *  application; as an operator used as an operand to another
-   *  operator, and as an operator being substituted for a suitable
-   *  constant in module instantiation.
-   *
-   *  Returns a GenID object, which must be further processed.
-   */
-  private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn) throws AbortException {
-    return generateGenID(syntaxTreeNode, mn, false);
-  }
-
-  /*************************************************************************
-  * It appears that this is called with unaryNegKludge = true only when    *
-  * processing a prefix operator.                                          *
-  *************************************************************************/
-  private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn, boolean unaryNegKludge)
-  throws AbortException {
-    GenID      genID;             // Holds components of the generalized ID for this operator
-    TreeNode[] children = syntaxTreeNode.heirs(); // To contain N_IdPrefix node and the main operator
-    TreeNode[] prefix = null;     // Contains array of prefix elements
-                                  // This is the array of N_IdPrefixElements for the operator, 
-                                  // i.e. A!B(3)!C has 2 N_IdPrefixElements, A and B(3).
-    TreeNode[] prefixElt;         // a prefixElement; 2- or 3-elem array: [op, (args), "!"]
-    TreeNode[] allArgs  = null;   // to collect arg arrays from prefix
-    TreeNode[] argsList = null;   // Will hold an arg list tree
-
-    if (children == null || children.length <= 0) {
-      // almost certainly an @ used outside of EXCEPT, which is detected elsewhere
-      return null;
-    }
-
-    prefix = children[0].heirs();
-
-    // Allocate object to hold the Generized ID that is part of this OpAppl
-    genID = new GenID(syntaxTreeNode);
-
-    // Number of elements in the prefix
-    int len = prefix.length;
-
-    // Allocate array of SyntaxTreeNodes, one for each prefix element
-    allArgs = new SyntaxTreeNode[ len ]; 
-
-    // Process all of the prefix elements; construct the compound
-    // identifier (with embedded !-characters), and also accumulate an
-    // array of argument arrays (allArgs) for each prefix element
-    for (int i = 0; i < len; i++ ) {
-      // prefixElt becomes a 2- or 3-element array containing
-      // [operator, args (optional), and "!"]
-      prefixElt = prefix[i].heirs();    
-        
-      // Append the next part of compound identifier name and a "!"
-      genID.append(prefixElt[0].getImage());
-      genID.append("!");
-
-      // allArgs[i] = array of arg syntax trees for next prefix
-      // element (if any) or "!" (if not)
-      allArgs[i] = prefixElt[1];          // Note: whether this is args or a "!" is checked below
-    }
-
-    // Append the primary (rightmost) operator name to compoundID;
-    // calling "finalAppend" signals that the appending is finished,
-    // i.e. that the string can be converted to a UniqueString and to
-    // a SymbolNode inside the genID object
-    genID.finalAppend(children[1].getImage(), unaryNegKludge);
-
-    // for each argument in each potential argument list in the prefix, 
-    // generate the expression or opArg corresponding to it
-
-    // for each argument list in prefix
-    int iarg = 0;
-    for (int i = 0; i < allArgs.length ; i++ ) {
-      // if there is an actual arg list here (instead of a "!" or null)
-      if ( allArgs[i] != null && allArgs[i].isKind( N_OpArgs ) ) {
-        // pick up array of arg list syntax elements
-        argsList = allArgs[ i ].heirs();
-
-        // The odd numbered syntax elements are the args expressions;
-        // the even numbered ones are parens and commas.
-        for (int ia = 1; ia < argsList.length; ia += 2) {
-          // Each arg may be an ordinary expression, or it may be an OpArg; 
-          //   produce appropriate semantic tree or node for it.
-          // Note that operators can be used in place of expressions in only two contexts:
-          //   as argument to suitable user-defined ops, and in the RHS of a substitution 
-          //   in module instantiation
-          genID.addArg(generateExprOrOpArg(genID.getFullyQualifiedOp(), syntaxTreeNode,
-                                           iarg, argsList[ia], mn));
-          iarg++;
-        }
-      }
-    }
-
-    // "finalize the GenID object (i.e. convert argument vector to array)
-    genID.finalizeID();
-    return genID;
-  }
-
-  /*************************************************************************
-  * Added by LL on 27 March 2007.                                          *
-  *                                                                        *
-  * A lambda expression is represented as an OpDefNode.  Since it is       *
-  * always used only as an operator argument, this OpDefNode will appear   *
-  * in an OpArgNode.                                                       *
-  *************************************************************************/
-  private final OpDefNode generateLambda(TreeNode syntaxTreeNode, 
-                                         ModuleNode cm)
-  throws AbortException { 
-    TreeNode []  children = syntaxTreeNode.heirs();
-    int          arity    = (children.length - 2) / 2 ;
-      /*********************************************************************
-      * children = <<"LAMBDA", arg_1, ",", ...  , "," , arg_arity, ":",    *
-      *               body>>                                               *
-      * so   children.length = 3 + arity + arity-1                         *
-      * so   arity = (children.length - 2) / 2.                            *
-      *********************************************************************/
-    Context      ctxt     = new Context(moduleTable, errors);
-    symbolTable.pushContext( ctxt );
-      /*********************************************************************
-      * The context ctxt will hold the parameters of the lambda            *
-      * expression, which may appear in its body.                          *
-      *********************************************************************/
-    FormalParamNode [] params   = new FormalParamNode[arity] ;
-    int argPos = 1 ;
-      /*********************************************************************
-      * children[argPos] is the next parameter's identifier node.          *
-      *********************************************************************/
-    for (int i = 0 ; i < arity ; i++) {
-      /*********************************************************************
-      * Set params[i] to the FormalParamNode for the i-th formal           *
-      * parameter.                                                         *
-      *********************************************************************/
-      params[i] = new FormalParamNode(children[argPos].getUS(),
-                                      0,  // parameters of a lambda expression
-                                          // have arity 0.
-                                      children[argPos],
-                                      symbolTable,
-                                      cm) ;
-      argPos = argPos + 2 ;
-      } // for
-    
+				apn.assumes[i] = generateExpressionOrLAP(tn, cm, true);
+				break;
+			}
+			; // end switch
+			if (assumeProveDepth == 1) {
+				currentGoalClause = currentGoalClause + 1;
+			}
+			;
+		}
+		; // end for
+
+		apn.prove = generateExpression(children[numOfChildren - 1], cm);
+
+		if (assumeProveDepth != 1) {
+			symbolTable.popContext();
+			/****************************************************************
+			 * Restore the current context, removing the declarations by * the assumptions.
+			 * assumption list. *
+			 ****************************************************************/
+		}
+		;
+		assumeProveDepth--;
+		return apn;
+	}
+
+	private final NewSymbNode generateNewSymb(TreeNode treeNode, ModuleNode cm)
+			/***********************************************************************
+			 * Added by LL on 21 Mar 2007. *
+			 ***********************************************************************/
+			throws AbortException {
+		TreeNode[] children = treeNode.heirs();
+		int numOfChildren = children.length;
+
+		/*********************************************************************
+		 * Determine if this node should have a non-null "set" field, which * is the
+		 * case iff the declaration ends with "\in S", in which case * set the set field
+		 * to the ExprNode for S. *
+		 *********************************************************************/
+		ExprNode set = null;
+		if (children[numOfChildren - 2].getKind() == IN) {
+			set = generateExpression(children[numOfChildren - 1], cm);
+		}
+		;
+
+		int declKind;
+		int declLevel;
+		/*********************************************************************
+		 * Set declKind to the kind of the NewSymbNode object's OpDeclNode. * Set
+		 * declLevel to its level. * * Start by setting i to 1 or 0 depending on whether
+		 * the declaration * begins with a NEW token. *
+		 *********************************************************************/
+		int i = 0;
+		if (children[0].getKind() == NEW) {
+			i = 1;
+		}
+		;
+
+		/*********************************************************************
+		 * Set declKind, and leave i so that children[i+1] is the syntax * tree node
+		 * describing the declared symbol and its arity. * * We do this by seeing if the
+		 * next token is "CONSTANT", "VARIABLE", * etc. If it isn't, then the
+		 * declaration begins "NEW id", so it's * a CONSTANT declaration and i must be
+		 * set to 0. *
+		 *********************************************************************/
+		switch (children[i].getKind()) {
+		case CONSTANT:
+			declKind = NewConstantKind;
+			declLevel = ConstantLevel;
+			break;
+		case VARIABLE:
+			declKind = NewVariableKind;
+			declLevel = VariableLevel;
+			break;
+		case STATE:
+			declKind = NewStateKind;
+			declLevel = VariableLevel;
+			break;
+		case ACTION:
+			declKind = NewActionKind;
+			declLevel = ActionLevel;
+			break;
+		case TEMPORAL:
+			declKind = NewTemporalKind;
+			declLevel = TemporalLevel;
+			break;
+		default:
+			declKind = NewConstantKind;
+			declLevel = ConstantLevel;
+			i = 0;
+		}
+		; // switch
+		return new NewSymbNode(buildParameter(children[i + 1], declKind, declLevel, cm, true), set, treeNode);
+	}
+
+	private final ExprNode processChoose(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException {
+		ExprNode[] semanticNode = new ExprNode[1];
+		TreeNode[] syntaxTreeNode = children[2].heirs();
+		OpApplNode result;
+
+		symbolTable.pushContext(new Context(moduleTable, errors));
+
+		if (syntaxTreeNode == null || syntaxTreeNode.length == 0) {
+			// unbounded case
+			FormalParamNode[] odn;
+			boolean tuple;
+
+			// either Tuple or single identifier
+			if (children[1].isKind(N_IdentifierTuple)) {
+				syntaxTreeNode = children[1].heirs();
+				odn = new FormalParamNode[syntaxTreeNode.length / 2];
+				for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++) {
+					odn[lvj] = new FormalParamNode(syntaxTreeNode[2 * lvj + 1].getUS(), 0, syntaxTreeNode[2 * lvj + 1],
+							symbolTable, cm);
+				}
+				tuple = true;
+			} else {
+				odn = new FormalParamNode[1];
+				odn[0] = new FormalParamNode(children[1].getUS(), 0, children[0], symbolTable, cm);
+				tuple = false;
+			}
+			pushFormalParams(odn);
+			/*******************************************************************
+			 * Push formal parameters on Last(LS).paramSeq for processing the * body. *
+			 *******************************************************************/
+			semanticNode[0] = generateExpression(children[4], cm);
+			popFormalParams();
+
+			result = new OpApplNode(OP_uc, semanticNode, odn, treeNode, cm);
+		} else {
+			// bounded case
+			FormalParamNode[][] odna = new FormalParamNode[1][0];
+			boolean[] tuples = new boolean[1];
+			ExprNode[] exprs = new ExprNode[1];
+
+			// syntaxTreeNode can be reused further down.
+			exprs[0] = generateExpression(syntaxTreeNode[1], cm);
+
+			if (children[1].isKind(N_IdentifierTuple)) {
+				syntaxTreeNode = children[1].heirs();
+				odna[0] = new FormalParamNode[syntaxTreeNode.length / 2];
+				for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++) {
+					odna[0][lvj] = new FormalParamNode(syntaxTreeNode[2 * lvj + 1].getUS(), 0,
+							syntaxTreeNode[2 * lvj + 1], symbolTable, cm);
+				}
+				tuples[0] = true;
+			} else {
+				odna[0] = new FormalParamNode[1];
+				odna[0][0] = new FormalParamNode(children[1].getUS(), 0, children[1], symbolTable, cm);
+				tuples[0] = false;
+			}
+			pushFormalParams(flattenParams(odna));
+			/*******************************************************************
+			 * Push the bound variables on Last(LS).paramSeq and process the * body of the
+			 * CHOOSE. *
+			 *******************************************************************/
+			semanticNode[0] = generateExpression(children[4], cm);
+			popFormalParams();
+
+			result = new OpApplNode(OP_bc, null, semanticNode, odna, tuples, exprs, treeNode, cm);
+		}
+		symbolTable.popContext();
+		return result;
+	}
+
+	private final ExprNode processBoundQuant(TreeNode treeNode, TreeNode[] children, ModuleNode cm)
+			throws AbortException {
+		// Create data structures for all parameters
+		int length = (children.length - 2) / 2;
+		FormalParamNode[][] odna = new FormalParamNode[length][0];
+		boolean[] bt = new boolean[length];
+		ExprNode[] ea = new ExprNode[length];
+
+		// then process parameters
+		symbolTable.pushContext(new Context(moduleTable, errors));
+		processQuantBoundArgs(children, 1, odna, bt, ea, cm);
+
+		// process expression
+		ExprNode semanticNode[] = new ExprNode[1];
+		pushFormalParams(flattenParams(odna));
+		/*******************************************************************
+		 * Push the bound variables on Last(LS).paramSeq and process the * body of the
+		 * quantified expression. *
+		 *******************************************************************/
+		semanticNode[0] = generateExpression(children[children.length - 1], cm);
+		popFormalParams();
+
+		symbolTable.popContext();
+
+		// then return new node.
+		// which variety? look under first child.
+		boolean isExists = children[0].getUS().equals(S_e) || children[0].getUS().equals(S_ex);
+		if (isExists) {
+			return new OpApplNode(OP_be, null, semanticNode, odna, bt, ea, treeNode, cm);
+		} else {
+			return new OpApplNode(OP_bf, null, semanticNode, odna, bt, ea, treeNode, cm);
+		}
+	}
+
+	private final ExprNode processUnboundQuant(TreeNode treeNode, TreeNode[] children, ModuleNode cm)
+			throws AbortException {
+		// which variety? look under first child.
+		UniqueString us = children[0].getUS();
+		UniqueString r_us;
+		int level;
+
+		if (us.equals(S_e)) {
+			r_us = OP_ue;
+			level = 0;
+		} // \E
+		else if (us.equals(S_ex)) {
+			r_us = OP_ue;
+			level = 0;
+		} // \exists
+		else if (us.equals(S_f)) {
+			r_us = OP_uf;
+			level = 0;
+		} // \A
+		else if (us.equals(S_fx)) {
+			r_us = OP_uf;
+			level = 0;
+		} // \always
+		else if (us.equals(S_te)) {
+			r_us = OP_te;
+			level = 1;
+		} // \EE
+		else {
+			r_us = OP_tf;
+			level = 1;
+		} // \AA
+
+		// Process all identifiers bound by thus quantifier
+		int length = (children.length - 2) / 2;
+		FormalParamNode odn[] = new FormalParamNode[length];
+		symbolTable.pushContext(new Context(moduleTable, errors));
+
+		for (int lvi = 0; lvi < length; lvi++) {
+			odn[lvi] = new FormalParamNode(children[2 * lvi + 1].getUS(), 0, children[2 * lvi + 1], symbolTable, cm);
+		}
+
+		// now the expression
+		ExprNode semanticNode[] = new ExprNode[1];
+		pushFormalParams(odn);
+		/**********************************************************************
+		 * Push formal parameters on Last(LS).paramSeq for processing the * body. *
+		 **********************************************************************/
+		semanticNode[0] = generateExpression(children[children.length - 1], cm);
+		popFormalParams();
+
+		// wrap up.
+		symbolTable.popContext();
+		return new OpApplNode(r_us, semanticNode, odn, treeNode, cm);
+	}
+
+	private final ExprNode processCase(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException {
+		// number of arms to CASE-expr, not counting the CASE nodse itself
+		// or the []-separators
+		int armCount = children.length / 2;
+		ExprNode[] casePairs = new ExprNode[armCount];
+
+		for (int lvi = 0; lvi < armCount; lvi++) {
+			TreeNode caseArm = children[2 * lvi + 1];
+			TreeNode[] ss = caseArm.heirs();
+			ExprNode[] sops = new ExprNode[2];
+			if (!caseArm.isKind(N_OtherArm)) {
+				sops[0] = this.generateExpression(ss[0], cm);
+			}
+			sops[1] = this.generateExpression(ss[2], cm);
+			casePairs[lvi] = new OpApplNode(OP_pair, sops, caseArm, cm);
+		}
+		return new OpApplNode(OP_case, casePairs, treeNode, cm);
+	}
+
+	private final ExprNode processSubsetOf(TreeNode treeNode, TreeNode children[], ModuleNode cm)
+			throws AbortException {
+		// cfr. unbounded choose
+		ExprNode[] ops = new ExprNode[1];
+		FormalParamNode[][] odna = new FormalParamNode[1][0];
+		boolean[] tuples = new boolean[1];
+		ExprNode[] exprs = new ExprNode[1];
+
+		exprs[0] = generateExpression(children[3], cm);
+
+		symbolTable.pushContext(new Context(moduleTable, errors));
+
+		if (children[1].isKind(N_IdentifierTuple)) {
+			TreeNode[] ss = children[1].heirs();
+			odna[0] = new FormalParamNode[ss.length / 2];
+			for (int lvj = 0; lvj < ss.length / 2; lvj++) {
+				odna[0][lvj] = new FormalParamNode(ss[2 * lvj + 1].getUS(), 0, ss[2 * lvj + 1], symbolTable, cm);
+			}
+			tuples[0] = true;
+		} else {
+			odna[0] = new FormalParamNode[1];
+			odna[0][0] = new FormalParamNode(children[1].getUS(), 0, children[1], symbolTable, cm);
+			tuples[0] = false;
+		}
+
+		pushFormalParams(flattenParams(odna));
+		/*********************************************************************
+		 * Push the bound variables on Last(LS).paramSeq and process the * body of the
+		 * CHOOSE. *
+		 *********************************************************************/
+		ops[0] = generateExpression(children[5], cm);
+		popFormalParams();
+
+		symbolTable.popContext();
+		return new OpApplNode(OP_sso, null, ops, odna, tuples, exprs, treeNode, cm);
+	}
+
+	private final ExprNode processSetOfAll(TreeNode treeNode, TreeNode children[], ModuleNode cm)
+			throws AbortException {
+		ExprNode[] ops = new ExprNode[1];
+		int length = (children.length - 3) / 2;
+		FormalParamNode[][] odna = new FormalParamNode[length][0];
+		boolean[] tuples = new boolean[length];
+		ExprNode[] exprs = new ExprNode[length];
+
+		symbolTable.pushContext(new Context(moduleTable, errors));
+		processQuantBoundArgs(children, 3, odna, tuples, exprs, cm);
+
+		pushFormalParams(flattenParams(odna));
+		/*********************************************************************
+		 * Push the bound variables on Last(LS).paramSeq and process the * body of the
+		 * CHOOSE. *
+		 *********************************************************************/
+		ops[0] = generateExpression(children[1], cm);
+		popFormalParams();
+
+		symbolTable.popContext();
+
+		return new OpApplNode(OP_soa, null, ops, odna, tuples, exprs, treeNode, cm);
+	}
+
+	private final ExprNode processFcnConst(TreeNode treeNode, TreeNode children[], ModuleNode cm)
+			throws AbortException {
+		ExprNode[] ops = new ExprNode[1];
+		int length = (children.length - 3) / 2; // number of args to function constant
+		FormalParamNode[][] odna = new FormalParamNode[length][0];
+		boolean[] tuples = new boolean[length];
+		ExprNode[] exprs = new ExprNode[length];
+
+		symbolTable.pushContext(new Context(moduleTable, errors));
+		processQuantBoundArgs(children, 1, odna, tuples, exprs, cm);
+
+		pushFormalParams(flattenParams(odna));
+		/*********************************************************************
+		 * Push the bound variables on Last(LS).paramSeq and process the * body of the
+		 * CHOOSE. *
+		 *********************************************************************/
+		ops[0] = generateExpression(children[children.length - 2], cm);
+		popFormalParams();
+
+		symbolTable.popContext();
+
+		return new OpApplNode(OP_fc, null, ops, odna, tuples, exprs, treeNode, cm);
+	}
+
+	/**
+	 * This method processes both the RecordConstructor construct and the
+	 * SetOfRecords operator. The two are essentially identical except for which
+	 * builtin operator is used.
+	 */
+	private final ExprNode processRcdForms(UniqueString operator, TreeNode treeNode, TreeNode children[], ModuleNode cm)
+			throws AbortException {
+		// handles RcdConstructor or SetOfRcds
+		int length = (children.length - 1) / 2;
+
+		// Create an array of pairs to handle all of the fields mentioned in the form
+		ExprNode[] fieldPairs = new ExprNode[length];
+		// Create an array of Unique Strings to check uniqueness of fields. Not very
+		// efficient
+		// but a HashTable may be overkill most of the time.
+		// Remember a label is a string.
+		UniqueString[] labels = new UniqueString[length];
+
+		// For each field in the RcdConstructor or SetOfRecords
+		for (int lvi = 0; lvi < length; lvi++) {
+			TreeNode syntaxTreeNode[] = children[2 * lvi + 1].heirs();
+
+			// Create a pair of SemanticNodes to represent one record component
+			ExprNode sops[] = new ExprNode[2];
+
+			// The first one gets a new StringNode indicating the field name
+			sops[0] = new StringNode(syntaxTreeNode[0], false);
+			labels[lvi] = ((StringNode) sops[0]).getRep();
+			for (int cmpIndex = 0; cmpIndex < lvi; cmpIndex++) {
+				if (labels[lvi].compareTo(labels[cmpIndex]) == 0) {
+					errors.addError(syntaxTreeNode[0].getLocation(), "Non-unique fields in constructor.");
+				}
+			}
+
+			// The second one gets the expression indicating the field value (or set of
+			// values)
+			sops[1] = generateExpression(syntaxTreeNode[2], cm);
+
+			// Put the $Pair OpApplNode into the fieldPairs array
+			fieldPairs[lvi] = new OpApplNode(OP_pair, sops, children[2 * lvi + 1], cm);
+		}
+		// Create the top-level OpApplNode, for either the SetOfRecords op
+		// or the RcdConstructor op.
+		return new OpApplNode(operator, fieldPairs, treeNode, cm);
+	}
+
+	private final ExprNode processAction(TreeNode treeNode, TreeNode children[], ModuleNode cm) throws AbortException {
+		UniqueString match = children[0].getUS();
+		if (match.equals(S_a))
+			match = OP_aa;
+		else if (match.equals(S_brack))
+			match = OP_sa;
+		else if (match.equals(S_sf))
+			match = OP_sf;
+		else if (match.equals(S_wf))
+			match = OP_wf;
+
+		ExprNode ops[] = new ExprNode[2];
+		ops[0] = generateExpression(children[1], cm);
+		ops[1] = generateExpression(children[3], cm);
+		return new OpApplNode(match, ops, treeNode, cm);
+	}
+
+	private final ExprNode processExcept(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException {
+		int numExcepts = (children.length - 3) / 2; // number of ExceptSpec's;
+		ExprNode[] operands = new ExprNode[numExcepts + 1]; // 1 for each ExceptionSpec + first expr
+		OpApplNode excNode; // Holds OpApplNode for $Except operator
+		OpApplNode excSpecNode; // Holds $Pair node for ExceptSpec
+
+		// The first operand of the $Except operator is the expression to
+		// which the exceptions apply Note this first operand is generated
+		// BEFORE the $Except node is stacked in the next couple of lines,
+		// because an @ in the first expression does NOT refer to the
+		// $Except node currently being generated, but to the next outer
+		// $Except node.
+		operands[0] = generateExpression(children[1], cm);
+
+		// Create the $Except OpApplNode that will be returned by this
+		// method. We create it now, and fill out its contents later,
+		// because we need a reference to it in order to process @ properly.
+		excNode = new OpApplNode(OP_exc, operands, treeNode, cm);
+
+		// for each of the ExceptSpecs produce another element of the operands array
+		for (int excSpecIx = 0; excSpecIx < numExcepts; excSpecIx++) {
+			TreeNode[] syntaxTreeNode = children[3 + 2 * excSpecIx].heirs(); // extract ExceptSpec
+			ExprNode[] sops = new ExprNode[2]; // Each ExceptionSpec is a $Pair
+			int slength = syntaxTreeNode.length - 3; // # of ExceptComponents in ExceptSpec
+			ExprNode[] ssops = new ExprNode[slength]; // to store ExceptComponents
+
+			// Process the LHS of the ExceptSpec
+
+			// for each ExceptComponent of the form .h or [i] or [i,j,k] add
+			// an arg to $SEQ node and add build up the syntax tree for
+			// exceptionTarget
+			for (int excCompIx = 0; excCompIx < slength; excCompIx++) {
+				// the heirs of an ExceptComponent
+				TreeNode subSyntaxTreeNode[] = syntaxTreeNode[1 + excCompIx].heirs();
+
+				if (subSyntaxTreeNode[0].getUS().equals(S_brack)) {
+					// The first heir is "[" , indicates one or more fcn args;
+					// add expressions as function args
+					if (subSyntaxTreeNode.length > 3) {
+						// if so, must generate a tuple for comma list of fcn args
+						int sslength = (subSyntaxTreeNode.length - 1) / 2; // number of func args in comma list
+						ExprNode[] sssops = new ExprNode[sslength]; // holds the comma list of fcn args
+
+						// for each of multiple function args in the ExceptComponent
+						for (int fArgIx = 0; fArgIx < sslength; fArgIx++) {
+							sssops[fArgIx] = generateExpression(subSyntaxTreeNode[1 + 2 * fArgIx], cm);
+						}
+
+						// add one ExceptComponent to vector of ExceptComponents
+						ssops[excCompIx] = new OpApplNode(OP_tup, sssops, syntaxTreeNode[2 * excCompIx + 1], cm);
+					} else {
+						// add one ExceptComponent to vector of ExceptComponents
+						ssops[excCompIx] = generateExpression(subSyntaxTreeNode[1], cm);
+					}
+				} else {
+					// otherwise a "." indicates record selection; add a
+					// StringNode operand as record selector add one
+					// ExceptComponent to vector of ExceptComponents
+					ssops[excCompIx] = new StringNode(subSyntaxTreeNode[1], false);
+				}
+			} // end for (each ExceptionComponent)
+
+			// Create OpAppl for $SEQ applied to array of ExceptComponents
+			// following record or func expr or !
+			// This is the LHS of an ExceptionSpec
+			sops[0] = new OpApplNode(OP_seq, ssops, children[3 + 2 * excSpecIx], cm);
+
+			// Process the RHS of the ExceptionSpec
+
+			// Create exceptSpec node now, so that it can be available in
+			// case the RHS expression of this ExceptSpec contains an @
+			excSpecNode = new OpApplNode(OP_pair, sops, children[3 + 2 * excSpecIx], cm);
+
+			// Push the except node and the except spec node on stacks so
+			// that the RHS of the ExceptSpec, which might contain an @, can
+			// be evaluated in their "context".
+			excSpecStack.push(excSpecNode);
+			excStack.push(excNode);
+
+			// Generate the expression constituting the RHS of the
+			// ExceptionSpec allow @ in the context of this expression.
+			sops[1] = generateExpression(syntaxTreeNode[syntaxTreeNode.length - 1], cm);
+
+			// Pop them back off
+			excSpecStack.pop();
+			excStack.pop();
+
+			// Store excSpecNode as another operand of $Except
+			operands[excSpecIx + 1] = excSpecNode;
+		} // end for (each ExceptionSpec)
+
+		return excNode;
+	} // end processExcept()
+
+	/**
+	 * This method generates an expression tree or an OpArgNode as an argument to an
+	 * OpApplNode
+	 * 
+	 * mainOp is the operator under that node mainSTN is the "parent" OpApplNode,
+	 * used in case an error message must be generated argPosition is the argument
+	 * position (counting from 0) that treeNode represents argRoot is the argument
+	 * syntax tree that either becomes an ExprNode or an OpArgNode mn is the module
+	 * these expressions are part of
+	 */
+	private ExprOrOpArgNode generateExprOrOpArg(SymbolNode mainOp, TreeNode mainSTN, int argPosition, TreeNode argRoot,
+			ModuleNode mn) throws AbortException {
+		SymbolNode argOp = null;
+		// the SymbolNode that heads the argRoot expression
+		int argArity;
+		// number of actual arguments under argRoot
+		int arityExpected;
+		// arity that mainOp expects of argument number <argPosition>
+
+		if (mainOp == null) {
+			errors.addError(mainSTN.getLocation(), "Unable to generate expression or operator argument; "
+					+ "this is probably because of previously reported errors.");
+			return nullOAN;
+		}
+
+		// Are we sure the "operator" is not a ModuleNode?
+		if (mainOp instanceof ModuleNode) {
+			errors.addError(mainSTN.getLocation(), "Module name '" + mainOp.getName() + "' used as operator.");
+			return nullOAN;
+		}
+
+		// Are there too many arguments to mainOp?
+		if (argPosition + 1 > mainOp.getArity()) {
+			errors.addError(mainSTN.getLocation(), "Too many arguments for operator '" + mainOp.getName()
+					+ "'.  There should be only " + mainOp.getArity() + ".");
+
+			return nullOAN;
+		}
+
+		// For user-defined mainOp check the FormalParamNodes array
+		// associated with the mainOp to find out whether it expects an
+		// operator argument or an ordinary expression argument in
+		// position number argPosition. (Only UserDefined ops can have
+		// other multi-arg ops passed to them as params, so we can just
+		// assign arityExpected = 0 in other cases.)
+		/***********************************************************************
+		 * In SANY2, mainOp can also be a ModuleInstanceKind, so the if test * was
+		 * modified appropriately. *
+		 ***********************************************************************/
+		if ((mainOp.getKind() == UserDefinedOpKind) || (mainOp.getKind() == ModuleInstanceKind)) {
+			arityExpected = ((OpDefNode) mainOp).getParams()[argPosition].getArity();
+		} else {
+			arityExpected = 0;
+		}
+		;
+
+		// if mainOp expects zero arguments, then it expects an ordinary
+		// expression arg in this position, so generate an expression.
+		// Any errors will be found in the generateExpression method.
+		if (arityExpected == 0) {
+			return generateExpression(argRoot, mn);
+		} else {
+			// otherwise, we are expecting an OpArg or Lambda
+			/*********************************************************************
+			 * The following test was originally * * if
+			 * (argRoot.getImage().equals("N_OpApplication")) * * However, that was not
+			 * sufficient, because there are lots of other * nodes that represent
+			 * expressions. At best, these caused weird * error messages. At worst, as in
+			 * the case of a number or string, * they caused an array out of bounds
+			 * exception when generateGenID * was called a few lines later. I hope that the
+			 * following test * covers all the cases in which this should be called with
+			 * argRoot * not an expression. * * Change made by LL on 9 May 2007. *
+			 *********************************************************************/
+			if (!(argRoot.getImage().equals("N_GeneralId") || argRoot.getImage().equals("N_GenInfixOp")
+					|| argRoot.getImage().equals("N_GenNonExpPrefixOp") || argRoot.getImage().equals("N_GenPostfixOp")
+					|| argRoot.getImage().equals("N_GenPrefixOp") || argRoot.getImage().equals("N_Lambda"))) {
+				errors.addError(argRoot.getLocation(),
+						"An expression appears as argument number " + (argPosition + 1)
+								+ " (counting from 1) to operator '" + mainOp.getName()
+								+ "', in a position an operator is required.");
+				return nullOAN;
+			} // end if
+
+			if (argRoot.getKind() == N_Lambda) {
+				/*******************************************************************
+				 * The argument is a lambda expression. *
+				 *******************************************************************/
+				argOp = generateLambda(argRoot, mn);
+				if (arityExpected == argOp.getArity()) {
+					return new OpArgNode(argOp, argRoot, mn);
+				} // if
+				else {
+					errors.addError(mainSTN.getLocation(),
+							"Lambda expression with arity " + argOp.getArity() + " used as argument "
+									+ (argPosition + 1) + " of operator `" + mainOp.getName()
+									+ "', \nbut an operator of arity " + arityExpected + " is required.");
+					return nullOpArg;
+				} // else
+			} // if (argRoot.kind == N_Lambda)
+			else {
+				/*******************************************************************
+				 * The argument is not a lambda expression. *
+				 *******************************************************************/
+
+				/*******************************************************************
+				 * If the argument is a GeneralId node, then we use * genIdToSelector and
+				 * selectorToNode to generate the OpArg node. *
+				 *******************************************************************/
+				if (argRoot.getKind() == N_GeneralId) {
+					return (ExprOrOpArgNode) selectorToNode(genIdToSelector((SyntaxTreeNode) argRoot), arityExpected,
+							false, false, mn);
+				}
+				;
+
+				/*******************************************************************
+				 * If the argument is not a GeneralId node, we use the code from * SANY1 to
+				 * generate the OpArg node. *
+				 *******************************************************************/
+				GenID genID = generateGenID(argRoot, mn);
+				argOp = genID.getFullyQualifiedOp();
+				;
+
+				// If the symbol has not been defined, then indicate an error and
+				// return a nullOAN, allowing semantic analysis to continue
+				if (argOp == null)
+					return nullOAN;
+
+				argArity = argOp.getArity();
+				if (arityExpected == argArity && genID.getArgs().length == 0) {
+					return new OpArgNode(genID.getFullyQualifiedOp(), argRoot, mn);
+				} else if (genID.getArgs().length > 0) {
+					// expression (with or without correct number of args) being
+					// used where operator should be
+					errors.addError(mainSTN.getLocation(),
+							"Expression used in argument position " + (argPosition + 1)
+									+ " (counting from 1) of operator `" + mainOp.getName()
+									+ "', whereas an operator of arity " + arityExpected + " is required.");
+					return nullOpArg;
+				} else {
+					// operator of the wrong arity is being passed as argument
+					errors.addError(mainSTN.getLocation(),
+							"Operator with incorrect arity passed as argument. " + "\nOperator '" + argOp.getName()
+									+ "' of arity " + argArity + " is argument number " + (argPosition + 1)
+									+ " (counting from 1) to operator `" + mainOp + "', \nbut an operator of arity "
+									+ arityExpected + " was expected.");
+					return nullOpArg;
+				}
+			} // else of non-Lambda expression case
+		} // end else of if (arityExpected == 0)
+	} // end generateExprOrOpArgOrLambda
+
+	/**
+	 * Process the General ID syntax tree, i.e. the part that contains an expression
+	 * of the form A(x,y)!B!C(u,v,w)!D (with no params to D) and represents an
+	 * operator. A General ID occurs in the syntax in only a few places: as an
+	 * operator in an operator application; as an operator used as an operand to
+	 * another operator, and as an operator being substituted for a suitable
+	 * constant in module instantiation.
+	 *
+	 * Returns a GenID object, which must be further processed.
+	 */
+	private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn) throws AbortException {
+		return generateGenID(syntaxTreeNode, mn, false);
+	}
+
+	/*************************************************************************
+	 * It appears that this is called with unaryNegKludge = true only when *
+	 * processing a prefix operator. *
+	 *************************************************************************/
+	private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn, boolean unaryNegKludge) throws AbortException {
+		GenID genID; // Holds components of the generalized ID for this operator
+		TreeNode[] children = syntaxTreeNode.heirs(); // To contain N_IdPrefix node and the main operator
+		TreeNode[] prefix = null; // Contains array of prefix elements
+									// This is the array of N_IdPrefixElements for the operator,
+									// i.e. A!B(3)!C has 2 N_IdPrefixElements, A and B(3).
+		TreeNode[] prefixElt; // a prefixElement; 2- or 3-elem array: [op, (args), "!"]
+		TreeNode[] allArgs = null; // to collect arg arrays from prefix
+		TreeNode[] argsList = null; // Will hold an arg list tree
+
+		if (children == null || children.length <= 0) {
+			// almost certainly an @ used outside of EXCEPT, which is detected elsewhere
+			return null;
+		}
+
+		prefix = children[0].heirs();
+
+		// Allocate object to hold the Generized ID that is part of this OpAppl
+		genID = new GenID(syntaxTreeNode);
+
+		// Number of elements in the prefix
+		int len = prefix.length;
+
+		// Allocate array of SyntaxTreeNodes, one for each prefix element
+		allArgs = new SyntaxTreeNode[len];
+
+		// Process all of the prefix elements; construct the compound
+		// identifier (with embedded !-characters), and also accumulate an
+		// array of argument arrays (allArgs) for each prefix element
+		for (int i = 0; i < len; i++) {
+			// prefixElt becomes a 2- or 3-element array containing
+			// [operator, args (optional), and "!"]
+			prefixElt = prefix[i].heirs();
+
+			// Append the next part of compound identifier name and a "!"
+			genID.append(prefixElt[0].getImage());
+			genID.append("!");
+
+			// allArgs[i] = array of arg syntax trees for next prefix
+			// element (if any) or "!" (if not)
+			allArgs[i] = prefixElt[1]; // Note: whether this is args or a "!" is checked below
+		}
+
+		// Append the primary (rightmost) operator name to compoundID;
+		// calling "finalAppend" signals that the appending is finished,
+		// i.e. that the string can be converted to a UniqueString and to
+		// a SymbolNode inside the genID object
+		genID.finalAppend(children[1].getImage(), unaryNegKludge);
+
+		// for each argument in each potential argument list in the prefix,
+		// generate the expression or opArg corresponding to it
+
+		// for each argument list in prefix
+		int iarg = 0;
+		for (int i = 0; i < allArgs.length; i++) {
+			// if there is an actual arg list here (instead of a "!" or null)
+			if (allArgs[i] != null && allArgs[i].isKind(N_OpArgs)) {
+				// pick up array of arg list syntax elements
+				argsList = allArgs[i].heirs();
+
+				// The odd numbered syntax elements are the args expressions;
+				// the even numbered ones are parens and commas.
+				for (int ia = 1; ia < argsList.length; ia += 2) {
+					// Each arg may be an ordinary expression, or it may be an OpArg;
+					// produce appropriate semantic tree or node for it.
+					// Note that operators can be used in place of expressions in only two contexts:
+					// as argument to suitable user-defined ops, and in the RHS of a substitution
+					// in module instantiation
+					genID.addArg(
+							generateExprOrOpArg(genID.getFullyQualifiedOp(), syntaxTreeNode, iarg, argsList[ia], mn));
+					iarg++;
+				}
+			}
+		}
+
+		// "finalize the GenID object (i.e. convert argument vector to array)
+		genID.finalizeID();
+		return genID;
+	}
+
+	/*************************************************************************
+	 * Added by LL on 27 March 2007. * * A lambda expression is represented as an
+	 * OpDefNode. Since it is * always used only as an operator argument, this
+	 * OpDefNode will appear * in an OpArgNode. *
+	 *************************************************************************/
+	private final OpDefNode generateLambda(TreeNode syntaxTreeNode, ModuleNode cm) throws AbortException {
+		TreeNode[] children = syntaxTreeNode.heirs();
+		int arity = (children.length - 2) / 2;
+		/*********************************************************************
+		 * children = <<"LAMBDA", arg_1, ",", ... , "," , arg_arity, ":", * body>> * so
+		 * children.length = 3 + arity + arity-1 * so arity = (children.length - 2) / 2.
+		 * *
+		 *********************************************************************/
+		Context ctxt = new Context(moduleTable, errors);
+		symbolTable.pushContext(ctxt);
+		/*********************************************************************
+		 * The context ctxt will hold the parameters of the lambda * expression, which
+		 * may appear in its body. *
+		 *********************************************************************/
+		FormalParamNode[] params = new FormalParamNode[arity];
+		int argPos = 1;
+		/*********************************************************************
+		 * children[argPos] is the next parameter's identifier node. *
+		 *********************************************************************/
+		for (int i = 0; i < arity; i++) {
+			/*********************************************************************
+			 * Set params[i] to the FormalParamNode for the i-th formal * parameter. *
+			 *********************************************************************/
+			params[i] = new FormalParamNode(children[argPos].getUS(), 0, // parameters of a lambda expression
+																			// have arity 0.
+					children[argPos], symbolTable, cm);
+			argPos = argPos + 2;
+		} // for
+
 //    /***********************************************************************
 //    * Convert the FormalParamNode array params to the OpDeclNode array     *
 //    * odn.                                                                 *
@@ -4531,2903 +4245,2754 @@ OpDefNode node = (OpDefNode) vec.elementAt(i);
 //                              null,           // SymbolTable
 //                              params[i].stn); // TreeNode
 //      }; // for
-    pushFormalParams(params) ;
-        /*******************************************************************
-        * Push formal parameters on Last(LS).paramSeq for processing the   *
-        * body
-        *******************************************************************/
-    ExprNode body = generateExpression( children[children.length - 1], cm );
-      /*********************************************************************
-      * Generate the lambda expression's body.                             *
-      *********************************************************************/
-    popFormalParams() ;
-
-    symbolTable.popContext();
-      /*********************************************************************
-      * Restore original context.                                          *
-      *********************************************************************/
-    return new OpDefNode(
-               S_lambda,          // The operator name is "LAMBDA"
-               UserDefinedOpKind, // The node kind for a lambda expression
-               params,            // the array of formal parameters
-               false ,            // localness, which is meaningless
-               body,              // the body (an expression node)
-               cm,                // the module
-               null,              // the symbol table, which is null for an
-                                  // OpDefNode representing a lambda expression.
-               syntaxTreeNode,
-               true,              // Is defined.  Its value should not matter.
-               null ) ;           // Source
-  } // generateLambda
-
-  /**
-   * Generates an OpApplNode or an OpArgNode for a SyntaxTreeNode,
-   * according to whether the value of "typeExpected" is either opAppl
-   * or opArg.
-   */
-  private final ExprNode generateOpAppl(TreeNode syntaxTreeNode, ModuleNode cm)
-  throws AbortException {
-    TreeNode     primaryArgs = null;     
-      // points to syntax tree of primary arg list (if any); otherwise null
-    boolean      isOpApp     = syntaxTreeNode.isKind( N_OpApplication ) ;
-      // true ==> to indicate this is an OpAppl; must have primary args
-      // false ==> operator used as an argument (OpArg); has no primary args
-    int          primaryArgCount = 0;    
-      // total # of arguments, including those of prefix, if any
-    int          len;                    
-      // number of prefix elements
-    TreeNode[]   children    = syntaxTreeNode.heirs(); 
-      // temp used for finding the prefix
-    TreeNode []  prefix;                 
-      // array of prefix elements
-    TreeNode[]   allArgs     = null;     
-      // to collect arg arrays from both prefix and the main op (if any)
-    TreeNode []  prefixElt;              
-      // a prefixElement; 2 or 3 elem array: [op, (args), "!"]
-    UniqueString symbol;                 
-      // UniqueString name of the fully-qualified operator
-    SymbolNode   fullOperator;           
-      // The SymbolNode for the fully-qualified operator
-    int          iarg        = 0;        
-      // loop counter for actual args (as opposed to 
-      //   arg syntax elements like commas and parens)
-    TreeNode[]   argsList    = null;     
-      // Will hold an array of arg syntax trees for primary operator
-    ExprOrOpArgNode[] args   = null;     
-      // Will hold an array of arg semantic trees for primary operator
-
-    // Process the Generized ID that is the operator for this OpAppl
-    GenID genID = generateGenID(children[0], cm);
-
-    // Set up pointers to OpAppl's primary args
-    primaryArgs = children[1];  
-      // Array of argument list syntax elements for the main
-      // (rightmost) operator, including parens and commas;
-      //   should be an N_OpArgs node
-
-    // calc number of primary args; 
-    // args are interspersed w/ parens & commas--hence the /2
-    primaryArgCount = primaryArgs.heirs().length / 2; 
-
-    if (genID == null || genID.getFullyQualifiedOp() == null) {
-      // if operator is @ or an unresolved symbol; error has already
-      // been generated inside genID
-      return nullOAN;
-    }
-
-    args = new ExprOrOpArgNode[primaryArgCount]; 
-      // Array to hold semantic trees for primary args
-
-    // pick up array of arg list syntax elements
-    argsList = primaryArgs.heirs();
-
-    // The odd numbered syntax elements are the args expressions; the
-    // even numbered ones are parens and commas.
-    // for each arg in this arg list ...
-    for ( int ia = 1; ia < argsList.length; ia += 2 ) {
-      // Each arg may be an ordinary expression, or it may be an OpArg; 
-      //   produce appropriate semantic tree or node for it.
-      // Note that operators can be used in place of expressions 
-      // in only two contexts:
-      //   as argument to suitable user-defined ops, and in the RHS 
-      // of a substitution in module instantiation
-      args[iarg] = generateExprOrOpArg(genID.getFullyQualifiedOp(), 
-                                       syntaxTreeNode,
-                                       iarg, argsList[ia], cm);
-      iarg++;   // count the actual args
-    } // end for
-
-    // Concatenate the list of args in the GenID object to the 
-    // primary arg list just created
-    Vector            genIDArgList = genID.getArgsVector();
-    ExprOrOpArgNode[] finalArgList = 
-                        new ExprOrOpArgNode[genIDArgList.size() + iarg];
-
-    // Copy the args from the prefix
-    for (int i = 0; i < genIDArgList.size(); i++) {
-      finalArgList[i] = (ExprOrOpArgNode)(genIDArgList.elementAt(i));
-    }
-    // Copy the primary args
-    for (int i = 0, j = genIDArgList.size(); i < iarg; i++, j++ ) {
-      finalArgList[j] = args[i];
-    }
-
-    // return an OpApplNode constructed from the fully-qualified
-    // operator and the final arg list
-    return new OpApplNode(genID.getFullyQualifiedOp(), finalArgList, 
-                          syntaxTreeNode, cm);
-  } // end generateOpAppl()
-
-  /**
-   * Process a named, parameterixed instantiation, e.g. of the form
-   * D(p1,...pn) = INSTANCE M WITH a1 <- e1 ,..., ar <- er
-   */
-  /*************************************************************************
-  * Note: the returned value does not seem to be used.                     *
-  *************************************************************************/
-  private final OpDefNode processModuleDefinition(
-                  TreeNode treeNode,
-                  Vector defs,  
-                    /*******************************************************
-                    * This is non-null when called from inside a LET, in   *
-                    * which case the OpDef node is appended to defs.  If   *
-                    * null, the OpDef node is appended to the              *
-                    * module-level lists of such nodes.                    *
-                    *******************************************************/
-                  Vector insts,
-                    /*******************************************************
-                    * If non-null, then a vector of InstanceNode objects   *
-                    * to which the current instance node is to be          *
-                    * appended.  If null, cm.appendInstance is called to   *
-                    * put the InstanceNode onto the module-level lists of  *
-                    * such nodes.                                          *
-                    *******************************************************/
-                  ModuleNode cm)
-  throws AbortException {
-    // Start with a LHS for an instance: we must extract from it name
-    // and possibly parameters. Then we need the external Context of
-    // the module and to extract all non-local, non-builtin
-    // symbols. Then build the proper symbol list and add it.
-  
-    // We must remember to identify explicitly whether or not the new
-    // nodes would be local.
-  
-    // assert treeNode.isKind(N_ModuleDefinition)    
-    // 
-    // Note that this code does nothing about THEOREMS and ASSUMES in
-    // modules being instantiated
-    boolean      localness = treeNode.zero() != null;
-    TreeNode[]   children  = treeNode.one()[0].heirs(); // heirs of IdentLHS
-    UniqueString name      = children[0].getUS();
-
-    // processing of LHS of the definition, i.e. the name and parameters
-    FormalParamNode[] args = nullParam;
-    Context parmCtxt = null;
-
-    // If the operator being defined as a module instance has any parameters
-    if (children.length > 1) {
-      // Create new array of FormalParamNodes for the new operator
-      args = new FormalParamNode[ children.length /2 -1 ];
-
-      // Push a new context in current module's SymbolTable
-      parmCtxt = new Context(moduleTable, errors);
-      symbolTable.pushContext(parmCtxt);
-
-      // For each formal parameter declared for the op being defined
-      for (int i = 0; i < args.length; i++) {
-        TreeNode     child = children[2 + 2 * i];
-        UniqueString id    = null;
-        int          count = 0;
-  
-        if ( child.isKind(N_IdentDecl)) {
-          id = child.heirs()[0].getUS();
-          count = (child.heirs().length -1)/ 2;
-        }
-        else if ( child.isKind(N_InfixDecl)) {
-          id = child.heirs()[1].getUS();
-          count = 2;
-        }
-        else if ( child.isKind(N_PrefixDecl)) {
-          id = child.heirs()[0].getUS();
-          count = 1;
-        }
-        else if ( child.isKind(N_PostfixDecl)) {
-          id = child.heirs()[1].getUS();
-          count = 1;
-        }
-        else {
-          errors.addAbort(
-            treeNode.getLocation(),
-            "Internal error: Error in formal params part of parse tree.",
-            true);
-        }
- 
-        // If there was no error
-        if ( id != null ) {
-          // Create a new FormalParamNode for the defined Op and put 
-          // it in the SymbolTable
-          args[i] = new FormalParamNode(id, count, child, symbolTable, cm);
-        } // end if
-      } // end for
-    } // end if
-
-    // processing RHS of the definition, starting with identification
-    // of module being instantiated, followed by processing of the
-    // WITH clause (if any)
-    children = treeNode.one()[2].heirs(); // heirs of NonLocalInstance
-
-    // Find the Context and ModuleNode for the module being instantiated
-    Context instanceeCtxt = this.getContext(children[1].getUS());
-    ModuleNode instanceeModule = symbolTable.resolveModule(children[1].getUS());
-
-    if (instanceeCtxt == null) {
-      errors.addError(
-        children[1].getLocation(),
-        "Module " + children[1].getImage() + " does not have a context.");
-      return nullODN;
-    }
-
-    if (instanceeModule == null) {
-      errors.addError(children[1].getLocation(),
-                      "Module name " + children[1].getImage() + 
-                      " is not known" + " in current context.");
-      return nullODN;
-    }
-
-    /*
-     * Set isInstantiated field of instancee.  Added by LL 23 July 2013
-     */
-    instanceeModule.setInstantiated(true) ;
-
-
-    // Create a SubstInNode that will be used to wrap each definition
-    // body in the module being defined. "children" is the array of
-    // explicit substitution clauses used in the module definition.
-    // Both instanceeCtxt and symbolTable are involved here since for
-    // each substitution c <- e, c must be resolved in the
-    // instanceeCtxt, and e must be interpreted in symbolTable
-    SubstInNode substIn = processSubst(treeNode, children, symbolTable,
-                                       instanceeCtxt, instanceeModule, cm);
-
-    // We are done with the local context (if one was created because
-    // of parameters)
-    if (parmCtxt != null) symbolTable.popContext();
-  
-    // Create a vector of all of the OpDefNodes in the instancee module
-    /***********************************************************************
-    * I have no idea why the module's getOpDefs method isn't used here.    *
-    ***********************************************************************/
-    Vector elts = instanceeCtxt.getByClass( OpDefNode.class );
-
-    // For each definition in the instancee module, create a 
-    // corresponding definition in the instancer module
-    for (int i = 0; i < elts.size(); i++) {
-      // Find the OpDefNode to be instantiated
-      OpDefNode odn = (OpDefNode)elts.elementAt(i);
-
-      /**********************************************************************
-      * Ignore it if it is local or a builtin def.                          *
-      **********************************************************************/
-      if (    !odn.isLocal() 
-           && (   (odn.getKind() == UserDefinedOpKind)
-               || (odn.getKind() == ModuleInstanceKind) )) { 
-        // Create the new name prepended with "name!"
-        String compoundID = name + "!" + odn.getName();
-        UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID);
-
-        // Copy parameters for the op being defined
-        FormalParamNode [] fpn    = odn.getParams();
-        FormalParamNode [] params = new FormalParamNode[fpn.length + args.length];
-        System.arraycopy(args, 0, params, 0, args.length);
-        System.arraycopy(fpn, 0, params, args.length, fpn.length);
-
-        OpDefNode newOdn ;
-        if (odn.getKind() == UserDefinedOpKind) {
-          if (substIn.getSubsts().length > 0) {
-            // If there are substitutions, then the body of the new
-            // definition instance must be wrapped in a SUBST-IN node.
-            // Create the "wrapping" SubstInNode as a clone of "subst"
-            // above, but with a body from the OpDefNode in the module
-            // being instantiated
-            SubstInNode substInNode = 
-              new SubstInNode(treeNode, substIn.getSubsts(), odn.getBody(),
-                              cm, instanceeModule);
-  
-            // Create the OpDefNode for the new instance of this
-            // definition; because of the new operator name, cm is the
-            // module of origin for purposes of deciding of two defs are
-            // "the same" or "different"
-            newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, 
-                                   localness, substInNode, cm, symbolTable, 
-                                   treeNode, true, odn.getSource());   
-            setOpDefNodeRecursionFields(newOdn, cm) ;
-            newOdn.setLabels(odn.getLabelsHT()) ;
-           } // if (substIn.getSubsts().length > 0) 
-          else {
-            // no SUBST-IN node required; but because of the new
-            // operator name, cm is the module of origin for purposes of
-            // deciding of two defs are "the same" or "different"
-            newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, 
-                                   localness, odn.getBody(), cm, symbolTable, 
-                                   treeNode, true, odn.getSource());   
-            setOpDefNodeRecursionFields(newOdn, cm) ;
-            newOdn.setLabels(odn.getLabelsHT()) ;
-           } // else
-         } // if (odn.kind == UserDefinedOpKind) 
-        else {
-          /*****************************************************************
-          * This is a ModuleInstanceKind node.                             *
-          *****************************************************************/
-          newOdn = new OpDefNode(
-                    qualifiedName, params, localness, 
-                    odn.getOriginallyDefinedInModuleNode(), symbolTable, 
-                    treeNode, odn.getSource());
-        } ; // else
-        // defs is non-null iff this module definition is in the Let
-        // part of a Let-In expression.  Add this newly created OpDef
-        // to either the LET list or the module cm's definition list.
-        if (defs == null) {
-          cm.appendDef(newOdn);
-         }
-        else {
-          defs.addElement(newOdn);
-         } 
-      } // if (!odn.isLocal()&& ... )
-    } // for
-
-     /**********************************************************************
-     * Import the ThmOrAssumpDefNode objects to the current module.  The   *
-     * following code for doing this was copied and modified without       *
-     * much thinking from the code above for OpDefNode objects             *
-     **********************************************************************/
-    // Create a vector of all of the ThmOrAssumpDefNodes in the 
-    // instancee module
-    Vector taelts = instanceeCtxt.getByClass( ThmOrAssumpDefNode.class );
-
-    // For each definition in the instancee module, create a 
-    // corresponding definition in the instancer module
-    for (int i = 0; i < taelts.size(); i++) {
-      // Find the ThmOrAssumpDefNode to be instantiated
-      ThmOrAssumpDefNode taOdn = (ThmOrAssumpDefNode)taelts.elementAt(i);
-
-      /*********************************************************************
-      * There are no builtin ThmOrAssumpDefNode objects.                   *
-      *********************************************************************/
-      // Ignore it if it is local 
-      if (!taOdn.isLocal()) { 
-        // Create the new name prepended with "name!"
-        String compoundID = name + "!" + taOdn.getName();
-        UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID);
-
-        // Copy parameters for the op being defined
-        /*******************************************************************
-        * Theorem or assumption definitions have no parameters.            *
-        *******************************************************************/
-        FormalParamNode [] fpn    = taOdn.getParams();
-        FormalParamNode [] params = 
-                            new FormalParamNode[fpn.length + args.length];
-        System.arraycopy(args, 0, params, 0, args.length);
-        System.arraycopy(fpn, 0, params, args.length, fpn.length);
-
-        ThmOrAssumpDefNode newtaOdn;
-        if (substIn.getSubsts().length > 0) {
-          // If there are substitutions, then the body of the new
-          // definition instance must be wrapped in a SUBST-IN node.
-          // Create the "wrapping" SubstInNode as a clone of "subst"
-          // above, but with a body from the ThmOrAssumpDefNode in the module
-          // being instantiated
-          APSubstInNode substInNode = 
-            new APSubstInNode(treeNode, substIn.getSubsts(), taOdn.getBody(),
-                              cm, instanceeModule);
-
-          // Create the ThmOrAssumpDefNode for the new instance of this
-          // definition; because of the new operator name, cm is the
-          // module of origin for purposes of deciding of two defs are
-          // "the same" or "different"
-          newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(),
-                                substInNode, cm, symbolTable, 
-                                 treeNode, params, instanceeModule,
-                                 taOdn.getSource());   
-          // Following statement added by LL on 30 Oct 2012 to handle locally 
-          // instantiated theorems and assumptions. Added setLabels call
-          // on 31 Oct 2012.
-          newtaOdn.setLocal(localness);
-          newtaOdn.setLabels(taOdn.getLabelsHT()) ;
-          
-          /*****************************************************************
-          * No recursion fields needed for a theorem or assumption         *
-          * because it can't appear in a recursive section.                *
-          *****************************************************************/
-          // setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ;
-        }
-        else {
-          // no SUBST-IN node required; but because of the new
-          // operator name, cm is the module of origin for purposes of
-          // deciding if two defs are "the same" or "different"
-          newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(),
-                                 taOdn.getBody(), cm, symbolTable, 
-                                 treeNode, params, instanceeModule,
-                                 taOdn.getSource());
-          // Following statement added by LL on 30 Oct 2012 to handle locally 
-          // instantiated theorems and assumptions.   Added setLabels call
-          // on 31 Oct 2012.       
-          newtaOdn.setLocal(localness);
-          newtaOdn.setLabels(taOdn.getLabelsHT()) ;
-          /*****************************************************************
-          * No recursion fields needed for theorems or assumptions         *
-          * because they can't appear in a recursive section.              *
-          *****************************************************************/
-          // setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ;
-        }
-  
-        // defs is non-null iff this module definition is in the Let
-        // part of a Let-In expression.  Add this newly created ThmOrAssumpDef
-        // to either the LET list or the module cm's definition list.
-        if (defs == null) {
-          cm.appendDef(newtaOdn);
-        }
-        else {
-          defs.addElement(newtaOdn);
-        }
-      } // if (!taOdn.isLocal()&& ... )
-    } // for
-
-    // Create a new InstanceNode to represent this INSTANCE stmt 
-    // in the current module
-    InstanceNode inst = new InstanceNode(name, localness, args, 
-                                         instanceeModule,
-                                         substIn.getSubsts(), treeNode);
-
-    // Append this new InstanceNode to the vector of InstanceNodes
-    // being accumulated for this module
-    if (insts == null) {
-      cm.appendInstance(inst);
-    }
-    else {
-      insts.addElement(inst);
-    }
-
-    // Create new OpDefNode with ModuleInstanceKind.  The reason for
-    // doing this is to get the name in the symbol table so the name
-    // cannot be re-used later in this module for a user-defined
-    // operator.
-    return new OpDefNode(name, args, localness, cm, symbolTable, treeNode, 
-                         null);
-      /*********************************************************************
-      * Note: the module's OpDefNode does not have recursive parameters    *
-      * set.  If the module definition statement occurs in a recursive     *
-      * section, then it is the instantiated definitions that are          *
-      * recursive, not the module definition itself.  (The name under      *
-      * which the module is instantiated cannot appear in a RECURSIVE      *
-      * statement.)                                                        *
-      *********************************************************************/
-      
-  } // end processModuleDefinition()
-  
-  /**
-   * From a particular explicit substitution (substTarget <- substValue)
-   * check the legality of the substTarget, and if OK, generate the
-   * appropriate type of node for substValue
-   */
-  private ExprOrOpArgNode generateSubst(
-              Context instanceeCtxt, 
-              TreeNode substTarget,
-              TreeNode substValue, 
-              ModuleNode mn) 
-  throws AbortException {
-    SymbolNode targetSymbol = instanceeCtxt.getSymbol(substTarget.getUS());
-
-    // if the targetSymbol cannot be found in the instancee context, 
-    // or if it does not correspond to a declaration, then it is an 
-    // illegal substitution target
-
-    if (targetSymbol == null || ! (targetSymbol instanceof OpDeclNode) ) {
-      errors.addError(
-        substTarget.getLocation(),
-        "Identifier '" + substTarget.getUS() + "' is not a legal" +
-        " target of a substitution. \nA legal target must be a declared" +
-        " CONSTANT or VARIABLE in the module being instantiated." +
-        " \n(Also, check for warnings about multiple declarations of" +
-        " this same identifier.)");
-      return nullOAN;
-    }
-    
-    // but if the symbol is found, then if it has arity 0, the RHS 
-    // should be an expression, if arity > 0, the the RHS should be an OpArg
-    ExprOrOpArgNode returnObject;
-
-    if ( targetSymbol.getArity() == 0 ) {
-      // if the target of the substitution has arity 0,
-      // then we expect an expression to be substituted for it
-      returnObject = generateExpression(substValue, mn);
-    }
-    else { 
-      // if the target of the substitution has arity > 0,
-      // then and operator must be substituted for it
-      returnObject = generateOpArg(targetSymbol, substValue, mn);
-
-      // and it better have the same arity as the target
-      if ( ((OpArgNode)returnObject).getArity() != targetSymbol.getArity() ) {
-        errors.addError(substValue.getLocation(),
-                        "An operator must be substituted for symbol '" +
-                        targetSymbol.getName() + "', and it must have arity " +
-                        targetSymbol.getArity() + ".");
-      }
-    }
-    return returnObject;
-  } // end generateSubst()
-
-  /**
-   * Return an OpArgNode constructed from a GeneralId tree to be used
-   * in the RHS of a substitution
-   */
-  private OpArgNode generateOpArg(SymbolNode targetSymbol, 
-                                  TreeNode opArgSyntaxNode,
-                                  ModuleNode mn) 
-  throws AbortException {
-    /***********************************************************************
-    * If this is a lambda expressiion, then just get it and go.            *
-    ***********************************************************************/
-    if (opArgSyntaxNode.isKind(N_Lambda)) {
-      return new OpArgNode(generateLambda(opArgSyntaxNode, mn), 
-                           opArgSyntaxNode, mn) ;
-      } ;
-    // First, make sure that an operator ID is present, and not an expression
-    if ( ! ( opArgSyntaxNode.isKind(N_GeneralId)           ||
-             opArgSyntaxNode.isKind(N_GenInfixOp)          ||
-             opArgSyntaxNode.isKind(N_GenPrefixOp)         ||
-             opArgSyntaxNode.isKind(N_GenNonExpPrefixOp)   ||
-               /************************************************************
-               * This last disjunct was added by LL on 9 May 2007.         *
-               *                                                           *
-               * The original parser phase produced an N_GenPrefixOp       *
-               * here, but an N_GenNonExpPrefixOp for the syntactically    *
-               * identical situation in an operator argument.  The new     *
-               * TLA+2 parser produces N_GenNonExpPrefixOp nodes here,     *
-               * but I left the N_GenPrefixOp case in here just in case    *
-               * this is called somewhere else that I haven't found.       *
-               ************************************************************/
-               
-             opArgSyntaxNode.isKind(N_GenPostfixOp) 
-           )
-       ) {
-      errors.addError(opArgSyntaxNode.getLocation(), 
-                      "Arity " + targetSymbol.getArity() + 
-                      " operator (not an expression) is expected" + 
-                      " \nto substitute for CONSTANT '" + 
-                      targetSymbol.getName() + "'." );
-      return nullOpArg;
-    }
-
-    /***********************************************************************
-    * If the argument is a GeneralId node, then we use                     *
-    * genIdToSelector and selectorToNode to generate the OpArg node.       *
-    ***********************************************************************/
-    if (opArgSyntaxNode.getKind() == N_GeneralId) {
-      /*********************************************************************
-      * First, a sanity check to make sure we're never looking for an      *
-      * expression argument.                                               *
-      *********************************************************************/
-      if (targetSymbol.getArity() <= 0) {
-        errors.addAbort(opArgSyntaxNode.getLocation(),
-                       "Internal error: expected to find arity > 0.", true);
-        } ;
-
-         LevelNode ln = selectorToNode(genIdToSelector(
-                            (SyntaxTreeNode) opArgSyntaxNode), 
-                            targetSymbol.getArity(), false, false, mn);
-
-        /*******************************************************************
-        * Added 23 February 2009: It appears that, in case of an error,    *
-        * selectorToNode can return something other than an OpArgNode      *
-        * here.  (In particular, an OpApplNode.)  Rather than tix          *
-        * selectorToNode, I am adding a kludge to simply ignore the        *
-        * problem and hope that it can be caused only by some other        *
-        * error.  If no error has been found, then we abort and debug if   *
-        * this is encountered.                                             *
-        *******************************************************************/
-         if (!(ln instanceof OpArgNode)) { 
-           if (errors.getNumErrors() > 0) { return nullOpArg; }
-           errors.addAbort(opArgSyntaxNode.getLocation(),
-                           "Internal error: " +
-                           "Expected an operator argument but " +
-                           "found something else.");
-           };
-         return (OpArgNode) ln ;
-    } ;
-       
-    /*******************************************************************
-    * If the argument is not a GeneralId node, we use the code from    *
-    * SANY1 to generate the OpArg node.                                *
-    *******************************************************************/
-  
-    // Assemble the (possibly compound) generalized identifier, and resolve it.
-    GenID genID = generateGenID(opArgSyntaxNode, mn);
-
-    // If the fully-qualified op is undefined, then a message has already been
-    // put in errors, but we must insert a nullOpArgNode in the tree.
-    if (genID.getFullyQualifiedOp() != null && genID.getArgs().length == 0 ) {
-      // Create an OpArgNode from it.
-      return new OpArgNode(genID.getFullyQualifiedOp(), opArgSyntaxNode, mn);
-    }
-    else if ( genID.getArgs().length > 0 ) { 
-      // Expression being used where Operator is required
-      errors.addError(opArgSyntaxNode.getLocation(), 
-                      "Arity " + targetSymbol.getArity() + 
-                      " operator (not an expression) is expected" + 
-                      " to substitute for CONSTANT '" + 
-                      targetSymbol.getName() + "'." );
-      return nullOpArg;      
-    }
-    else {
-      return nullOpArg;
-    }
-  } // end generateOpArg()
-
-  /**
-   * Process the substitutions clause of a module definition or instantiation;
-   * returns a SubstInNode that can be used as a template for the wrapper that
-   * must be present around each OpDefNode body created from the module
-   * instantiation or module definition.
-   */
-  private SubstInNode processSubst(
-            TreeNode    treeNode,
-            TreeNode[]  substNodes, 
-               // array of subst nodes [c1 <- e1, ... ,cn <- en]
-            SymbolTable instancerST,    
-               // SymbolTable in which the ei must be resolved
-            Context     instanceeCtxt,  
-               // Context in which the ci must be resolved
-            ModuleNode  instanceeModule,
-               // the ModuleNode of the module in which ci must be resolved
-            ModuleNode  mn) throws AbortException {
-    TreeNode[] children;    // find the substitution part of the syntax tree
-
-    // Create a vector of all declarations of CONSTANTS and VARIABLES 
-    // in the context of the module being instantiated (instancee).
-    // These are all the symbols that must have substitutions defined
-    // for them in the instantiation, either explictly or implicitly.
-    Vector decls = instanceeCtxt.getByClass( OpDeclNode.class );
-
-    // Create a SubstInNode to be used as a template for the SubstInNodes 
-    // in the body of every newly instantiated OpDef in the module.  
-    // The substitutions in the returned SubstInNode object will be the 
-    // implicit substitutions of the form c<-c for all CONSTANTS and 
-    // VARIABLES c that are BOTH declared in instancee and for which the 
-    // same name is declared or defined in instancer.  Note the instancerST 
-    // must be passed to this constructor because with a default substitution 
-    // LHS<-RHS the LHS is resolved in the instanceeCtxt, (done
-    // in the previous line) and the RHS is resolved in the instancerST.
-    SubstInNode substIn = 
-         new SubstInNode(treeNode, instancerST, decls, mn, instanceeModule);
-
-    // For each explicit substitution in the syntax tree, overwrite or add
-    // the corresponding default entry in SubstInNode just created
-    for (int i = 3; i < substNodes.length; i += 2) {
-      // pick up array of syntax elements for one substitution, 
-      // e.g. ["c","<-",expr]
-      TreeNode sc[] = substNodes[i].heirs(); 
-
-      // substRHS is the expression "exp" in "c <- exp"; this stmt first 
-      // checks that c is properly declared in the instancee context, 
-      // and then generates an ExprOrOpArgNode.  If c is a constant 
-      // with parameters, then an OpArgNode is returned; otherwise it is 
-      // an ExprNode. 
-      ExprOrOpArgNode substRHS = 
-                        generateSubst(instanceeCtxt, sc[0], sc[2], mn);
-
-      // Overwrite an implicit substitution if there is one, or add a new one, 
-      // checking for duplicate substitutions for the same symbol
-      substIn.addExplicitSubstitute(instanceeCtxt, sc[0].getUS(), 
-                                    sc[2], substRHS );
-    }
-
-    // Check if substitution is complete, i.e. that all constants and vars 
-    // have been substituted for.  
-    substIn.matchAll( decls );  
-    return substIn;
-  } // end processSubst
-
-  /**
-   * This method treats *unnamed* INSTANCE stmts
-   * NOTE: this code does nothing with ASSUMES or THEOREMS
-   */
-  /*************************************************************************
-  * However, SANY2 imports ThmOrAssumpDef nodes.                           *
-  *************************************************************************/
-  /**
- * @param treeNode
- * @param cm
- * @param topLevel
- * @return
- * @throws AbortException
- */
-/**
- * @param treeNode
- * @param cm
- * @param topLevel
- * @return
- * @throws AbortException
- */
-/**
- * @param treeNode
- * @param cm
- * @param topLevel
- * @return
- * @throws AbortException
- */
-/**
- * @param treeNode
- * @param cm
- * @param topLevel
- * @return
- * @throws AbortException
- */
-private final InstanceNode 
-   generateInstance(TreeNode treeNode, ModuleNode cm, boolean topLevel)
-   throws AbortException {
-     /**********************************************************************
-     * topLevel argument is true for a top-level INSTANCE statement, and   *
-     * false elsewise--that is, for an INSTANCE inside a proof.            *
-     **********************************************************************/
-     TreeNode[] children ;
-     if (topLevel) {
-       children = treeNode.one()[0].heirs(); }
-                        // skip one generation below NonLocalInstance
-                        // because we know zero defines local in a 
-                        // top-level INSTANCE
-     else {children = treeNode.heirs();} ;
-     // id of module being instanced
-     UniqueString moduleId = children[1].getUS();  
-                        
-     // If this module instance is declared "LOCAL" then all of the 
-     // definitions in it must be instanced as if they were "LOCAL"
-     boolean localness = treeNode.local();
-
-     // Create a list of all declarations for module moduleId.
-     // Match them against either something in the substitutions or 
-     // something in the current context (symbol table) for the 
-     // substitution.  Check that the symbol does occur in the module 
-     // and as a declaration.
-     Context instanceeCtxt = this.getContext(moduleId);
-     if (instanceeCtxt == null) {
-       errors.addAbort(children[1].getLocation(),
-                       "Internal error: No context available for module `" +
-                       moduleId.toString() + "'.", true);
-      } ;
-     // Try to find the ModuleNode for the module being instanced in
-     // the symbolTable
-     ModuleNode instanceeModuleNode = symbolTable.resolveModule(moduleId);
-
-     // It must be an external module if it isn't in the symbolTable;
-     // try to find it in moduleTable (it cannot be in both places, or
-     // a name conflict would have resulted)
-     if (instanceeModuleNode == null) { 
-       instanceeModuleNode = moduleTable.getModuleNode(moduleId);
-     }
-
-     if (instanceeModuleNode == null) {
-        errors.addAbort(children[1].getLocation(), 
-                        "Could not find module " + moduleId.toString(),
-                        false);
-     }
-    
-     /*
-      * Set isInstantiated field of instancee.  Added by LL 23 July 2013
-      */
-     instanceeModuleNode.setInstantiated(true) ;
-     
-     // Create the SubstInNode that will act as a template "wrapper"
-     // for each definition in the module being instantiated; this
-     // SubstInNode itself gets discarded after being used as template
-     // however many times is necessary
-     SubstInNode subst = processSubst(treeNode, children, symbolTable,
-                                      instanceeCtxt, instanceeModuleNode, cm);
-
-     // Create a vector of all of the OpDefNodes in the module being 
-     // instantiated
-     Vector defs = instanceeCtxt.getByClass(OpDefNode.class);
-
-     OpDefNode odn;       // OpDefNode in module being instantiated (instancee)
-     OpDefNode newOdn;    // Its counterpart current module (instancer)
-     SubstInNode substInTemplate;  // Template to be used for any 
-                                   // SubstInNode wrappers required
-     
-     // Duplicate the OpDef records from the module being INSTANCE'd 
-     for (int i = 0; i < defs.size(); i++) {
-       odn = (OpDefNode)defs.elementAt(i); 
-          // OpDefNode in module being instantiated (instancee)
-
-       // Do not instantiate built-in or local operators, or those 
-       // OpDefNodes created solely to prevent a ModuleName from being 
-       // used as an operator node.
-       if (odn.getKind() == BuiltInKind ||
-           odn.getKind() == ModuleInstanceKind ||
-           odn.isLocal()) {
-         continue;
-       }
-
-       // If there are parameters to the module being instantiated, then 
-       // a SubstInNode is required, and possibly a different module of 
-       // origin should be indicated
-       if (!instanceeModuleNode.isParameterFree()) {
-         // Create the OpDefNode for the new instance of this definition
-         // Note that the new OpDefNode shares the array of FormalParamNodes 
-         //   with the old OpDefNode, as well as large parts of its body 
-         // (all but the SubstInNode). Hence, changes by a tool to an Original 
-         // OpDefNode will likely be reflected in all instances of it.
-         if ( odn.getOriginallyDefinedInModuleNode().isParameterFree() ) {  
-
-           /****************************************************************
-           * Originally, newOdn was set the way it now is if localness =   *
-           * true.  Here's the problem with it.  Suppose the instantiated  *
-           * module EXTENDS the Naturals module.  Then this will add new   *
-           * OpDefNodes for all the symbols defined in Naturals.  If the   *
-           * current module EXTENDS Naturals, this will lead to multiple   *
-           * definitions.  So, for nonlocal definitions, we just           *
-           * set newOdn to odn.                                            *
-           *                                                               *
-           * However, now the problem is: suppose the current module       *
-           * does not EXTEND the Naturals module.  Then the operators      *
-           * defined in Naturals, which should be defined in the current   *
-           * module, are not.  So, we add them to symbolTable.  This does  *
-           * not lead to a multiple definition error because apparently    *
-           * it's the addSymbol method that is smart enough to detect if   *
-           * we are adding a definition that comes from the same source as *
-           * the original one.                                             *
-           *                                                               *
-           * This fix was made by LL on 16 Feb 2009.                       *
-           *                                                               *
-           * On 6 June 2010, LL add "&& topLevel" to the following `if'    *
-           * test.  This was needed because an INSTANCE inside a proof     *
-           * was producing a "Multiple declarations or definition"         *
-           * warning if the INSTANCEd module and the current module both   *
-           * EXTENDed Naturals.  This fix seems to do the right thing,     *
-           * but I have not extensively tested it and I have no idea what  *
-           * problems may remain.                                          *
-           ****************************************************************/
-           if (localness  && topLevel ) {
-             newOdn = new OpDefNode( odn.getName(), UserDefinedOpKind, odn.getParams(),
-                            localness, odn.getBody(), 
-                            odn.getOriginallyDefinedInModuleNode(), 
-                            symbolTable, treeNode, true, odn.getSource() );   
-             /***************************************************************
-             * The following statement was added by LL on 16 Feb 2009, by  *
-             * analogy with the corresponding code about 45 lines below.  I *
-             * have no idea if it was originally omitted for a good reason. *
-             ***************************************************************/
-             newOdn.setLabels(odn.getLabelsHT()) ;   
-            }
-           else {
-             newOdn = odn;
-             symbolTable.addSymbol(odn.getName(), odn);
-           }
-         }
-         else {        
-           // Create the "wrapping" SubstInNode as a clone of "subst" above, 
-           // but with a body from the OpDefNode in the module being 
-           // instantiated
-           substInTemplate = new SubstInNode(treeNode, subst.getSubsts(),
-                                             odn.getBody(), cm, 
-                                             instanceeModuleNode);
-           newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, 
-                                  odn.getParams(),  localness, 
-                                  substInTemplate, cm, symbolTable, treeNode, 
-                                  true, odn.getSource());   
-                newOdn.setLabels(odn.getLabelsHT()) ;    
-
-
-         }
-       }
-       else { 
-         // There are no parameters to the instancee module; this
-         // means that a SubstInNode is not necessary, and also that
-         // the new operator should be considered to be "originally
-         // defined in" the same module as the old one for purposes of
-         // telling whether they are "the same" or different definitions
-
-         // Create an OpDefNode whose body is the same as the instancer's.
-
-         if (localness) {
-           /****************************************************************
-           * See the comments about the similar change made to the         *
-           * setting of newOdn in the `then' clause, just above.           *
-           * This entire change was made by LL on 16 Feb 2009.             *
-           ****************************************************************/
-           newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(),  
-                         localness, odn.getBody(), 
-                         odn.getOriginallyDefinedInModuleNode(), 
-                         symbolTable, treeNode, true, odn.getSource()); 
-                   newOdn.setLabels(odn.getLabelsHT()) ;   
-          }
-         else {
-           newOdn = odn;
-           symbolTable.addSymbol(odn.getName(), odn);
-          }
-       }
-       cm.appendDef(newOdn);
-       setOpDefNodeRecursionFields(newOdn, cm) ;
-     } // end for
-
-     /**********************************************************************
-     * Import the ThmOrAssumpDefNode objects to the current module.  The   *
-     * following code for doing this was copied and modified without       *
-     * thinking from the code above for OpDefNode objects                  *
-     **********************************************************************/
-     Vector tadefs = instanceeCtxt.getByClass(ThmOrAssumpDefNode.class);
-
-     ThmOrAssumpDefNode tadn;       
-        // ThmOrAssumpDefNode in module being instantiated (instancee)
-     ThmOrAssumpDefNode newTadn;    
-       // Its counterpart current module (instancer)
-     APSubstInNode tasubstInTemplate;  // Template to be used for any 
-                                   // SubstInNode wrappers required
-     
-     // Duplicate the OpDef records from the module being INSTANCE'd 
-     for (int i = 0; i < tadefs.size(); i++) {
-       tadn = (ThmOrAssumpDefNode)tadefs.elementAt(i); 
-          // ThmOrAssumpDefNode in module being instantiated (instancee)
-
-       // Following statement added by LL on 30 Oct 2012 to handle locally 
-       // instantiated theorems and assumptions.          
-       if (tadn.isLocal()) {
-           continue ;
-       }
-
-       // If there are parameters to the module being instantiated, then 
-       // a SubstInNode is required, and possibly a different module of 
-       // origin should be indicated
-       if (!instanceeModuleNode.isParameterFree()) {
-         // Create the ThmOrAssumpDefNode for the new instance of this 
-         // definition. Note that the new ThmOrAssumpDefNode shares the 
-         // array of FormalParamNodes with the old ThmOrAssumpDefNode, 
-         // as well as large parts of its body (all but the SubstInNode). 
-         // Hence, changes by a tool to an Original ThmOrAssumpDefNode will 
-         // likely be reflected in all instances of it.
-         if ( tadn.getOriginallyDefinedInModuleNode().isParameterFree() ) { 
-           // Following if/else added by LL on 30 Oct 2012 to handle locally
-           // instantiated theorems and assumptions.
-           if (localness  && topLevel ) {
-               newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), 
-                                           tadn.getBody(), cm, symbolTable, treeNode,
-                                           tadn.getParams(), instanceeModuleNode, 
-                                           tadn.getSource()) ;
-               newTadn.setLocal(true);
-           }
-           else {
-              newTadn = tadn;
-              // On 30 Oct 2012, LL noticed that code was added on 16 Feb 2009
-              // to the corresponding place in the code for an OpDefNode, but that
-              // nothing was added here.  I suspect that something should have been
-              // that the 2009 code should also have been added here, but wasn't--
-              // perhaps because there's no getLabelsHT method for a ThmOrAssumpDefNode.
-              // This may mean that there's a bug in handling labels in the
-              // instantiated theorem or assumption.
-           }
-           /*
-             new ThmOrAssumpDefNode( tadn.getName(), UserDefinedOpKind, 
-                            tadn.getParams(),
-                            localness, tasubstInTemplate, 
-                            tadn.getOriginallyDefinedInModuleNode(), 
-                            symbolTable, treeNode, true );   
-           */
-         }
-         else {        
-           // Create the "wrapping" SubstInNode as a clone of "subst" above, 
-           // but with a body from the ThmOrAssumpDefNode in the module being 
-           // instantiated
-           tasubstInTemplate = new APSubstInNode(treeNode, subst.getSubsts(),
-                                                 tadn.getBody() , cm, 
-                                                 instanceeModuleNode);
-           newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(),
-                                  tasubstInTemplate, cm, symbolTable, 
-                                  treeNode, tadn.getParams(), 
-                                  instanceeModuleNode, tadn.getSource());
-           // Following if/else added by LL on 30 Oct 2012 to handle locally
-           // instantiated theorems and assumptions.
-           newTadn.setLocal(localness) ;
-           // cm.appendDef(newTadn);
-           newTadn.setLabels(tadn.getLabelsHT()) ;
-           
-         }
-       }
-       else { 
-         // There are no parameters to the instancee module; this
-         // means that a SubstInNode is not necessary, and also that
-         // the new operator should be considered to be "originally
-         // defined in" the same module as the old one for purposes of
-         // telling whether they are "the same" or different definitions
-
-         // Create a ThmOrAssumpDefNode whose body is the same as 
-         // the instancer's.
-         if (localness && topLevel) {
-           newTadn = 
-              new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(),
-                            tadn.getBody(), 
-                            tadn.getOriginallyDefinedInModuleNode(), 
-                            symbolTable, treeNode, tadn.getParams(),
-                            instanceeModuleNode, tadn.getSource()); 
-            // Following if/else added by LL on 30 Oct 2012 to handle locally
-            // instantiated theorems and assumptions.
-            newTadn.setLocal(localness) ;
-            newTadn.setLabels(tadn.getLabelsHT()) ;
-         }
-         else {
-         newTadn = tadn;
-         symbolTable.addSymbol(tadn.getName(), tadn);
-         }
-        }
-       if (topLevel) {cm.appendDef(newTadn);} ;
-       /********************************************************************
-       * No recursion fields needed for theorems or assumptions because    *
-       * they can't appear in a recursive section.                         *
-       ********************************************************************/
-       // setOpDefNodeRecursionFields(newTadn, cm) ;
-     } // end for
-
-     // Create a new InstanceNode to represent this INSTANCE stmt in 
-     // the current module
-     InstanceNode inst = 
-         new InstanceNode(null /*no name*/, localness, null /*no parms*/, 
-                          instanceeModuleNode, subst.getSubsts(), treeNode);
-
-     // Append this new InstanceNode to the vector of InstanceNodes
-     // being accumulated for this module
-     if (topLevel){ cm.appendInstance(inst); } ;
-     return inst;
-
-  } // void generateInstance
-
-
-  private final void processTheorem(TreeNode stn, ModuleNode cm) 
-   throws AbortException {
-    ThmOrAssumpDefNode tadn = null ;
-    LevelNode body ;  
-    ProofNode proof = null ;
-    int bodyIndex = stn.heirs().length - 1 ;
-    boolean isAssumeProve = false ;
-      /*********************************************************************
-      * Set true if the body is an ASSUME/PROVE.                           *
-      *********************************************************************/
-    boolean hasProof =    
-                  (stn.heirs()[bodyIndex].getKind() == N_Proof)
-               || (stn.heirs()[bodyIndex].getKind() == N_TerminalProof) ;
-    if (hasProof) {bodyIndex--;} ;
-
-    if (bodyIndex > 1) {
-      /*********************************************************************
-      * If this is a named theorem, start fresh processing of labels,      *
-      * create the object.                                                 *
-      *********************************************************************/
-      pushLS();
-      UniqueString name = stn.heirs()[1].getUS() ;
-      tadn = new ThmOrAssumpDefNode(name, stn) ;
-     } ;
-    if (stn.zero()[bodyIndex].isKind(N_AssumeProve))
-      { /*******************************************************************
-        * Theorem statement is an ASSUME/PROVE.  Must set currentGoal.     *
-        *******************************************************************/
-        currentGoal = tadn ;
-        isAssumeProve = true ;
-
-        /*******************************************************************
-        * We want the symbols declared in top-level NEW statements of the  *
-        * ASSUME clause to be visible in the proof.  Therefore, we do the  *
-        * context push for the top-level AssumeProveNode here instead of   *
-        * in generateAssumeProve.                                          *
-        *******************************************************************/
-        symbolTable.pushContext(new Context(moduleTable, errors)) ;
-        body = generateAssumeProve(stn.heirs()[bodyIndex], cm);
-        currentGoal = null ;
-      } 
-    else { /****************************************************************
-           * Theorem statement must be an ExprNode.                        *
-           ****************************************************************/
-           body = generateExpression(stn.heirs()[bodyIndex], cm);
-      } ;
-   if (bodyIndex > 1) {
-     /**********************************************************************
-     * The theorem node has the form                                       *
-     *      THEOREM Ident == statement                                     *
-     **********************************************************************/
-     Context assumeContext = null;
-     if (isAssumeProve) {
-       /********************************************************************
-       * If the body is an ASSUME/PROVE, we must save in assumeContext     *
-       * the context containing the symbols declared in the ASSUME         *
-       * clause and pop it, so Ident is made visible outside the proof.    *
-       ********************************************************************/
-       assumeContext = symbolTable.getContext() ;
-       symbolTable.popContext() ;
-      } ;
-     UniqueString name = stn.heirs()[1].getUS() ;
-     tadn.construct(true, body, cm, symbolTable, null) ;
-     tadn.setLabels(popLabelNodeSet()) ;
-     cm.appendDef(tadn) ;
-     if (isAssumeProve) {
-       /********************************************************************
-       * After putting Ident into the symbol table, we push the ASSUME     *
-       * context back onto symboltable.                                    *
-       ********************************************************************/
-        symbolTable.pushContext(assumeContext) ;
-      };
-     } ; // if (bodyIndex > 1)
-
-   /************************************************************************
-   * Note: If this is a named theorem, then the name has been added to     *
-   * symbolTable so it can be referred to within the theorem's proof (if   *
-   * it has one).                                                          *
-   ************************************************************************/
-   if (hasProof) {proof = generateProof(stn.heirs()[bodyIndex+1], cm);} ;
-
-   if (isAssumeProve) { 
-     symbolTable.popContext(); 
-       /********************************************************************
-       * Pop the context containing ASSUME declarations.                   *
-       ********************************************************************/
-     ((AssumeProveNode) body).inProof = false ;
-       /********************************************************************
-       * Reset the AssumeProve node's inProof field.                       *
-       ********************************************************************/
-    } ;
-   cm.addTheorem(stn, body, proof, tadn) ;   
-  } // ProcessTheorem
-
-  private final void processAssumption(TreeNode stn, ModuleNode cm) 
-  throws AbortException {
-    ThmOrAssumpDefNode tadn = null ;
-    int lastIndex = stn.heirs().length - 1 ;
-    if (lastIndex > 1) {pushLS();} ;
-      /*********************************************************************
-      * If this is a named assumption, start fresh processing of labels.   *
-      *********************************************************************/
-    ExprNode expr = generateExpression(stn.heirs()[lastIndex], cm);
-
-    if (lastIndex > 1) {
-      /**********************************************************************
-      * The assumption node has the form                                    *
-      *      ASSUME Ident == expression                                     *
-      **********************************************************************/
-      UniqueString name = stn.heirs()[1].getUS() ;
-      tadn = new ThmOrAssumpDefNode(name, false, expr, cm, symbolTable, stn,
-                                    null, null, null) ;
-      tadn.setLabels(popLabelNodeSet()) ;
-      cm.appendDef(tadn) ;
-     } ;
-    cm.addAssumption(stn, expr, symbolTable, tadn);
-    return;
-  } // processAssumption
-
-
-  private final ProofNode generateProof(TreeNode stn, ModuleNode cm) 
-    /***********************************************************************
-    * Node stn is of kind N_TerminalProof or an N_Proof.  The heirs of an  *
-    * N_Proof node consist of an optional PROOF token followed by a        *
-    * seequence of N_ProofStep nodes.  The heirs of an N_ProofStep node    *
-    * are a StartStep() token, a statement body, and an optional proof.    *
-    * A statement body is one of the following node kinds:                 *
-    *                                                                      *
-    *   Have no proof:                                                     *
-    *     N_DefStep   N_UseOrHide   N_NonLocalInstance   N_HaveStep,       *
-    *     N_TakeStep  N_WitnessStep                                        *
-    *                                                                      *
-    *   Have a proof                                                       *
-    *     N_QEDStep  N_PickStep   N_CaseStep   N_AssertStep                *
-    *                                                                      *
-    * Each step produces the following kind of semantic node:              *
-    *                                                                      *
-    *     N_DefStep   : DefStepNode                                        *
-    *                                                                      *
-    *     N_UseOrHide : UseOrNideNode                                      *
-    *                                                                      *
-    *     N_NonLocalInstance : InstanceNode                                *
-    *                                                                      *
-    *     Others: TheoremNode                                              *
-    *       The type of statement is indicated by the body of the          *
-    *       node.  For any step other than an N_AssertStep, the body       *
-    *       is an OpApplNode with the following dummy operator:            *
-    *                                                                      *
-    *          N_HaveStep     : $Have                                      *
-    *          N_CaseStep     : $Pfcase                                    *
-    *          N_TakeStep     : $Take                                      *
-    *          N_PickStep     : $Pick                                      *
-    *          N_WitnessStep  : $Witness                                   *
-    *          N_QEDStep      : $Qed                                       *
-    ***********************************************************************/
-  throws AbortException {
-    int numberOfPops = 0;
-      /*********************************************************************
-      * For each SUFFICES ASSUME/PROVE step, we push a new context onto    *
-      * the symbol table containing declarations of any NEW symbols.       *
-      * These need to be popped off when through processing the proof.     *
-      *********************************************************************/
-    if (stn.getKind() == N_TerminalProof) {
-       return generateLeafProof(stn, cm) ;} ;
-
-    Context pfCtxt = new Context(moduleTable, errors) ;
-    symbolTable.pushContext(pfCtxt);
-      /*********************************************************************
-      * Create a new sub-Context for the proof.                            *
-      *********************************************************************/
-
-    TreeNode heirs[] = stn.heirs() ;
-    int offset = 0 ;
-    if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { offset = 1 ; } ;
-    LevelNode[] steps = new LevelNode[heirs.length - offset] ;
-    Vector iVec = new Vector() ;
-      /*********************************************************************
-      * A vector to hold the InstanceNodes generated by steps of the form  *
-      * Id == INSTANCE ...  so they can be level checked.                  *
-      *********************************************************************/
-
-    /***********************************************************************
-    * At the beginning of each loop iteration, the variable prevIsInFix    *
-    * equals true iff the previous step consists of a formula whose main   *
-    * operator is an infix operator, including an "@-step" of the form "@  *
-    * infix-op expression".  If it is true, then rhSide is the ExprNode    *
-    * of the infix-op's right-hand-side expression.                        *
-    ***********************************************************************/
-    boolean prevIsInfix = false ;
-    ExprNode prevRHS = null ;
-
-    for (int i = offset ; i < heirs.length; i++) {
-      /*********************************************************************
-      * Process proof step i.                                              *
-      *********************************************************************/
-      boolean isAssumeProve = false ;
-        /*******************************************************************
-        * Will be set true for an ASSUME/PROVE, so we can reset the        *
-        * node's inProof field.                                            *
-        *******************************************************************/
-      boolean isSuffices = false ;
-        /*******************************************************************
-        * Will be set to true for a SUFFICES statement.  This is used to   *
-        * do the right thing with ASSUME declarations in an ASSUME/PROVE   *
-        * step, and to set the suffices field of the ThmOrAssumpDefNode    *
-        * if this is a named step.                                         *
-        *******************************************************************/
-        
-      /*********************************************************************
-      * For an ASSUME/PROVE, we set assumeContext to a context containing  *
-      * the declarations from the outer-most NEWs of the ASSUME. For an    *
-      * ordinary ASSUME/PROVE, this context is pushed onto the symbol      *
-      * table for statement's processing the proof.  For a SUFFICE         *
-      * ASSUME/PROVE, it is pushed onto the symbol table after processing  *
-      * the statement's proof so the outermost NEW declarations are        *
-      * visible for the rest of the current proof.                         *
-      * The handling of the assumeContext was changed on 1 Jul 2009        *
-      * because SUFFICE ASSUME/PROVE was not being handled properly.       *
-      *********************************************************************/
-      Context assumeContext = null;
-
-      /*********************************************************************
-      * For "PICK x : exp", the symbol x is declared in exp, undeclared    *
-      * in the proof of the step, and declared afterwards.  (There'd be a  *
-      * similar problem with TAKE if it took a proof.)  The following      *
-      * variables are used to manipulate this, with pickContext holding    *
-      * the declarations of the symbols introduced by the PICK step.       *
-      *********************************************************************/
-      boolean isPick = false;
-      Context pickContext = null;
-
-      /*********************************************************************
-      * Set stepNumSTN, stepBodySTN, and stepPfSTN to the syntax tree      *
-      * nodes of the step number, the body, and the proof (the latter      *
-      * equal to null if there's no proof.                                 *
-      *********************************************************************/
-      TreeNode pfStepSTN   = heirs[i] ;
-      TreeNode stepNumSTN  = pfStepSTN.heirs()[0] ;
-      TreeNode stepBodySTN = pfStepSTN.heirs()[1] ;
-      TreeNode stepPfSTN   = null ;
-      if (pfStepSTN.heirs().length > 2) {stepPfSTN = pfStepSTN.heirs()[2];};
-
-      LevelNode pfNumNode = null ;
-      boolean makePfNumNode = true ;
-        /*******************************************************************
-        * For a numbered step that doesn't produce a TheoremNode,          *
-        * pfNumNode is set to a node that will be the stepNode of a        *
-        * NumberedProofStepKind OpDefNode.                                 *
-        *******************************************************************/
-      /*
-       * On 23 Feb 2014 LL added the following code to make step numbers
-       * like <*>13 illegal, so <+> and <*> can be used only for unnamed
-       * steps.  It would be most natural to make the change age the
-       * parsing level by changing the JavaCC code.  However, that code
-       * is a Kludge that it is best not to touch unless absolutely necessary.
-       * Also, detecting the error here allows multiple instances of the
-       * error to be reported in a single execution of the parser.
-       * 
-       * The modification is based on the following empirical observation:
-       * 
-       *   - For a step number like "<3>13."
-       *       stepNumSTN.image = stepNumSTN.originalImage = "<3>13."
-       *   - For a step number like "<*>13.",
-       *       stepNumSTN.image = "<3>13." and stepNumSTN.originalImage = "<*>13."
-       *   - For unnumbered steps,
-       *       stepNumSTN.originalImage = null  and    stepNumSTN.image = the actual token.
-       */
-      SyntaxTreeNode STN = (SyntaxTreeNode) stepNumSTN ;
-      if (    (STN.originalImage != null)
-           && (STN.originalImage != STN.image) 
-           ) {
-          String oimage = STN.originalImage.toString() ;
-          if   (    (! oimage.equals(STN.image.toString()))
-                 && ( (oimage.charAt(1) == '*') || (oimage.charAt(1) == '+') )
-               ) {
-              errors.addError(stepNumSTN.getLocation(),
-                      "<*> and <+> cannot be used for a named step.");
-          }          
-      }
-
-      /*********************************************************************
-      * Set stepNum to the step number, or null if its an unnumbered step. *
-      *********************************************************************/
-      UniqueString stepNum = null ;
-      switch (stepNumSTN.getKind()) {
-      // LL: On 25 Feb 2010 I discovered the following comment here:
-      //      XXXXXX  xyz: need to add the following case
-      // The ProofImplicitStepLexeme case (something like <*>3) seems to be 
-      // handled properly in tests.  I presume that this is an obsolete
-      // comment that I didn't remove when I added the case to the code. 
-        case TLAplusParserConstants.ProofImplicitStepLexeme :
-        case TLAplusParserConstants.ProofStepLexeme :
-          stepNum = stepNumSTN.getUS() ; 
-          break ;
-        case TLAplusParserConstants.ProofStepDotLexeme :
-          String stNum = stepNumSTN.getUS().toString() ;
-          stepNum = UniqueString.uniqueStringOf(
-                      stNum.substring(0, stNum.indexOf("."))) ;
-          break ;
-        default : 
-          makePfNumNode = false ;
-          break ;
-      } ; // switch
-
-      /*********************************************************************
-      * If this is a numbered step, process labels.                        *
-      *********************************************************************/
-      if (stepNum != null) {
-        pushLS();
-        } ;
-
-       /********************************************************************
-       * Construct the ThmOrOpDefNode if needed.  (We need to do it now    *
-       * because we have to set currentGoal before we generate the body.)  *
-       ********************************************************************/
-       ThmOrAssumpDefNode tadn = null ;
-       if (stepNum != null) { 
-         tadn = new ThmOrAssumpDefNode(stepNum, stepNumSTN) ;
-        } ;
-      
-      /*********************************************************************
-      * Set prevIsInfix false unless this is an AssertStep node.           *
-      *********************************************************************/
-      int stepKind = stepBodySTN.getKind();
-      if (stepKind != N_AssertStep) {prevIsInfix = false ;} ;
-
-      switch (stepKind) {
-        case N_DefStep :
-          /*****************************************************************
-          * Set defSTNs to the array of heirs, and defOffSet so that       *
-          * defSTNs[defOffSet] ...  defSTNs[defSTNs.length-1] is the       *
-          * sequence of definitions.                                       *
-          *****************************************************************/
-          TreeNode[] defSTNs = stepBodySTN.heirs();
-          int defOffSet = 0 ;
-          if (defSTNs[0].getKind() == DEFINE) {defOffSet = 1;};
-
-          OpDefNode[] defs = new OpDefNode[defSTNs.length - defOffSet] ;
-            /***************************************************************
-            * Will be set to the sequence of OpDefNodes for the            *
-            * definitions.                                                 *
-            ***************************************************************/
-          for (int j = defOffSet; j < defSTNs.length ; j++) {
-            TreeNode defSTN = defSTNs[j]; ;
-            Vector vec = new Vector() ;
-            switch (defSTN.getKind()) {
-              /***************************************************************
-              * Need to check if it's an operator, function, or module       *
-              * definition.                                                  *
-              ***************************************************************/
-              case N_FunctionDefinition :
-                processFunction(defSTN, vec, cm) ;
-                break ;
-              case N_ModuleDefinition :
-                /*************************************************************
-                * The call to processModuleDefinition sets defsVec to a      *
-                * vector of all the definitions it makes, and adds the new   *
-                * InstanceNode to iVec.  For now, we're just throwing        *
-                * away defsVec.  (If defsVec were null, then                 *
-                * processModuleDefinition would add these definitions to to  *
-                * the module's list of top-level definitions.)               *
-                *************************************************************/
-                Vector defsVec = new Vector() ;
-                vec.addElement(
-                   processModuleDefinition(defSTN, defsVec, iVec, cm)) ;
-                break ;
-              case N_OperatorDefinition :
-                processOperator(defSTN, vec, cm) ;
-                /*************************************************************
-                * processOperator creates an OpDefNode, puts an entry for    *
-                * it in symbolTable, and adds the OpDefNode to vec.          *
-                *************************************************************/
-                break ;
-             }; // switch (def.getKind())
-            defs[j - defOffSet] = (OpDefNode) vec.elementAt(0);
-           }; // for j 
-          pfNumNode = new DefStepNode(stepBodySTN, stepNum, defs) ;
-          steps[i - offset] = pfNumNode ;
-          break ;
-
-        case N_UseOrHide :
-          UseOrHideNode uohn = generateUseOrHide(stepBodySTN, cm) ;
-
-          // Added by LL on 16 Jun 2010 so location returnedby getLocation() will
-          // include the step number.
-          uohn.stn = pfStepSTN; 
-
-          uohn.setStepName(stepNum);  // Added 6 June 2010 by LL.
-          
-          if (uohn.facts.length + uohn.defs.length == 0) {
-            errors.addError(stepBodySTN.getLocation(),
-                            "Empty USE or HIDE statement.");
-           };
-           uohn.factCheck();
-             // Added 4 Mar 2009.
-           pfNumNode = uohn ;
-          steps[i - offset] = pfNumNode ;
-          break ;
-
-        case N_NonLocalInstance :
-          // Code to set step name added by LL on 6 June 2010
-          InstanceNode inst = generateInstance(stepBodySTN, cm, false);
-          inst.setStepName(stepNum);
-          pfNumNode = inst;
-          steps[i - offset] = pfNumNode ;
-          break ;
-
-        default :
-          makePfNumNode = false ;
-          TreeNode[] bodyHeirs = stepBodySTN.heirs() ;
-          LevelNode body = null ;
-            /***************************************************************
-            * This will be set to the body of the TheoremNode or           *
-            * ThmOrAssumpDefNode.                                          *
-            ***************************************************************/
-          UniqueString op = null ;
-          ExprNode[] args ;
-            /***************************************************************
-            * For anything but an N_Assert node, body is set to an         *
-            * OpApplNode having these as its operator and arguments.       *
-            ***************************************************************/
-
-          switch (stepBodySTN.getKind()) {
-           case N_AssertStep :
-             int bodyNext = 0 ;
-             if (bodyHeirs[0].getKind() == 
-                    TLAplusParserConstants.SUFFICES) {
-               bodyNext = 1 ;
-               isSuffices = true ;
-               /************************************************************
-               * We can't have an "@" in a SUFFICES step.                  *
-               ************************************************************/
-              } ;
-             if (bodyHeirs[bodyNext].getKind() == N_AssumeProve) {
-               /************************************************************
-               * This is an AssumeProve node.                              *
-               ************************************************************/
-               isAssumeProve = true ;
-
-               /************************************************************
-               * For an ASSUME/PROVE, we need save the symbol              *
-               * declarations from top-level NEW statements in the ASSUME  *
-               * to make them visible only in the statement's proof for    *
-               * an ordinary ASSUME/PROVE, and only after the statement's  *
-               * proof for a SUFFICES ASSUME/PROVE.                        *
-               ************************************************************/
-               symbolTable.pushContext(new Context(moduleTable, errors)) ;
-
-               currentGoal = tadn ;
-                 /**********************************************************
-                 * Need to set currentGoal before generating the           *
-                 * AssumeProve node.                                       *
-                 **********************************************************/
-               body = generateAssumeProve(bodyHeirs[bodyNext], cm);
-
-               if (isSuffices) { ((AssumeProveNode) body).setSuffices(); };
-                 /**********************************************************
-                 * Added 16 Feb 2009 by LL.                                *
-                 **********************************************************/
-               currentGoal = null ;
-               assumeContext = symbolTable.getContext() ;
-               symbolTable.popContext() ;
-               prevIsInfix = false ;
-               }
-             else {  
-               /************************************************************
-               * This is an ordinary expression.                           *
-               ************************************************************/
-               TreeNode curExpr = bodyHeirs[bodyNext] ;
-
-               /************************************************************
-               * Special handling of SUFFICES added by LL 16 Feb 2009.     *
-               ************************************************************/
-               if (isSuffices) {  
-                 args = new ExprNode[1] ;
-                 args[0] = generateExpression(curExpr, cm) ;
-                 body = new OpApplNode(OP_suffices, args, stepBodySTN, cm) ;
-                } // if (isSuffices) 
-               else {  
-                 /************************************************************
-                 * If the current expression is an infix expression, set     *
-                 * curLHS to its current LHS, otherwise set it to null.      *
-                 ************************************************************/
-                 SyntaxTreeNode curLHS  = null ;
-                 if (curExpr.getKind() == N_InfixExpr) {
-                   curLHS = (SyntaxTreeNode) curExpr.heirs()[0] ;
-                  } ;
-                 /************************************************************
-                 * If prevIsInfix is true and curLHS is an "@", then         *
-                 * process it, using as the right-hand side a new $Nop node  *
-                 * with prevRHS as its argument.                             *
-                 ************************************************************/
-                 if (   prevIsInfix
-                     && (curLHS != null)
-                     && (curLHS.heirs().length > 0)
-                        /***************************************************
-                        * This test added 25 Feb 2010 because if curLHS    *
-                        * is a string, then curLHS.heirs() is a            *
-                        * zero-length array, so the following test threw   *
-                        * an out-of-bounds array index exception.  Note    *
-                        * that curLHS.heirs() should never be null,        *
-                        * because the heirs() method can never return      *
-                        * null.                                            *
-                        ***************************************************/
-                     && (((SyntaxTreeNode) 
-                             curLHS.heirs()[0]).heirs().length == 0) 
-                     && (curLHS.heirs()).length > 1
-                        /***************************************************
-                        * This test added 2 Mar 2009 to fix following      *
-                        * bug.  When we are here and the left-hand side    *
-                        * is something like a number, then curLHS.heirs()  *
-                        * seems to have length 1 and the following test    *
-                        * causes an ArrayIndexOverflowException.           *
-                        ***************************************************/
-                     && (curLHS.heirs()[1].getKind() == IDENTIFIER)
-                     && (curLHS.heirs()[1].getUS() == AtUS)) {
-  
-                   /**********************************************************
-                   * The following code obtained by a simple modification    *
-                   * of the N_InfixExpr case of generateExpression.          *
-                   **********************************************************/
-                   TreeNode[] children = curExpr.heirs() ;
-                   GenID genID = generateGenID(children[1], cm);
-                   ExprNode[]sns = new ExprNode[2];
-                   SymbolNode opn = 
-                      symbolTable.resolveSymbol(
-                        Operators.resolveSynonym(genID.getCompoundIDUS()));
-                   if ( opn == null ) {
-                      errors.addError(curExpr.getLocation(),
-                          "Couldn't resolve infix operator symbol `" + 
-                          genID.getCompoundIDUS() + "'." );
-                      return null;
-                    } ;
-                   sns[1] = generateExpression( children[2], cm );
-                   /**********************************************************
-                   * Set sns[1] to a new $Nop OpApplNode whose argument is   *
-                   * prevRHS.                                                *
-                   **********************************************************/
-                   ExprNode[] nopArgs = new ExprNode[1] ;
-                   nopArgs[0] = prevRHS ;
-                   sns[0] = new OpApplNode(OP_nop, nopArgs,  curLHS, cm) ;
-                   body = new OpApplNode(opn, sns, curExpr, cm);
-                  }  // if ( prevIsInfix ...)
-                 else { // this is not an @-step
-                   body = generateExpression(curExpr, cm) ;
-                  } ;
-                 
-                 /************************************************************
-                 * If this is an infix ioperator, set prevIsInfix true and   *
-                 * prevRHS equal to its right-hand argument, else set        *
-                 * prevIsInfix false.                                        *
-                 ************************************************************/
-                 prevIsInfix = false ;
-                 if  (   (curLHS != null)
-                      /*******************************************************
-                      * The following conjuncts should be true unless there  *
-                      * was an error in the expression.                      *
-                      *******************************************************/
-                      && (body != null)    
-                      && (body.getKind() == OpApplKind)
-                      && ( ((OpApplNode) body).getArgs().length > 1)) {
-                   prevIsInfix = true ;
-                   prevRHS = (ExprNode) ((OpApplNode) body).getArgs()[1] ;
-                  }
-                }
-              }; // else This is an ordinary expression.
-             break ;
- 
-           case N_HaveStep :
-           case N_CaseStep :
-             if (stepBodySTN.getKind() == N_HaveStep) {op = OP_have ;}
-             else {op = OP_pfcase ;} ;
-             args = new ExprNode[1] ;
-             args[0] = generateExpression(bodyHeirs[1], cm) ;
-             body = new OpApplNode(op, args, stepBodySTN, cm) ;
-             break ;
-
-           case N_TakeStep :
-           case N_PickStep :
-             if (stepBodySTN.getKind() == N_TakeStep) {op = OP_take ;}
-             else {
-               op = OP_pick ;
-               isPick = true;
-               /************************************************************
-               * Push a new context onto the symbolTable stack to get the  *
-               * declarations of the PICK symbols.                         *
-               ************************************************************/
-               symbolTable.pushContext(new Context(moduleTable, errors)) ;
-              } ;
-
-             if (bodyHeirs[1].getKind() == N_QuantBound) {
-               /************************************************************
-               * The introduced identifiers are bounded--e.g., "TAKE id    *
-               * \in Set".                                                 *
-               ************************************************************/
-               
-               /************************************************************
-               * Set quants to the number of N_QuantBound nodes.           *
-               ************************************************************/
-               int quants = 1 ;
-               int nextTok = 2;
-               while (   (nextTok < bodyHeirs.length) 
-                      && (bodyHeirs[nextTok].getKind() 
-                             == TLAplusParserConstants.COMMA)) {
-                 quants++ ;
-                 nextTok = nextTok + 2 ;
-                };
-               FormalParamNode[][] params = new FormalParamNode[quants][0];
-               boolean[]           bt = new boolean[quants] ;
-               ExprNode[]          paramBounds = new ExprNode[quants];
-               processQuantBoundArgs(
-                   bodyHeirs, 1, params, bt, paramBounds, cm) ;
-
-               if (isPick) {
-                 /**********************************************************
-                 * Save the declarations in pickContext.                   *
-                 **********************************************************/
-                 pickContext = symbolTable.getContext() ;
-                 /**********************************************************
-                 * This is a PICK step; get the ": expr".                  *
-                 **********************************************************/
-                 nextTok++ ; // Skip over the ":"
-                 args = new ExprNode[1] ;
-                 pushFormalParams(flattenParams(params)) ;
-                   /********************************************************
-                   * Push the bound variables on Last(LS).paramSeq and     *
-                   * process the body of the PICK.                         *
-                   ********************************************************/
-                 args[0] = generateExpression(bodyHeirs[nextTok], cm) ;
-                 popFormalParams() ;
-                 symbolTable.popContext() ;
-                   /********************************************************
-                   * Remove the bound symbols from the symbol table so     *
-                   * they're undefined for the proof of the PICK.          *
-                   ********************************************************/
-                }  
-               else {
-                 /**********************************************************
-                 * This is a TAKE step.                                    *
-                 **********************************************************/
-                 args = new ExprNode[0] ;
-                };
-               body = new OpApplNode(op, null, args, params, 
-                                     bt, paramBounds, stepBodySTN, cm) ;
-              }
-             else {
-               /************************************************************
-               * The introduced identifiers are unbounded--e.g., "TAKE     *
-               * id1, id2".                                                *
-               ************************************************************/
-               
-               /************************************************************
-               * Set ids to the number of introduced identifiers.          *
-               ************************************************************/
-               int ids = 1 ;
-               while (   (2*ids < bodyHeirs.length) 
-                      && (bodyHeirs[2*ids].getKind() == 
-                            TLAplusParserConstants.COMMA)) {ids++;} ;
-     
-               /************************************************************
-               * Set params to the array of new FormalParamNodes for the   *
-               * identifiers.  The identifiers are added to the current    *
-               * symbol table.                                             *
-               ************************************************************/
-               FormalParamNode[]   params = new FormalParamNode[ids];
-               for (int j = 0 ; j < ids ; j++) {
-                  params[j] = new FormalParamNode(
-                                    bodyHeirs[2*j + 1].getUS(), 0, 
-                                    bodyHeirs[2*j + 1], symbolTable, cm);
-                 } ;
-     
-               if (isPick) {
-                 /**********************************************************
-                 * Save the declarations in pickContext.                   *
-                 **********************************************************/
-                  pickContext = symbolTable.getContext() ;
- 
-                 /**********************************************************
-                 * This is a PICK step; get the ": expr".                  *
-                 **********************************************************/
-                 pushFormalParams(params) ;
-                   /********************************************************
-                   * Push formal parameters on Last(LS).paramSeq for       *
-                   * processing the body.                                  *
-                   ********************************************************/
-                 args = new ExprNode[1] ;
-                 args[0] = generateExpression(bodyHeirs[2*ids + 1], cm) ;
-                 popFormalParams() ;
-                 symbolTable.popContext() ;
-                   /********************************************************
-                   * Remove the bound symbols from the symbol table so     *
-                   * they're undefined for the proof of the PICK.          *
-                   ********************************************************/
-               }
-               else {
-                 /**********************************************************
-                 * This is a TAKE step.                                    *
-                 **********************************************************/
-                 args = new ExprNode[0] ;
-                };
-               body = new OpApplNode(op, args, params, stepBodySTN, cm) ;
-              } ;
-
-             break ;
-
-           case N_WitnessStep :
-             /**************************************************************
-             * Set ids to the number of expressions.                       *
-             **************************************************************/
-             int ids = 1 ;
-             while (   (2 * ids < bodyHeirs.length) 
-                    && (bodyHeirs[2 * ids].getKind() == 
-                          TLAplusParserConstants.COMMA)) {ids++;} ;
-
-             args = new ExprNode[ids] ;
-             for (int j = 0 ; j < ids ; j++) {
-               args[j] = generateExpression(bodyHeirs[2*j + 1], cm);
-                 } ;
-             body = new OpApplNode(OP_witness, args, stepBodySTN, cm) ;
-             break ;
-
-           case N_QEDStep :
-             args = new ExprNode[0] ;
-             body = new OpApplNode(OP_qed, args, stepBodySTN, cm) ;
-             break ;
-
-           default :
-             errors.addAbort(
-                stn.getLocation(),
-                "Internal error: Unexpected SyntaxTreeNode kind: " 
-                  + heirs[i].getKind()) ;
-             break ;
-        }; // switch
-
-
-       /********************************************************************
-       * Set the fields of the ThmOrOpDefNode if there is one, including   *
-       * the suffices field.                                               *
-       ********************************************************************/
-       if (stepNum != null) { 
-         tadn.construct(true, body, cm, symbolTable, null) ;
-         tadn.setLabels(popLabelNodeSet()) ;
-         if (isSuffices) { tadn.setSuffices() ;} ;
-        } ;
-       /***********************************************************************
-       * Set proof to the proof, or to null if there is none.  There is no    *
-       * check made to see if this is a kind of step that should have a       *
-       * proof.  Thus, adding a proof to something like a WITNESS statement   *
-       * requires changing only the parsing phase (specified by tla+.jj).     *
-       ***********************************************************************/
-       ProofNode proof = null ;
-       if (stepPfSTN != null) { 
-         /******************************************************************
-         * For an ordinary ASSUME/PROVE, must make the ASSUME's            *
-         * declarations visible in the statement's proof.                  *
-         ******************************************************************/
-         if (isAssumeProve && !isSuffices) {
-           symbolTable.pushContext(assumeContext) ;
-          } ;
-         proof = generateProof(stepPfSTN, cm); 
-         if (isAssumeProve && !isSuffices) {symbolTable.popContext(); } ;
-        } ;
-
-       /********************************************************************
-       * For a SUFFICES ASSUME/PROVE, must make the ASSUME's declarations  *
-       * visible after the proof.  This is done by pushing assumeContext   *
-       * onto the symbol table and incrementing numberOfPops so it will    *
-       * be popped at the end of the proof.                                *
-       ********************************************************************/
-       if (isAssumeProve && isSuffices) {
-           numberOfPops++ ;
-           symbolTable.pushContext(assumeContext) ; } ;
-       if (isAssumeProve) { ((AssumeProveNode) body).inProof = false ; } ;
-         /******************************************************************
-         * For an ASSUME/PROVE, set the inProof field to false.            *
-         ******************************************************************/
-       TheoremNode thm = new TheoremNode(stepBodySTN, body, cm, proof, tadn);
-       
-       // Added by LL on 16 Jun 2010 so location returnedby getLocation() will
-       // include the step number.
-       thm.stn = pfStepSTN; 
-
-       thm.suffices = isSuffices ;
-       steps[i - offset] = thm; 
-       }; // switch
-      if (makePfNumNode) {
-        /*******************************************************************
-        * Make an OpDefNode for the numbered step and add any label        *
-        * declarations to it.                                              *
-        *******************************************************************/
-        OpDefNode nodeMadeOnlyToBePutInSymbolTable =
-           new OpDefNode(stepNum, pfNumNode, cm, symbolTable, pfStepSTN) ;
-        nodeMadeOnlyToBePutInSymbolTable.setLabels(popLabelNodeSet()) ;
-       }
-      if (isPick) {
-        /*******************************************************************
-        * We have to take the symbol declarations from pickContext and     *
-        * put them into the current symbol table.                          *
-        *******************************************************************/
-        Enumeration e = pickContext.content() ;
-        while (e.hasMoreElements()) {
-          SymbolNode sym = ((Context.Pair)(e.nextElement())).getSymbol();
-          symbolTable.addSymbol(sym.getName(), sym) ;
-         }
-       }
-     } ; // for i
-    InstanceNode[] insts = new InstanceNode[iVec.size()] ;
-    for (int i = 0 ; i < insts.length; i++) {
-      insts[i] = (InstanceNode) iVec.elementAt(i) ; 
-     }; 
-
-    /***********************************************************************
-    * Pop the contexts that were pushed onto the symbol table for SUFFICE  *
-    * ASSUME/PROVE steps.                                                  *
-    ***********************************************************************/
-    for (int i = 0; i < numberOfPops; i++) {
-      // Added by LL on 24 June 2010
-      // Need to add the symbols in the context being popped to pfCtxt
-      // so they will be put into the context of the NonLeafProofNode.
-      Context topContext = symbolTable.getContext();
-      Enumeration e = topContext.content() ;
-      while (e.hasMoreElements()) {
-        SymbolNode sym = ((Context.Pair)(e.nextElement())).getSymbol();
-        pfCtxt.addSymbolToContext(sym.getName(), sym) ;
-       }
-      symbolTable.popContext(); 
-     };
-    symbolTable.popContext();
-    return new NonLeafProofNode(stn, steps, insts, pfCtxt);
-      /*********************************************************************
-      * Pop the sub-Context.                                               *
-      *********************************************************************/
-   } // generateProof
-
-/***************************************************************************
-* The following method is not used and I have no idea why it's still       *
-* here.                                                                    *
-***************************************************************************/
-  private final LevelNode generateNumerableStep(
-          TreeNode stn,
+		pushFormalParams(params);
+		/*******************************************************************
+		 * Push formal parameters on Last(LS).paramSeq for processing the * body
+		 *******************************************************************/
+		ExprNode body = generateExpression(children[children.length - 1], cm);
+		/*********************************************************************
+		 * Generate the lambda expression's body. *
+		 *********************************************************************/
+		popFormalParams();
+
+		symbolTable.popContext();
+		/*********************************************************************
+		 * Restore original context. *
+		 *********************************************************************/
+		return new OpDefNode(S_lambda, // The operator name is "LAMBDA"
+				UserDefinedOpKind, // The node kind for a lambda expression
+				params, // the array of formal parameters
+				false, // localness, which is meaningless
+				body, // the body (an expression node)
+				cm, // the module
+				null, // the symbol table, which is null for an
+						// OpDefNode representing a lambda expression.
+				syntaxTreeNode, true, // Is defined. Its value should not matter.
+				null); // Source
+	} // generateLambda
+
+	/**
+	 * Generates an OpApplNode or an OpArgNode for a SyntaxTreeNode, according to
+	 * whether the value of "typeExpected" is either opAppl or opArg.
+	 */
+	private final ExprNode generateOpAppl(TreeNode syntaxTreeNode, ModuleNode cm) throws AbortException {
+		TreeNode primaryArgs = null;
+		// points to syntax tree of primary arg list (if any); otherwise null
+		boolean isOpApp = syntaxTreeNode.isKind(N_OpApplication);
+		// true ==> to indicate this is an OpAppl; must have primary args
+		// false ==> operator used as an argument (OpArg); has no primary args
+		int primaryArgCount = 0;
+		// total # of arguments, including those of prefix, if any
+		int len;
+		// number of prefix elements
+		TreeNode[] children = syntaxTreeNode.heirs();
+		// temp used for finding the prefix
+		TreeNode[] prefix;
+		// array of prefix elements
+		TreeNode[] allArgs = null;
+		// to collect arg arrays from both prefix and the main op (if any)
+		TreeNode[] prefixElt;
+		// a prefixElement; 2 or 3 elem array: [op, (args), "!"]
+		UniqueString symbol;
+		// UniqueString name of the fully-qualified operator
+		SymbolNode fullOperator;
+		// The SymbolNode for the fully-qualified operator
+		int iarg = 0;
+		// loop counter for actual args (as opposed to
+		// arg syntax elements like commas and parens)
+		TreeNode[] argsList = null;
+		// Will hold an array of arg syntax trees for primary operator
+		ExprOrOpArgNode[] args = null;
+		// Will hold an array of arg semantic trees for primary operator
+
+		// Process the Generized ID that is the operator for this OpAppl
+		GenID genID = generateGenID(children[0], cm);
+
+		// Set up pointers to OpAppl's primary args
+		primaryArgs = children[1];
+		// Array of argument list syntax elements for the main
+		// (rightmost) operator, including parens and commas;
+		// should be an N_OpArgs node
+
+		// calc number of primary args;
+		// args are interspersed w/ parens & commas--hence the /2
+		primaryArgCount = primaryArgs.heirs().length / 2;
+
+		if (genID == null || genID.getFullyQualifiedOp() == null) {
+			// if operator is @ or an unresolved symbol; error has already
+			// been generated inside genID
+			return nullOAN;
+		}
+
+		args = new ExprOrOpArgNode[primaryArgCount];
+		// Array to hold semantic trees for primary args
+
+		// pick up array of arg list syntax elements
+		argsList = primaryArgs.heirs();
+
+		// The odd numbered syntax elements are the args expressions; the
+		// even numbered ones are parens and commas.
+		// for each arg in this arg list ...
+		for (int ia = 1; ia < argsList.length; ia += 2) {
+			// Each arg may be an ordinary expression, or it may be an OpArg;
+			// produce appropriate semantic tree or node for it.
+			// Note that operators can be used in place of expressions
+			// in only two contexts:
+			// as argument to suitable user-defined ops, and in the RHS
+			// of a substitution in module instantiation
+			args[iarg] = generateExprOrOpArg(genID.getFullyQualifiedOp(), syntaxTreeNode, iarg, argsList[ia], cm);
+			iarg++; // count the actual args
+		} // end for
+
+		// Concatenate the list of args in the GenID object to the
+		// primary arg list just created
+		Vector genIDArgList = genID.getArgsVector();
+		ExprOrOpArgNode[] finalArgList = new ExprOrOpArgNode[genIDArgList.size() + iarg];
+
+		// Copy the args from the prefix
+		for (int i = 0; i < genIDArgList.size(); i++) {
+			finalArgList[i] = (ExprOrOpArgNode) (genIDArgList.elementAt(i));
+		}
+		// Copy the primary args
+		for (int i = 0, j = genIDArgList.size(); i < iarg; i++, j++) {
+			finalArgList[j] = args[i];
+		}
+
+		// return an OpApplNode constructed from the fully-qualified
+		// operator and the final arg list
+		return new OpApplNode(genID.getFullyQualifiedOp(), finalArgList, syntaxTreeNode, cm);
+	} // end generateOpAppl()
+
+	/**
+	 * Process a named, parameterixed instantiation, e.g. of the form D(p1,...pn) =
+	 * INSTANCE M WITH a1 <- e1 ,..., ar <- er
+	 */
+	/*************************************************************************
+	 * Note: the returned value does not seem to be used. *
+	 *************************************************************************/
+	private final OpDefNode processModuleDefinition(TreeNode treeNode, Vector defs,
+			/*******************************************************
+			 * This is non-null when called from inside a LET, in * which case the OpDef
+			 * node is appended to defs. If * null, the OpDef node is appended to the *
+			 * module-level lists of such nodes. *
+			 *******************************************************/
+			Vector insts,
+			/*******************************************************
+			 * If non-null, then a vector of InstanceNode objects * to which the current
+			 * instance node is to be * appended. If null, cm.appendInstance is called to *
+			 * put the InstanceNode onto the module-level lists of * such nodes. *
+			 *******************************************************/
+			ModuleNode cm) throws AbortException {
+		// Start with a LHS for an instance: we must extract from it name
+		// and possibly parameters. Then we need the external Context of
+		// the module and to extract all non-local, non-builtin
+		// symbols. Then build the proper symbol list and add it.
+
+		// We must remember to identify explicitly whether or not the new
+		// nodes would be local.
+
+		// assert treeNode.isKind(N_ModuleDefinition)
+		//
+		// Note that this code does nothing about THEOREMS and ASSUMES in
+		// modules being instantiated
+		boolean localness = treeNode.zero() != null;
+		TreeNode[] children = treeNode.one()[0].heirs(); // heirs of IdentLHS
+		UniqueString name = children[0].getUS();
+
+		// processing of LHS of the definition, i.e. the name and parameters
+		FormalParamNode[] args = nullParam;
+		Context parmCtxt = null;
+
+		// If the operator being defined as a module instance has any parameters
+		if (children.length > 1) {
+			// Create new array of FormalParamNodes for the new operator
+			args = new FormalParamNode[children.length / 2 - 1];
+
+			// Push a new context in current module's SymbolTable
+			parmCtxt = new Context(moduleTable, errors);
+			symbolTable.pushContext(parmCtxt);
+
+			// For each formal parameter declared for the op being defined
+			for (int i = 0; i < args.length; i++) {
+				TreeNode child = children[2 + 2 * i];
+				UniqueString id = null;
+				int count = 0;
+
+				if (child.isKind(N_IdentDecl)) {
+					id = child.heirs()[0].getUS();
+					count = (child.heirs().length - 1) / 2;
+				} else if (child.isKind(N_InfixDecl)) {
+					id = child.heirs()[1].getUS();
+					count = 2;
+				} else if (child.isKind(N_PrefixDecl)) {
+					id = child.heirs()[0].getUS();
+					count = 1;
+				} else if (child.isKind(N_PostfixDecl)) {
+					id = child.heirs()[1].getUS();
+					count = 1;
+				} else {
+					errors.addAbort(treeNode.getLocation(),
+							"Internal error: Error in formal params part of parse tree.", true);
+				}
+
+				// If there was no error
+				if (id != null) {
+					// Create a new FormalParamNode for the defined Op and put
+					// it in the SymbolTable
+					args[i] = new FormalParamNode(id, count, child, symbolTable, cm);
+				} // end if
+			} // end for
+		} // end if
+
+		// processing RHS of the definition, starting with identification
+		// of module being instantiated, followed by processing of the
+		// WITH clause (if any)
+		children = treeNode.one()[2].heirs(); // heirs of NonLocalInstance
+
+		// Find the Context and ModuleNode for the module being instantiated
+		Context instanceeCtxt = this.getContext(children[1].getUS());
+		ModuleNode instanceeModule = symbolTable.resolveModule(children[1].getUS());
+
+		if (instanceeCtxt == null) {
+			errors.addError(children[1].getLocation(),
+					"Module " + children[1].getImage() + " does not have a context.");
+			return nullODN;
+		}
+
+		if (instanceeModule == null) {
+			errors.addError(children[1].getLocation(),
+					"Module name " + children[1].getImage() + " is not known" + " in current context.");
+			return nullODN;
+		}
+
+		/*
+		 * Set isInstantiated field of instancee. Added by LL 23 July 2013
+		 */
+		instanceeModule.setInstantiated(true);
+
+		// Create a SubstInNode that will be used to wrap each definition
+		// body in the module being defined. "children" is the array of
+		// explicit substitution clauses used in the module definition.
+		// Both instanceeCtxt and symbolTable are involved here since for
+		// each substitution c <- e, c must be resolved in the
+		// instanceeCtxt, and e must be interpreted in symbolTable
+		SubstInNode substIn = processSubst(treeNode, children, symbolTable, instanceeCtxt, instanceeModule, cm);
+
+		// We are done with the local context (if one was created because
+		// of parameters)
+		if (parmCtxt != null)
+			symbolTable.popContext();
+
+		// Create a vector of all of the OpDefNodes in the instancee module
+		/***********************************************************************
+		 * I have no idea why the module's getOpDefs method isn't used here. *
+		 ***********************************************************************/
+		Vector elts = instanceeCtxt.getByClass(OpDefNode.class);
+
+		// For each definition in the instancee module, create a
+		// corresponding definition in the instancer module
+		for (int i = 0; i < elts.size(); i++) {
+			// Find the OpDefNode to be instantiated
+			OpDefNode odn = (OpDefNode) elts.elementAt(i);
+
+			/**********************************************************************
+			 * Ignore it if it is local or a builtin def. *
+			 **********************************************************************/
+			if (!odn.isLocal() && ((odn.getKind() == UserDefinedOpKind) || (odn.getKind() == ModuleInstanceKind))) {
+				// Create the new name prepended with "name!"
+				String compoundID = name + "!" + odn.getName();
+				UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID);
+
+				// Copy parameters for the op being defined
+				FormalParamNode[] fpn = odn.getParams();
+				FormalParamNode[] params = new FormalParamNode[fpn.length + args.length];
+				System.arraycopy(args, 0, params, 0, args.length);
+				System.arraycopy(fpn, 0, params, args.length, fpn.length);
+
+				OpDefNode newOdn;
+				if (odn.getKind() == UserDefinedOpKind) {
+					if (substIn.getSubsts().length > 0) {
+						// If there are substitutions, then the body of the new
+						// definition instance must be wrapped in a SUBST-IN node.
+						// Create the "wrapping" SubstInNode as a clone of "subst"
+						// above, but with a body from the OpDefNode in the module
+						// being instantiated
+						SubstInNode substInNode = new SubstInNode(treeNode, substIn.getSubsts(), odn.getBody(), cm,
+								instanceeModule);
+
+						// Create the OpDefNode for the new instance of this
+						// definition; because of the new operator name, cm is the
+						// module of origin for purposes of deciding of two defs are
+						// "the same" or "different"
+						newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, localness, substInNode, cm,
+								symbolTable, treeNode, true, odn.getSource());
+						setOpDefNodeRecursionFields(newOdn, cm);
+						newOdn.setLabels(odn.getLabelsHT());
+					} // if (substIn.getSubsts().length > 0)
+					else {
+						// no SUBST-IN node required; but because of the new
+						// operator name, cm is the module of origin for purposes of
+						// deciding of two defs are "the same" or "different"
+						newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, localness, odn.getBody(), cm,
+								symbolTable, treeNode, true, odn.getSource());
+						setOpDefNodeRecursionFields(newOdn, cm);
+						newOdn.setLabels(odn.getLabelsHT());
+					} // else
+				} // if (odn.kind == UserDefinedOpKind)
+				else {
+					/*****************************************************************
+					 * This is a ModuleInstanceKind node. *
+					 *****************************************************************/
+					newOdn = new OpDefNode(qualifiedName, params, localness, odn.getOriginallyDefinedInModuleNode(),
+							symbolTable, treeNode, odn.getSource());
+				}
+				; // else
+				// defs is non-null iff this module definition is in the Let
+				// part of a Let-In expression. Add this newly created OpDef
+				// to either the LET list or the module cm's definition list.
+				if (defs == null) {
+					cm.appendDef(newOdn);
+				} else {
+					defs.addElement(newOdn);
+				}
+			} // if (!odn.isLocal()&& ... )
+		} // for
+
+		/**********************************************************************
+		 * Import the ThmOrAssumpDefNode objects to the current module. The * following
+		 * code for doing this was copied and modified without * much thinking from the
+		 * code above for OpDefNode objects *
+		 **********************************************************************/
+		// Create a vector of all of the ThmOrAssumpDefNodes in the
+		// instancee module
+		Vector taelts = instanceeCtxt.getByClass(ThmOrAssumpDefNode.class);
+
+		// For each definition in the instancee module, create a
+		// corresponding definition in the instancer module
+		for (int i = 0; i < taelts.size(); i++) {
+			// Find the ThmOrAssumpDefNode to be instantiated
+			ThmOrAssumpDefNode taOdn = (ThmOrAssumpDefNode) taelts.elementAt(i);
+
+			/*********************************************************************
+			 * There are no builtin ThmOrAssumpDefNode objects. *
+			 *********************************************************************/
+			// Ignore it if it is local
+			if (!taOdn.isLocal()) {
+				// Create the new name prepended with "name!"
+				String compoundID = name + "!" + taOdn.getName();
+				UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID);
+
+				// Copy parameters for the op being defined
+				/*******************************************************************
+				 * Theorem or assumption definitions have no parameters. *
+				 *******************************************************************/
+				FormalParamNode[] fpn = taOdn.getParams();
+				FormalParamNode[] params = new FormalParamNode[fpn.length + args.length];
+				System.arraycopy(args, 0, params, 0, args.length);
+				System.arraycopy(fpn, 0, params, args.length, fpn.length);
+
+				ThmOrAssumpDefNode newtaOdn;
+				if (substIn.getSubsts().length > 0) {
+					// If there are substitutions, then the body of the new
+					// definition instance must be wrapped in a SUBST-IN node.
+					// Create the "wrapping" SubstInNode as a clone of "subst"
+					// above, but with a body from the ThmOrAssumpDefNode in the module
+					// being instantiated
+					APSubstInNode substInNode = new APSubstInNode(treeNode, substIn.getSubsts(), taOdn.getBody(), cm,
+							instanceeModule);
+
+					// Create the ThmOrAssumpDefNode for the new instance of this
+					// definition; because of the new operator name, cm is the
+					// module of origin for purposes of deciding of two defs are
+					// "the same" or "different"
+					newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(), substInNode, cm, symbolTable,
+							treeNode, params, instanceeModule, taOdn.getSource());
+					// Following statement added by LL on 30 Oct 2012 to handle locally
+					// instantiated theorems and assumptions. Added setLabels call
+					// on 31 Oct 2012.
+					newtaOdn.setLocal(localness);
+					newtaOdn.setLabels(taOdn.getLabelsHT());
+
+					/*****************************************************************
+					 * No recursion fields needed for a theorem or assumption * because it can't
+					 * appear in a recursive section. *
+					 *****************************************************************/
+					// setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ;
+				} else {
+					// no SUBST-IN node required; but because of the new
+					// operator name, cm is the module of origin for purposes of
+					// deciding if two defs are "the same" or "different"
+					newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(), taOdn.getBody(), cm,
+							symbolTable, treeNode, params, instanceeModule, taOdn.getSource());
+					// Following statement added by LL on 30 Oct 2012 to handle locally
+					// instantiated theorems and assumptions. Added setLabels call
+					// on 31 Oct 2012.
+					newtaOdn.setLocal(localness);
+					newtaOdn.setLabels(taOdn.getLabelsHT());
+					/*****************************************************************
+					 * No recursion fields needed for theorems or assumptions * because they can't
+					 * appear in a recursive section. *
+					 *****************************************************************/
+					// setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ;
+				}
+
+				// defs is non-null iff this module definition is in the Let
+				// part of a Let-In expression. Add this newly created ThmOrAssumpDef
+				// to either the LET list or the module cm's definition list.
+				if (defs == null) {
+					cm.appendDef(newtaOdn);
+				} else {
+					defs.addElement(newtaOdn);
+				}
+			} // if (!taOdn.isLocal()&& ... )
+		} // for
+
+		// Create a new InstanceNode to represent this INSTANCE stmt
+		// in the current module
+		InstanceNode inst = new InstanceNode(name, localness, args, instanceeModule, substIn.getSubsts(), treeNode);
+
+		// Append this new InstanceNode to the vector of InstanceNodes
+		// being accumulated for this module
+		if (insts == null) {
+			cm.appendInstance(inst);
+		} else {
+			insts.addElement(inst);
+		}
+
+		// Create new OpDefNode with ModuleInstanceKind. The reason for
+		// doing this is to get the name in the symbol table so the name
+		// cannot be re-used later in this module for a user-defined
+		// operator.
+		return new OpDefNode(name, args, localness, cm, symbolTable, treeNode, null);
+		/*********************************************************************
+		 * Note: the module's OpDefNode does not have recursive parameters * set. If the
+		 * module definition statement occurs in a recursive * section, then it is the
+		 * instantiated definitions that are * recursive, not the module definition
+		 * itself. (The name under * which the module is instantiated cannot appear in a
+		 * RECURSIVE * statement.) *
+		 *********************************************************************/
+
+	} // end processModuleDefinition()
+
+	/**
+	 * From a particular explicit substitution (substTarget <- substValue) check the
+	 * legality of the substTarget, and if OK, generate the appropriate type of node
+	 * for substValue
+	 */
+	private ExprOrOpArgNode generateSubst(Context instanceeCtxt, TreeNode substTarget, TreeNode substValue,
+			ModuleNode mn) throws AbortException {
+		SymbolNode targetSymbol = instanceeCtxt.getSymbol(substTarget.getUS());
+
+		// if the targetSymbol cannot be found in the instancee context,
+		// or if it does not correspond to a declaration, then it is an
+		// illegal substitution target
+
+		if (targetSymbol == null || !(targetSymbol instanceof OpDeclNode)) {
+			errors.addError(substTarget.getLocation(), "Identifier '" + substTarget.getUS() + "' is not a legal"
+					+ " target of a substitution. \nA legal target must be a declared"
+					+ " CONSTANT or VARIABLE in the module being instantiated."
+					+ " \n(Also, check for warnings about multiple declarations of" + " this same identifier.)");
+			return nullOAN;
+		}
+
+		// but if the symbol is found, then if it has arity 0, the RHS
+		// should be an expression, if arity > 0, the the RHS should be an OpArg
+		ExprOrOpArgNode returnObject;
+
+		if (targetSymbol.getArity() == 0) {
+			// if the target of the substitution has arity 0,
+			// then we expect an expression to be substituted for it
+			returnObject = generateExpression(substValue, mn);
+		} else {
+			// if the target of the substitution has arity > 0,
+			// then and operator must be substituted for it
+			returnObject = generateOpArg(targetSymbol, substValue, mn);
+
+			// and it better have the same arity as the target
+			if (((OpArgNode) returnObject).getArity() != targetSymbol.getArity()) {
+				errors.addError(substValue.getLocation(), "An operator must be substituted for symbol '"
+						+ targetSymbol.getName() + "', and it must have arity " + targetSymbol.getArity() + ".");
+			}
+		}
+		return returnObject;
+	} // end generateSubst()
+
+	/**
+	 * Return an OpArgNode constructed from a GeneralId tree to be used in the RHS
+	 * of a substitution
+	 */
+	private OpArgNode generateOpArg(SymbolNode targetSymbol, TreeNode opArgSyntaxNode, ModuleNode mn)
+			throws AbortException {
+		/***********************************************************************
+		 * If this is a lambda expressiion, then just get it and go. *
+		 ***********************************************************************/
+		if (opArgSyntaxNode.isKind(N_Lambda)) {
+			return new OpArgNode(generateLambda(opArgSyntaxNode, mn), opArgSyntaxNode, mn);
+		}
+		;
+		// First, make sure that an operator ID is present, and not an expression
+		if (!(opArgSyntaxNode.isKind(N_GeneralId) || opArgSyntaxNode.isKind(N_GenInfixOp)
+				|| opArgSyntaxNode.isKind(N_GenPrefixOp) || opArgSyntaxNode.isKind(N_GenNonExpPrefixOp) ||
+				/************************************************************
+				 * This last disjunct was added by LL on 9 May 2007. * * The original parser
+				 * phase produced an N_GenPrefixOp * here, but an N_GenNonExpPrefixOp for the
+				 * syntactically * identical situation in an operator argument. The new * TLA+2
+				 * parser produces N_GenNonExpPrefixOp nodes here, * but I left the
+				 * N_GenPrefixOp case in here just in case * this is called somewhere else that
+				 * I haven't found. *
+				 ************************************************************/
+
+				opArgSyntaxNode.isKind(N_GenPostfixOp))) {
+			errors.addError(opArgSyntaxNode.getLocation(),
+					"Arity " + targetSymbol.getArity() + " operator (not an expression) is expected"
+							+ " \nto substitute for CONSTANT '" + targetSymbol.getName() + "'.");
+			return nullOpArg;
+		}
+
+		/***********************************************************************
+		 * If the argument is a GeneralId node, then we use * genIdToSelector and
+		 * selectorToNode to generate the OpArg node. *
+		 ***********************************************************************/
+		if (opArgSyntaxNode.getKind() == N_GeneralId) {
+			/*********************************************************************
+			 * First, a sanity check to make sure we're never looking for an * expression
+			 * argument. *
+			 *********************************************************************/
+			if (targetSymbol.getArity() <= 0) {
+				errors.addAbort(opArgSyntaxNode.getLocation(), "Internal error: expected to find arity > 0.", true);
+			}
+			;
+
+			LevelNode ln = selectorToNode(genIdToSelector((SyntaxTreeNode) opArgSyntaxNode), targetSymbol.getArity(),
+					false, false, mn);
+
+			/*******************************************************************
+			 * Added 23 February 2009: It appears that, in case of an error, *
+			 * selectorToNode can return something other than an OpArgNode * here. (In
+			 * particular, an OpApplNode.) Rather than tix * selectorToNode, I am adding a
+			 * kludge to simply ignore the * problem and hope that it can be caused only by
+			 * some other * error. If no error has been found, then we abort and debug if *
+			 * this is encountered. *
+			 *******************************************************************/
+			if (!(ln instanceof OpArgNode)) {
+				if (errors.getNumErrors() > 0) {
+					return nullOpArg;
+				}
+				errors.addAbort(opArgSyntaxNode.getLocation(),
+						"Internal error: " + "Expected an operator argument but " + "found something else.");
+			}
+			;
+			return (OpArgNode) ln;
+		}
+		;
+
+		/*******************************************************************
+		 * If the argument is not a GeneralId node, we use the code from * SANY1 to
+		 * generate the OpArg node. *
+		 *******************************************************************/
+
+		// Assemble the (possibly compound) generalized identifier, and resolve it.
+		GenID genID = generateGenID(opArgSyntaxNode, mn);
+
+		// If the fully-qualified op is undefined, then a message has already been
+		// put in errors, but we must insert a nullOpArgNode in the tree.
+		if (genID.getFullyQualifiedOp() != null && genID.getArgs().length == 0) {
+			// Create an OpArgNode from it.
+			return new OpArgNode(genID.getFullyQualifiedOp(), opArgSyntaxNode, mn);
+		} else if (genID.getArgs().length > 0) {
+			// Expression being used where Operator is required
+			errors.addError(opArgSyntaxNode.getLocation(),
+					"Arity " + targetSymbol.getArity() + " operator (not an expression) is expected"
+							+ " to substitute for CONSTANT '" + targetSymbol.getName() + "'.");
+			return nullOpArg;
+		} else {
+			return nullOpArg;
+		}
+	} // end generateOpArg()
+
+	/**
+	 * Process the substitutions clause of a module definition or instantiation;
+	 * returns a SubstInNode that can be used as a template for the wrapper that
+	 * must be present around each OpDefNode body created from the module
+	 * instantiation or module definition.
+	 */
+	private SubstInNode processSubst(TreeNode treeNode, TreeNode[] substNodes,
+			// array of subst nodes [c1 <- e1, ... ,cn <- en]
+			SymbolTable instancerST,
+			// SymbolTable in which the ei must be resolved
+			Context instanceeCtxt,
+			// Context in which the ci must be resolved
+			ModuleNode instanceeModule,
+			// the ModuleNode of the module in which ci must be resolved
+			ModuleNode mn) throws AbortException {
+		TreeNode[] children; // find the substitution part of the syntax tree
+
+		// Create a vector of all declarations of CONSTANTS and VARIABLES
+		// in the context of the module being instantiated (instancee).
+		// These are all the symbols that must have substitutions defined
+		// for them in the instantiation, either explictly or implicitly.
+		Vector decls = instanceeCtxt.getByClass(OpDeclNode.class);
+
+		// Create a SubstInNode to be used as a template for the SubstInNodes
+		// in the body of every newly instantiated OpDef in the module.
+		// The substitutions in the returned SubstInNode object will be the
+		// implicit substitutions of the form c<-c for all CONSTANTS and
+		// VARIABLES c that are BOTH declared in instancee and for which the
+		// same name is declared or defined in instancer. Note the instancerST
+		// must be passed to this constructor because with a default substitution
+		// LHS<-RHS the LHS is resolved in the instanceeCtxt, (done
+		// in the previous line) and the RHS is resolved in the instancerST.
+		SubstInNode substIn = new SubstInNode(treeNode, instancerST, decls, mn, instanceeModule);
+
+		// For each explicit substitution in the syntax tree, overwrite or add
+		// the corresponding default entry in SubstInNode just created
+		for (int i = 3; i < substNodes.length; i += 2) {
+			// pick up array of syntax elements for one substitution,
+			// e.g. ["c","<-",expr]
+			TreeNode sc[] = substNodes[i].heirs();
+
+			// substRHS is the expression "exp" in "c <- exp"; this stmt first
+			// checks that c is properly declared in the instancee context,
+			// and then generates an ExprOrOpArgNode. If c is a constant
+			// with parameters, then an OpArgNode is returned; otherwise it is
+			// an ExprNode.
+			ExprOrOpArgNode substRHS = generateSubst(instanceeCtxt, sc[0], sc[2], mn);
+
+			// Overwrite an implicit substitution if there is one, or add a new one,
+			// checking for duplicate substitutions for the same symbol
+			substIn.addExplicitSubstitute(instanceeCtxt, sc[0].getUS(), sc[2], substRHS);
+		}
+
+		// Check if substitution is complete, i.e. that all constants and vars
+		// have been substituted for.
+		substIn.matchAll(decls);
+		return substIn;
+	} // end processSubst
+
+	/**
+	 * This method treats *unnamed* INSTANCE stmts NOTE: this code does nothing with
+	 * ASSUMES or THEOREMS
+	 */
+	/*************************************************************************
+	 * However, SANY2 imports ThmOrAssumpDef nodes. *
+	 *************************************************************************/
+	/**
+	 * @param treeNode
+	 * @param cm
+	 * @param topLevel
+	 * @return
+	 * @throws AbortException
+	 */
+	/**
+	 * @param treeNode
+	 * @param cm
+	 * @param topLevel
+	 * @return
+	 * @throws AbortException
+	 */
+	/**
+	 * @param treeNode
+	 * @param cm
+	 * @param topLevel
+	 * @return
+	 * @throws AbortException
+	 */
+	/**
+	 * @param treeNode
+	 * @param cm
+	 * @param topLevel
+	 * @return
+	 * @throws AbortException
+	 */
+	private final InstanceNode generateInstance(TreeNode treeNode, ModuleNode cm, boolean topLevel)
+			throws AbortException {
+		/**********************************************************************
+		 * topLevel argument is true for a top-level INSTANCE statement, and * false
+		 * elsewise--that is, for an INSTANCE inside a proof. *
+		 **********************************************************************/
+		TreeNode[] children;
+		if (topLevel) {
+			children = treeNode.one()[0].heirs();
+		}
+		// skip one generation below NonLocalInstance
+		// because we know zero defines local in a
+		// top-level INSTANCE
+		else {
+			children = treeNode.heirs();
+		}
+		;
+		// id of module being instanced
+		UniqueString moduleId = children[1].getUS();
+
+		// If this module instance is declared "LOCAL" then all of the
+		// definitions in it must be instanced as if they were "LOCAL"
+		boolean localness = treeNode.local();
+
+		// Create a list of all declarations for module moduleId.
+		// Match them against either something in the substitutions or
+		// something in the current context (symbol table) for the
+		// substitution. Check that the symbol does occur in the module
+		// and as a declaration.
+		Context instanceeCtxt = this.getContext(moduleId);
+		if (instanceeCtxt == null) {
+			errors.addAbort(children[1].getLocation(),
+					"Internal error: No context available for module `" + moduleId.toString() + "'.", true);
+		}
+		;
+		// Try to find the ModuleNode for the module being instanced in
+		// the symbolTable
+		ModuleNode instanceeModuleNode = symbolTable.resolveModule(moduleId);
+
+		// It must be an external module if it isn't in the symbolTable;
+		// try to find it in moduleTable (it cannot be in both places, or
+		// a name conflict would have resulted)
+		if (instanceeModuleNode == null) {
+			instanceeModuleNode = moduleTable.getModuleNode(moduleId);
+		}
+
+		if (instanceeModuleNode == null) {
+			errors.addAbort(children[1].getLocation(), "Could not find module " + moduleId.toString(), false);
+		}
+
+		/*
+		 * Set isInstantiated field of instancee. Added by LL 23 July 2013
+		 */
+		instanceeModuleNode.setInstantiated(true);
+
+		// Create the SubstInNode that will act as a template "wrapper"
+		// for each definition in the module being instantiated; this
+		// SubstInNode itself gets discarded after being used as template
+		// however many times is necessary
+		SubstInNode subst = processSubst(treeNode, children, symbolTable, instanceeCtxt, instanceeModuleNode, cm);
+
+		// Create a vector of all of the OpDefNodes in the module being
+		// instantiated
+		Vector defs = instanceeCtxt.getByClass(OpDefNode.class);
+
+		OpDefNode odn; // OpDefNode in module being instantiated (instancee)
+		OpDefNode newOdn; // Its counterpart current module (instancer)
+		SubstInNode substInTemplate; // Template to be used for any
+										// SubstInNode wrappers required
+
+		// Duplicate the OpDef records from the module being INSTANCE'd
+		for (int i = 0; i < defs.size(); i++) {
+			odn = (OpDefNode) defs.elementAt(i);
+			// OpDefNode in module being instantiated (instancee)
+
+			// Do not instantiate built-in or local operators, or those
+			// OpDefNodes created solely to prevent a ModuleName from being
+			// used as an operator node.
+			if (odn.getKind() == BuiltInKind || odn.getKind() == ModuleInstanceKind || odn.isLocal()) {
+				continue;
+			}
+
+			// If there are parameters to the module being instantiated, then
+			// a SubstInNode is required, and possibly a different module of
+			// origin should be indicated
+			if (!instanceeModuleNode.isParameterFree()) {
+				// Create the OpDefNode for the new instance of this definition
+				// Note that the new OpDefNode shares the array of FormalParamNodes
+				// with the old OpDefNode, as well as large parts of its body
+				// (all but the SubstInNode). Hence, changes by a tool to an Original
+				// OpDefNode will likely be reflected in all instances of it.
+				if (odn.getOriginallyDefinedInModuleNode().isParameterFree()) {
+
+					/****************************************************************
+					 * Originally, newOdn was set the way it now is if localness = * true. Here's
+					 * the problem with it. Suppose the instantiated * module EXTENDS the Naturals
+					 * module. Then this will add new * OpDefNodes for all the symbols defined in
+					 * Naturals. If the * current module EXTENDS Naturals, this will lead to
+					 * multiple * definitions. So, for nonlocal definitions, we just * set newOdn to
+					 * odn. * * However, now the problem is: suppose the current module * does not
+					 * EXTEND the Naturals module. Then the operators * defined in Naturals, which
+					 * should be defined in the current * module, are not. So, we add them to
+					 * symbolTable. This does * not lead to a multiple definition error because
+					 * apparently * it's the addSymbol method that is smart enough to detect if * we
+					 * are adding a definition that comes from the same source as * the original
+					 * one. * * This fix was made by LL on 16 Feb 2009. * * On 6 June 2010, LL add
+					 * "&& topLevel" to the following `if' * test. This was needed because an
+					 * INSTANCE inside a proof * was producing a "Multiple declarations or
+					 * definition" * warning if the INSTANCEd module and the current module both *
+					 * EXTENDed Naturals. This fix seems to do the right thing, * but I have not
+					 * extensively tested it and I have no idea what * problems may remain. *
+					 ****************************************************************/
+					if (localness && topLevel) {
+						newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), localness,
+								odn.getBody(), odn.getOriginallyDefinedInModuleNode(), symbolTable, treeNode, true,
+								odn.getSource());
+						/***************************************************************
+						 * The following statement was added by LL on 16 Feb 2009, by * analogy with the
+						 * corresponding code about 45 lines below. I * have no idea if it was
+						 * originally omitted for a good reason. *
+						 ***************************************************************/
+						newOdn.setLabels(odn.getLabelsHT());
+					} else {
+						newOdn = odn;
+						symbolTable.addSymbol(odn.getName(), odn);
+					}
+				} else {
+					// Create the "wrapping" SubstInNode as a clone of "subst" above,
+					// but with a body from the OpDefNode in the module being
+					// instantiated
+					substInTemplate = new SubstInNode(treeNode, subst.getSubsts(), odn.getBody(), cm,
+							instanceeModuleNode);
+					newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), localness,
+							substInTemplate, cm, symbolTable, treeNode, true, odn.getSource());
+					newOdn.setLabels(odn.getLabelsHT());
+
+				}
+			} else {
+				// There are no parameters to the instancee module; this
+				// means that a SubstInNode is not necessary, and also that
+				// the new operator should be considered to be "originally
+				// defined in" the same module as the old one for purposes of
+				// telling whether they are "the same" or different definitions
+
+				// Create an OpDefNode whose body is the same as the instancer's.
+
+				if (localness) {
+					/****************************************************************
+					 * See the comments about the similar change made to the * setting of newOdn in
+					 * the `then' clause, just above. * This entire change was made by LL on 16 Feb
+					 * 2009. *
+					 ****************************************************************/
+					newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), localness, odn.getBody(),
+							odn.getOriginallyDefinedInModuleNode(), symbolTable, treeNode, true, odn.getSource());
+					newOdn.setLabels(odn.getLabelsHT());
+				} else {
+					newOdn = odn;
+					symbolTable.addSymbol(odn.getName(), odn);
+				}
+			}
+			cm.appendDef(newOdn);
+			setOpDefNodeRecursionFields(newOdn, cm);
+		} // end for
+
+		/**********************************************************************
+		 * Import the ThmOrAssumpDefNode objects to the current module. The * following
+		 * code for doing this was copied and modified without * thinking from the code
+		 * above for OpDefNode objects *
+		 **********************************************************************/
+		Vector tadefs = instanceeCtxt.getByClass(ThmOrAssumpDefNode.class);
+
+		ThmOrAssumpDefNode tadn;
+		// ThmOrAssumpDefNode in module being instantiated (instancee)
+		ThmOrAssumpDefNode newTadn;
+		// Its counterpart current module (instancer)
+		APSubstInNode tasubstInTemplate; // Template to be used for any
+		// SubstInNode wrappers required
+
+		// Duplicate the OpDef records from the module being INSTANCE'd
+		for (int i = 0; i < tadefs.size(); i++) {
+			tadn = (ThmOrAssumpDefNode) tadefs.elementAt(i);
+			// ThmOrAssumpDefNode in module being instantiated (instancee)
+
+			// Following statement added by LL on 30 Oct 2012 to handle locally
+			// instantiated theorems and assumptions.
+			if (tadn.isLocal()) {
+				continue;
+			}
+
+			// If there are parameters to the module being instantiated, then
+			// a SubstInNode is required, and possibly a different module of
+			// origin should be indicated
+			if (!instanceeModuleNode.isParameterFree()) {
+				// Create the ThmOrAssumpDefNode for the new instance of this
+				// definition. Note that the new ThmOrAssumpDefNode shares the
+				// array of FormalParamNodes with the old ThmOrAssumpDefNode,
+				// as well as large parts of its body (all but the SubstInNode).
+				// Hence, changes by a tool to an Original ThmOrAssumpDefNode will
+				// likely be reflected in all instances of it.
+				if (tadn.getOriginallyDefinedInModuleNode().isParameterFree()) {
+					// Following if/else added by LL on 30 Oct 2012 to handle locally
+					// instantiated theorems and assumptions.
+					if (localness && topLevel) {
+						newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), tadn.getBody(), cm,
+								symbolTable, treeNode, tadn.getParams(), instanceeModuleNode, tadn.getSource());
+						newTadn.setLocal(true);
+					} else {
+						newTadn = tadn;
+						// On 30 Oct 2012, LL noticed that code was added on 16 Feb 2009
+						// to the corresponding place in the code for an OpDefNode, but that
+						// nothing was added here. I suspect that something should have been
+						// that the 2009 code should also have been added here, but wasn't--
+						// perhaps because there's no getLabelsHT method for a ThmOrAssumpDefNode.
+						// This may mean that there's a bug in handling labels in the
+						// instantiated theorem or assumption.
+					}
+					/*
+					 * new ThmOrAssumpDefNode( tadn.getName(), UserDefinedOpKind, tadn.getParams(),
+					 * localness, tasubstInTemplate, tadn.getOriginallyDefinedInModuleNode(),
+					 * symbolTable, treeNode, true );
+					 */
+				} else {
+					// Create the "wrapping" SubstInNode as a clone of "subst" above,
+					// but with a body from the ThmOrAssumpDefNode in the module being
+					// instantiated
+					tasubstInTemplate = new APSubstInNode(treeNode, subst.getSubsts(), tadn.getBody(), cm,
+							instanceeModuleNode);
+					newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), tasubstInTemplate, cm,
+							symbolTable, treeNode, tadn.getParams(), instanceeModuleNode, tadn.getSource());
+					// Following if/else added by LL on 30 Oct 2012 to handle locally
+					// instantiated theorems and assumptions.
+					newTadn.setLocal(localness);
+					// cm.appendDef(newTadn);
+					newTadn.setLabels(tadn.getLabelsHT());
+
+				}
+			} else {
+				// There are no parameters to the instancee module; this
+				// means that a SubstInNode is not necessary, and also that
+				// the new operator should be considered to be "originally
+				// defined in" the same module as the old one for purposes of
+				// telling whether they are "the same" or different definitions
+
+				// Create a ThmOrAssumpDefNode whose body is the same as
+				// the instancer's.
+				if (localness && topLevel) {
+					newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), tadn.getBody(),
+							tadn.getOriginallyDefinedInModuleNode(), symbolTable, treeNode, tadn.getParams(),
+							instanceeModuleNode, tadn.getSource());
+					// Following if/else added by LL on 30 Oct 2012 to handle locally
+					// instantiated theorems and assumptions.
+					newTadn.setLocal(localness);
+					newTadn.setLabels(tadn.getLabelsHT());
+				} else {
+					newTadn = tadn;
+					symbolTable.addSymbol(tadn.getName(), tadn);
+				}
+			}
+			if (topLevel) {
+				cm.appendDef(newTadn);
+			}
+			;
+			/********************************************************************
+			 * No recursion fields needed for theorems or assumptions because * they can't
+			 * appear in a recursive section. *
+			 ********************************************************************/
+			// setOpDefNodeRecursionFields(newTadn, cm) ;
+		} // end for
+
+		// Create a new InstanceNode to represent this INSTANCE stmt in
+		// the current module
+		InstanceNode inst = new InstanceNode(null /* no name */, localness, null /* no parms */, instanceeModuleNode,
+				subst.getSubsts(), treeNode);
+
+		// Append this new InstanceNode to the vector of InstanceNodes
+		// being accumulated for this module
+		if (topLevel) {
+			cm.appendInstance(inst);
+		}
+		;
+		return inst;
+
+	} // void generateInstance
+
+	private final void processTheorem(TreeNode stn, ModuleNode cm) throws AbortException {
+		ThmOrAssumpDefNode tadn = null;
+		LevelNode body;
+		ProofNode proof = null;
+		int bodyIndex = stn.heirs().length - 1;
+		boolean isAssumeProve = false;
+		/*********************************************************************
+		 * Set true if the body is an ASSUME/PROVE. *
+		 *********************************************************************/
+		boolean hasProof = (stn.heirs()[bodyIndex].getKind() == N_Proof)
+				|| (stn.heirs()[bodyIndex].getKind() == N_TerminalProof);
+		if (hasProof) {
+			bodyIndex--;
+		}
+		;
+
+		if (bodyIndex > 1) {
+			/*********************************************************************
+			 * If this is a named theorem, start fresh processing of labels, * create the
+			 * object. *
+			 *********************************************************************/
+			pushLS();
+			UniqueString name = stn.heirs()[1].getUS();
+			tadn = new ThmOrAssumpDefNode(name, stn);
+		}
+		;
+		if (stn.zero()[bodyIndex]
+				.isKind(N_AssumeProve)) { /*******************************************************************
+											 * Theorem statement is an ASSUME/PROVE. Must set currentGoal. *
+											 *******************************************************************/
+			currentGoal = tadn;
+			isAssumeProve = true;
+
+			/*******************************************************************
+			 * We want the symbols declared in top-level NEW statements of the * ASSUME
+			 * clause to be visible in the proof. Therefore, we do the * context push for
+			 * the top-level AssumeProveNode here instead of * in generateAssumeProve. *
+			 *******************************************************************/
+			symbolTable.pushContext(new Context(moduleTable, errors));
+			body = generateAssumeProve(stn.heirs()[bodyIndex], cm);
+			currentGoal = null;
+		} else { /****************************************************************
+					 * Theorem statement must be an ExprNode. *
+					 ****************************************************************/
+			body = generateExpression(stn.heirs()[bodyIndex], cm);
+		}
+		;
+		if (bodyIndex > 1) {
+			/**********************************************************************
+			 * The theorem node has the form * THEOREM Ident == statement *
+			 **********************************************************************/
+			Context assumeContext = null;
+			if (isAssumeProve) {
+				/********************************************************************
+				 * If the body is an ASSUME/PROVE, we must save in assumeContext * the context
+				 * containing the symbols declared in the ASSUME * clause and pop it, so Ident
+				 * is made visible outside the proof. *
+				 ********************************************************************/
+				assumeContext = symbolTable.getContext();
+				symbolTable.popContext();
+			}
+			;
+			UniqueString name = stn.heirs()[1].getUS();
+			tadn.construct(true, body, cm, symbolTable, null);
+			tadn.setLabels(popLabelNodeSet());
+			cm.appendDef(tadn);
+			if (isAssumeProve) {
+				/********************************************************************
+				 * After putting Ident into the symbol table, we push the ASSUME * context back
+				 * onto symboltable. *
+				 ********************************************************************/
+				symbolTable.pushContext(assumeContext);
+			}
+			;
+		}
+		; // if (bodyIndex > 1)
+
+		/************************************************************************
+		 * Note: If this is a named theorem, then the name has been added to *
+		 * symbolTable so it can be referred to within the theorem's proof (if * it has
+		 * one). *
+		 ************************************************************************/
+		if (hasProof) {
+			proof = generateProof(stn.heirs()[bodyIndex + 1], cm);
+		}
+		;
+
+		if (isAssumeProve) {
+			symbolTable.popContext();
+			/********************************************************************
+			 * Pop the context containing ASSUME declarations. *
+			 ********************************************************************/
+			((AssumeProveNode) body).inProof = false;
+			/********************************************************************
+			 * Reset the AssumeProve node's inProof field. *
+			 ********************************************************************/
+		}
+		;
+		cm.addTheorem(stn, body, proof, tadn);
+	} // ProcessTheorem
+
+	private final void processAssumption(TreeNode stn, ModuleNode cm) throws AbortException {
+		ThmOrAssumpDefNode tadn = null;
+		int lastIndex = stn.heirs().length - 1;
+		if (lastIndex > 1) {
+			pushLS();
+		}
+		;
+		/*********************************************************************
+		 * If this is a named assumption, start fresh processing of labels. *
+		 *********************************************************************/
+		ExprNode expr = generateExpression(stn.heirs()[lastIndex], cm);
+
+		if (lastIndex > 1) {
+			/**********************************************************************
+			 * The assumption node has the form * ASSUME Ident == expression *
+			 **********************************************************************/
+			UniqueString name = stn.heirs()[1].getUS();
+			tadn = new ThmOrAssumpDefNode(name, false, expr, cm, symbolTable, stn, null, null, null);
+			tadn.setLabels(popLabelNodeSet());
+			cm.appendDef(tadn);
+		}
+		;
+		cm.addAssumption(stn, expr, symbolTable, tadn);
+		return;
+	} // processAssumption
+
+	private final ProofNode generateProof(TreeNode stn, ModuleNode cm)
+			/***********************************************************************
+			 * Node stn is of kind N_TerminalProof or an N_Proof. The heirs of an * N_Proof
+			 * node consist of an optional PROOF token followed by a * seequence of
+			 * N_ProofStep nodes. The heirs of an N_ProofStep node * are a StartStep()
+			 * token, a statement body, and an optional proof. * A statement body is one of
+			 * the following node kinds: * * Have no proof: * N_DefStep N_UseOrHide
+			 * N_NonLocalInstance N_HaveStep, * N_TakeStep N_WitnessStep * * Have a proof *
+			 * N_QEDStep N_PickStep N_CaseStep N_AssertStep * * Each step produces the
+			 * following kind of semantic node: * * N_DefStep : DefStepNode * * N_UseOrHide
+			 * : UseOrNideNode * * N_NonLocalInstance : InstanceNode * * Others: TheoremNode
+			 * * The type of statement is indicated by the body of the * node. For any step
+			 * other than an N_AssertStep, the body * is an OpApplNode with the following
+			 * dummy operator: * * N_HaveStep : $Have * N_CaseStep : $Pfcase * N_TakeStep :
+			 * $Take * N_PickStep : $Pick * N_WitnessStep : $Witness * N_QEDStep : $Qed *
+			 ***********************************************************************/
+			throws AbortException {
+		int numberOfPops = 0;
+		/*********************************************************************
+		 * For each SUFFICES ASSUME/PROVE step, we push a new context onto * the symbol
+		 * table containing declarations of any NEW symbols. * These need to be popped
+		 * off when through processing the proof. *
+		 *********************************************************************/
+		if (stn.getKind() == N_TerminalProof) {
+			return generateLeafProof(stn, cm);
+		}
+		;
+
+		Context pfCtxt = new Context(moduleTable, errors);
+		symbolTable.pushContext(pfCtxt);
+		/*********************************************************************
+		 * Create a new sub-Context for the proof. *
+		 *********************************************************************/
+
+		TreeNode heirs[] = stn.heirs();
+		int offset = 0;
+		if (heirs[0].getKind() == TLAplusParserConstants.PROOF) {
+			offset = 1;
+		}
+		;
+		LevelNode[] steps = new LevelNode[heirs.length - offset];
+		Vector iVec = new Vector();
+		/*********************************************************************
+		 * A vector to hold the InstanceNodes generated by steps of the form * Id ==
+		 * INSTANCE ... so they can be level checked. *
+		 *********************************************************************/
+
+		/***********************************************************************
+		 * At the beginning of each loop iteration, the variable prevIsInFix * equals
+		 * true iff the previous step consists of a formula whose main * operator is an
+		 * infix operator, including an "@-step" of the form "@ * infix-op expression".
+		 * If it is true, then rhSide is the ExprNode * of the infix-op's
+		 * right-hand-side expression. *
+		 ***********************************************************************/
+		boolean prevIsInfix = false;
+		ExprNode prevRHS = null;
+
+		for (int i = offset; i < heirs.length; i++) {
+			/*********************************************************************
+			 * Process proof step i. *
+			 *********************************************************************/
+			boolean isAssumeProve = false;
+			/*******************************************************************
+			 * Will be set true for an ASSUME/PROVE, so we can reset the * node's inProof
+			 * field. *
+			 *******************************************************************/
+			boolean isSuffices = false;
+			/*******************************************************************
+			 * Will be set to true for a SUFFICES statement. This is used to * do the right
+			 * thing with ASSUME declarations in an ASSUME/PROVE * step, and to set the
+			 * suffices field of the ThmOrAssumpDefNode * if this is a named step. *
+			 *******************************************************************/
+
+			/*********************************************************************
+			 * For an ASSUME/PROVE, we set assumeContext to a context containing * the
+			 * declarations from the outer-most NEWs of the ASSUME. For an * ordinary
+			 * ASSUME/PROVE, this context is pushed onto the symbol * table for statement's
+			 * processing the proof. For a SUFFICE * ASSUME/PROVE, it is pushed onto the
+			 * symbol table after processing * the statement's proof so the outermost NEW
+			 * declarations are * visible for the rest of the current proof. * The handling
+			 * of the assumeContext was changed on 1 Jul 2009 * because SUFFICE ASSUME/PROVE
+			 * was not being handled properly. *
+			 *********************************************************************/
+			Context assumeContext = null;
+
+			/*********************************************************************
+			 * For "PICK x : exp", the symbol x is declared in exp, undeclared * in the
+			 * proof of the step, and declared afterwards. (There'd be a * similar problem
+			 * with TAKE if it took a proof.) The following * variables are used to
+			 * manipulate this, with pickContext holding * the declarations of the symbols
+			 * introduced by the PICK step. *
+			 *********************************************************************/
+			boolean isPick = false;
+			Context pickContext = null;
+
+			/*********************************************************************
+			 * Set stepNumSTN, stepBodySTN, and stepPfSTN to the syntax tree * nodes of the
+			 * step number, the body, and the proof (the latter * equal to null if there's
+			 * no proof. *
+			 *********************************************************************/
+			TreeNode pfStepSTN = heirs[i];
+			TreeNode stepNumSTN = pfStepSTN.heirs()[0];
+			TreeNode stepBodySTN = pfStepSTN.heirs()[1];
+			TreeNode stepPfSTN = null;
+			if (pfStepSTN.heirs().length > 2) {
+				stepPfSTN = pfStepSTN.heirs()[2];
+			}
+			;
+
+			LevelNode pfNumNode = null;
+			boolean makePfNumNode = true;
+			/*******************************************************************
+			 * For a numbered step that doesn't produce a TheoremNode, * pfNumNode is set to
+			 * a node that will be the stepNode of a * NumberedProofStepKind OpDefNode. *
+			 *******************************************************************/
+			/*
+			 * On 23 Feb 2014 LL added the following code to make step numbers like <*>13
+			 * illegal, so <+> and <*> can be used only for unnamed steps. It would be most
+			 * natural to make the change age the parsing level by changing the JavaCC code.
+			 * However, that code is a Kludge that it is best not to touch unless absolutely
+			 * necessary. Also, detecting the error here allows multiple instances of the
+			 * error to be reported in a single execution of the parser.
+			 * 
+			 * The modification is based on the following empirical observation:
+			 * 
+			 * - For a step number like "<3>13." stepNumSTN.image = stepNumSTN.originalImage
+			 * = "<3>13." - For a step number like "<*>13.", stepNumSTN.image = "<3>13." and
+			 * stepNumSTN.originalImage = "<*>13." - For unnumbered steps,
+			 * stepNumSTN.originalImage = null and stepNumSTN.image = the actual token.
+			 */
+			SyntaxTreeNode STN = (SyntaxTreeNode) stepNumSTN;
+			if ((STN.originalImage != null) && (STN.originalImage != STN.image)) {
+				String oimage = STN.originalImage.toString();
+				if ((!oimage.equals(STN.image.toString()))
+						&& ((oimage.charAt(1) == '*') || (oimage.charAt(1) == '+'))) {
+					errors.addError(stepNumSTN.getLocation(), "<*> and <+> cannot be used for a named step.");
+				}
+			}
+
+			/*********************************************************************
+			 * Set stepNum to the step number, or null if its an unnumbered step. *
+			 *********************************************************************/
+			UniqueString stepNum = null;
+			switch (stepNumSTN.getKind()) {
+			// LL: On 25 Feb 2010 I discovered the following comment here:
+			// XXXXXX xyz: need to add the following case
+			// The ProofImplicitStepLexeme case (something like <*>3) seems to be
+			// handled properly in tests. I presume that this is an obsolete
+			// comment that I didn't remove when I added the case to the code.
+			case TLAplusParserConstants.ProofImplicitStepLexeme:
+			case TLAplusParserConstants.ProofStepLexeme:
+				stepNum = stepNumSTN.getUS();
+				break;
+			case TLAplusParserConstants.ProofStepDotLexeme:
+				String stNum = stepNumSTN.getUS().toString();
+				stepNum = UniqueString.uniqueStringOf(stNum.substring(0, stNum.indexOf(".")));
+				break;
+			default:
+				makePfNumNode = false;
+				break;
+			}
+			; // switch
+
+			/*********************************************************************
+			 * If this is a numbered step, process labels. *
+			 *********************************************************************/
+			if (stepNum != null) {
+				pushLS();
+			}
+			;
+
+			/********************************************************************
+			 * Construct the ThmOrOpDefNode if needed. (We need to do it now * because we
+			 * have to set currentGoal before we generate the body.) *
+			 ********************************************************************/
+			ThmOrAssumpDefNode tadn = null;
+			if (stepNum != null) {
+				tadn = new ThmOrAssumpDefNode(stepNum, stepNumSTN);
+			}
+			;
+
+			/*********************************************************************
+			 * Set prevIsInfix false unless this is an AssertStep node. *
+			 *********************************************************************/
+			int stepKind = stepBodySTN.getKind();
+			if (stepKind != N_AssertStep) {
+				prevIsInfix = false;
+			}
+			;
+
+			switch (stepKind) {
+			case N_DefStep:
+				/*****************************************************************
+				 * Set defSTNs to the array of heirs, and defOffSet so that * defSTNs[defOffSet]
+				 * ... defSTNs[defSTNs.length-1] is the * sequence of definitions. *
+				 *****************************************************************/
+				TreeNode[] defSTNs = stepBodySTN.heirs();
+				int defOffSet = 0;
+				if (defSTNs[0].getKind() == DEFINE) {
+					defOffSet = 1;
+				}
+				;
+
+				OpDefNode[] defs = new OpDefNode[defSTNs.length - defOffSet];
+				/***************************************************************
+				 * Will be set to the sequence of OpDefNodes for the * definitions. *
+				 ***************************************************************/
+				for (int j = defOffSet; j < defSTNs.length; j++) {
+					TreeNode defSTN = defSTNs[j];
+					;
+					Vector vec = new Vector();
+					switch (defSTN.getKind()) {
+					/***************************************************************
+					 * Need to check if it's an operator, function, or module * definition. *
+					 ***************************************************************/
+					case N_FunctionDefinition:
+						processFunction(defSTN, vec, cm);
+						break;
+					case N_ModuleDefinition:
+						/*************************************************************
+						 * The call to processModuleDefinition sets defsVec to a * vector of all the
+						 * definitions it makes, and adds the new * InstanceNode to iVec. For now, we're
+						 * just throwing * away defsVec. (If defsVec were null, then *
+						 * processModuleDefinition would add these definitions to to * the module's list
+						 * of top-level definitions.) *
+						 *************************************************************/
+						Vector defsVec = new Vector();
+						vec.addElement(processModuleDefinition(defSTN, defsVec, iVec, cm));
+						break;
+					case N_OperatorDefinition:
+						processOperator(defSTN, vec, cm);
+						/*************************************************************
+						 * processOperator creates an OpDefNode, puts an entry for * it in symbolTable,
+						 * and adds the OpDefNode to vec. *
+						 *************************************************************/
+						break;
+					}
+					; // switch (def.getKind())
+					defs[j - defOffSet] = (OpDefNode) vec.elementAt(0);
+				}
+				; // for j
+				pfNumNode = new DefStepNode(stepBodySTN, stepNum, defs);
+				steps[i - offset] = pfNumNode;
+				break;
+
+			case N_UseOrHide:
+				UseOrHideNode uohn = generateUseOrHide(stepBodySTN, cm);
+
+				// Added by LL on 16 Jun 2010 so location returnedby getLocation() will
+				// include the step number.
+				uohn.stn = pfStepSTN;
+
+				uohn.setStepName(stepNum); // Added 6 June 2010 by LL.
+
+				if (uohn.facts.length + uohn.defs.length == 0) {
+					errors.addError(stepBodySTN.getLocation(), "Empty USE or HIDE statement.");
+				}
+				;
+				uohn.factCheck();
+				// Added 4 Mar 2009.
+				pfNumNode = uohn;
+				steps[i - offset] = pfNumNode;
+				break;
+
+			case N_NonLocalInstance:
+				// Code to set step name added by LL on 6 June 2010
+				InstanceNode inst = generateInstance(stepBodySTN, cm, false);
+				inst.setStepName(stepNum);
+				pfNumNode = inst;
+				steps[i - offset] = pfNumNode;
+				break;
+
+			default:
+				makePfNumNode = false;
+				TreeNode[] bodyHeirs = stepBodySTN.heirs();
+				LevelNode body = null;
+				/***************************************************************
+				 * This will be set to the body of the TheoremNode or * ThmOrAssumpDefNode. *
+				 ***************************************************************/
+				UniqueString op = null;
+				ExprNode[] args;
+				/***************************************************************
+				 * For anything but an N_Assert node, body is set to an * OpApplNode having
+				 * these as its operator and arguments. *
+				 ***************************************************************/
+
+				switch (stepBodySTN.getKind()) {
+				case N_AssertStep:
+					int bodyNext = 0;
+					if (bodyHeirs[0].getKind() == TLAplusParserConstants.SUFFICES) {
+						bodyNext = 1;
+						isSuffices = true;
+						/************************************************************
+						 * We can't have an "@" in a SUFFICES step. *
+						 ************************************************************/
+					}
+					;
+					if (bodyHeirs[bodyNext].getKind() == N_AssumeProve) {
+						/************************************************************
+						 * This is an AssumeProve node. *
+						 ************************************************************/
+						isAssumeProve = true;
+
+						/************************************************************
+						 * For an ASSUME/PROVE, we need save the symbol * declarations from top-level
+						 * NEW statements in the ASSUME * to make them visible only in the statement's
+						 * proof for * an ordinary ASSUME/PROVE, and only after the statement's * proof
+						 * for a SUFFICES ASSUME/PROVE. *
+						 ************************************************************/
+						symbolTable.pushContext(new Context(moduleTable, errors));
+
+						currentGoal = tadn;
+						/**********************************************************
+						 * Need to set currentGoal before generating the * AssumeProve node. *
+						 **********************************************************/
+						body = generateAssumeProve(bodyHeirs[bodyNext], cm);
+
+						if (isSuffices) {
+							((AssumeProveNode) body).setSuffices();
+						}
+						;
+						/**********************************************************
+						 * Added 16 Feb 2009 by LL. *
+						 **********************************************************/
+						currentGoal = null;
+						assumeContext = symbolTable.getContext();
+						symbolTable.popContext();
+						prevIsInfix = false;
+					} else {
+						/************************************************************
+						 * This is an ordinary expression. *
+						 ************************************************************/
+						TreeNode curExpr = bodyHeirs[bodyNext];
+
+						/************************************************************
+						 * Special handling of SUFFICES added by LL 16 Feb 2009. *
+						 ************************************************************/
+						if (isSuffices) {
+							args = new ExprNode[1];
+							args[0] = generateExpression(curExpr, cm);
+							body = new OpApplNode(OP_suffices, args, stepBodySTN, cm);
+						} // if (isSuffices)
+						else {
+							/************************************************************
+							 * If the current expression is an infix expression, set * curLHS to its current
+							 * LHS, otherwise set it to null. *
+							 ************************************************************/
+							SyntaxTreeNode curLHS = null;
+							if (curExpr.getKind() == N_InfixExpr) {
+								curLHS = (SyntaxTreeNode) curExpr.heirs()[0];
+							}
+							;
+							/************************************************************
+							 * If prevIsInfix is true and curLHS is an "@", then * process it, using as the
+							 * right-hand side a new $Nop node * with prevRHS as its argument. *
+							 ************************************************************/
+							if (prevIsInfix && (curLHS != null) && (curLHS.heirs().length > 0)
+							/***************************************************
+							 * This test added 25 Feb 2010 because if curLHS * is a string, then
+							 * curLHS.heirs() is a * zero-length array, so the following test threw * an
+							 * out-of-bounds array index exception. Note * that curLHS.heirs() should never
+							 * be null, * because the heirs() method can never return * null. *
+							 ***************************************************/
+									&& (((SyntaxTreeNode) curLHS.heirs()[0]).heirs().length == 0)
+									&& (curLHS.heirs()).length > 1
+									/***************************************************
+									 * This test added 2 Mar 2009 to fix following * bug. When we are here and the
+									 * left-hand side * is something like a number, then curLHS.heirs() * seems to
+									 * have length 1 and the following test * causes an ArrayIndexOverflowException.
+									 * *
+									 ***************************************************/
+									&& (curLHS.heirs()[1].getKind() == IDENTIFIER)
+									&& (curLHS.heirs()[1].getUS() == AtUS)) {
+
+								/**********************************************************
+								 * The following code obtained by a simple modification * of the N_InfixExpr
+								 * case of generateExpression. *
+								 **********************************************************/
+								TreeNode[] children = curExpr.heirs();
+								GenID genID = generateGenID(children[1], cm);
+								ExprNode[] sns = new ExprNode[2];
+								SymbolNode opn = symbolTable
+										.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS()));
+								if (opn == null) {
+									errors.addError(curExpr.getLocation(), "Couldn't resolve infix operator symbol `"
+											+ genID.getCompoundIDUS() + "'.");
+									return null;
+								}
+								;
+								sns[1] = generateExpression(children[2], cm);
+								/**********************************************************
+								 * Set sns[1] to a new $Nop OpApplNode whose argument is * prevRHS. *
+								 **********************************************************/
+								ExprNode[] nopArgs = new ExprNode[1];
+								nopArgs[0] = prevRHS;
+								sns[0] = new OpApplNode(OP_nop, nopArgs, curLHS, cm);
+								body = new OpApplNode(opn, sns, curExpr, cm);
+							} // if ( prevIsInfix ...)
+							else { // this is not an @-step
+								body = generateExpression(curExpr, cm);
+							}
+							;
+
+							/************************************************************
+							 * If this is an infix ioperator, set prevIsInfix true and * prevRHS equal to
+							 * its right-hand argument, else set * prevIsInfix false. *
+							 ************************************************************/
+							prevIsInfix = false;
+							if ((curLHS != null)
+									/*******************************************************
+									 * The following conjuncts should be true unless there * was an error in the
+									 * expression. *
+									 *******************************************************/
+									&& (body != null) && (body.getKind() == OpApplKind)
+									&& (((OpApplNode) body).getArgs().length > 1)) {
+								prevIsInfix = true;
+								prevRHS = (ExprNode) ((OpApplNode) body).getArgs()[1];
+							}
+						}
+					}
+					; // else This is an ordinary expression.
+					break;
+
+				case N_HaveStep:
+				case N_CaseStep:
+					if (stepBodySTN.getKind() == N_HaveStep) {
+						op = OP_have;
+					} else {
+						op = OP_pfcase;
+					}
+					;
+					args = new ExprNode[1];
+					args[0] = generateExpression(bodyHeirs[1], cm);
+					body = new OpApplNode(op, args, stepBodySTN, cm);
+					break;
+
+				case N_TakeStep:
+				case N_PickStep:
+					if (stepBodySTN.getKind() == N_TakeStep) {
+						op = OP_take;
+					} else {
+						op = OP_pick;
+						isPick = true;
+						/************************************************************
+						 * Push a new context onto the symbolTable stack to get the * declarations of
+						 * the PICK symbols. *
+						 ************************************************************/
+						symbolTable.pushContext(new Context(moduleTable, errors));
+					}
+					;
+
+					if (bodyHeirs[1].getKind() == N_QuantBound) {
+						/************************************************************
+						 * The introduced identifiers are bounded--e.g., "TAKE id * \in Set". *
+						 ************************************************************/
+
+						/************************************************************
+						 * Set quants to the number of N_QuantBound nodes. *
+						 ************************************************************/
+						int quants = 1;
+						int nextTok = 2;
+						while ((nextTok < bodyHeirs.length)
+								&& (bodyHeirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) {
+							quants++;
+							nextTok = nextTok + 2;
+						}
+						;
+						FormalParamNode[][] params = new FormalParamNode[quants][0];
+						boolean[] bt = new boolean[quants];
+						ExprNode[] paramBounds = new ExprNode[quants];
+						processQuantBoundArgs(bodyHeirs, 1, params, bt, paramBounds, cm);
+
+						if (isPick) {
+							/**********************************************************
+							 * Save the declarations in pickContext. *
+							 **********************************************************/
+							pickContext = symbolTable.getContext();
+							/**********************************************************
+							 * This is a PICK step; get the ": expr". *
+							 **********************************************************/
+							nextTok++; // Skip over the ":"
+							args = new ExprNode[1];
+							pushFormalParams(flattenParams(params));
+							/********************************************************
+							 * Push the bound variables on Last(LS).paramSeq and * process the body of the
+							 * PICK. *
+							 ********************************************************/
+							args[0] = generateExpression(bodyHeirs[nextTok], cm);
+							popFormalParams();
+							symbolTable.popContext();
+							/********************************************************
+							 * Remove the bound symbols from the symbol table so * they're undefined for the
+							 * proof of the PICK. *
+							 ********************************************************/
+						} else {
+							/**********************************************************
+							 * This is a TAKE step. *
+							 **********************************************************/
+							args = new ExprNode[0];
+						}
+						;
+						body = new OpApplNode(op, null, args, params, bt, paramBounds, stepBodySTN, cm);
+					} else {
+						/************************************************************
+						 * The introduced identifiers are unbounded--e.g., "TAKE * id1, id2". *
+						 ************************************************************/
+
+						/************************************************************
+						 * Set ids to the number of introduced identifiers. *
+						 ************************************************************/
+						int ids = 1;
+						while ((2 * ids < bodyHeirs.length)
+								&& (bodyHeirs[2 * ids].getKind() == TLAplusParserConstants.COMMA)) {
+							ids++;
+						}
+						;
+
+						/************************************************************
+						 * Set params to the array of new FormalParamNodes for the * identifiers. The
+						 * identifiers are added to the current * symbol table. *
+						 ************************************************************/
+						FormalParamNode[] params = new FormalParamNode[ids];
+						for (int j = 0; j < ids; j++) {
+							params[j] = new FormalParamNode(bodyHeirs[2 * j + 1].getUS(), 0, bodyHeirs[2 * j + 1],
+									symbolTable, cm);
+						}
+						;
+
+						if (isPick) {
+							/**********************************************************
+							 * Save the declarations in pickContext. *
+							 **********************************************************/
+							pickContext = symbolTable.getContext();
+
+							/**********************************************************
+							 * This is a PICK step; get the ": expr". *
+							 **********************************************************/
+							pushFormalParams(params);
+							/********************************************************
+							 * Push formal parameters on Last(LS).paramSeq for * processing the body. *
+							 ********************************************************/
+							args = new ExprNode[1];
+							args[0] = generateExpression(bodyHeirs[2 * ids + 1], cm);
+							popFormalParams();
+							symbolTable.popContext();
+							/********************************************************
+							 * Remove the bound symbols from the symbol table so * they're undefined for the
+							 * proof of the PICK. *
+							 ********************************************************/
+						} else {
+							/**********************************************************
+							 * This is a TAKE step. *
+							 **********************************************************/
+							args = new ExprNode[0];
+						}
+						;
+						body = new OpApplNode(op, args, params, stepBodySTN, cm);
+					}
+					;
+
+					break;
+
+				case N_WitnessStep:
+					/**************************************************************
+					 * Set ids to the number of expressions. *
+					 **************************************************************/
+					int ids = 1;
+					while ((2 * ids < bodyHeirs.length)
+							&& (bodyHeirs[2 * ids].getKind() == TLAplusParserConstants.COMMA)) {
+						ids++;
+					}
+					;
+
+					args = new ExprNode[ids];
+					for (int j = 0; j < ids; j++) {
+						args[j] = generateExpression(bodyHeirs[2 * j + 1], cm);
+					}
+					;
+					body = new OpApplNode(OP_witness, args, stepBodySTN, cm);
+					break;
+
+				case N_QEDStep:
+					args = new ExprNode[0];
+					body = new OpApplNode(OP_qed, args, stepBodySTN, cm);
+					break;
+
+				default:
+					errors.addAbort(stn.getLocation(),
+							"Internal error: Unexpected SyntaxTreeNode kind: " + heirs[i].getKind());
+					break;
+				}
+				; // switch
+
+				/********************************************************************
+				 * Set the fields of the ThmOrOpDefNode if there is one, including * the
+				 * suffices field. *
+				 ********************************************************************/
+				if (stepNum != null) {
+					tadn.construct(true, body, cm, symbolTable, null);
+					tadn.setLabels(popLabelNodeSet());
+					if (isSuffices) {
+						tadn.setSuffices();
+					}
+					;
+				}
+				;
+				/***********************************************************************
+				 * Set proof to the proof, or to null if there is none. There is no * check made
+				 * to see if this is a kind of step that should have a * proof. Thus, adding a
+				 * proof to something like a WITNESS statement * requires changing only the
+				 * parsing phase (specified by tla+.jj). *
+				 ***********************************************************************/
+				ProofNode proof = null;
+				if (stepPfSTN != null) {
+					/******************************************************************
+					 * For an ordinary ASSUME/PROVE, must make the ASSUME's * declarations visible
+					 * in the statement's proof. *
+					 ******************************************************************/
+					if (isAssumeProve && !isSuffices) {
+						symbolTable.pushContext(assumeContext);
+					}
+					;
+					proof = generateProof(stepPfSTN, cm);
+					if (isAssumeProve && !isSuffices) {
+						symbolTable.popContext();
+					}
+					;
+				}
+				;
+
+				/********************************************************************
+				 * For a SUFFICES ASSUME/PROVE, must make the ASSUME's declarations * visible
+				 * after the proof. This is done by pushing assumeContext * onto the symbol
+				 * table and incrementing numberOfPops so it will * be popped at the end of the
+				 * proof. *
+				 ********************************************************************/
+				if (isAssumeProve && isSuffices) {
+					numberOfPops++;
+					symbolTable.pushContext(assumeContext);
+				}
+				;
+				if (isAssumeProve) {
+					((AssumeProveNode) body).inProof = false;
+				}
+				;
+				/******************************************************************
+				 * For an ASSUME/PROVE, set the inProof field to false. *
+				 ******************************************************************/
+				TheoremNode thm = new TheoremNode(stepBodySTN, body, cm, proof, tadn);
+
+				// Added by LL on 16 Jun 2010 so location returnedby getLocation() will
+				// include the step number.
+				thm.stn = pfStepSTN;
+
+				thm.suffices = isSuffices;
+				steps[i - offset] = thm;
+			}
+			; // switch
+			if (makePfNumNode) {
+				/*******************************************************************
+				 * Make an OpDefNode for the numbered step and add any label * declarations to
+				 * it. *
+				 *******************************************************************/
+				OpDefNode nodeMadeOnlyToBePutInSymbolTable = new OpDefNode(stepNum, pfNumNode, cm, symbolTable,
+						pfStepSTN);
+				nodeMadeOnlyToBePutInSymbolTable.setLabels(popLabelNodeSet());
+			}
+			if (isPick) {
+				/*******************************************************************
+				 * We have to take the symbol declarations from pickContext and * put them into
+				 * the current symbol table. *
+				 *******************************************************************/
+				Enumeration e = pickContext.content();
+				while (e.hasMoreElements()) {
+					SymbolNode sym = ((Context.Pair) (e.nextElement())).getSymbol();
+					symbolTable.addSymbol(sym.getName(), sym);
+				}
+			}
+		}
+		; // for i
+		InstanceNode[] insts = new InstanceNode[iVec.size()];
+		for (int i = 0; i < insts.length; i++) {
+			insts[i] = (InstanceNode) iVec.elementAt(i);
+		}
+		;
+
+		/***********************************************************************
+		 * Pop the contexts that were pushed onto the symbol table for SUFFICE *
+		 * ASSUME/PROVE steps. *
+		 ***********************************************************************/
+		for (int i = 0; i < numberOfPops; i++) {
+			// Added by LL on 24 June 2010
+			// Need to add the symbols in the context being popped to pfCtxt
+			// so they will be put into the context of the NonLeafProofNode.
+			Context topContext = symbolTable.getContext();
+			Enumeration e = topContext.content();
+			while (e.hasMoreElements()) {
+				SymbolNode sym = ((Context.Pair) (e.nextElement())).getSymbol();
+				pfCtxt.addSymbolToContext(sym.getName(), sym);
+			}
+			symbolTable.popContext();
+		}
+		;
+		symbolTable.popContext();
+		return new NonLeafProofNode(stn, steps, insts, pfCtxt);
+		/*********************************************************************
+		 * Pop the sub-Context. *
+		 *********************************************************************/
+	} // generateProof
+
+	/***************************************************************************
+	 * The following method is not used and I have no idea why it's still * here. *
+	 ***************************************************************************/
+	private final LevelNode generateNumerableStep(TreeNode stn,
 //          TreeNode stmt, 
 //          UniqueString stepNum,
 //          TreeNode proof, 
-          ModuleNode cm) 
-    throws AbortException {
-    /***********************************************************************
-    * Used to generate the step for a NumerableStep or QEDStep token.      *
-    * Returns a TheoremNode or ThmOrAssumpDefNode.                         *
-    *                                                                      *
-    * The parsing into the syntactic tree makes it hard to rationalize     *
-    * processing of an N_NumerableStep node because there are two          *
-    * different ways such a step can be parsed                             *
-    *                                                                      *
-    *  1. [step number] (sequence of tokens of the statement) [proof]      *
-    *  2. [step number] (NonExprBody) [proof]                              *
-    *                                                                      *
-    * where, in case 2, the NonExprBody node contains all the tokens of    *
-    * the statement except the optional step number.  Here are the         *
-    * classes into which the different kinds of NumerableStep nodes fall.  *
-    *                                                                      *
-    * 1. CASE, (step number) expression.                                   *
-    *                                                                      *
-    * 2. WITNESS, TAKE, PICK, HAVE, ASSUME/PROVE,  SUFFICES,               *
-    *    and PROVE expression                                              *
-    ***********************************************************************/
-errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
-    UniqueString stepNum = null ;
-    TreeNode[] heirs = stn.heirs() ;
-    int nextTok = 0 ;
-    ThmOrAssumpDefNode tadn = null ;
-    boolean isSuffices = false ;
-    boolean isAssumeProve = false ;
-      /*********************************************************************
-      * Set true if this is an ASSUME/PROVE step, in which case some       *
-      * messing with the symbol table is necessary so that symbols         *
-      * declared in the ASSUME are visible in the proof of this step.      *
-      * They should be visible in the rest of the proof iff this is a      *
-      * SUFFICES ASSUME...  step.                                          *
-      *********************************************************************/
-    Context assumeContext = null;
-      /*********************************************************************
-      * For use with an ASSUME/PROVE.                                      *
-      *********************************************************************/
-    LevelNode body = null ;
-      /*********************************************************************
-      * This will be set to the body of the TheoremNode or                 *
-      * ThmOrAssumpDefNode.                                                *
-      *********************************************************************/
-      
-    /***********************************************************************
-    * We now save the values of heirs and nextTok.  If the tokens of the   *
-    * statement are inside a NonExprBody node, we set heirs to the         *
-    * children of that node, nextTok to 0, and hasNonExprBody true.        *
-    * After processing the statement, we will reset heirs and set nextTok  *
-    * to the its saved value plus 1.                                       *
-    ***********************************************************************/
-    TreeNode[] savedHeirs = heirs ;
-    int        savedNextTok = nextTok ;
-    boolean    hasNonExprBody = false ;
-    if (heirs[nextTok].getKind() == N_NonExprBody) {
-      heirs = heirs[nextTok].heirs() ;
-      nextTok = 0 ;
-      hasNonExprBody = true ;
-     } ;
-
-    /***********************************************************************
-    * If next token is "SUFFICES", then skip over it and set isSuffices    *
-    * true.  With the current grammar, this is the case only for an        *
-    * ordinary assertion (a statement or ASSUME/PROVE).                    *
-    ***********************************************************************/
-    if (heirs[nextTok].getKind() == TLAplusParserConstants.SUFFICES) {
-      nextTok++ ;
-      isSuffices = true ;
-     } ;
-    
-    /***********************************************************************
-    * Skip over the next token if it's "PROVE".  With current grammar,     *
-    * this only happens when nextTok = 0.                                  *
-    ***********************************************************************/
-    if (heirs[nextTok].getKind() == TLAplusParserConstants.PROVE) {
-      nextTok++ ;
-     } ;
-
-    /***********************************************************************
-    * If body will be an OpApplNode with a dummy operator (like $Pick),    *
-    * then set op to the operator's name and move past the determining     *
-    * token (like "PICK").  Otherwise, the body will be an ordinary        *
-    * assertion and op is left null.                                       *
-    ***********************************************************************/
-    UniqueString op = null ;
-    switch (heirs[nextTok].getKind()) {
-      case TLAplusParserConstants.QED :
-        body = new OpApplNode(OP_qed, new ExprNode[0], heirs[nextTok], cm) ;
-        nextTok++ ;
-        break ;
-
-      case TLAplusParserConstants.CASE :
-      case TLAplusParserConstants.HAVE :
-        op = OP_have ;
-        if (heirs[nextTok].getKind() == TLAplusParserConstants.CASE) {
-          op = OP_pfcase ;
-         };
-        nextTok++ ;
-        ExprNode[] args = new ExprNode[1] ;
-        args[0] = generateExpression(heirs[nextTok], cm) ;
-        body = new OpApplNode(op, args, heirs[nextTok], cm) ;
-        nextTok++ ;
-        break ;
-
-      case TLAplusParserConstants.TAKE :
-      case TLAplusParserConstants.PICK :
-        op = OP_take ;
-        if (heirs[nextTok].getKind() == TLAplusParserConstants.PICK) {
-          op = OP_pick ;
-         };
-        nextTok++ ;
-          
-        if (heirs[nextTok].getKind() == N_QuantBound) {
-          int offset = nextTok;
-          /*****************************************************************
-          * The introduced identifiers are bounded--e.g.,                  *
-          * "TAKE id \in Set".                                             *
-          *****************************************************************/
-          /*****************************************************************
-          * Set quants to the number of N_QuantBound nodes.                *
-          *****************************************************************/
-          int quants = 1 ;
-          nextTok++ ;
-          while (   (nextTok < heirs.length) 
-                 && (heirs[nextTok].getKind() 
-                        == TLAplusParserConstants.COMMA)) {
-            quants++ ;
-            nextTok = nextTok + 2 ;
-           };
-          FormalParamNode[][] params = new FormalParamNode[quants][0];
-          boolean[]           bt = new boolean[quants] ;
-          ExprNode[]          paramBounds = new ExprNode[quants];
-          processQuantBoundArgs(heirs, offset, params, bt, paramBounds, cm) ;
+			ModuleNode cm) throws AbortException {
+		/***********************************************************************
+		 * Used to generate the step for a NumerableStep or QEDStep token. * Returns a
+		 * TheoremNode or ThmOrAssumpDefNode. * * The parsing into the syntactic tree
+		 * makes it hard to rationalize * processing of an N_NumerableStep node because
+		 * there are two * different ways such a step can be parsed * * 1. [step number]
+		 * (sequence of tokens of the statement) [proof] * 2. [step number]
+		 * (NonExprBody) [proof] * * where, in case 2, the NonExprBody node contains all
+		 * the tokens of * the statement except the optional step number. Here are the *
+		 * classes into which the different kinds of NumerableStep nodes fall. * * 1.
+		 * CASE, (step number) expression. * * 2. WITNESS, TAKE, PICK, HAVE,
+		 * ASSUME/PROVE, SUFFICES, * and PROVE expression *
+		 ***********************************************************************/
+		errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step");
+		UniqueString stepNum = null;
+		TreeNode[] heirs = stn.heirs();
+		int nextTok = 0;
+		ThmOrAssumpDefNode tadn = null;
+		boolean isSuffices = false;
+		boolean isAssumeProve = false;
+		/*********************************************************************
+		 * Set true if this is an ASSUME/PROVE step, in which case some * messing with
+		 * the symbol table is necessary so that symbols * declared in the ASSUME are
+		 * visible in the proof of this step. * They should be visible in the rest of
+		 * the proof iff this is a * SUFFICES ASSUME... step. *
+		 *********************************************************************/
+		Context assumeContext = null;
+		/*********************************************************************
+		 * For use with an ASSUME/PROVE. *
+		 *********************************************************************/
+		LevelNode body = null;
+		/*********************************************************************
+		 * This will be set to the body of the TheoremNode or * ThmOrAssumpDefNode. *
+		 *********************************************************************/
+
+		/***********************************************************************
+		 * We now save the values of heirs and nextTok. If the tokens of the * statement
+		 * are inside a NonExprBody node, we set heirs to the * children of that node,
+		 * nextTok to 0, and hasNonExprBody true. * After processing the statement, we
+		 * will reset heirs and set nextTok * to the its saved value plus 1. *
+		 ***********************************************************************/
+		TreeNode[] savedHeirs = heirs;
+		int savedNextTok = nextTok;
+		boolean hasNonExprBody = false;
+		if (heirs[nextTok].getKind() == N_NonExprBody) {
+			heirs = heirs[nextTok].heirs();
+			nextTok = 0;
+			hasNonExprBody = true;
+		}
+		;
+
+		/***********************************************************************
+		 * If next token is "SUFFICES", then skip over it and set isSuffices * true.
+		 * With the current grammar, this is the case only for an * ordinary assertion
+		 * (a statement or ASSUME/PROVE). *
+		 ***********************************************************************/
+		if (heirs[nextTok].getKind() == TLAplusParserConstants.SUFFICES) {
+			nextTok++;
+			isSuffices = true;
+		}
+		;
+
+		/***********************************************************************
+		 * Skip over the next token if it's "PROVE". With current grammar, * this only
+		 * happens when nextTok = 0. *
+		 ***********************************************************************/
+		if (heirs[nextTok].getKind() == TLAplusParserConstants.PROVE) {
+			nextTok++;
+		}
+		;
+
+		/***********************************************************************
+		 * If body will be an OpApplNode with a dummy operator (like $Pick), * then set
+		 * op to the operator's name and move past the determining * token (like
+		 * "PICK"). Otherwise, the body will be an ordinary * assertion and op is left
+		 * null. *
+		 ***********************************************************************/
+		UniqueString op = null;
+		switch (heirs[nextTok].getKind()) {
+		case TLAplusParserConstants.QED:
+			body = new OpApplNode(OP_qed, new ExprNode[0], heirs[nextTok], cm);
+			nextTok++;
+			break;
+
+		case TLAplusParserConstants.CASE:
+		case TLAplusParserConstants.HAVE:
+			op = OP_have;
+			if (heirs[nextTok].getKind() == TLAplusParserConstants.CASE) {
+				op = OP_pfcase;
+			}
+			;
+			nextTok++;
+			ExprNode[] args = new ExprNode[1];
+			args[0] = generateExpression(heirs[nextTok], cm);
+			body = new OpApplNode(op, args, heirs[nextTok], cm);
+			nextTok++;
+			break;
+
+		case TLAplusParserConstants.TAKE:
+		case TLAplusParserConstants.PICK:
+			op = OP_take;
+			if (heirs[nextTok].getKind() == TLAplusParserConstants.PICK) {
+				op = OP_pick;
+			}
+			;
+			nextTok++;
+
+			if (heirs[nextTok].getKind() == N_QuantBound) {
+				int offset = nextTok;
+				/*****************************************************************
+				 * The introduced identifiers are bounded--e.g., * "TAKE id \in Set". *
+				 *****************************************************************/
+				/*****************************************************************
+				 * Set quants to the number of N_QuantBound nodes. *
+				 *****************************************************************/
+				int quants = 1;
+				nextTok++;
+				while ((nextTok < heirs.length) && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) {
+					quants++;
+					nextTok = nextTok + 2;
+				}
+				;
+				FormalParamNode[][] params = new FormalParamNode[quants][0];
+				boolean[] bt = new boolean[quants];
+				ExprNode[] paramBounds = new ExprNode[quants];
+				processQuantBoundArgs(heirs, offset, params, bt, paramBounds, cm);
 
 //          ExprNode[]          args ;
-          if (op == OP_pick) {
-           /****************************************************************
-           * This is a PICK step; get the ": expr".                        *
-           ****************************************************************/
-           nextTok++ ; // Skip over the ":"
-           args = new ExprNode[1] ;
-           args[0] = generateExpression(heirs[nextTok], cm) ;
-           nextTok++ ;
-           }
-          else {
-           /****************************************************************
-           * This is a TAKE step.                                          *
-           ****************************************************************/
-           args = new ExprNode[0] ;
-           };
-          body = 
-           new OpApplNode(op, null, args, params, bt, paramBounds, stn, cm) ;
-         }
-        else {
-          /*****************************************************************
-          * The introduced identifiers are unbounded--e.g.,                *
-          * "TAKE id1, id2".                                               *
-          *****************************************************************/
-          /*****************************************************************
-          * Set ids to the number of introduced identifiers.               *
-          *****************************************************************/
-          int ids = 1 ;
-          while (   (nextTok + 2*ids - 1 < heirs.length) 
-                 && (heirs[nextTok + 2*ids - 1].getKind() == 
-                       TLAplusParserConstants.COMMA)) {ids++;} ;
-
-          /*****************************************************************
-          * Set params to the array of new FormalParamNodes for the        *
-          * identifiers.  The identifiers are added to the current symbol  *
-          * table.                                                         *
-          *****************************************************************/
-          FormalParamNode[]   params = new FormalParamNode[ids];
-          for (int i = 0 ; i < ids ; i++) {
-             params[i] = new FormalParamNode(
-                               heirs[2*i + nextTok].getUS(), 0, 
-                               heirs[2*i + nextTok], symbolTable, cm);
-            } ;
-          /*****************************************************************
-          * Skip over the identifier-list tokens.                          *
-          *****************************************************************/
-          nextTok = nextTok + 2*ids - 1;
+				if (op == OP_pick) {
+					/****************************************************************
+					 * This is a PICK step; get the ": expr". *
+					 ****************************************************************/
+					nextTok++; // Skip over the ":"
+					args = new ExprNode[1];
+					args[0] = generateExpression(heirs[nextTok], cm);
+					nextTok++;
+				} else {
+					/****************************************************************
+					 * This is a TAKE step. *
+					 ****************************************************************/
+					args = new ExprNode[0];
+				}
+				;
+				body = new OpApplNode(op, null, args, params, bt, paramBounds, stn, cm);
+			} else {
+				/*****************************************************************
+				 * The introduced identifiers are unbounded--e.g., * "TAKE id1, id2". *
+				 *****************************************************************/
+				/*****************************************************************
+				 * Set ids to the number of introduced identifiers. *
+				 *****************************************************************/
+				int ids = 1;
+				while ((nextTok + 2 * ids - 1 < heirs.length)
+						&& (heirs[nextTok + 2 * ids - 1].getKind() == TLAplusParserConstants.COMMA)) {
+					ids++;
+				}
+				;
+
+				/*****************************************************************
+				 * Set params to the array of new FormalParamNodes for the * identifiers. The
+				 * identifiers are added to the current symbol * table. *
+				 *****************************************************************/
+				FormalParamNode[] params = new FormalParamNode[ids];
+				for (int i = 0; i < ids; i++) {
+					params[i] = new FormalParamNode(heirs[2 * i + nextTok].getUS(), 0, heirs[2 * i + nextTok],
+							symbolTable, cm);
+				}
+				;
+				/*****************************************************************
+				 * Skip over the identifier-list tokens. *
+				 *****************************************************************/
+				nextTok = nextTok + 2 * ids - 1;
 
 //          ExprNode[] args ;
-          if (op == OP_pick) {
-           /****************************************************************
-           * This is a PICK step; get the ": expr".                        *
-           ****************************************************************/
-           nextTok++ ; // Skip over the ":"
-           args = new ExprNode[1] ;
-           args[0] = generateExpression(heirs[nextTok], cm) ;
-           nextTok++ ;
-           }
-          else {
-           /****************************************************************
-           * This is a TAKE step.                                          *
-           ****************************************************************/
-           args = new ExprNode[0] ;
-           };
-          body = 
-           new OpApplNode(op, args, params, stn, cm) ;
-         } ;
-        break ;
-
-      case TLAplusParserConstants.WITNESS :
-        nextTok++ ;
-
-        /*******************************************************************
-        * Set ids to the number of expressions.                            *
-        *******************************************************************/
-        int ids = 1 ;
-        while (   (nextTok + 2*ids - 1 < heirs.length) 
-               && (heirs[nextTok + 2*ids - 1].getKind() == 
-                     TLAplusParserConstants.COMMA)) {ids++;} ;
-        ExprNode[] exprs = new ExprNode[ids] ;
-        for (int i = 0 ; i < ids ; i++) {
-          exprs[i] = generateExpression(heirs[2*i + nextTok], cm);
-            } ;
-
-        body = new OpApplNode(OP_tup, exprs, stn, cm) ;
-        nextTok = nextTok + 2*ids - 1;
-        break ;
-
-      default:
-        /*******************************************************************
-        * This is an ordinary assertion--either an ExprNode or an          *
-        * AssumeProve node.                                                *
-        *******************************************************************/
-        if (heirs[nextTok].getKind() == N_AssumeProve) {
-          /*****************************************************************
-          * This is an AssumeProve node.                                   *
-          *                                                                *
-          * For an ordinary ASSUME/PROVE, we need to save symbol           *
-          * declarations from top-level NEW statements in the ASSUME to    *
-          * make them visible only in the proof.  If this is a SUFFICES    *
-          * ASSUME/PROVE, we don't need to do that because we want those   *
-          * symbol declarations to be visible outside the PROVE clause as  *
-          * well.                                                          *
-          *****************************************************************/
-          isAssumeProve = true ;
-          if (!isSuffices) {
-            symbolTable.pushContext(new Context(moduleTable, errors)) ;
+				if (op == OP_pick) {
+					/****************************************************************
+					 * This is a PICK step; get the ": expr". *
+					 ****************************************************************/
+					nextTok++; // Skip over the ":"
+					args = new ExprNode[1];
+					args[0] = generateExpression(heirs[nextTok], cm);
+					nextTok++;
+				} else {
+					/****************************************************************
+					 * This is a TAKE step. *
+					 ****************************************************************/
+					args = new ExprNode[0];
+				}
+				;
+				body = new OpApplNode(op, args, params, stn, cm);
+			}
+			;
+			break;
+
+		case TLAplusParserConstants.WITNESS:
+			nextTok++;
+
+			/*******************************************************************
+			 * Set ids to the number of expressions. *
+			 *******************************************************************/
+			int ids = 1;
+			while ((nextTok + 2 * ids - 1 < heirs.length)
+					&& (heirs[nextTok + 2 * ids - 1].getKind() == TLAplusParserConstants.COMMA)) {
+				ids++;
+			}
+			;
+			ExprNode[] exprs = new ExprNode[ids];
+			for (int i = 0; i < ids; i++) {
+				exprs[i] = generateExpression(heirs[2 * i + nextTok], cm);
+			}
+			;
+
+			body = new OpApplNode(OP_tup, exprs, stn, cm);
+			nextTok = nextTok + 2 * ids - 1;
+			break;
+
+		default:
+			/*******************************************************************
+			 * This is an ordinary assertion--either an ExprNode or an * AssumeProve node. *
+			 *******************************************************************/
+			if (heirs[nextTok].getKind() == N_AssumeProve) {
+				/*****************************************************************
+				 * This is an AssumeProve node. * * For an ordinary ASSUME/PROVE, we need to
+				 * save symbol * declarations from top-level NEW statements in the ASSUME to *
+				 * make them visible only in the proof. If this is a SUFFICES * ASSUME/PROVE, we
+				 * don't need to do that because we want those * symbol declarations to be
+				 * visible outside the PROVE clause as * well. *
+				 *****************************************************************/
+				isAssumeProve = true;
+				if (!isSuffices) {
+					symbolTable.pushContext(new Context(moduleTable, errors));
 // System.out.println("here") ;
-           } ;
+				}
+				;
 // System.out.println("here and there") ;
-          body = generateAssumeProve(heirs[nextTok], cm);
-          if (!isSuffices) {
-            assumeContext = symbolTable.getContext() ;
-            symbolTable.popContext() ;
-           } ;
-         }
-        else {
-          /*****************************************************************
-          * This is an ordinary expression.                                *
-          *****************************************************************/
-          body = generateExpression(heirs[nextTok], cm) ;
-         };
-        nextTok++ ;
-        break ;
-     } ; // switch
-
-    /***********************************************************************
-    * Complete the ThmOrOpDefNode, if there is one.                        *
-    ***********************************************************************/
-    if (stepNum != null) { 
-      // SZA: the next line commented, to prevent a NullPointerException
-      // The method is not executed anyways.
-      // tadn.construct(true, body, cm, symbolTable, null) ;
-     } ;
-
-
-    /***********************************************************************
-    * Restore heirs and nextTok if the statement's tokens were inside a    *
-    * NonExprBody node.                                                    *
-    ***********************************************************************/
-    if (hasNonExprBody) {
-      heirs = savedHeirs ;
-      nextTok = savedNextTok + 1;
-     } ;
-    
-    /***********************************************************************
-    * Set proof to the proof, or to null if there is none.  There is no    *
-    * check made to see if this is a kind of step that should have a       *
-    * proof.  Thus, adding a proof to something like a WITNESS statement   *
-    * requires changing only the parsing phase (specified by tla+.jj).     *
-    ***********************************************************************/
-    ProofNode proof = null ;
-    if (heirs.length > nextTok) { 
-      if (isAssumeProve && !isSuffices) {
-        symbolTable.pushContext(assumeContext) ;
-       } ;
-      proof = generateProof(heirs[nextTok], cm); 
-      if (isAssumeProve && !isSuffices) {
-        symbolTable.popContext();
-       } ;
-     } ;
-
-    TheoremNode thm = new TheoremNode(stn, body, cm, proof, tadn);
-    thm.suffices = isSuffices ;
-    return thm; 
-
-   } // generateNumerableStep
-
-  private final LeafProofNode generateLeafProof(TreeNode stn, ModuleNode cm) 
-  throws AbortException {
-    TreeNode heirs[] = stn.heirs() ;
-    LevelNode[] facts ;
-    SymbolNode[] defs ;
-    int nextTok = 0 ;
-    boolean omitted = false ;
-
-    /***********************************************************************
-    * Skip over an optional "PROOF" (which can occur in a BY statement).   *
-    ***********************************************************************/
-    if (heirs[0].getKind() == TLAplusParserConstants.PROOF) {
-      nextTok++ ;
-     } ;
-    
-    boolean isOnly = false ;
-    
-    if (heirs[nextTok].getKind() == TLAplusParserConstants.BY) {
-      /*********************************************************************
-      * For a BY proof, call generate a UseOrHideNode and use its facts    *
-      * and defs field.                                                    *
-      *********************************************************************/
-      UseOrHideNode uh = generateUseOrHide(stn, cm) ;
-      isOnly = uh.isOnly ;
-      facts = uh.facts;
-      defs  = uh.defs;
-      /*********************************************************************
-      * The following check added by LL on 16 Feb 2009.                    *
-      *********************************************************************/
-      if (facts.length + defs.length == 0) { 
-          errors.addError(stn.getLocation(), "Empty BY");
-         };
-     } 
-    else{
-      facts = new LevelNode[0];
-      defs  = new SymbolNode[0];
-      if (heirs[nextTok].getKind() == TLAplusParserConstants.OMITTED) {
-       omitted = true ; };
-     } ;
-    return new LeafProofNode(stn, facts, defs, omitted, isOnly) ;    
-   }
-
-  UseOrHideNode 
-  generateUseOrHide(TreeNode stn, ModuleNode cm) throws AbortException {
-    /***********************************************************************
-    * Since a BY statement currently has essentially the same syntax as    *
-    * USE and HIDE, this is also used to parse a BY statement.  If given   *
-    * a BY statement, it returns a UseOrHideNode that will be used to      *
-    * form the LeafProofNode (and then thrown away).                       *
-    ***********************************************************************/
-    int kind = UseKind ;
-    TreeNode heirs[] = stn.heirs() ;
-   
-    boolean isOnly = false;
-      /*********************************************************************
-      * True iff this is an "ONLY" step--either a BY ONLY or a USE ONLY.   *
-      * However, we may decide not to include USE ONLY in the language,    *
-      * which wil require a simple modification of the javacc code.        *
-      *********************************************************************/
-      
-    if (heirs[0].getKind() == TLAplusParserConstants.HIDE)
-      {kind = HideKind; } ;
-
-    int nextTok = 1 ;
-
-    /***********************************************************************
-    * Skip over an optional "PROOF" (which can occur in a BY statement).   *
-    ***********************************************************************/
-    if (heirs[0].getKind() == TLAplusParserConstants.PROOF) {
-      nextTok++ ;
-     } ;
-
-    if (nextTok >= heirs.length) {
-        errors.addError(stn.getLocation(), "Empty BY, USE, or HIDE");
-        return new UseOrHideNode(kind, stn, new LevelNode[0],  new SymbolNode[0], isOnly);
-    }
-    Vector vec = new Vector() ;
-      /*********************************************************************
-      * To hold the facts and then the defs.                               *
-      *********************************************************************/
-
-    if (heirs[nextTok].getKind() == TLAplusParserConstants.ONLY) {
-      isOnly = true;
-      nextTok++ ;
-     } ;
-
-    /***********************************************************************
-    * Get the facts.                                                       *
-    ***********************************************************************/
-    while (  (nextTok < heirs.length) 
-           && (heirs[nextTok].getKind() != TLAplusParserConstants.DF)) {
-      if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) {
-        nextTok++ ;
-        UniqueString moduleId = heirs[nextTok].getUS();
-        ModuleNode moduleNode = symbolTable.resolveModule(moduleId);
-
-        /*******************************************************************
-        * The following added 16 Oct 2007 to allow a fact to be the        *
-        * current module.  This will probably mean to use or hide facts    *
-        * introduced so far in the current module.                         *
-        *******************************************************************/
-        if ((moduleNode == null) && (moduleId == cm.getName())) {
-          moduleNode = cm ;} ;
-        if (moduleNode != null) { vec.addElement(moduleNode) ; } 
-        else {
-          errors.addError(
-            heirs[nextTok].getLocation(),
-            "Module `" + moduleId + 
-              "' used without being extended or instantiated.");
-         }
-       } // if
-      else {
-        /*******************************************************************
-        * If the this token is an N_GeneralId, then we generate it here    *
-        * using selectorToNode so we can call that method with the isFact  *
-        * argument true.  Otherwise, we just call generateExpression.      *
-        *******************************************************************/
-        if (heirs[nextTok].getKind() == N_GeneralId) {
-          /*****************************************************************
-          * Here, we are using the fact that a theorem name must be a      *
-          * GeneralId and not something like an N_GenInfixOp.              *
-          *****************************************************************/
-          vec.addElement(
-             selectorToNode(genIdToSelector((SyntaxTreeNode) heirs[nextTok]), 
-                                            0, true, false, cm)) ;
-         }
-        else if (heirs[nextTok].getKind() == N_AssumeProve) {
-          vec.addElement(generateAssumeProve(heirs[nextTok], cm)) ;
-          }
-        else {
-          vec.addElement(generateExpression(heirs[nextTok], cm)) ;
-         } // else
-       } // else
-      nextTok++ ;
-      if (   (nextTok < heirs.length)
-          && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) {
-        nextTok++ ;
-       };
-     } ; // while  
-    LevelNode[] facts = new LevelNode[vec.size()] ;
-    for (int i = 0 ; i < vec.size() ; i++) {
-      facts[i] = (LevelNode) vec.elementAt(i) ;
-     } ;
-
-    /***********************************************************************
-    * Get the defs.                                                        *
-    ***********************************************************************/
-    SymbolNode[] defs ;
-    if (nextTok >= heirs.length) {
-      defs = new SymbolNode[0] ;
-     }
-    else {
-      vec = new Vector() ;
-      nextTok++ ;
-      while (nextTok < heirs.length) {
-        if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) {
-          nextTok++ ;
-          UniqueString moduleId = heirs[nextTok].getUS();
-          ModuleNode moduleNode = symbolTable.resolveModule(moduleId);
-
-          /*****************************************************************
-          * The following added 16 Oct 2007 to allow a fact to be the      *
-          * current module.  This will probably mean to use or hide        *
-          * definitions introduced so far in the current module.           *
-          *****************************************************************/
-          if ((moduleNode == null) && (moduleId == cm.getName())) {
-            moduleNode = cm ;} ;
-          if (moduleNode != null) { vec.addElement(moduleNode) ; } 
-          else {
-            errors.addError(
-              heirs[nextTok].getLocation(),
-              "Module `" + moduleId + 
-                "' used without being extended or instantiated.");
-           }
-         } // if
-        else {
-          Selector sel = genIdToSelector((SyntaxTreeNode) heirs[nextTok]) ;
-          SemanticNode selToNd = selectorToNode(sel, -1, false, true, cm);
-          if (   (selToNd instanceof OpDefNode)
-              || (  (selToNd instanceof ThmOrAssumpDefNode)
-                  && // This conjunct added 4 Feb 2015 by LL to forbid step
-                     // names in a DEF clause.
-                     (((ThmOrAssumpDefNode) selToNd).getName().toString().charAt(0) != '<' )
-                  )) { 
-            SymbolNode def = (SymbolNode) selToNd;
-            vec.addElement(def) ;
-           } 
-          else {
-            /***************************************************************
-            * This error is redundant, because it should have been caught  *
-            * in selectorToNode.  But a little redundancy can't hurt.      *
-            * But a little redundancy can't hurt.                          *
-            ***************************************************************/
-            errors.addError(
-              heirs[nextTok].getLocation(),
-              "DEF clause entry should describe a defined operator.") ;
-           } // else
-         } // else
-        nextTok++ ;
-        if (   (nextTok < heirs.length)
-            && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) {
-          nextTok++ ;
-          };
-       } ; // while  
-      defs = new SymbolNode[vec.size()] ;
-      for (int i = 0 ; i < vec.size() ; i++) {
-        defs[i] = (SymbolNode) vec.elementAt(i) ;
-       } ;
-     } // else of if (nextTok >= heirs.length)
-    return new UseOrHideNode(kind, stn, facts, defs, isOnly) ;
-   } // generateUseOrHide
-
-  void processRecursive(TreeNode tn, ModuleNode cm) {
-    /***********************************************************************
-    * Process a RECURSIVE statement.  Creates an OpDefNode for each of     *
-    * the declared operators.                                              *
-    ***********************************************************************/
-    TreeNode[] children = tn.heirs() ;
-
-    /***********************************************************************
-    * Increment unresolvedCnt unresolvedSum, and, if necessary,            *
-    * recursiveSectionCount.  This needs to be done before the calls to    *
-    * startOpDefNode so that we are in a recursive section when it is      *
-    * called.  Note that for                                               *
-    *                                                                      *
-    *   RECURSIVE op_1, ... , op_n                                         *
-    *                                                                      *
-    * children is the sequence of tokens                                   *
-    *                                                                      *
-    *   "RECURSIVE",  op_1,  ",",  ... ,  "," , op_n                       *
-    *                                                                      *
-    * so   children.length = 2*n  and  n = children.length / 2.            *
-    ***********************************************************************/
-    if (unresolvedSum == 0) {recursiveSectionCount ++; };
-    int numOfDecls = children.length / 2 ;
-    unresolvedCnt[curLevel] = unresolvedCnt[curLevel] + numOfDecls ;
-    unresolvedSum = unresolvedSum + numOfDecls;
-
-    for (int i = 1 ; i < children.length; i = i+2) {
-      /*********************************************************************
-      * Process the i-th declaration in the RECURSIVE statement.           *
-      *********************************************************************/
-      TreeNode declNode = children[i] ;
-
-      /*********************************************************************
-      * Set odn to an OpDeclNode made from the i-th declaration.           *
-      *********************************************************************/
-      OpDeclNode odn = 
-         buildParameter(declNode, 
-                        ConstantDeclKind, // Value of argument doesn't matter.
-                        ConstantLevel,    // Value of argument doesn't matter.
-                        cm,
-                        false) ; // Not adding OpDeclNode to symbolTable
-
-      /*********************************************************************
-      * Set params to the array of parameters for the declared operator's  *
-      * OpDefNode.                                                         *
-      *********************************************************************/
-      FormalParamNode[] params = new FormalParamNode[odn.getArity()] ;
-      for (int ip = 0 ; ip < params.length ; ip++) {
-        params[ip] = new FormalParamNode(null, 0, null, null, cm) ;
-       } ;
-      OpDefNode node =  startOpDefNode(odn.getName(), 
-                                       declNode,
-                                       UserDefinedOpKind,
-                                       params,
-                                       false, //localness
-                                       cm,
-                                       symbolTable);
-         /******************************************************************
-         * This node isn't saved anywhere.  It will be found either by     *
-         * looking up the operator when the N_OperatorDefinition node is   *
-         * encountered, or else by looking through the module's            *
-         * recursiveDecls vector when it is discovered that some operator  *
-         * has been declared but not defined (by finding unresolvedCnt >   *
-         * 0 when coming to the end of a LET or of the module).            *
-         ******************************************************************/
-     cm.recursiveOpDefNodes.addElement(node) ;
-         
-     } // for (int i = 1 ...)   
-    }
-
-
-
-/***************************************************************************
-* LABEL HANDLING                                                           *
-*                                                                          *
-* The following methods are used to create the labels fields of OpDefNode  *
-* and LabelNode objects.  They are specified in terms of an abstract data  *
-* object LS which is an element of                                         *
-*                                                                          *
-*   Seq( [labels   : SUBSET LabelNode,                                     *
-*         paramSeq : Seq (SUBSET OpDeclNode)] )                            *
-*                                                                          *
-* Thus, for all i \in 1..Len(LS) :                                         *
-*   LS[i].labels is a set of LabelNode objects                             *
-*   LS[i].paramSeq is a sequence of sets of OpDeclNode objects.            *
-*                                                                          *
-* Initially, LS equals the empty sequence << >>.                           *
-*                                                                          *
-* We define                                                                *
-*   Front(LS) == [i \in 1..Len(LS-1) |-> LS[i]]                            *
-*   Last(LS)  == LS[Len(LS)]                                               *
-*                                                                          *
-* LS is a stack containing one element for each OpDefNode and LabelNode    *
-* within which we are currently processing nodes, where Last(LS) is the    *
-* inner-most such node.  Last(LS).paramSeq is a stack containing one       *
-* element for each node that comes between the the preceding OpDefNode or  *
-* LabelNode and the current node that introduces bound variables.          *
-* Last(LS).paramSeq[j] is the set of bound variables introduced by the     *
-* j-th such node, where Last(Last(LS).paramSeq) is the most recent such    *
-* set of bound variables.                                                  *
-***************************************************************************/
-
-/***************************************************************************
-* Code for handling labels was added in the following methods:             *
-*    generateAssumeProve                                                   *
-*    generateLambda                                                        *
-*    processTheorem                                                        *
-*    processAssumption                                                     *
-*    processOperator                                                       *
-*    processFunction                                                       *
-*    processChoose                                                         *
-*    processBoundQuant                                                     *
-*    processUnboundQuant                                                   *
-*    processSubsetOf                                                       *
-*    processSetOfAll                                                       *
-*    processFcnConst                                                       *
-*    generateExpression                                                    *
-*    generateProof                                                         *
-***************************************************************************/
-
-  LabelNode generateLabel(TreeNode stn, ModuleNode cm) 
-   throws AbortException {
-
-    boolean isAssumeProve = false ;
-      /*********************************************************************
-      * Set true iff this is a labeled ASSUME/PROVE.                       *
-      *********************************************************************/
-      
-    if (!inOpDefNode()) {
-      errors.addError(stn.getLocation(),
-                      "Label not in definition or proof step.");
-      return nullLabelNode ;
-     };
-
-    if (noLabelsAllowed()) {
-      errors.addError(
-        stn.getLocation(),
-        "Label not allowed within scope of declaration in " +
-        "nested ASSUME/PROVE.");
-      return nullLabelNode ;
-      } ;
-
-    /***********************************************************************
-    * For now at least, and probably forever, we are not allowing an       *
-    * expression to be labeled if it lies inside an EXCEPT clause--which   *
-    * means if it is used where an "@" could appear.  The test for this    *
-    * is taken from generateExpression.                                    *
-    ***********************************************************************/
-    if (!((excStack.empty() || excSpecStack.empty()))) {
-          errors.addError(stn.getLocation(),
-             "Labels inside EXCEPT clauses are not yet implemented.");
-          return nullLabelNode ;
-      } ;
-
-    TreeNode[] labelExpChildren  = stn.heirs() ;
-
-    /***********************************************************************
-    * We first process the body, since we need it to create the LabelNode. *
-    ***********************************************************************/
-    pushLS() ;
-    LevelNode body ;
-      if (labelExpChildren[2].getKind() == N_AssumeProve) {
-        body = generateAssumeProve(labelExpChildren[2], cm) ;
-        isAssumeProve = true ;
-       }
-      else { body = generateExpression(labelExpChildren[2], cm) ; } ;
-    Hashtable ht = popLabelNodeSet() ;
-
-    /***********************************************************************
-    * We now create the LabelNode.                                         *
-    ***********************************************************************/
-    UniqueString name ;
-    FormalParamNode[] params;
-      /*********************************************************************
-      * The label's name and parameter list.                               *
-      *********************************************************************/
-    if (labelExpChildren[0].getKind() == N_GeneralId) {
-      /*********************************************************************
-      * There are no arguments to the label.                               *
-      *********************************************************************/
-      name = labelExpChildren[0].heirs()[1].getUS() ;
-      params = new FormalParamNode[0] ;
-     }
-    else {
-     /**********************************************************************
-     * The label should be represented as an N_OpApplication node.         *
-     **********************************************************************/
-      if (labelExpChildren[0].getKind() != N_OpApplication)
-        { throw new WrongInvocationException("Label has unexpected syntax tree kind.") ; };
-
-      TreeNode[] opApplChildren = labelExpChildren[0].heirs() ;
-      name = opApplChildren[0].heirs()[1].getUS() ;
-      TreeNode[] opArgsChildren =  opApplChildren[1].heirs() ;
-      int numOfParams = (opArgsChildren.length - 1) / 2 ;
-      params = new FormalParamNode[numOfParams] ;
-      for (int i = 0; i < numOfParams; i++) {
-        /*******************************************************************
-        * We have to get the FormalParamNode objects for the parameter     *
-        * array params by looking them up in the current context, since    *
-        * they could be either ConstantLevel (normal case) or StateLevel   *
-        * (if they're introduced in a \AA or \EE expression).              *
-        *******************************************************************/
-        TreeNode argSyntaxNode = opArgsChildren[2*i + 1];
-        UniqueString argName = argSyntaxNode.heirs()[1].getUS() ;
-        SymbolNode argNode = symbolTable.resolveSymbol(argName) ;
-        FormalParamNode arg = null ;
-        if (argNode instanceof FormalParamNode) 
-          {arg = (FormalParamNode) argNode ;}
-         else {errors.addError(argSyntaxNode.getLocation(),
-                               "Illegal parameter " + argName.toString() +
-                               " of label `" + name.toString() + "'.");  
-               arg = new FormalParamNode(argName, 0, argSyntaxNode, null, cm);
-                 /**********************************************************
-                 * Create a dummy FormalParamNode to prevent a null        *
-                 * pointer exception in later processing.                  *
-                 **********************************************************/
-         };      
-        params[i] = arg ;
-       } // for
-     } ; // else
-    SemanticNode cg = null ;
-    if (assumeProveDepth > 0) {cg = currentGoal;} ;
-    LabelNode retVal = new LabelNode(stn, name, params, currentGoal, 
-                                     currentGoalClause, body, isAssumeProve) ;
-    retVal.setLabels(ht) ;
-    boolean ignore = formalParamsEqual(retVal) ;
-      /*********************************************************************
-      * We throw away the return value because if there's an error, it is  *
-      * reported by the method.                                            *
-      *********************************************************************/
-    if (!addLabelNodeToSet(retVal)) {
-      errors.addError(stn.getLocation(),
-                      "Duplicate label `" + name.toString() + "'."); 
-     } ;
-    return retVal ;
-   } // generateLabel
-
-  Vector LSlabels   = new Vector() ;
-    /***********************************************************************
-    * LSlabels.elementAt(i) is a HashTable that represents                 *
-    * LS.labels[i+1].  The values in the Hashtable are LabelNode objects,  *
-    * where ln.getName() is the key of object ln.  The empty set is        *
-    * represented by null.                                                 *
-    ***********************************************************************/
-
-  Vector LSparamSeq = new Vector() ;
-    /***********************************************************************
-    * LsparamSeq.elementAt(i) is a Vector whose elements are of type       *
-    * FormalParamNode[] that represents LS.paramSeq[i+1].  In particular   *
-    * LS.paramSeq[i+1][j+1] equals                                         *
-    *   LET foo == (FormalParamNode[])                                     *
-    *                  ((Vector) LsparamSeq.elementAt(i))).elementAt(j)    *
-    *   IN  { (FormalParamNode) foo[j] : j \in 0 .. foo.length}            *
-    ***********************************************************************/
-
-  void pushLS() {
-    /***********************************************************************
-    * Implements   LS' = Append(LS, [labels |-> {}, paramSeq |-> << >>])   *
-    * That is, it pushes an "empty" record onto the end of LS.             *
-    ***********************************************************************/
-    LSlabels.addElement(null) ;
-    LSparamSeq.addElement(new Vector()) ;
-   }
-
-  Hashtable popLabelNodeSet() {
-    /***********************************************************************
-    * Implements  LS' = Front(LS)                                          *
-    *             return Last(LS).labels                                   *
-    ***********************************************************************/
-    int size = LSlabels.size() ;
-    if (size == 0) 
-      {throw new WrongInvocationException("popLabelNodeSet called on empty stack.");} ;
-    Hashtable retVal = (Hashtable) LSlabels.elementAt(size-1) ;
-    LSlabels.removeElementAt(size-1) ;
-    LSparamSeq.removeElementAt(size-1) ;
-    return retVal ;
-   }    
-
-  boolean addLabelNodeToSet(LabelNode ln) {
-    /***********************************************************************
-    * Implements                                                           *
-    *   LET succ == \A eln \in Last(LS).labels : eln.name # ln.name        *
-    *   IN  LS' = IF succ THEN [LS EXCEPT                                  *
-    *                             ![Len(LS)].labels = @ \cup {ln}]         *
-    *                     ELSE Labeling Stack                              *
-    *       return succ                                                    *
-    * That is, it adds LabelNode ln to Last(LS).labels iff there is not    *
-    * already a LabelNode with the same name in it, otherwise do nothing.  *
-    * It returns true iff ln was added.                                    *
-    ***********************************************************************/
-    int size = LSlabels.size() ;
-    if (size == 0) 
-      {throw new WrongInvocationException("addLabelNodeToSet called on empty stack.");} ;
-    Hashtable ht = (Hashtable) LSlabels.elementAt(size-1) ;
-    if (ht == null) {
-      ht = new Hashtable() ;
-      LSlabels.setElementAt(ht, size-1) ;
-     } 
-    boolean retVal = ! ht.containsKey(ln.getName()) ;
-    if (retVal) { ht.put(ln.getName(), ln); } ;
-    return retVal ;
-   }    
-
-  void pushFormalParams(FormalParamNode[] odns) {
-    /***********************************************************************
-    * Implements                                                           *
-    *                                                                      *
-    *   LS' = IF LS # << >>                                                *
-    *           THEN [LS EXCEPT ![Len(LS)].paramSeq =                      *
-    *                         Append(@, {odns[i] : i \in DOMAIN odns)]     *
-    *           ELSE LS                                                    *
-    *                                                                      *
-    * That is, if LS is not empty, then it pushes the set of               *
-    * FormalParamNode objects in the array odns onto the end of            *
-    * Last(LS).paramSeq.                                                   *
-    ***********************************************************************/
-    if (!inOpDefNode()){return;} ;
-    Vector lastFormalParams = 
-            (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1) ;
-    lastFormalParams.addElement(odns) ;
-   }
-
-  void popFormalParams() {
-    /***********************************************************************
-    * Implements                                                           *
-    *                                                                      *
-    *   LS' = IF LS # << >>                                                *
-    *           THEN [LS EXCEPT ![Len(LS)].paramSeq = Front(@)]            *
-    *           ELSE LS                                                    *
-    * That is, if LS is not empty, then it removes the last item from      *
-    * Last(LS).paramSeq.                                                   *
-    ***********************************************************************/
-    if (!inOpDefNode()){return;} ;
-    Vector lastFormalParams = 
-            (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1) ;
-    int size = lastFormalParams.size() ;
-    if (size == 0) 
-      {throw new WrongInvocationException("popFormalParams called on empty stack.");} ;
-    lastFormalParams.removeElementAt(size - 1);
-   }    
-
-  boolean formalParamsEqual(LabelNode ln) {
-    /***********************************************************************
-    * Returns  LET odns == ln.params                                       *
-    *          IN  /\ \A i, j \in DOMAIN odns :                            *
-    *                     (i # j) => odns[i] # odns[j]                     *
-    *              /\ {odns[i] : i \in DOMAINE odns} =                     *
-    *                    UNION {Last(LS).parmSeq[i] :                      *
-    *                              i \in DOMAIN Last(LS).parmSeq}          *
-    * That is, it returns true iff all the FormalParamNode objects in      *
-    * odns are distinct and the set of all thos objects equals the union   *
-    * of all the sets in the sequence Last(LS).paramSeq.                   *
-    *                                                                      *
-    * If this returns false, then an error message explains why.           *
-    ***********************************************************************/
-    boolean retVal = true ;
-    HashSet opParams = new HashSet() ;
-    FormalParamNode[] odns = ln.params ;
-    for (int i = 0; i < odns.length; i++) {
-      if (!opParams.add(odns[i])) {
-        retVal = false;
-        errors.addError(ln.stn.getLocation(),
-                        "Repeated formal parameter " + 
-                        odns[i].getName().toString() + " \nin label `" +
-                        ln.getName().toString() + "'.") ;
-       };
-      } // for ;
-    Vector lastFormalParams = 
-            (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1) ;
-    int size = lastFormalParams.size() ;
-    for (int i = 0 ; i < size; i++) {
-      FormalParamNode[] ops = (FormalParamNode[]) lastFormalParams.elementAt(i);
-      for (int j = 0 ; j < ops.length ; j++) {
-        if (! opParams.remove(ops[j])) {
-         retVal = false;
-         errors.addError(ln.stn.getLocation(),
-                         "Label " + ln.getName().toString() + 
-                         " must contain formal parameter `" + 
-                         ops[j].getName().toString() + "'.") ;         
-         };
-       } // for j;
-      } // for i;
-    if (!opParams.isEmpty()) {
-     retVal = false ;
-     Iterator iter = opParams.iterator();
-     String res = "Label " + ln.getName().toString() +
-                  " declares extra parameter(s)  ";
-      while (iter.hasNext()){
-        FormalParamNode nd = (FormalParamNode) iter.next() ;
-        res = res + nd.getName().toString() + "  ";
-       } // while
-      errors.addError(ln.stn.getLocation(), res) ;
-      } // if
-    return retVal ;
-   }
-
-  boolean inOpDefNode() { return LSlabels.size() > 0; }
-    /***********************************************************************
-    * Returns true iff LS is not equal to the empty sequence << >>.        *
-    ***********************************************************************/
-
-  FormalParamNode[] flattenParams(FormalParamNode[][] array) {
-    /***********************************************************************
-    * Flatten a 2-dimensional array of FormalParamNodes into a             *
-    * 1-dimensional array.  Used because the oan.boundedBoundSymbols       *
-    * field for an OpApplNode is such a 2-dimensional array, since         *
-    * "bounded bound symbols" may be tuples.                               *
-    ***********************************************************************/
-    int size = 0 ;
-    for (int i = 0 ; i < array.length; i++) { size = size + array[i].length; } ;
-    FormalParamNode[] res = new FormalParamNode[size] ;
-    int k = 0 ;
-    for (int i = 0 ; i < array.length; i++) { 
-      for (int j = 0 ; j < array[i].length; j++) {
-        res[k] = array[i][j] ;
-        k++ ;
-        } ; // for j
-     } ; // for i
-    return res ;
-   }    
-
-/***************************************************************************
-*                            Recursion Processing                          *
-*                                                                          *
-* This is the code for setting the fields of OpDefNode objects that are    *
-* related to recursion--namely, letInLevel, inRecursive,                   *
-* inRecursiveSection.  See the description of these fields in              *
-* semantic/OpDefNode.java to see what they mean.                           *
-*                                                                          *
-* A tool might also want to compute the strongly connected components of   *
-* the dependency graph--the graph of OpDefNode objects containing an edge  *
-* from n to m iff m's operator appears in the body of n.                   *
-*                                                                          *
-* I originally thought that the dependency graph could be created while    *
-* the semantic graph was being constructed, and that the strongly          *
-* connected components could be computed incrementally by running a        *
-* connected-component algorithm at the end of each recursive section.  I   *
-* thought that could be done easily by creating an OpDefNode for a         *
-* definition before processing the definition's body, and adding to a      *
-* field in that OpDefNode a list of the OpDefNodes that appeared in        *
-* OpApplNodes in the body.  However, that didn't work because of           *
-* definitions like                                                         *
-*                                                                          *
-*   f == LET f == ...                                                      *
-*        IN  ...                                                           *
-*                                                                          *
-* So, I abandoned that idea.  It's probably easiest to compute the         *
-* connected components after the module is processed.                      *
-*                                                                          *
-* If we ever do want to construct those connected components, we can run   *
-* a version of Tarjan's algorithm for computing strongly connected         *
-* components of a directed graph.  A +cal coding of this algorithm is in   *
-* the file Tarjan.tla, a copy of which appears as a comment at the end of  *
-* this file.                                                               *
-***************************************************************************/
-
-/***************************************************************************
-* The fields.                                                              *
-***************************************************************************/
-  final int MaxLetInLevel = 100 ;
-    /***********************************************************************
-    * It seems safe to assume that LETs won't be nested more than 100      *
-    * deep, so we can use arrays instead of having to deal with vectors    *
-    * to allow arbitrary depths.                                           *
-    ***********************************************************************/
-
-  int   curLevel = 0 ;
-    /***********************************************************************
-    * The current LET/IN nesting level--that is, the number of LET/IN      *
-    * statements within which the nodes we are currently processing lie.   *
-    ***********************************************************************/
-
-  int[] unresolvedCnt = new int[MaxLetInLevel] ;
-    /***********************************************************************
-    * For i \in 0..curLevel, the value of unresolvedCnt[i] is the number   *
-    * of operators declared in RECURSIVE statements at let/in level i that *
-    * have not yet been defined.                                           *
-    ***********************************************************************/
-
-  int unresolvedSum = 0;
-    /***********************************************************************
-    * Equals the sum from i = 0 to curLevel of unresolvedCnt[i].           *
-    ***********************************************************************/
-
-  int recursiveSectionCount = 0 ;
-    /***********************************************************************
-    * This field is incremented whenever a new recursive section is        *
-    * begun--that is, when unresolvedSum changes from 0 to a positive      *
-    * value (which must be 1).                                             *
-    ***********************************************************************/
+				body = generateAssumeProve(heirs[nextTok], cm);
+				if (!isSuffices) {
+					assumeContext = symbolTable.getContext();
+					symbolTable.popContext();
+				}
+				;
+			} else {
+				/*****************************************************************
+				 * This is an ordinary expression. *
+				 *****************************************************************/
+				body = generateExpression(heirs[nextTok], cm);
+			}
+			;
+			nextTok++;
+			break;
+		}
+		; // switch
+
+		/***********************************************************************
+		 * Complete the ThmOrOpDefNode, if there is one. *
+		 ***********************************************************************/
+		if (stepNum != null) {
+			// SZA: the next line commented, to prevent a NullPointerException
+			// The method is not executed anyways.
+			// tadn.construct(true, body, cm, symbolTable, null) ;
+		}
+		;
+
+		/***********************************************************************
+		 * Restore heirs and nextTok if the statement's tokens were inside a *
+		 * NonExprBody node. *
+		 ***********************************************************************/
+		if (hasNonExprBody) {
+			heirs = savedHeirs;
+			nextTok = savedNextTok + 1;
+		}
+		;
+
+		/***********************************************************************
+		 * Set proof to the proof, or to null if there is none. There is no * check made
+		 * to see if this is a kind of step that should have a * proof. Thus, adding a
+		 * proof to something like a WITNESS statement * requires changing only the
+		 * parsing phase (specified by tla+.jj). *
+		 ***********************************************************************/
+		ProofNode proof = null;
+		if (heirs.length > nextTok) {
+			if (isAssumeProve && !isSuffices) {
+				symbolTable.pushContext(assumeContext);
+			}
+			;
+			proof = generateProof(heirs[nextTok], cm);
+			if (isAssumeProve && !isSuffices) {
+				symbolTable.popContext();
+			}
+			;
+		}
+		;
+
+		TheoremNode thm = new TheoremNode(stn, body, cm, proof, tadn);
+		thm.suffices = isSuffices;
+		return thm;
+
+	} // generateNumerableStep
+
+	private final LeafProofNode generateLeafProof(TreeNode stn, ModuleNode cm) throws AbortException {
+		TreeNode heirs[] = stn.heirs();
+		LevelNode[] facts;
+		SymbolNode[] defs;
+		int nextTok = 0;
+		boolean omitted = false;
+
+		/***********************************************************************
+		 * Skip over an optional "PROOF" (which can occur in a BY statement). *
+		 ***********************************************************************/
+		if (heirs[0].getKind() == TLAplusParserConstants.PROOF) {
+			nextTok++;
+		}
+		;
+
+		boolean isOnly = false;
+
+		if (heirs[nextTok].getKind() == TLAplusParserConstants.BY) {
+			/*********************************************************************
+			 * For a BY proof, call generate a UseOrHideNode and use its facts * and defs
+			 * field. *
+			 *********************************************************************/
+			UseOrHideNode uh = generateUseOrHide(stn, cm);
+			isOnly = uh.isOnly;
+			facts = uh.facts;
+			defs = uh.defs;
+			/*********************************************************************
+			 * The following check added by LL on 16 Feb 2009. *
+			 *********************************************************************/
+			if (facts.length + defs.length == 0) {
+				errors.addError(stn.getLocation(), "Empty BY");
+			}
+			;
+		} else {
+			facts = new LevelNode[0];
+			defs = new SymbolNode[0];
+			if (heirs[nextTok].getKind() == TLAplusParserConstants.OMITTED) {
+				omitted = true;
+			}
+			;
+		}
+		;
+		return new LeafProofNode(stn, facts, defs, omitted, isOnly);
+	}
+
+	UseOrHideNode generateUseOrHide(TreeNode stn, ModuleNode cm) throws AbortException {
+		/***********************************************************************
+		 * Since a BY statement currently has essentially the same syntax as * USE and
+		 * HIDE, this is also used to parse a BY statement. If given * a BY statement,
+		 * it returns a UseOrHideNode that will be used to * form the LeafProofNode (and
+		 * then thrown away). *
+		 ***********************************************************************/
+		int kind = UseKind;
+		TreeNode heirs[] = stn.heirs();
+
+		boolean isOnly = false;
+		/*********************************************************************
+		 * True iff this is an "ONLY" step--either a BY ONLY or a USE ONLY. * However,
+		 * we may decide not to include USE ONLY in the language, * which wil require a
+		 * simple modification of the javacc code. *
+		 *********************************************************************/
+
+		if (heirs[0].getKind() == TLAplusParserConstants.HIDE) {
+			kind = HideKind;
+		}
+		;
+
+		int nextTok = 1;
+
+		/***********************************************************************
+		 * Skip over an optional "PROOF" (which can occur in a BY statement). *
+		 ***********************************************************************/
+		if (heirs[0].getKind() == TLAplusParserConstants.PROOF) {
+			nextTok++;
+		}
+		;
+
+		if (nextTok >= heirs.length) {
+			errors.addError(stn.getLocation(), "Empty BY, USE, or HIDE");
+			return new UseOrHideNode(kind, stn, new LevelNode[0], new SymbolNode[0], isOnly);
+		}
+		Vector vec = new Vector();
+		/*********************************************************************
+		 * To hold the facts and then the defs. *
+		 *********************************************************************/
+
+		if (heirs[nextTok].getKind() == TLAplusParserConstants.ONLY) {
+			isOnly = true;
+			nextTok++;
+		}
+		;
+
+		/***********************************************************************
+		 * Get the facts. *
+		 ***********************************************************************/
+		while ((nextTok < heirs.length) && (heirs[nextTok].getKind() != TLAplusParserConstants.DF)) {
+			if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) {
+				nextTok++;
+				UniqueString moduleId = heirs[nextTok].getUS();
+				ModuleNode moduleNode = symbolTable.resolveModule(moduleId);
+
+				/*******************************************************************
+				 * The following added 16 Oct 2007 to allow a fact to be the * current module.
+				 * This will probably mean to use or hide facts * introduced so far in the
+				 * current module. *
+				 *******************************************************************/
+				if ((moduleNode == null) && (moduleId == cm.getName())) {
+					moduleNode = cm;
+				}
+				;
+				if (moduleNode != null) {
+					vec.addElement(moduleNode);
+				} else {
+					errors.addError(heirs[nextTok].getLocation(),
+							"Module `" + moduleId + "' used without being extended or instantiated.");
+				}
+			} // if
+			else {
+				/*******************************************************************
+				 * If the this token is an N_GeneralId, then we generate it here * using
+				 * selectorToNode so we can call that method with the isFact * argument true.
+				 * Otherwise, we just call generateExpression. *
+				 *******************************************************************/
+				if (heirs[nextTok].getKind() == N_GeneralId) {
+					/*****************************************************************
+					 * Here, we are using the fact that a theorem name must be a * GeneralId and not
+					 * something like an N_GenInfixOp. *
+					 *****************************************************************/
+					vec.addElement(
+							selectorToNode(genIdToSelector((SyntaxTreeNode) heirs[nextTok]), 0, true, false, cm));
+				} else if (heirs[nextTok].getKind() == N_AssumeProve) {
+					vec.addElement(generateAssumeProve(heirs[nextTok], cm));
+				} else {
+					vec.addElement(generateExpression(heirs[nextTok], cm));
+				} // else
+			} // else
+			nextTok++;
+			if ((nextTok < heirs.length) && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) {
+				nextTok++;
+			}
+			;
+		}
+		; // while
+		LevelNode[] facts = new LevelNode[vec.size()];
+		for (int i = 0; i < vec.size(); i++) {
+			facts[i] = (LevelNode) vec.elementAt(i);
+		}
+		;
+
+		/***********************************************************************
+		 * Get the defs. *
+		 ***********************************************************************/
+		SymbolNode[] defs;
+		if (nextTok >= heirs.length) {
+			defs = new SymbolNode[0];
+		} else {
+			vec = new Vector();
+			nextTok++;
+			while (nextTok < heirs.length) {
+				if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) {
+					nextTok++;
+					UniqueString moduleId = heirs[nextTok].getUS();
+					ModuleNode moduleNode = symbolTable.resolveModule(moduleId);
+
+					/*****************************************************************
+					 * The following added 16 Oct 2007 to allow a fact to be the * current module.
+					 * This will probably mean to use or hide * definitions introduced so far in the
+					 * current module. *
+					 *****************************************************************/
+					if ((moduleNode == null) && (moduleId == cm.getName())) {
+						moduleNode = cm;
+					}
+					;
+					if (moduleNode != null) {
+						vec.addElement(moduleNode);
+					} else {
+						errors.addError(heirs[nextTok].getLocation(),
+								"Module `" + moduleId + "' used without being extended or instantiated.");
+					}
+				} // if
+				else {
+					Selector sel = genIdToSelector((SyntaxTreeNode) heirs[nextTok]);
+					SemanticNode selToNd = selectorToNode(sel, -1, false, true, cm);
+					if ((selToNd instanceof OpDefNode) || ((selToNd instanceof ThmOrAssumpDefNode) && // This conjunct
+																										// added 4 Feb
+																										// 2015 by LL to
+																										// forbid step
+																										// names in a
+																										// DEF clause.
+							(((ThmOrAssumpDefNode) selToNd).getName().toString().charAt(0) != '<'))) {
+						SymbolNode def = (SymbolNode) selToNd;
+						vec.addElement(def);
+					} else {
+						/***************************************************************
+						 * This error is redundant, because it should have been caught * in
+						 * selectorToNode. But a little redundancy can't hurt. * But a little redundancy
+						 * can't hurt. *
+						 ***************************************************************/
+						errors.addError(heirs[nextTok].getLocation(),
+								"DEF clause entry should describe a defined operator.");
+					} // else
+				} // else
+				nextTok++;
+				if ((nextTok < heirs.length) && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) {
+					nextTok++;
+				}
+				;
+			}
+			; // while
+			defs = new SymbolNode[vec.size()];
+			for (int i = 0; i < vec.size(); i++) {
+				defs[i] = (SymbolNode) vec.elementAt(i);
+			}
+			;
+		} // else of if (nextTok >= heirs.length)
+		return new UseOrHideNode(kind, stn, facts, defs, isOnly);
+	} // generateUseOrHide
+
+	void processRecursive(TreeNode tn, ModuleNode cm) {
+		/***********************************************************************
+		 * Process a RECURSIVE statement. Creates an OpDefNode for each of * the
+		 * declared operators. *
+		 ***********************************************************************/
+		TreeNode[] children = tn.heirs();
+
+		/***********************************************************************
+		 * Increment unresolvedCnt unresolvedSum, and, if necessary, *
+		 * recursiveSectionCount. This needs to be done before the calls to *
+		 * startOpDefNode so that we are in a recursive section when it is * called.
+		 * Note that for * * RECURSIVE op_1, ... , op_n * * children is the sequence of
+		 * tokens * * "RECURSIVE", op_1, ",", ... , "," , op_n * * so children.length =
+		 * 2*n and n = children.length / 2. *
+		 ***********************************************************************/
+		if (unresolvedSum == 0) {
+			recursiveSectionCount++;
+		}
+		;
+		int numOfDecls = children.length / 2;
+		unresolvedCnt[curLevel] = unresolvedCnt[curLevel] + numOfDecls;
+		unresolvedSum = unresolvedSum + numOfDecls;
+
+		for (int i = 1; i < children.length; i = i + 2) {
+			/*********************************************************************
+			 * Process the i-th declaration in the RECURSIVE statement. *
+			 *********************************************************************/
+			TreeNode declNode = children[i];
+
+			/*********************************************************************
+			 * Set odn to an OpDeclNode made from the i-th declaration. *
+			 *********************************************************************/
+			OpDeclNode odn = buildParameter(declNode, ConstantDeclKind, // Value of argument doesn't matter.
+					ConstantLevel, // Value of argument doesn't matter.
+					cm, false); // Not adding OpDeclNode to symbolTable
+
+			/*********************************************************************
+			 * Set params to the array of parameters for the declared operator's *
+			 * OpDefNode. *
+			 *********************************************************************/
+			FormalParamNode[] params = new FormalParamNode[odn.getArity()];
+			for (int ip = 0; ip < params.length; ip++) {
+				params[ip] = new FormalParamNode(null, 0, null, null, cm);
+			}
+			;
+			OpDefNode node = startOpDefNode(odn.getName(), declNode, UserDefinedOpKind, params, false, // localness
+					cm, symbolTable);
+			/******************************************************************
+			 * This node isn't saved anywhere. It will be found either by * looking up the
+			 * operator when the N_OperatorDefinition node is * encountered, or else by
+			 * looking through the module's * recursiveDecls vector when it is discovered
+			 * that some operator * has been declared but not defined (by finding
+			 * unresolvedCnt > * 0 when coming to the end of a LET or of the module). *
+			 ******************************************************************/
+			cm.recursiveOpDefNodes.addElement(node);
+
+		} // for (int i = 1 ...)
+	}
+
+	/***************************************************************************
+	 * LABEL HANDLING * * The following methods are used to create the labels fields
+	 * of OpDefNode * and LabelNode objects. They are specified in terms of an
+	 * abstract data * object LS which is an element of * * Seq( [labels : SUBSET
+	 * LabelNode, * paramSeq : Seq (SUBSET OpDeclNode)] ) * * Thus, for all i \in
+	 * 1..Len(LS) : * LS[i].labels is a set of LabelNode objects * LS[i].paramSeq is
+	 * a sequence of sets of OpDeclNode objects. * * Initially, LS equals the empty
+	 * sequence << >>. * * We define * Front(LS) == [i \in 1..Len(LS-1) |-> LS[i]] *
+	 * Last(LS) == LS[Len(LS)] * * LS is a stack containing one element for each
+	 * OpDefNode and LabelNode * within which we are currently processing nodes,
+	 * where Last(LS) is the * inner-most such node. Last(LS).paramSeq is a stack
+	 * containing one * element for each node that comes between the the preceding
+	 * OpDefNode or * LabelNode and the current node that introduces bound
+	 * variables. * Last(LS).paramSeq[j] is the set of bound variables introduced by
+	 * the * j-th such node, where Last(Last(LS).paramSeq) is the most recent such *
+	 * set of bound variables. *
+	 ***************************************************************************/
+
+	/***************************************************************************
+	 * Code for handling labels was added in the following methods: *
+	 * generateAssumeProve * generateLambda * processTheorem * processAssumption *
+	 * processOperator * processFunction * processChoose * processBoundQuant *
+	 * processUnboundQuant * processSubsetOf * processSetOfAll * processFcnConst *
+	 * generateExpression * generateProof *
+	 ***************************************************************************/
+
+	LabelNode generateLabel(TreeNode stn, ModuleNode cm) throws AbortException {
+
+		boolean isAssumeProve = false;
+		/*********************************************************************
+		 * Set true iff this is a labeled ASSUME/PROVE. *
+		 *********************************************************************/
+
+		if (!inOpDefNode()) {
+			errors.addError(stn.getLocation(), "Label not in definition or proof step.");
+			return nullLabelNode;
+		}
+		;
+
+		if (noLabelsAllowed()) {
+			errors.addError(stn.getLocation(),
+					"Label not allowed within scope of declaration in " + "nested ASSUME/PROVE.");
+			return nullLabelNode;
+		}
+		;
+
+		/***********************************************************************
+		 * For now at least, and probably forever, we are not allowing an * expression
+		 * to be labeled if it lies inside an EXCEPT clause--which * means if it is used
+		 * where an "@" could appear. The test for this * is taken from
+		 * generateExpression. *
+		 ***********************************************************************/
+		if (!((excStack.empty() || excSpecStack.empty()))) {
+			errors.addError(stn.getLocation(), "Labels inside EXCEPT clauses are not yet implemented.");
+			return nullLabelNode;
+		}
+		;
+
+		TreeNode[] labelExpChildren = stn.heirs();
+
+		/***********************************************************************
+		 * We first process the body, since we need it to create the LabelNode. *
+		 ***********************************************************************/
+		pushLS();
+		LevelNode body;
+		if (labelExpChildren[2].getKind() == N_AssumeProve) {
+			body = generateAssumeProve(labelExpChildren[2], cm);
+			isAssumeProve = true;
+		} else {
+			body = generateExpression(labelExpChildren[2], cm);
+		}
+		;
+		Hashtable ht = popLabelNodeSet();
+
+		/***********************************************************************
+		 * We now create the LabelNode. *
+		 ***********************************************************************/
+		UniqueString name;
+		FormalParamNode[] params;
+		/*********************************************************************
+		 * The label's name and parameter list. *
+		 *********************************************************************/
+		if (labelExpChildren[0].getKind() == N_GeneralId) {
+			/*********************************************************************
+			 * There are no arguments to the label. *
+			 *********************************************************************/
+			name = labelExpChildren[0].heirs()[1].getUS();
+			params = new FormalParamNode[0];
+		} else {
+			/**********************************************************************
+			 * The label should be represented as an N_OpApplication node. *
+			 **********************************************************************/
+			if (labelExpChildren[0].getKind() != N_OpApplication) {
+				throw new WrongInvocationException("Label has unexpected syntax tree kind.");
+			}
+			;
+
+			TreeNode[] opApplChildren = labelExpChildren[0].heirs();
+			name = opApplChildren[0].heirs()[1].getUS();
+			TreeNode[] opArgsChildren = opApplChildren[1].heirs();
+			int numOfParams = (opArgsChildren.length - 1) / 2;
+			params = new FormalParamNode[numOfParams];
+			for (int i = 0; i < numOfParams; i++) {
+				/*******************************************************************
+				 * We have to get the FormalParamNode objects for the parameter * array params
+				 * by looking them up in the current context, since * they could be either
+				 * ConstantLevel (normal case) or StateLevel * (if they're introduced in a \AA
+				 * or \EE expression). *
+				 *******************************************************************/
+				TreeNode argSyntaxNode = opArgsChildren[2 * i + 1];
+				UniqueString argName = argSyntaxNode.heirs()[1].getUS();
+				SymbolNode argNode = symbolTable.resolveSymbol(argName);
+				FormalParamNode arg = null;
+				if (argNode instanceof FormalParamNode) {
+					arg = (FormalParamNode) argNode;
+				} else {
+					errors.addError(argSyntaxNode.getLocation(),
+							"Illegal parameter " + argName.toString() + " of label `" + name.toString() + "'.");
+					arg = new FormalParamNode(argName, 0, argSyntaxNode, null, cm);
+					/**********************************************************
+					 * Create a dummy FormalParamNode to prevent a null * pointer exception in later
+					 * processing. *
+					 **********************************************************/
+				}
+				;
+				params[i] = arg;
+			} // for
+		}
+		; // else
+		SemanticNode cg = null;
+		if (assumeProveDepth > 0) {
+			cg = currentGoal;
+		}
+		;
+		LabelNode retVal = new LabelNode(stn, name, params, currentGoal, currentGoalClause, body, isAssumeProve);
+		retVal.setLabels(ht);
+		boolean ignore = formalParamsEqual(retVal);
+		/*********************************************************************
+		 * We throw away the return value because if there's an error, it is * reported
+		 * by the method. *
+		 *********************************************************************/
+		if (!addLabelNodeToSet(retVal)) {
+			errors.addError(stn.getLocation(), "Duplicate label `" + name.toString() + "'.");
+		}
+		;
+		return retVal;
+	} // generateLabel
+
+	Vector LSlabels = new Vector();
+	/***********************************************************************
+	 * LSlabels.elementAt(i) is a HashTable that represents * LS.labels[i+1]. The
+	 * values in the Hashtable are LabelNode objects, * where ln.getName() is the
+	 * key of object ln. The empty set is * represented by null. *
+	 ***********************************************************************/
+
+	Vector LSparamSeq = new Vector();
+
+	/***********************************************************************
+	 * LsparamSeq.elementAt(i) is a Vector whose elements are of type *
+	 * FormalParamNode[] that represents LS.paramSeq[i+1]. In particular *
+	 * LS.paramSeq[i+1][j+1] equals * LET foo == (FormalParamNode[]) * ((Vector)
+	 * LsparamSeq.elementAt(i))).elementAt(j) * IN { (FormalParamNode) foo[j] : j
+	 * \in 0 .. foo.length} *
+	 ***********************************************************************/
+
+	void pushLS() {
+		/***********************************************************************
+		 * Implements LS' = Append(LS, [labels |-> {}, paramSeq |-> << >>]) * That is,
+		 * it pushes an "empty" record onto the end of LS. *
+		 ***********************************************************************/
+		LSlabels.addElement(null);
+		LSparamSeq.addElement(new Vector());
+	}
+
+	Hashtable popLabelNodeSet() {
+		/***********************************************************************
+		 * Implements LS' = Front(LS) * return Last(LS).labels *
+		 ***********************************************************************/
+		int size = LSlabels.size();
+		if (size == 0) {
+			throw new WrongInvocationException("popLabelNodeSet called on empty stack.");
+		}
+		;
+		Hashtable retVal = (Hashtable) LSlabels.elementAt(size - 1);
+		LSlabels.removeElementAt(size - 1);
+		LSparamSeq.removeElementAt(size - 1);
+		return retVal;
+	}
+
+	boolean addLabelNodeToSet(LabelNode ln) {
+		/***********************************************************************
+		 * Implements * LET succ == \A eln \in Last(LS).labels : eln.name # ln.name * IN
+		 * LS' = IF succ THEN [LS EXCEPT * ![Len(LS)].labels = @ \cup {ln}] * ELSE
+		 * Labeling Stack * return succ * That is, it adds LabelNode ln to
+		 * Last(LS).labels iff there is not * already a LabelNode with the same name in
+		 * it, otherwise do nothing. * It returns true iff ln was added. *
+		 ***********************************************************************/
+		int size = LSlabels.size();
+		if (size == 0) {
+			throw new WrongInvocationException("addLabelNodeToSet called on empty stack.");
+		}
+		;
+		Hashtable ht = (Hashtable) LSlabels.elementAt(size - 1);
+		if (ht == null) {
+			ht = new Hashtable();
+			LSlabels.setElementAt(ht, size - 1);
+		}
+		boolean retVal = !ht.containsKey(ln.getName());
+		if (retVal) {
+			ht.put(ln.getName(), ln);
+		}
+		;
+		return retVal;
+	}
+
+	void pushFormalParams(FormalParamNode[] odns) {
+		/***********************************************************************
+		 * Implements * * LS' = IF LS # << >> * THEN [LS EXCEPT ![Len(LS)].paramSeq = *
+		 * Append(@, {odns[i] : i \in DOMAIN odns)] * ELSE LS * * That is, if LS is not
+		 * empty, then it pushes the set of * FormalParamNode objects in the array odns
+		 * onto the end of * Last(LS).paramSeq. *
+		 ***********************************************************************/
+		if (!inOpDefNode()) {
+			return;
+		}
+		;
+		Vector lastFormalParams = (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1);
+		lastFormalParams.addElement(odns);
+	}
+
+	void popFormalParams() {
+		/***********************************************************************
+		 * Implements * * LS' = IF LS # << >> * THEN [LS EXCEPT ![Len(LS)].paramSeq =
+		 * Front(@)] * ELSE LS * That is, if LS is not empty, then it removes the last
+		 * item from * Last(LS).paramSeq. *
+		 ***********************************************************************/
+		if (!inOpDefNode()) {
+			return;
+		}
+		;
+		Vector lastFormalParams = (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1);
+		int size = lastFormalParams.size();
+		if (size == 0) {
+			throw new WrongInvocationException("popFormalParams called on empty stack.");
+		}
+		;
+		lastFormalParams.removeElementAt(size - 1);
+	}
+
+	boolean formalParamsEqual(LabelNode ln) {
+		/***********************************************************************
+		 * Returns LET odns == ln.params * IN /\ \A i, j \in DOMAIN odns : * (i # j) =>
+		 * odns[i] # odns[j] * /\ {odns[i] : i \in DOMAINE odns} = * UNION
+		 * {Last(LS).parmSeq[i] : * i \in DOMAIN Last(LS).parmSeq} * That is, it returns
+		 * true iff all the FormalParamNode objects in * odns are distinct and the set
+		 * of all thos objects equals the union * of all the sets in the sequence
+		 * Last(LS).paramSeq. * * If this returns false, then an error message explains
+		 * why. *
+		 ***********************************************************************/
+		boolean retVal = true;
+		HashSet opParams = new HashSet();
+		FormalParamNode[] odns = ln.params;
+		for (int i = 0; i < odns.length; i++) {
+			if (!opParams.add(odns[i])) {
+				retVal = false;
+				errors.addError(ln.stn.getLocation(), "Repeated formal parameter " + odns[i].getName().toString()
+						+ " \nin label `" + ln.getName().toString() + "'.");
+			}
+			;
+		} // for ;
+		Vector lastFormalParams = (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1);
+		int size = lastFormalParams.size();
+		for (int i = 0; i < size; i++) {
+			FormalParamNode[] ops = (FormalParamNode[]) lastFormalParams.elementAt(i);
+			for (int j = 0; j < ops.length; j++) {
+				if (!opParams.remove(ops[j])) {
+					retVal = false;
+					errors.addError(ln.stn.getLocation(), "Label " + ln.getName().toString()
+							+ " must contain formal parameter `" + ops[j].getName().toString() + "'.");
+				}
+				;
+			} // for j;
+		} // for i;
+		if (!opParams.isEmpty()) {
+			retVal = false;
+			Iterator iter = opParams.iterator();
+			String res = "Label " + ln.getName().toString() + " declares extra parameter(s)  ";
+			while (iter.hasNext()) {
+				FormalParamNode nd = (FormalParamNode) iter.next();
+				res = res + nd.getName().toString() + "  ";
+			} // while
+			errors.addError(ln.stn.getLocation(), res);
+		} // if
+		return retVal;
+	}
+
+	boolean inOpDefNode() {
+		return LSlabels.size() > 0;
+	}
+
+	/***********************************************************************
+	 * Returns true iff LS is not equal to the empty sequence << >>. *
+	 ***********************************************************************/
+
+	FormalParamNode[] flattenParams(FormalParamNode[][] array) {
+		/***********************************************************************
+		 * Flatten a 2-dimensional array of FormalParamNodes into a * 1-dimensional
+		 * array. Used because the oan.boundedBoundSymbols * field for an OpApplNode is
+		 * such a 2-dimensional array, since * "bounded bound symbols" may be tuples. *
+		 ***********************************************************************/
+		int size = 0;
+		for (int i = 0; i < array.length; i++) {
+			size = size + array[i].length;
+		}
+		;
+		FormalParamNode[] res = new FormalParamNode[size];
+		int k = 0;
+		for (int i = 0; i < array.length; i++) {
+			for (int j = 0; j < array[i].length; j++) {
+				res[k] = array[i][j];
+				k++;
+			}
+			; // for j
+		}
+		; // for i
+		return res;
+	}
+
+	/***************************************************************************
+	 * Recursion Processing * * This is the code for setting the fields of OpDefNode
+	 * objects that are * related to recursion--namely, letInLevel, inRecursive, *
+	 * inRecursiveSection. See the description of these fields in *
+	 * semantic/OpDefNode.java to see what they mean. * * A tool might also want to
+	 * compute the strongly connected components of * the dependency graph--the
+	 * graph of OpDefNode objects containing an edge * from n to m iff m's operator
+	 * appears in the body of n. * * I originally thought that the dependency graph
+	 * could be created while * the semantic graph was being constructed, and that
+	 * the strongly * connected components could be computed incrementally by
+	 * running a * connected-component algorithm at the end of each recursive
+	 * section. I * thought that could be done easily by creating an OpDefNode for a
+	 * * definition before processing the definition's body, and adding to a * field
+	 * in that OpDefNode a list of the OpDefNodes that appeared in * OpApplNodes in
+	 * the body. However, that didn't work because of * definitions like * * f ==
+	 * LET f == ... * IN ... * * So, I abandoned that idea. It's probably easiest to
+	 * compute the * connected components after the module is processed. * * If we
+	 * ever do want to construct those connected components, we can run * a version
+	 * of Tarjan's algorithm for computing strongly connected * components of a
+	 * directed graph. A +cal coding of this algorithm is in * the file Tarjan.tla,
+	 * a copy of which appears as a comment at the end of * this file. *
+	 ***************************************************************************/
+
+	/***************************************************************************
+	 * The fields. *
+	 ***************************************************************************/
+	final int MaxLetInLevel = 100;
+	/***********************************************************************
+	 * It seems safe to assume that LETs won't be nested more than 100 * deep, so we
+	 * can use arrays instead of having to deal with vectors * to allow arbitrary
+	 * depths. *
+	 ***********************************************************************/
+
+	int curLevel = 0;
+	/***********************************************************************
+	 * The current LET/IN nesting level--that is, the number of LET/IN * statements
+	 * within which the nodes we are currently processing lie. *
+	 ***********************************************************************/
+
+	int[] unresolvedCnt = new int[MaxLetInLevel];
+	/***********************************************************************
+	 * For i \in 0..curLevel, the value of unresolvedCnt[i] is the number * of
+	 * operators declared in RECURSIVE statements at let/in level i that * have not
+	 * yet been defined. *
+	 ***********************************************************************/
+
+	int unresolvedSum = 0;
+	/***********************************************************************
+	 * Equals the sum from i = 0 to curLevel of unresolvedCnt[i]. *
+	 ***********************************************************************/
+
+	int recursiveSectionCount = 0;
+	/***********************************************************************
+	 * This field is incremented whenever a new recursive section is * begun--that
+	 * is, when unresolvedSum changes from 0 to a positive * value (which must be
+	 * 1). *
+	 ***********************************************************************/
 
 //  Code to construct dependence graph removed by LL on 7 Apr 2007
 //  OpDefNode[] defStack = new OpDefNode[MaxLetInLevel] ;
@@ -7458,59 +7023,51 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
 //    * Perhaps some other use will be found for it, in which case it        *
 //    * should be put into the module's ModuleNode object.                   *
 //    ***********************************************************************/
-    
-  int max_dfs = 0;
-    /***********************************************************************
-    * A variable used in the Tarjan algorithm.                             *
-    ***********************************************************************/
-    
-  Vector nstack = new Vector(10) ;
-    /***********************************************************************
-    * A vector of OpDefNode objects, representing the stack of the Tarjan  *
-    * algorithm.                                                           *
-    ***********************************************************************/
-
-  int moduleNestingLevel = -1 ;
-    /***********************************************************************
-    * When processing a module, this is its nesting level--that is, its    *
-    * depth in the tree of inner modules of the outermost module.          *
-    ***********************************************************************/
 
-/***************************************************************************
-* Methods.                                                                 *
-***************************************************************************/
-  OpDefNode startOpDefNode(UniqueString us,
-                           TreeNode tn, 
-                           int kind,
-                           FormalParamNode[] params,
-                           boolean localness,
-                           ModuleNode oModNode,
-                           SymbolTable st
-                           )       {
-    /***********************************************************************
-    * Called to create an OpDefNode for a RECURSIVE declaration.  The      *
-    * params argument should be an array of dummy FormalParamNode          *
-    * objects, with null names.  The OpDefNode's setParams method should   *
-    * be used to add the actual formal parameter's to the object.          *
-    *                                                                      *
-    * Note: for an operator declared in a RECURSIVE statement, this        *
-    * method is called when processing that statement with the Treenode    *
-    * tn equal to the syntax tree node for the declaration.                *
-    ***********************************************************************/
-    OpDefNode odn = new OpDefNode(us, UserDefinedOpKind, params, localness,
-                                  null, // the expression 
-                                  oModNode, st, tn, false, null) ;
+	int max_dfs = 0;
+	/***********************************************************************
+	 * A variable used in the Tarjan algorithm. *
+	 ***********************************************************************/
+
+	Vector nstack = new Vector(10);
+	/***********************************************************************
+	 * A vector of OpDefNode objects, representing the stack of the Tarjan *
+	 * algorithm. *
+	 ***********************************************************************/
+
+	int moduleNestingLevel = -1;
+
+	/***********************************************************************
+	 * When processing a module, this is its nesting level--that is, its * depth in
+	 * the tree of inner modules of the outermost module. *
+	 ***********************************************************************/
+
+	/***************************************************************************
+	 * Methods. *
+	 ***************************************************************************/
+	OpDefNode startOpDefNode(UniqueString us, TreeNode tn, int kind, FormalParamNode[] params, boolean localness,
+			ModuleNode oModNode, SymbolTable st) {
+		/***********************************************************************
+		 * Called to create an OpDefNode for a RECURSIVE declaration. The * params
+		 * argument should be an array of dummy FormalParamNode * objects, with null
+		 * names. The OpDefNode's setParams method should * be used to add the actual
+		 * formal parameter's to the object. * * Note: for an operator declared in a
+		 * RECURSIVE statement, this * method is called when processing that statement
+		 * with the Treenode * tn equal to the syntax tree node for the declaration. *
+		 ***********************************************************************/
+		OpDefNode odn = new OpDefNode(us, UserDefinedOpKind, params, localness, null, // the expression
+				oModNode, st, tn, false, null);
 //  was incrementing it twice
 //    unresolvedCnt[curLevel] ++ ;
 //    unresolvedSum = unresolvedSum ++ ;
 
-    oModNode.recursiveDecls.addElement(odn) ;
+		oModNode.recursiveDecls.addElement(odn);
 
-    odn.letInLevel         = curLevel ;
-    odn.inRecursive        = true ;
-    odn.inRecursiveSection = true ;
-    odn.recursiveSection   = recursiveSectionCount;
-    oModNode.opDefsInRecursiveSection.addElement(odn) ;
+		odn.letInLevel = curLevel;
+		odn.inRecursive = true;
+		odn.inRecursiveSection = true;
+		odn.recursiveSection = recursiveSectionCount;
+		oModNode.opDefsInRecursiveSection.addElement(odn);
 
 // the participating and nbrs field have been removed from OpDefNode objects
 //    odn.participating      = true ;
@@ -7521,29 +7078,25 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
 //        defStack[defStackLen] = odn ;
 //        defStackLen ++ ;
 //       } ;
-    return odn;
-   }
-
-  void endOpDefNode(OpDefNode nd,
-                    ExprNode  exp,
-                    TreeNode  stn)  {
-    /***********************************************************************
-    * Called to complete the creation of an OpDefNode nd that was created  *
-    * by an invocation of startOpDefNode.  If we are processing recursive  *
-    * definitions, then it modifies unresolvedCnt and unresolvedSum if     *
-    * necessary.                                                           *
-    *                                                                      *
-    * When called for an operator not declared in a RECURSIVE statement,   *
-    * the TreeNode stn is the same one that startOpDefNode was called      *
-    * with to create the OpDefNode.                                        *
-    ***********************************************************************/
-    nd.isDefined = true ;
-
-    /***********************************************************************
-    * Set the node's body and syntax-tree node.                            *
-    ***********************************************************************/
-    nd.setBody(exp) ;
-    nd.stn = stn ;
+		return odn;
+	}
+
+	void endOpDefNode(OpDefNode nd, ExprNode exp, TreeNode stn) {
+		/***********************************************************************
+		 * Called to complete the creation of an OpDefNode nd that was created * by an
+		 * invocation of startOpDefNode. If we are processing recursive * definitions,
+		 * then it modifies unresolvedCnt and unresolvedSum if * necessary. * * When
+		 * called for an operator not declared in a RECURSIVE statement, * the TreeNode
+		 * stn is the same one that startOpDefNode was called * with to create the
+		 * OpDefNode. *
+		 ***********************************************************************/
+		nd.isDefined = true;
+
+		/***********************************************************************
+		 * Set the node's body and syntax-tree node. *
+		 ***********************************************************************/
+		nd.setBody(exp);
+		nd.stn = stn;
 
 //  Code to construct dependence graph removed by LL on 7 Apr 2007
 //    if (unresolvedSum > 0) {
@@ -7553,9 +7106,9 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
 //      defStackLen -- ;
 //     } ;
 
-    if (nd.inRecursive) { 
-      unresolvedCnt[curLevel] -- ;
-      unresolvedSum -- ;
+		if (nd.inRecursive) {
+			unresolvedCnt[curLevel]--;
+			unresolvedSum--;
 //  Code to construct dependence graph removed by LL on 7 Apr 2007
 //      if (unresolvedSum == 0) {
 //        tarjan() ; 
@@ -7564,12 +7117,13 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
 //         } ; // for
 //        participants = new Vector(100) ;
 //       } // if (unresolvedSum == 0)
-      if (unresolvedSum < 0) { 
-          throw new WrongInvocationException("Defined more recursive operators than were declared " +
-                    "in RECURSIVE statements.") ;
-       };
-     } // if (nd.inRecursive)
-   }
+			if (unresolvedSum < 0) {
+				throw new WrongInvocationException(
+						"Defined more recursive operators than were declared " + "in RECURSIVE statements.");
+			}
+			;
+		} // if (nd.inRecursive)
+	}
 
 //  Code to construct dependence graph removed by LL on 7 Apr 2007
 //  void registerSymbolNode(SymbolNode nd) {
@@ -7610,72 +7164,64 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
 //    return nd ;
 //   } // findSymbol
 
-
-  void setOpDefNodeRecursionFields(OpDefNode odn, ModuleNode cm) {
-    /***********************************************************************
-    * Called to set the field odn.letInLevel and the fields                *
-    * odn.recursiveSection and odn.inRecursiveSection if they need         *
-    * non-default values, and to add odn to cm.opDefsInRecursiveSection    *
-    * if necessary.                                                        *
-    ***********************************************************************/
-    odn.letInLevel = curLevel ;
-    if (unresolvedSum > 0) {
-        odn.recursiveSection   = recursiveSectionCount;
-        odn.inRecursiveSection = (unresolvedCnt[curLevel] > 0) ;
-        cm.opDefsInRecursiveSection.addElement(odn) ;
-       }
-    
-    } 
-
-  void checkIfInRecursiveSection(TreeNode tn, String type) {
-    /***********************************************************************
-    * Report an error if we are in a recursive section--between an         *
-    * operator's declaration in a RECURSIVE statement and its definition.  *
-    ***********************************************************************/
-    if (unresolvedSum > 0) {
-            errors.addError(tn.getLocation(),
-                            type + " may not appear within " +
-                            "a recursive definition section."   
-                           ) ;
-     }
-    }
-
-  void checkForUndefinedRecursiveOps(ModuleNode cm) {
-    /***********************************************************************
-    * Called at the end of a LET clause and at the end of processing a     *
-    * module to check for operators that were declared in a RECURSIVE      *
-    * statement but not defined.  It calls errors.addError to report any   *
-    * that it finds.                                                       *
-    ***********************************************************************/
-    /***********************************************************************
-    * The number of operators declared in RECURSIVE statements within the  *
-    * LET but not defined equals unresolvedCnt[curLevel].                  *
-    ***********************************************************************/
-    if (unresolvedCnt[curLevel] > 0) {
-      /*********************************************************************
-      * Go through the module's recursiveDecls vector to find all symbols  *
-      * declared at the current LET/IN level but not defined.              *
-      *********************************************************************/
-      for (int i = 0 ; i < cm.recursiveDecls.size() ; i++) {
-        OpDefNode odn = (OpDefNode) cm.recursiveDecls.elementAt(i) ;
-        if (   (odn.letInLevel == curLevel)
-            && odn.inRecursive
-            && (! odn.isDefined)) {
-            errors.addError(odn.getTreeNode().getLocation(),
-                            "Symbol " + odn.getName().toString() +
-                            " declared in RECURSIVE statement but not defined."   
-                           ) ;
-          } ;
-       }; // for
-
-      unresolvedSum = unresolvedSum - unresolvedCnt[curLevel] ;
-        /*******************************************************************
-        * Need to update unresolvedSum correct because we are effectively  *
-        * setting unresolvedCnt[curLevel] to 0.                            *
-        *******************************************************************/
-     }; // if (unresolvedCnt[curLevel] > 0) 
-    }
-
+	void setOpDefNodeRecursionFields(OpDefNode odn, ModuleNode cm) {
+		/***********************************************************************
+		 * Called to set the field odn.letInLevel and the fields * odn.recursiveSection
+		 * and odn.inRecursiveSection if they need * non-default values, and to add odn
+		 * to cm.opDefsInRecursiveSection * if necessary. *
+		 ***********************************************************************/
+		odn.letInLevel = curLevel;
+		if (unresolvedSum > 0) {
+			odn.recursiveSection = recursiveSectionCount;
+			odn.inRecursiveSection = (unresolvedCnt[curLevel] > 0);
+			cm.opDefsInRecursiveSection.addElement(odn);
+		}
+
+	}
+
+	void checkIfInRecursiveSection(TreeNode tn, String type) {
+		/***********************************************************************
+		 * Report an error if we are in a recursive section--between an * operator's
+		 * declaration in a RECURSIVE statement and its definition. *
+		 ***********************************************************************/
+		if (unresolvedSum > 0) {
+			errors.addError(tn.getLocation(), type + " may not appear within " + "a recursive definition section.");
+		}
+	}
+
+	void checkForUndefinedRecursiveOps(ModuleNode cm) {
+		/***********************************************************************
+		 * Called at the end of a LET clause and at the end of processing a * module to
+		 * check for operators that were declared in a RECURSIVE * statement but not
+		 * defined. It calls errors.addError to report any * that it finds. *
+		 ***********************************************************************/
+		/***********************************************************************
+		 * The number of operators declared in RECURSIVE statements within the * LET but
+		 * not defined equals unresolvedCnt[curLevel]. *
+		 ***********************************************************************/
+		if (unresolvedCnt[curLevel] > 0) {
+			/*********************************************************************
+			 * Go through the module's recursiveDecls vector to find all symbols * declared
+			 * at the current LET/IN level but not defined. *
+			 *********************************************************************/
+			for (int i = 0; i < cm.recursiveDecls.size(); i++) {
+				OpDefNode odn = (OpDefNode) cm.recursiveDecls.elementAt(i);
+				if ((odn.letInLevel == curLevel) && odn.inRecursive && (!odn.isDefined)) {
+					errors.addError(odn.getTreeNode().getLocation(),
+							"Symbol " + odn.getName().toString() + " declared in RECURSIVE statement but not defined.");
+				}
+				;
+			}
+			; // for
+
+			unresolvedSum = unresolvedSum - unresolvedCnt[curLevel];
+			/*******************************************************************
+			 * Need to update unresolvedSum correct because we are effectively * setting
+			 * unresolvedCnt[curLevel] to 0. *
+			 *******************************************************************/
+		}
+		; // if (unresolvedCnt[curLevel] > 0)
+	}
 
 //  void tarjan() {
 //    /***********************************************************************
@@ -7687,4489 +7233,2300 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ;
 //***************************************************************************/
 //   }
 
-}   
-
-/************************ file Tarjan.tla ***********************************
------------------------------- MODULE Tarjan --------------------------------
-(***************************************************************************)
-(* This version of Tarjan's algorithm for computing the strongly           *)
-(* connected components of a directed graph was adapted from the version   *)
-(* of 27 Mar 2007 on the Wikipedia page                                    *)
-(*                                                                         *)
-(* http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm *)
-(*                                                                         *)
-(* One modification is that the set of connected components it returns     *)
-(* does not contain a singleton {n} unless n has an edge pointing to       *)
-(* itself.                                                                 *)
-(*                                                                         *)
-(* This is an implementation of the algorithm in Tarjan.tla with data      *)
-(* structures that correspond to the ones used in the algorithm for        *)
-(* constructing dependency components in semantic/Generator.               *)
-(*                                                                         *)
-(*                                                                         *)
-(* I have not rigorously verified this algorithm, but I think I understand *)
-(* why it works.  I have tested it only on the 10 graphs at the end of     *)
-(* this file.                                                              *)
-(***************************************************************************)
-
-EXTENDS Integers, Sequences, TLC
-
-CONSTANT N,         \* The number of nodes
-         Neighbors  \* A sequence of edges.
-
-Node == 1..N
-
-ASSUME Neighbors \in Seq(Node \X Node) 
-
-Min(a, b) == IF a < b THEN a ELSE b
-
-Front(s) == [i \in 1 .. (Len(s)-1) |-> s[i]]
-  (*************************************************************************)
-  (* The sequence obtained from a sequence s by deleting the last item.    *)
-  (*************************************************************************)
-
-Last(s) == s[Len(s)]
-  (*************************************************************************)
-  (* The last item in the sequence s.                                      *)
-  (*************************************************************************)
-
-(***************************************************************************)
-(* I believe the algorithm works because it maintains the following        *)
-(* invariants (at reasonable points in the code).  We say that node        *)
-(* nstack[i] precedes nstack[j] iff i < j.                                 *)
-(*                                                                         *)
-(*   - A node n is in nstack iff n.dfs \geq 0                              *)
-(*                                                                         *)
-(*   - For all nodes n # m in nstack, if n precedes m then:                *)
-(*       /\ n.dfs < m.dfs.                                                 *)
-(*       /\ there is a path from n to m in the graph.                      *)
-(*                                                                         *)
-(*   - For every node n in nstack,                                         *)
-(*      /\ n.lowlink \leq n.dfs and                                        *)
-(*      /\ there is a node m in nstack such that                           *)
-(*          1. n.lowlink = m.dfs.                                          *)
-(*          2. if m # n then there is a path from n to m in the graph.     *)
-(*      /\ For every node m in the same connected component as n:          *)
-(*         m.examined = TRUE  implies  m is in nstack                      *)
-(***************************************************************************)
-
-(*************************
---algorithm Tarjan2
-variables nstack = << >> ;        
-            (***************************************************************)
-            (* The stack of nodes, where nstack[1] is the bottom of the    *)
-            (* stack.                                                      *)
-            (***************************************************************)
-          max_dfs = 0 ;      
-            (***************************************************************)
-            (* Incremented by 1 every time a new element is added to the   *)
-            (* stack.                                                      *)
-            (***************************************************************)
-
-          data = [nd \in Node |-> [lowlink  |-> -1, 
-                                   dfs      |-> -1, 
-                                   examined |-> FALSE,
-                                   nbrs     |-> << >>, 
-                                     (*****************************************)
-                                     (* A sequence of nodes to which there    *)
-                                     (* are edges from this node.  The        *)
-                                     (* same node may appear multiple         *)
-                                     (* times.                                *)
-                                     (*****************************************)
-                                   nxtDep   |-> -1 ]] ;
-            (***************************************************************)
-            (* The information maintained for the nodes.  Note that        *)
-            (*                                                             *)
-            (*  - Node nd is on nstack iff data[nd].dfs \geq 0, in         *)
-            (*    which case  nstack[data[nd].dfs + 1] = nd.               *)
-            (*                                                             *)
-            (*  - nbrs is set from Neighbors by the initialization code.   *)
-            (*                                                             *)
-            (*  - nxtDep represents the nextDependency field of the        *)
-            (*    OpDefNode object, where a value of -1 represents         *)
-            (*    null.  Following nxtDep pointers leads in a cycle of     *)
-            (*    the node's connected component.  See the comments on     *)
-            (*    the nextDependency field in semantic/OpDefNode.          *)
-            (***************************************************************)
-
-          idx ;  \* A counter variable.
-
-procedure tarjan(n)
- variable nxt ;     \* A counter variable
-          nxtnbr ;  \* An abbreviation
-          
- begin
-   data[n].lowlink  := max_dfs || 
-   data[n].dfs      := max_dfs || 
-   data[n].examined := TRUE ;
-   max_dfs := max_dfs + 1;
-   nstack  := Append(nstack, n) ;
-   nxt := 1 ;
-   while nxt \leq Len(data[n].nbrs) do
-     nxtnbr := data[n].nbrs[nxt] ;
-     if ~ data[nxtnbr].examined
-       then (***************************************************************)
-            (* nxtnbr is unexamined                                        *)
-            (***************************************************************)
-            call tarjan(nxtnbr) ;
-            data[n].lowlink := Min(data[n].lowlink, data[nxtnbr].lowlink) ;
-
-       elsif data[nxtnbr].dfs \geq 0  
-            then (**********************************************************)
-                 (* nxtnbr is in nstack                                    *)
-                 (**********************************************************)
-                 data[n].lowlink := Min(data[n].lowlink, data[nxtnbr].dfs) ;
-      end if ;
-      nxt := nxt + 1;
-   end while ;
-   if data[n].lowlink = data[n].dfs
-     then (*****************************************************************)
-          (* The set of all nodes on nstack from n to the end form a       *)
-          (* connected component.                                          *)
-          (*****************************************************************)
-          nxtnbr := Last(nstack) ;  \* save last node on stack.
-          while n # Last(nstack) do
-            data[Last(nstack)].nxtDep := nstack[Len(nstack)-1] ||
-            data[Last(nstack)].dfs    := -1 ;
-              (*************************************************************)
-              (* Make nxtDep field of last node on stack point to next to  *)
-              (* last node.                                                *)
-              (*************************************************************)
-            nstack := Front(nstack) ;
-          end while ;
-          if \/ n # nxtnbr
-             \/ \E i \in 1..Len(data[n].nbrs) : n = data[n].nbrs[i]
-            then (**********************************************************)
-                 (* Form a connected component consisting of just n only   *)
-                 (* if n is a neighbor of itself.                          *)
-                 (**********************************************************)
-            data[n].nxtDep := nxtnbr; 
-          end if;
-          data[n].dfs    := -1 ;
-          nstack := Front(nstack) ;
-   end if ;
-   data[n].nbrs := << >> ;
-     (**********************************************************************)
-     (* Can garbage collect the sequence of neighbors.                     *)
-     (**********************************************************************)
-   return ;
-end procedure 
-
-begin
-   (************************************************************************)
-   (* Initialize the nbrs field of data elements from Neighbors.           *)
-   (************************************************************************)
-   idx := 1 ;
-   while idx \leq Len(Neighbors) do
-     with edge = Neighbors[idx] do
-      data[edge[1]].nbrs := Append(data[edge[1]].nbrs, edge[2]) ;
-     end with ;
-   idx := idx + 1;
-   end while ;
-   
-   (************************************************************************)
-   (* while there is an unexamined node, call tarjan(nd)                   *)
-   (* for any unexamined node nd.                                          *)
-   (************************************************************************)
-   idx := 1 ;
-   while idx \leq N do
-     if data[idx].lowlink < 0    
-       then (***************************************************************)
-            (* idx is unexamined                                           *)
-            (***************************************************************)
-            call tarjan(idx) ;
-       end if;
-     idx := idx + 1 ;
-   end while ;
-
-   (************************************************************************)
-   (* print result.                                                        *)
-   (************************************************************************)
-   idx := 1 ;
-   while idx \leq N do
-     if data[idx].nxtDep \geq 0 
-       then print <<idx, "->", data[idx].nxtDep>>
-     end if ;
-     idx := idx+1;
-   end while
-end algorithm
-***********************) 
-
-\* BEGIN TRANSLATION
-\* END TRANSLATION
-
-\**** Test data
-
-Node1 == {1, 2, 3, 4}
-
-Nbrs1 == << <<1, 2>>,   \* components 1 and 3,4
-            <<1, 2>>,   \* components 1 and 3,4
-            <<1, 1>>,
-            <<1, 1>>,
-            <<3, 4>> ,
-            <<3, 4>> ,
-            <<4, 3>> ,
-            <<4, 3>>,
-            <<4, 3>>
-         >>
-
-Nbrs2 == << <<1, 2>>, \* components 1 and 3,4
-            <<1, 1>>,
-            <<1, 3>>,
-            <<3, 4>>,
-            <<4, 3>>
-         >>
-Nbrs3 == << <<1, 2>>, \* components 1 and 3,4
-            <<1, 1>>,
-            <<3, 1>>,
-            <<3, 4>>,
-            <<4, 3>>
-         >>
-Nbrs4 == << <<1, 3>>,  \* 
-           <<1, 8>>,   \* componetns:  2,3,4,5 and 7,8,9
-           <<1, 9>>,
-           <<1, 9>>,
-           <<1, 9>>,
-           <<3, 2>>,
-           <<3, 2>>,
-           <<2, 4>>,
-           <<4, 5>>,
-           <<4, 10>>,
-           <<5, 3>>,
-           <<8, 7>>,
-           <<8, 7>>,
-           <<9, 8>>,
-           <<7, 9>>,
-           <<7, 9>>,
-           <<9, 10>>,
-           <<9, 10>>,
-           <<9, 10>>
-         >>
-
-Nbrs5 == << <<1, 8>>,  \* components 7,8,9
-           <<1, 9>>,
-           <<4, 10>>,
-           <<8, 7>>,
-           <<9, 8>>,
-           <<7, 9>>,
-           <<9, 10>>
-         >>
-
-Nbrs6 == << <<6, 4>>,  \* components 2,3,4,5
-           <<3, 4>>,
-           <<4, 5>>,
-           <<5, 2>>,
-           <<2, 3>>,
-           <<5, 1>>,
-           <<2, 1>>
-         >>
-
-Nbrs7 == << <<1, 7>>, \* 1 component with 1..9 \ {3}
-           <<1, 9>>,
-           <<2, 1>>,  
-           <<3, 2>>,
-           <<3, 6>>,
-           <<4, 6>>,
-           <<5, 2>>,
-           <<6, 5>>,
-           <<6, 8>>,
-           <<7, 5>>,
-           <<7, 8>>,
-           <<8, 4>>,
-           <<8, 10>>,
-           <<9, 7>>,
-           <<9, 10>>
-         >>
-Nbrs8 == << <<1, 7>>, \* 1 component with 1..8 \ {3}
-           <<2, 1>>,  
-           <<3, 2>>,
-           <<3, 2>>,
-           <<3, 2>>,
-           <<3, 6>>,
-           <<4, 6>>,
-           <<5, 2>>,
-           <<6, 5>>,
-           <<6, 8>>,
-           <<6, 8>>,
-           <<6, 8>>,
-           <<7, 5>>,
-           <<7, 8>>,
-           <<8, 4>>,
-           <<8, 10>>,
-           <<9, 7>>,
-           <<9, 10>>
-         >>
-Nbrs9 == <<           \* 4,6, 7,8,9,10 
-           <<2, 1>>,  
-           <<3, 2>>,
-           <<3, 6>>,
-           <<4, 6>>,
-           <<4, 6>>,
-           <<4, 6>>,
-           <<5, 2>>,
-           <<6, 5>>,
-           <<6, 8>>,
-           <<7, 5>>,
-           <<7, 5>>,
-           <<7, 5>>,
-           <<7, 5>>,
-           <<7, 8>>,
-           <<8, 4>>,
-           <<8, 10>>,
-           <<9, 7>>,
-           <<10, 9>>
-         >>
-Nbrs == <<           \* 1, 3, 4, 5 and 6
-           <<1, 4>>,  
-           <<2, 1>>,
-           <<2, 3>>,
-           <<2, 3>>,
-           <<2, 3>>,
-           <<2, 5>>,
-           <<3, 4>>,
-           <<4, 1>>,
-           <<4, 5>>,
-           <<4, 5>>,
-           <<4, 5>>,
-           <<5, 3>>,
-           <<6, 5>>,
-           <<6, 6>>
-         >>
-
-=============================================================================
-
-************************** end file Tarjan.tla ******************************/
-
-/************************* file Subexpression.tla  **********************
-last modified on Fri 13 November 2009 at 14:11:05 PST by lamport
-------------------------  MODULE Subexpression --------------------------- 
-
-
-(***************************************************************************)
-(*                        NAMING                                           *)
-(*                                                                         *)
-(* There are four kinds of Nameable Objects:                               *)
-(*   - Expressions                                                         *)
-(*   - Operators that take an argument                                     *)
-(*      (and that can appear as operator arguments)                        *)
-(*   - Operator definitions (named in BY/USE/HIDE statements)              *)
-(*   - ASSUME/PROVEs.                                                      *)
-(*                                                                         *)
-(* There are three relevant kinds of primitive names that appear in a      *)
-(* TLA+ module.                                                            *)
-(*                                                                         *)
-(*   Defined Module Name (ModuleName):                                     *)
-(*      It appears to the left of "== INSTANCE...".  It may or may not     *)
-(*      contain arguments.                                                 *)
-(*   Primitive Operator Name (PrimOpName):                                 *)
-(*      It appear to the left of the "==" in a definition, theorem         *)
-(*      or assumption.  It may or may not contain arguments.               *)
-(*   Label:                                                                *)
-(*      It appears before a "::" in an expression.                         *)
-(*                                                                         *)
-(* Examples of possible primitive names are "Foo" and "Bar(x+1, 42)".      *)
-(*                                                                         *)
-(* The Built-In Operator names (like "SUBSET") will not be considered to   *)
-(* be primitive names in the following.                                    *)
-(*                                                                         *)
-(* To these explicit names we add the following Operand Selector (OpSel)   *)
-(* names:                                                                  *)
-(*   - numbers,                                                            *)
-(*   - "<<", ">>",                                                         *)
-(*   - Things of the form (exp_1, ... , exp_n) where the exp_i are         *)
-(*     expressions or operator arguments and n > 0.                        *)
-(*   - "@",                                                                *)
-(*                                                                         *)
-(* A Compound Name is a sequence of primitive names, operand selectors,    *)
-(* and ":"s separated by "!"s.                                             *)
-(*                                                                         *)
-(* In TLA+1, the general way of naming a non-built-in operator is with     *)
-(* what I will call an Elementary Operator Name (ElemOpName) which has     *)
-(* the following syntax.  For simplicity, I will ignore the "!"s in the    *)
-(* `formal' syntax.                                                        *)
-(*                                                                         *)
-(*    ElemOpName ::= (ModuleName)* PrimOpName                              *)
-(*                                                                         *)
-(* We consider this to name both the operator and its definition (the      *)
-(* expression to the right of the "==").  How the two meanings are         *)
-(* disambiguated will be explained below.                                  *)
-(*                                                                         *)
-(* If EON is an ElemOpName and Lab_1, ...  , Lab_n are a sequence of       *)
-(* label names, then EON ! Lab_1 ! ...  ! Lab_N names the expression       *)
-(* labeled by Lab_N, where each Lab_(i+1) must be the name of a labeled    *)
-(* expression within the expression labeled by Lab_i with no labels        *)
-(* between Lab_i and Lab_(i+1).  Thus a name described by                  *)
-(*                                                                         *)
-(*    ElemOpName (Label)*                                                  *)
-(*                                                                         *)
-(* describes an operator definition or a labeled expression.  To such a    *)
-(* name, we can append a sequence of OperandSelectors.  By rules           *)
-(* described later, the resulting name selects a subexpression (or         *)
-(* ASSUME/PROVE node) or Operator (appearing as an operator argument)      *)
-(* from the body of the operator definition or labeled expression.  If     *)
-(* the labeled expression is a LET/IN, then to this name can be appended   *)
-(* an ElemOpName defined in the LET part, and the naming process can then  *)
-(* be iterated.  The full syntax of a General Name  is:                    *)
-(*                                                                         *)
-(*    OperatorName ::=   ElemOpName                                        *)
-(*                     | ElemOpName (Label)* ((OpSel)+ | ":") ElemOpName   *)
-(*                                                                         *)
-(*    GeneralName ::= OperatorName (":" | (Label)* (OpSeq)* )              *)
-(*                                                                         *)
-(* Note that ":" is used in two ways:                                      *)
-(*                                                                         *)
-(*   - An OperatorName not followed by ":" names the operator; one         *)
-(*     followed by ":" names the operator's definition.                    *)
-(*   - Suppose a general name GN ending in a label names a LET/IN          *)
-(*     expression and EON is an ElemOpName.  Then GN !: !EON names         *)
-(*     an operator defined in the LET.  The general name GN ! Foo would    *)
-(*     name an expression labeled by Foo inside the IN clause.             *)
-(***************************************************************************)
-
-
-(***************************************************************************)
-(* This +cal program describes an algorithm used in generating the         *)
-(* semantic tree for a parse tree consisting of a GeneralId node or an     *)
-(* OpApplication node whose first child is a GeneralId node.  Two examples *)
-(* are                                                                     *)
-(*                                                                         *)
-(*   Foo(x)!Bar!<<!(a,b)!G                                                 *)
-(*   Foo(x)!Bar!<<!(a,b)!G(c)                                              *)
-(*                                                                         *)
-(* We assume that these are processed to yield two sequences of nodes:     *)
-(* op and args.  The sequences for the first are                           *)
-(*                                                                         *)
-(*   op   = << "Foo", "Bar", "<<",  "null",   "G"  >>                      *)
-(*   args = << <<x>>, << >>, << >>, <<a, b>>, <<>> >>                      *)
-(*                                                                         *)
-(* and for the second are                                                  *)
-(*                                                                         *)
-(*   op   = << "Foo", "Bar", "<<",  "null",   "G"   >>                     *)
-(*   args = << <<x>>, << >>, << >>, <<a, b>>, <<c>> >>                     *)
-(*                                                                         *)
-(* where each symbol in args represents the semantic node produced by that *)
-(* symbol.                                                                 *)
-(*                                                                         *)
-(* The other input to the algortithm is an integer expectedArity.  There   *)
-(* are three cases:                                                        *)
-(*                                                                         *)
-(*   expectedArity = 0 :                                                   *)
-(*     The parser is expecting an expression.  This is the case            *)
-(*     when the expression occurs in the first part of a USE               *)
-(*     or HIDE statement.                                                  *)
-(*                                                                         *)
-(*   expectedArity > 0  : The parser is expecting an operator              *)
-(*     argument of this arity.                                             *)
-(*                                                                         *)
-(*   expectedArity = -1 :                                                  *)
-(*     The parser is expecting the name of a definition.  This is          *)
-(*     the case when the expression occurs in the DEF clause of a USE      *)
-(*     or HIDE or BY statement.                                            *)
-(*                                                                         *)
-(* If expectedArity # 0, then args equals a sequence each of whose         *)
-(* element is the empty sequence.                                          *)
-(***************************************************************************)
-
-(***************************************************************************)
-(* Note: This spec is assuming that proof-step numbers are treated like    *)
-(* operator names.  That is, a step                                        *)
-(*                                                                         *)
-(*   <3>2. foo > bar                                                       *)
-(*                                                                         *)
-(* will be represented by a ThmOrAssumpDefKind node just as if it were     *)
-(*                                                                         *)
-(*   THEOREM <3>2 == foo > bar                                             *)
-(*                                                                         *)
-(* If this is not the case, then additional code needs to be added to deal *)
-(* with numbered steps.  But however it is handled, the "<3>2" will be a   *)
-(* name in the current symbol table.                                       *)
-(***************************************************************************)
-
-EXTENDS Integers, Sequences, TLC
-
-(***************************************************************************)
-(* SeqSeqToSeq(ss) is defined to be the concatenation of the sequences     *)
-(* that are the elements of the sequence ss.                               *)
-(***************************************************************************)
-RECURSIVE SeqSeqToSeq(_)
-SeqSeqToSeq(ss) == IF ss = << >> 
-                     THEN << >>
-                     ELSE Head(ss) \o SeqSeqToSeq(Tail(ss))
-
-(***************************************************************************)
-(* These are kinds of semantic nodes that can be encountered by the        *)
-(* algorithm.  See tlasany/sematic/ASTConstants.java.                      *)
-(***************************************************************************)
-ConstantDeclKind   == "ConstantDecl"
-VariableDeclKind   == "VariableDecl"
-BoundSymbolKind    == "BoundSymbol"
-UserDefinedOpKind  == "UserDefinedOp"
-ModuleInstanceKind == "ModuleInstance"
-BuiltInKind        == "BuiltIn"
-OpArgKind          == "OpArg"
-OpApplKind         == "OpAppl"
-LetInKind          == "LetIn"
-FormalParamKind    == "FormalParam"
-TheoremKind        == "Theorem"
-SubstInKind        == "SubstIn"
-AssumeProveKind    == "AssumeProve"
-ProofKind          == "Proof"
-NumeralKind        == "Numeral"
-DecimalKind        == "Decimal"
-StringKind         == "String"
-AtNodeKind         == "AtNode"
-AssumeKind         == "Assume"
-InstanceKind       == "Instance"
-ThmOrAssumpDefKind == "ThmOrAssumpDef"
-LabelKind          == "Label"
-APSubstInKind      == "APSubstIn"
-
-CONSTANT 
-  NodeId,        \* The set of all node identifiers.
-  Node,          \* A mapping from NodeId to nodes (which are records).
-  GlobalContext, \* The initial context.
-  null,          \* A special value not equal to anything else.
-  debug          \* Setting to TRUE causes TLC to print a trace if the
-                 \*  algorithm reports an error.
-
-(***************************************************************************)
-(*                              CONTEXTS                                   *)
-(*                                                                         *)
-(* A context is a mapping from names to NodeIds.  There are actually three *)
-(* different kinds of mappings from names to nodes in the implementation   *)
-(* that are all represented here as contexts:                              *)
-(*  - The symbolTable of the Generator.                                    *)
-(*  - The context field of a LetInNode                                     *)
-(*  - The labels field of a LabelNode                                      *)
-(***************************************************************************)
-IsContext(C) == 
-  (*************************************************************************)
-  (* True iff C is a context.                                              *)
-  (*************************************************************************)
-  /\ DOMAIN C \subseteq STRING
-  /\ \A str \in DOMAIN C : C[str] \in NodeId
-
-LookUp(nm, ctxt) == 
-  (*************************************************************************)
-  (* The value assigned to name nm by context cxt, or null if there is no  *)
-  (* value assigned.                                                       *)
-  (*************************************************************************)
-  IF nm \in DOMAIN ctxt THEN Node[ctxt[nm]] ELSE null
-
-Param == [name : STRING, arity : Nat]
-  (*************************************************************************)
-  (* The set of parameters, which can appear in operator definitions.      *)
-  (*************************************************************************)
-  
-(***************************************************************************)
-(* For simplicity, we assume that argument numbers range from 1 to 9, and  *)
-(* we define the following mappings from to and from argument numbers to   *)
-(* their string representations.                                           *)
-(***************************************************************************)
-NumberOp == {"1", "2", "3", "4", "5", "6", "7", "8", "9"}
-NumericVal(numOp) == CASE numOp = "1" -> 1 
-                       [] numOp = "2" -> 2 
-                       [] numOp = "3" -> 3 
-                       [] numOp = "4" -> 4 
-                       [] numOp = "5" -> 5 
-                       [] numOp = "6" -> 6 
-                       [] numOp = "7" -> 7 
-                       [] numOp = "8" -> 8 
-                       [] numOp = "9" -> 9
-
-NumToString(num) ==  CASE num = 1 -> "1" 
-                       [] num = 2 -> "2" 
-                       [] num = 3 -> "3" 
-                       [] num = 4 -> "4" 
-                       [] num = 5 -> "5" 
-                       [] num = 6 -> "6" 
-                       [] num = 7 -> "7" 
-                       [] num = 8 -> "8" 
-                       [] num = 9 -> "9"
-
-IsName(op) == 
-  (*************************************************************************)
-  (* A name is something other than an argument selector that can appear   *)
-  (* in a compound name.                                                   *)
-  (*************************************************************************)
-  op \in STRING \ ({"<<", ">>", "@", ":", "null"} \cup NumberOp)
-
-        
-ArgNum(op, arity) ==
-  (*************************************************************************)
-  (* The argument number chosen by argument selector op for an operator    *)
-  (* application with arity arguments.  It equals -1 if op is not a legal  *)
-  (* argument selector for this arity.                                     *)
-  (*************************************************************************)
-  CASE op \in NumberOp ->
-         IF NumericVal(op) <= arity THEN NumericVal(op) ELSE -1
-    [] op = "<<" -> IF arity > 0 THEN 1 ELSE -1
-    [] op = ">>" -> CASE arity = 1 -> 1
-                      [] arity = 2 -> 2 
-                      [] OTHER -> -1
-    [] OTHER     -> -1
-
-Arity(node) ==
-  (*************************************************************************)
-  (* The arity of a node--that is, the number of arguments it takes.       *)
-  (*************************************************************************)
-  IF node.kind \in {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind,
-                    ThmOrAssumpDefKind, OpArgKind, LabelKind, 
-                    ConstantDeclKind, FormalParamKind}
-    THEN node.arity
-    ELSE 0
-
-ParamArity(node, i) ==
-  (*************************************************************************)
-  (* The arity of the i-th parameter of the node of kind                   *)
-  (* UserDefinedOpKind, ThmOrAssumpDefKind, ModuleInstanceKind,            *)
-  (* ConstantDeclKind, or FormalParamKind                                  *)
-  (*************************************************************************)
-  IF node.kind \in {ConstantDeclKind, FormalParamKind}
-    THEN 0
-    ELSE node.params[i].arity
------------------------------------------------------------------------------
-
-(***************************************************************************)
-(* The following assumptions define the data types--in particular, the     *)
-(* record components that each node type must contain.                     *)
-(***************************************************************************)
-ASSUME IsContext(GlobalContext) 
-
-ASSUME DOMAIN Node = NodeId
-
-ASSUME
-/\ \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind \in 
-            {ConstantDeclKind, VariableDeclKind, BoundSymbolKind,
-             UserDefinedOpKind, ModuleInstanceKind, BuiltInKind,
-             OpArgKind, OpApplKind, LetInKind, FormalParamKind,
-             TheoremKind, SubstInKind, AssumeProveKind, ProofKind,
-             NumeralKind, DecimalKind, StringKind, AtNodeKind,
-             AssumeKind, InstanceKind, ThmOrAssumpDefKind, LabelKind,
-             APSubstInKind}
-
-ASSUME
-/\ \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind \in {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind, 
-                      ThmOrAssumpDefKind, LabelKind, ConstantDeclKind, 
-                      FormalParamKind}
-        => /\ node.name \in STRING
-           /\ node.arity \in Nat \cup {-1}
-           /\ (node.arity = -1) => (node.kind = BuiltInKind)
-
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-       node.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}  => 
-         /\ node.body \in NodeId 
-         /\ IsContext(node.labels)
-         /\ node.params \in Seq(Param)
-         /\ Len(node.params) = node.arity
-         /\ node.defined \in BOOLEAN 
-         /\ node.source \in {null} \cup NodeId
-            \* The original definition (before instantiation)
-            \* or null if this is it.
-
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind = OpApplKind =>
-        /\ node.operands \in Seq(NodeId)
-        /\ node.operator \in NodeId
-        /\ node.unboundedBoundSymbols \in Seq(STRING)
-        /\ node.boundedBoundSymbols \in Seq(Seq(STRING))
-             (**************************************************************)
-             (* These are actually arrays and arrays of arrays of          *)
-             (* FormalParamNode objects (formerly OpDeclNode objects).     *)
-             (**************************************************************)
-        /\ node.ranges \in Seq(NodeId)
-        /\ Len(node.ranges) = Len(node.boundedBoundSymbols)
-
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind = LetInKind =>
-        /\ node.body \in NodeId
-        /\ IsContext(node.context)
-
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind = OpArgKind =>
-        /\ node.op \in NodeId
-        /\ IsName(node.name)
-        /\ (node.arity \in Nat) /\ (node.arity > 0)
-        
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind = LabelKind =>
-         /\ node.body \in NodeId 
-         /\ IsContext(node.labels)
-         /\ node.params \in Seq(Param)
-
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind = SubstInKind =>
-         /\ node.body \in NodeId 
-         /\ node.subst \in STRING \* Just to identify it.
-
-ASSUME
- \A id \in NodeId :
-    LET node == Node[id] IN  
-    /\ node.kind = AssumeProveKind =>
-         /\ node.assumes\in Seq(NodeId)
-         /\ node.prove \in NodeId
-
-    
-RECURSIVE ExpandNode(_)
-ExpandNode(node) ==
-  CASE node.kind \in {UserDefinedOpKind, LetInKind} ->
-        [node EXCEPT !.body = ExpandNode(Node[@])]
-  []   node.kind = OpApplKind ->
-        [node EXCEPT 
-          !.operands = [i \in DOMAIN @ |-> ExpandNode(Node[@[i]])],
-          !.operator = ExpandNode(Node[@]),
-          !.ranges   = [i \in DOMAIN @ |-> ExpandNode(Node[@[i]])]]
-  []   OTHER -> node
-
-
------------------------------------------------------------------------------
-(***************************************************************************)
-(* Test Data                                                               *)
-(*                                                                         *)
-(* The algorithm has been tested on the following sets of data, where each *)
-(* operator MCxxx is either substituted for constant parameter xxx or is   *)
-(* used as initial value of the algorithm's input variable xxx.            *)
-(***************************************************************************)
-
-
-(*****************************
-
-
-(***************************************************************************)
-(* ------------------------ MODULE M ------------------------              *)
-(* CONSTANT C                                                              *)
-(* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> \in {3} :                     *)
-(*                      lab(x,y,z) :: LET a + b == <<Arg(a), b>>           *)
-(*                                    IN  1 + label2 :: p + C              *)
-(* Foo(u) == "FooBody(u)"                                                  *)
-(* =============================================================           *)
-(*                                                                         *)
-(* Inst(d) == INSTANCE M WITH C <- "expr(d)"                               *)
-(***************************************************************************)
-
-\* MCNode == 
-\*  1 :> [kind     |-> UserDefinedOpKind,          \*  Op(Arg(_), p) == ...
-\*        name     |-> "Op",
-\*        body     |-> 3,
-\*        labels   |->  "lab" :> 4,
-\*        arity    |-> 2,
-\*        params   |-> <<[name |-> "Arg",  arity |-> 1],
-\*                       [name |-> "p",    arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> null   ]
-\* @@
-\*  2 :> [kind  |-> BuiltInKind,             
-\*         name  |-> "$BoundedForall", 
-\*         arity |-> -1
-\*        ]
-\* @@
-\*  3 :> [kind     |-> OpApplKind,            \* \A x \in {1,2}, <<y, z>> \in {3} : ...
-\*        operands |-> << 4 >>,   
-\*        operator |-> 2,
-\*        unboundedBoundSymbols |-> <<>>,
-\*        boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>,
-\*        ranges |-> << 19, 21 >>]
-\* @@
-\*  4 :> [kind     |-> LabelKind,                    \* lab(x,y,z) :: LET a + b == <<Arg(a), b>>
-\*        name     |-> "lab",                        \*               IN  1 + label2 :: p + C
-\*        body     |-> 5,
-\*        arity    |-> 3,
-\*        params   |-> <<[name |-> "x", arity |->0], 
-\*                       [name |-> "y", arity |->0], 
-\*                       [name |-> "z", arity |->0] >>,
-\*        labels   |-> "label2" :> 15      ]
-\* @@
-\*  5 :>  [kind    |-> LetInKind,     \*  LET a + b == <<Arg(a), b>>
-\*         context |-> "+" :> 6,      \*  IN  1 + label2 :: p + C
-\*         body    |->  13 ]
-\* @@
-\*  6 :> [kind     |-> UserDefinedOpKind,    \* a + b == <<Arg(a), b>>
-\*        name     |-> "+",
-\*        body     |-> 7,
-\*        labels   |->  << >>,
-\*        arity    |-> 2,
-\*        params   |-> <<[name |-> "a",  arity |-> 0],
-\*                       [name |-> "b",  arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> null   ]
-\* @@
-\*  7 :> [kind     |-> OpApplKind,            \* <<Arg(a), b>>
-\*        operands |-> << 9, 12 >>,   
-\*        operator |-> 8,
-\*        unboundedBoundSymbols |-> <<>>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\*  8 :> [kind  |-> BuiltInKind,
-\*        name  |-> "$Tuple", 
-\*        arity |-> -1
-\*       ]
-\* @@
-\*  9 :> [kind     |-> OpApplKind,            \* Arg(a)
-\*        operands |-> <<11 >>,   
-\*        operator |-> 10,
-\*        unboundedBoundSymbols |-> <<>>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\* 10 :> [kind  |-> FormalParamKind,
-\*        name  |-> "Arg",  
-\*        arity |-> 1]
-\* 
-\* @@
-\* 11 :> [kind  |-> FormalParamKind,
-\*        name  |-> "a",  
-\*        arity |-> 0]
-\* @@
-\* 12 :> [kind  |-> FormalParamKind,
-\*        name  |-> "b",  
-\*        arity |-> 0]
-\* @@
-\* 13 :> [kind     |-> OpApplKind,            \* 1 + label2 :: p + C
-\*        operands |-> <<14, 15>>,   
-\*        operator |-> 6,
-\*        unboundedBoundSymbols |-> <<>>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\* 14 :> [kind |-> NumeralKind,
-\*        val  |-> 1]
-\* @@
-\* 15 :> [kind     |-> LabelKind,                    \* label2 :: p + C
-\*        name     |-> "label2",                        
-\*        body     |-> 16,
-\*        arity    |-> 0,
-\*        params   |-> <<>>,
-\*        labels   |-> << >>]
-\* @@
-\* 16 :> [kind     |-> OpApplKind,            \* p + C
-\*        operands |-> <<17, 18>>,   
-\*        operator |-> 6,
-\*        unboundedBoundSymbols |-> <<>>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\* 17 :> [kind  |-> FormalParamKind,
-\*        name  |-> "p",  
-\*        arity |-> 0]
-\* @@
-\* 18 :> [kind  |-> ConstantDeclKind,
-\*        name  |-> "C",
-\*        arity |-> 0  ]
-\* @@
-\* 19 :> [kind     |-> OpApplKind,            \* {1,2}
-\*        operands |-> << 14, 20 >>,   
-\*        operator |-> 8,                     \* Actually, specifying <<1, 2>>
-\*        unboundedBoundSymbols |-> << >>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\* 20 :> [kind |-> NumeralKind,
-\*        val  |-> 2]
-\* @@
-\* 21 :> [kind     |-> OpApplKind,            \* {1,2}
-\*        operands |-> << 22>>,   
-\*        operator |-> 8,                     \* Actually, specifying <<1, 2>>
-\*        unboundedBoundSymbols |-> << >>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\* 22 :> [kind |-> NumeralKind,
-\*        val  |-> 3]
-\* @@
-\* 23 :> [kind     |-> UserDefinedOpKind,          \*  Foo(u) == "FooBody(u)"
-\*        name     |-> "Foo",
-\*        body     |-> 24,
-\*        labels   |-> << >>,
-\*        arity    |-> 1,
-\*        params   |-> <<[name |-> "u",  arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> null   ]
-\* @@
-\* 24 :> [kind |-> StringKind,
-\*        val  |-> "FooBody(u)"]
-\* @@
-\* 25 :> [kind |-> StringKind,
-\*        val  |-> "string"]
-\* @@
-\* 26 :> [kind     |-> ModuleInstanceKind,    \* Inst(d) == INSTANCE M WITH C <- "expr(d)"
-\*        name     |-> "Inst",
-\*        arity    |-> 1,
-\*        params   |-> <<[name |-> "d", arity |-> 0] >>,
-\*        defined  |-> TRUE   ]
-\* @@
-\* 27 :> [kind  |-> SubstInKind,
-\*        body  |-> 24,
-\*        subst |-> "expr(d)"]
-\* @@
-\* 28 :> [kind     |-> UserDefinedOpKind,          \*  Foo(u) == "FooBody(u)"
-\*        name     |-> "Inst!Foo",
-\*        body     |-> 27,
-\*        labels   |-> << >>,
-\*        arity    |-> 2,
-\*        params   |-> << [name |-> "d", arity |-> 0],
-\*                        [name |-> "u",  arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> 23 ]
-\* @@
-\* 29 :> [kind     |-> UserDefinedOpKind,          \*  Op(Arg(_), p) == ...
-\*        name     |-> "Inst!Op",
-\*        body     |-> 30,
-\*        labels   |-> "lab" :> 4,
-\*        arity    |-> 3,
-\*        params   |-> <<[name |-> "d", arity |-> 0],
-\*                       [name |-> "Arg",  arity |-> 1],
-\*                       [name |-> "p",    arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> 1   ]
-\* @@
-\* 30 :> [kind  |-> SubstInKind,
-\*        body  |-> 3,
-\*        subst |-> "expr(d)"]
-\* @@
-\* 31 :> [kind |-> StringKind,
-\*        val  |-> "Argm"]
-\* 
-\* MCNodeId == 1..31
-\* 
-\* MCGlobalContext ==
-\*   "Op" :> 1
-\* @@
-\*   "Foo" :> 23
-\* @@
-\*   "Inst" :> 26
-\* @@
-\*   "Inst!Foo" :> 28
-\* @@
-\*   "Inst!Op" :> 29
-\* 
-\* \* Foo("string")
-\* \* MCops == <<"Foo">> 
-\* \* MCargs ==   << <<25>>>>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")
-\* \* MCops == <<"Op">> 
-\* \* MCargs ==   << <<23, 25>> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)
-\* \* MCops == <<"Op", "lab">> 
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22  >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)!<<
-\* \* MCops == <<"Op", "lab", "<<">> 
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> , << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)!<<!>>
-\* \* MCops == <<"Op", "lab", "<<", ">>">> 
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> , << >>, << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)!label2
-\* \* MCops == <<"Op", "lab", "label2">> 
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> , << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)!label2!<<
-\* \* MCops == <<"Op", "lab", "label2", "<<">> 
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> , << >>, << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)!:!+(3,2)
-\* \* MCops == <<"Op", "lab", ":", "+" >>
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!lab(1,2,3)!:!+(3,2)!>>
-\* \* MCops == <<"Op", "lab", ":", "+", ">>" >>
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >>, << >>  >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!(1,2,3)
-\* \* MCops == <<"Op", "null" >>
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!(1,2,3)!>>!>>!<<
-\* \* MCops == <<"Op", "null", ">>", ">>", "<<" >>
-\* \* MCargs ==   << <<23, 25>> , <<14, 20, 22>>, << >>, << >>,  << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Op(Foo, "string")!<<!>>
-\* \* MCops == <<"Op", "<<", ">>" >>
-\* \* MCargs ==   << <<23, 25>>,  << >>, << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Foo("string")
-\* \* MCops == <<"Inst", "Foo">> 
-\* \* MCargs ==   << <<31>>, <<25>>>>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")
-\* \* MCops == <<"Inst", "Op">> 
-\* \* MCargs ==   << <<31>>, <<23, 25>> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)
-\* \* MCops == <<"Inst", "Op", "lab">> 
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22  >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<<
-\* \* MCops == <<"Inst", "Op", "lab", "<<">> 
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<<!>>
-\* \* MCops == <<"Inst", "Op", "lab", "<<", ">>">> 
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >>, << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!label2
-\* \* MCops == <<"Inst", "Op", "lab", "label2">> 
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!label2!<<
-\* \* MCops == <<"Inst", "Op", "lab", "label2", "<<">> 
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >>, << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!:!+(3,2)
-\* \* MCops == <<"Inst", "Op", "lab", ":", "+" >>
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!:!+(3,2)!>>
-\* \* MCops == <<"Inst", "Op", "lab", ":", "+", ">>" >>
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >>, << >>  >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!(1,2,3)
-\* \* MCops == <<"Inst", "Op", "null" >>
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!(1,2,3)!>>!>>!<<
-\* \* MCops == <<"Inst", "Op", "null", ">>", ">>", "<<" >>
-\* \* MCargs ==   << <<31>>, <<23, 25>> , <<14, 20, 22>>, << >>, << >>,  << >> >>
-\* \* MCexpectedArity == 0 
-\* 
-\* \* Inst("Argm")!Op(Foo, "string")!<<!>>
-\* \* MCops == <<"Inst", "Op", "<<", ">>" >>
-\* \* MCargs ==   << <<31>>, <<23, 25>>,  << >>, << >> >>
-\* \* MCexpectedArity == 0 
-
-*****************************************)
-
-(***************************************************************************)
-(* ------------------------ MODULE M ------------------------              *)
-(* CONSTANT C, Arg1(_)                                                     *)
-(* Op(Arg, p) == \A x \in {1,2}, <<y, z>> \in {3} :                        *)
-(*                      lab(x,y,z) :: LET a + b == <<Arg1(a), b>>          *)
-(*                                    IN  1 + label2 :: p + C              *)
-(* Bar(AOp(_)) == AOp(1)                                                   *)
-(* Foo(u) == Bar(Arg1)                                                     *)
-(* Foo2 == Bar(LAMBDA m, n : <<m, n>>)                                     *)
-(* THEOREM Thm == ASSUME Arg1(1) , Bar(Arg1) PROVE << C, Bar(Arg1)>>       *)
-(* UU == lab:: C                                                           *)
-(* =============================================================           *)
-(*                                                                         *)
-(* Foo3 == \A x : LET Bar3 == 1 IN 2                                       *)
-(* Inst(d) == INSTANCE M WITH C <- "expr(d)"                               *)
-(***************************************************************************)
-
-
-MCNode == 
- 1 :> [kind     |-> UserDefinedOpKind,          \*  Op(Arg, p) == ...
-       name     |-> "Op",
-       body     |-> 3,
-       labels   |->  "lab" :> 4,
-       arity    |-> 2,
-       params   |-> <<[name |-> "Arg",  arity |-> 0],
-                      [name |-> "p",    arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
- 2 :> [kind  |-> BuiltInKind,             
-        name  |-> "$BoundedForall", 
-        arity |-> -1
-       ]
-@@
- 3 :> [kind     |-> OpApplKind,            \* \A x \in {1,2}, <<y, z>> \in {3} : ...
-       operands |-> << 4 >>,   
-       operator |-> 2,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>,
-       ranges |-> << 19, 21 >>]
-@@
- 4 :> [kind     |-> LabelKind,                    \* lab(x,y,z) :: LET a + b == <<Arg(a), b>>
-       name     |-> "lab",                        \*               IN  1 + label2 :: p + C
-       body     |-> 5,
-       arity    |-> 3,
-       params   |-> <<[name |-> "x", arity |->0], 
-                      [name |-> "y", arity |->0], 
-                      [name |-> "z", arity |->0] >>,
-       labels   |-> "label2" :> 15      ]
-@@
- 5 :>  [kind    |-> LetInKind,     \*  LET a + b == <<Arg(a), b>>
-        context |-> "+" :> 6,      \*  IN  1 + label2 :: p + C
-        body    |->  13 ]
-@@
- 6 :> [kind     |-> UserDefinedOpKind,    \* a + b == <<Arg(a), b>>
-       name     |-> "+",
-       body     |-> 7,
-       labels   |->  << >>,
-       arity    |-> 2,
-       params   |-> <<[name |-> "a",  arity |-> 0],
-                      [name |-> "b",  arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
- 7 :> [kind     |-> OpApplKind,            \* <<Arg(a), b>>
-       operands |-> << 9, 12 >>,   
-       operator |-> 8,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
- 8 :> [kind  |-> BuiltInKind,
-       name  |-> "$Tuple", 
-       arity |-> -1
-      ]
-@@
- 9 :> [kind     |-> OpApplKind,            \* Arg1(a)
-       operands |-> <<11 >>,   
-       operator |-> 10,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-10 :> [kind  |-> ConstantDeclKind,
-       name  |-> "Arg1",  
-       arity |-> 1]
-
-@@
-11 :> [kind  |-> FormalParamKind,
-       name  |-> "a",  
-       arity |-> 0]
-@@
-12 :> [kind  |-> FormalParamKind,
-       name  |-> "b",  
-       arity |-> 0]
-@@
-13 :> [kind     |-> OpApplKind,            \* 1 + label2 :: p + C
-       operands |-> <<14, 15>>,   
-       operator |-> 6,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-14 :> [kind |-> NumeralKind,
-       val  |-> 1]
-@@
-15 :> [kind     |-> LabelKind,                    \* label2 :: p + C
-       name     |-> "label2",                        
-       body     |-> 16,
-       arity    |-> 0,
-       params   |-> <<>>,
-       labels   |-> << >>]
-@@
-16 :> [kind     |-> OpApplKind,            \* p + C
-       operands |-> <<17, 18>>,   
-       operator |-> 6,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-17 :> [kind  |-> FormalParamKind,
-       name  |-> "p",  
-       arity |-> 0]
-@@
-18 :> [kind  |-> ConstantDeclKind,
-       name  |-> "C",
-       arity |-> 0  ]
-@@
-19 :> [kind     |-> OpApplKind,            \* {1,2}
-       operands |-> << 14, 20 >>,   
-       operator |-> 8,                     \* Actually, specifying <<1, 2>>
-       unboundedBoundSymbols |-> << >>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-20 :> [kind |-> NumeralKind,
-       val  |-> 2]
-@@
-21 :> [kind     |-> OpApplKind,            \* {1,2}
-       operands |-> << 22>>,   
-       operator |-> 8,                     \* Actually, specifying <<1, 2>>
-       unboundedBoundSymbols |-> << >>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-22 :> [kind |-> NumeralKind,
-       val  |-> 3]
-@@
-23 :> [kind     |-> UserDefinedOpKind,          \*  Foo(u) == Bar(Arg1)
-       name     |-> "Foo",
-       body     |-> 24,
-       labels   |-> << >>,
-       arity    |-> 1,
-       params   |-> <<[name |-> "u",  arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-24 :> [kind     |-> OpApplKind,            \* Bar(Arg1)
-       operands |-> <<34>>,
-       operator |-> 32,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-25 :> [kind |-> StringKind,
-       val  |-> "string"]
-@@
-26 :> [kind     |-> ModuleInstanceKind,    \* Inst(d) == INSTANCE M WITH C <- "expr(d)"
-       name     |-> "Inst",
-       arity    |-> 1,
-       params   |-> <<[name |-> "d", arity |-> 0] >>,
-       defined  |-> TRUE   ]
-@@
-27 :> [kind  |-> SubstInKind,
-       body  |-> 24,
-       subst |-> "expr(d)"]
-@@
-28 :> [kind     |-> UserDefinedOpKind,          \*  Foo(u) == "FooBody(u)"
-       name     |-> "Inst!Foo",
-       body     |-> 27,
-       labels   |-> << >>,
-       arity    |-> 2,
-       params   |-> << [name |-> "d", arity |-> 0],
-                       [name |-> "u",  arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> 23 ]
-@@
-29 :> [kind     |-> UserDefinedOpKind,          \*  Op(Arg, p) == ...
-       name     |-> "Inst!Op",
-       body     |-> 30,
-       labels   |-> "lab" :> 4,
-       arity    |-> 3,
-       params   |-> <<[name |-> "d", arity |-> 0],
-                      [name |-> "Arg",  arity |-> 0],
-                      [name |-> "p",    arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> 1   ]
-@@
-30 :> [kind  |-> SubstInKind,
-       body  |-> 3,
-       subst |-> "expr(d)"]
-@@
-31 :> [kind |-> StringKind,
-       val  |-> "Argm"]
-@@
-32 :> [kind     |-> UserDefinedOpKind,          \*  Bar(AOp(_)) == AOp(1)
-       name     |-> "Bar",
-       body     |-> 33,
-       labels   |-> << >>,
-       arity    |-> 1,
-       params   |-> <<[name |-> "AOp",  arity |-> 1]>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-33 :> [kind     |-> OpApplKind,            \* Arg1(1)
-       operands |-> <<14>>,
-       operator |-> 10,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-34 :> [kind  |-> OpArgKind,
-       op    |-> 10,
-       arity |-> 1,
-       name  |-> "Arg1"]
-@@ 
-35 :> [kind     |-> UserDefinedOpKind,          \*  Foo2 == Bar(LAMBDA m, n : <<m, n>>)   
-       name     |-> "Foo2",
-       body     |-> 36,
-       labels   |-> << >>,
-       arity    |-> 0,
-       params   |-> <<>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-36 :> [kind     |-> OpApplKind,            \* Bar(LAMBDA m, n : <<m, n>>)
-       operands |-> <<41>>,
-       operator |-> 32,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-37 :> [kind     |-> UserDefinedOpKind,          \* LAMBDA m, n : <<m, n>>
-       name     |-> "LAMBDA",
-       body     |-> 38,
-       labels   |->  << >>,
-       arity    |-> 2,
-       params   |-> <<[name |-> "m",  arity |-> 0],
-                      [name |-> "n",    arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-38 :> [kind     |-> OpApplKind,            \* <<m, n>>
-       operands |-> << 39, 40 >>,   
-       operator |-> 8,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-39 :> [kind  |-> FormalParamKind,
-       name  |-> "m",  
-       arity |-> 0]
-@@
-40 :> [kind  |-> FormalParamKind,
-       name  |-> "n",  
-       arity |-> 0]
-@@
-41 :> [kind  |-> OpArgKind,  \* LAMBDA m, n : <<m, n>>
-       op    |-> 37,
-       arity |-> 2,
-       name  |-> "LAMBDA"]
-@@ 
-42 :> [kind     |-> UserDefinedOpKind,          \*  Inst!Foo2 == Bar(LAMBDA m, n : <<m, n>>)   
-       name     |-> "Inst!Foo2",
-       body     |-> 43,
-       labels   |-> << >>,
-       arity    |-> 1,
-       params   |-> <<[name |-> "d", arity |-> 0]>>,
-       defined  |-> TRUE,
-       source   |-> 35    ]
-@@
-43 :> [kind  |-> SubstInKind,
-       body  |-> 36,
-       subst |-> "expr(d)"]
-@@ 
-44 :> [kind     |-> ThmOrAssumpDefKind, \*  Thm == ASSUME Arg1(1) ...
-       name     |-> "Thm",
-       body     |-> 45,
-       labels   |-> << >>,
-       arity    |-> 0,
-       params   |-> << >> ,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-45 :> [kind    |-> AssumeProveKind,
-       assumes |-> <<33, 24>>,
-       prove   |-> 46 ]
-@@
-46 :> [kind     |-> OpApplKind,            \* <<c, Bar(Arg1)>>
-       operands |-> << 59, 24 >>,   
-       operator |-> 8,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@ 
-47 :> [kind     |-> ThmOrAssumpDefKind,   \*  Thm == ASSUME Arg1(1) ...
-       name     |-> "Inst!Thm",
-       body     |-> 48,
-       labels   |-> << >>,
-       arity    |-> 1,
-       params   |-> << [name |-> "d", arity |-> 0]>> ,
-       defined  |-> TRUE,
-       source   |-> 44   ]
-@@
-48 :> [kind  |-> SubstInKind,
-       body  |-> 45,
-       subst |-> "expr(d)"]
-@@ 
-49 :> [kind     |-> UserDefinedOpKind,     \* Foo3 == \A x : LET ...
-       name     |-> "Foo3",
-       body     |-> 51,
-       labels   |-> << >>,
-       arity    |-> 0,
-       params   |-> <<>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-50 :>  [kind    |-> LetInKind,        \*  Foo3 == \A x : LET Bar3 == 1 IN 2
-        context |-> "Bar3" :> 53,     
-        body    |->  13 ]
-@@
-51 :> [kind     |-> OpApplKind,       \* \A x : LET Bar3 == 1 IN 2
-       operands |-> << 50 >>,   
-       operator |-> 52,
-       unboundedBoundSymbols |-> <<"x">>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-52 :> [kind  |-> BuiltInKind,             \* \A x :
-       name  |-> "$UnboundedForall", 
-       arity |-> -1]
-@@
-53 :> [kind     |-> UserDefinedOpKind,          \* Bar3 == 1
-       name     |-> "Bar3",
-       body     |-> 14,
-       labels   |->  << >>,
-       arity    |-> 0,
-       params   |-> <<>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-54 :> [kind     |-> FormalParamKind,          \* AOp
-       name     |-> "AOp",  
-       arity    |-> 1]
-@@
-55 :> [kind     |-> UserDefinedOpKind,          \*  Bar(AOp(_)) == AOp(1)
-       name     |-> "Inst!Bar",
-       body     |-> 56,
-       labels   |-> << >>,
-       arity    |-> 2,
-       params   |-> <<[name |-> "d", arity |-> 0],
-                      [name |-> "AOp",  arity |-> 1]>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-56 :> [kind  |-> SubstInKind,
-       body  |-> 33,
-       subst |-> "expr(d)"]
-@@
-57 :> [kind     |-> UserDefinedOpKind,    \* UU == lab:: C 
-       name     |-> "UU",
-       body     |-> 58,
-       labels   |->  "lab":> 58,
-       arity    |-> 0,
-       params   |-> <<>>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-@@
-58 :> [kind     |-> LabelKind,                    \* lab :: C
-       name     |-> "lab",                        
-       body     |-> 59,
-       arity    |-> 0,
-       params   |-> <<>>,
-       labels   |-> << >>]
-
-@@
-59 :> [kind     |-> OpApplKind,       \* C
-       operands |-> <<>>,   
-       operator |-> 18,
-       unboundedBoundSymbols |-> <<>>,
-       boundedBoundSymbols |-> << >>,
-       ranges |-> << >>]
-@@
-60 :> [kind     |-> UserDefinedOpKind,    \* UU == lab:: C 
-       name     |-> "Inst!UU",
-       body     |-> 58,
-       labels   |->  "lab":> 58,
-       arity    |-> 1,
-       params   |-> <<[name |-> "d", arity |-> 0] >>,
-       defined  |-> TRUE,
-       source   |-> null   ]
-
-
-  MCNodeId == 1..60
-
-MCGlobalContext ==
-  "Op" :> 1
-@@
-  "C" :> 18
-@@
-  "UU" :> 57
-@@
-  "Foo" :> 23
-@@
-  "Inst" :> 26
-@@
-  "Inst!Foo" :> 28
-@@
-  "Inst!Bar" :> 55
-@@
-  "Inst!Op" :> 29
-@@
-  "Inst!UU" :> 60
-@@
-  "Foo2" :> 35
-@@
-  "Inst!Foo2" :> 42
-@@ 
-  "Thm" :> 44
-@@ 
-  "Inst!Thm" :> 47
-@@
-  "Foo3" :> 49
-@@
-  "Bar" :> 32
-@@
-  "Arg1" :> 10
-@@
-  "AOp" :> 54
-@@
-  "p" :> 17
-
-\* Foo
-\* MCops == <<"Foo">> 
-\* MCargs ==   << << >> >>
-\* MCexpectedArity == 1
-
-\* Op()
-\* MCops == <<"Op">> 
-\* MCargs ==   << <<>> >>
-\* MCexpectedArity == 2
-
-\* Op!lab
-\* MCops == <<"Op", "lab">> 
-\* MCargs ==   << <<>> , <<>> >>
-\* MCexpectedArity == 5
-
-\* Op!lab!<<
-\* MCops == <<"Op", "lab", "<<">> 
-\* MCargs ==   << <<>> , <<>> , << >> >>
-\* MCexpectedArity == 5
-
-\* Op!lab!<<!>>
-\* MCops == <<"Op", "lab", "<<", ">>">> 
-\* MCargs ==   << << >> , << >> , << >>, << >> >>
-\* MCexpectedArity == 5 
-
-\* Op!lab!label2
-\* MCops == <<"Op", "lab", "label2">> 
-\* MCargs ==   << << >> , << >> , << >> >>
-\* MCexpectedArity == 5 
-
-\* Op!lab!label2!<<
-\* MCops == <<"Op", "lab", "label2", "<<">> 
-\* MCargs ==   << << >> , << >> , << >>, << >> >>
-\* MCexpectedArity == 5
-
-\* Op!lab!:!+
-\* MCops == <<"Op", "lab", ":", "+" >>
-\* MCargs ==   << << >> , << >> , << >> , << >> >>
-\* MCexpectedArity == 7 
-
-\* Op!lab!:!+
-\* MCops == <<"Op", "lab", ":", "+", ">>" >>
-\* MCargs ==   << << >> , << >> , << >> , << >>, << >>  >>
-\* MCexpectedArity == 7
-
-\* Op!@
-\* MCops == <<"Op", "@" >>
-\* MCargs ==   << << >> , << >> >>
-\* MCexpectedArity == 5 
-
-\* Op!@!>>!>>!<<
-\* MCops == <<"Op", "@", ">>", ">>", "<<" >>
-\* MCargs ==   << << >> , << >>, << >>, << >>,  << >> >>
-\* MCexpectedArity == 5 
-
-\* Op!<<!>>
-\* MCops == <<"Op", "<<", ">>" >>
-\* MCargs ==   << << >>,  << >>, << >> >>
-\* MCexpectedArity == 2 
-
-\* Inst!Foo
-\* MCops == <<"Inst", "Foo">> 
-\* MCargs ==   << << >>, << >> >>
-\* MCexpectedArity == 2 
-
-\* Inst!Op
-\* MCops == <<"Inst", "Op">> 
-\* MCargs ==   << << >>, << >> >>
-\* MCexpectedArity == 3 
-
-\* Inst!Op!lab
-\* MCops == <<"Inst", "Op", "lab">> 
-\* MCargs ==   << << >>, << >> , << >> >>
-\* MCexpectedArity == 6
-
-\* Inst!Op!lab!<<
-\* MCops == <<"Inst", "Op", "lab", "<<">> 
-\* MCargs ==   << << >>, << >> , << >> , << >> >>
-\* MCexpectedArity == 6
-
-\* Inst!Op!lab!<<!>>
-\* MCops == <<"Inst", "Op", "lab", "<<", ">>">> 
-\* MCargs ==   << << >>, << >> , << >> , << >>, << >> >>
-\* MCexpectedArity == 6 
-
-\* Inst!Op!lab!label2
-\* MCops == <<"Inst", "Op", "lab", "label2">> 
-\* MCargs ==   << << >>, << >> , << >> , << >> >>
-\* MCexpectedArity == 6
-
-\* Inst!Op!lab!label2!<<
-\* MCops == <<"Inst", "Op", "lab", "label2", "<<">> 
-\* MCargs ==   << << >>, << >> , << >> , << >>, << >> >>
-\* MCexpectedArity == 6
-
-\* Inst!Op!lab!:!+
-\* MCops == <<"Inst", "Op", "lab", ":", "+" >>
-\* MCargs ==   << << >>, << >> , << >> , << >> , << >> >>
-\* MCexpectedArity == 8 
-
-\* Inst!Op!lab!:!+!>>
-\* MCops == <<"Inst", "Op", "lab", ":", "+", ">>" >>
-\* MCargs ==   << << >>, << >> , << >> , << >> , << >>, << >>  >>
-\* MCexpectedArity == 8 
-
-\* Inst!Op!@
-\* MCops == <<"Inst", "Op", "@" >>
-\* MCargs ==   << << >>, << >> , << >> >>
-\* MCexpectedArity == 6 
-
-\* Inst!Op!@!>>!>>!<<
-\* MCops == <<"Inst", "Op", "@", ">>", ">>", "<<" >>
-\* MCargs ==   << << >>, << >> , << >>, << >>, << >>,  << >> >>
-\* MCexpectedArity == 6
-
-\* Inst!Op!<<!>>
-\* MCops == <<"Inst", "Op", "<<", ">>" >>
-\* MCargs ==   << << >>, << >>,  << >>, << >> >>
-\* MCexpectedArity == 3 
-
-\* Foo!1
-\* MCops == <<"Foo", "1">>
-\* MCargs ==   << << >>, << >> >>
-\* MCexpectedArity == 2
-
-\* Inst!Foo!1
-\* MCops == <<"Inst", "Foo", "1">>
-\* MCargs ==   << << >>, << >>, << >> >>
-\* MCexpectedArity == 3
-
-\* Foo2!1
-\* MCops == <<"Foo2", "1">>
-\* MCargs ==   << << >>, << >> >>
-\* MCexpectedArity == 2
-
-\* Foo2!1!(1, 2)
-\* MCops == <<"Foo2", "1", "null">>
-\* MCargs ==   << << >>, << >>, <<14, 20>> >>
-\* MCexpectedArity == 0
-
-\* Foo2!1!(1, 2)!2
-\* MCops == <<"Foo2", "1", "null", "2">>
-\* MCargs ==   << << >>, << >>, <<14, 20>>, << >> >>
-\* MCexpectedArity == 0
-
-\* Inst!Foo2!1
-\* MCops == <<"Inst", "Foo2", "1">>
-\* MCargs ==   << << >>, << >>, << >> >>
-\* MCexpectedArity == 3
-
-\* Inst!Foo2!1
-\* MCops == <<"Inst", "Foo2", "1", "@">>
-\* MCargs ==   << << >>, << >>, << >>, << >> >>
-\* MCexpectedArity == 3
-
-\* Inst!Foo2!1!2
-\* MCops == <<"Inst", "Foo2", "1", "@", "2">>
-\* MCargs ==   << << >>, << >>, << >>, << >>, << >> >>
-\* MCexpectedArity == 3
-
-\* Inst("Argm")!Foo2!1!(1, 2)
-\* MCops == <<"Inst", "Foo2", "1", "null">>
-\* MCargs ==   << <<31>>,  << >>, << >>, <<14, 20>> >>
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Foo2!1!(1, 2)!2
-\* MCops == <<"Inst", "Foo2", "1", "null", "2">>
-\* MCargs ==   << <<31>>, << >>, << >>, <<14, 20>>, << >> >>
-\* MCexpectedArity == 0
-
-\* Thm!1
-\* MCops == <<"Thm", "1" >>
-\* MCargs ==   << << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Thm!2
-\* MCops == <<"Thm", "2" >>
-\* MCargs ==   << << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Thm!3!<<
-\* MCops == <<"Thm", "3", "<<" >>
-\* MCargs ==   << << >>, << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Thm!1
-\* MCops == <<"Inst", "Thm", "1" >>
-\* MCargs ==   << <<31>>, << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Thm!2
-\* MCops == <<"Inst", "Thm", "2" >>
-\* MCargs ==   << <<31>>, << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Thm!3!<<  
-\* MCops == <<"Inst", "Thm", "3", "<<" >>
-\* MCargs ==   << <<31>>, << >>, << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!UU
-\* MCops == <<"Inst", "UU">>
-\* MCargs ==   << <<31>>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Bar(Arg1)!1<<  
-\* MCops == <<"Inst", "Bar", "1" >>
-\* MCargs ==   << <<31>>, <<10 >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Bar(Arg1)!1<<  
-\* MCops == <<"Inst", "Bar", "1" >>
-\* MCargs ==   << <<31>>, <<10 >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Thm             \* Note: doesn't report an error because 
-\* MCops == <<"Inst", "Thm" >>  \* Inst!Thm isn't expanded
-\* MCargs ==   << <<31>>, << >> >>
-\* MCexpectedArity == 0
-
-\* Inst("Argm")!Thm!:  ERROR!
-\* MCops == <<"Inst", "Thm", ":" >>
-\* MCargs ==   << <<31>>, << >>, << >> >>
-\* MCexpectedArity == 0
-
-
-\* Foo2
-\* MCops == <<"Foo2">>
-\* MCargs ==   <<<< >> >>
-\* MCexpectedArity == 0
-
-\* Foo3!(3)!:Bar3
-\* MCops == <<"Foo3", "null", "Bar3">>
-\* MCargs ==   << << >>, <<22>>, << >> >>
-\* MCexpectedArity == 0
-
-\* Bar(Arg1)
-\* \* MCops == <<"Bar">>
-\* \* MCargs ==   << <<10>> >>
-\* \* MCexpectedArity == 0
-
-\* Arg1(C)    
-\* MCops == <<"Arg1">>
-\* MCargs ==   << <<18>> >>
-\* MCexpectedArity == 0
-
-\* AOp  
-\* MCops == <<"AOp">>  
-\* MCargs ==   << <<18 >> >>
-\* MCexpectedArity == 0
-
-\* C  
-\* MCops == <<"C">>  
-\* MCargs ==   << << >> >>
-\* MCexpectedArity == 0
-
-\* p  
-\* MCops == <<"p">>  
-\* MCargs ==   << << >> >>
-\* MCexpectedArity == 0
-
-\* Foo3  
-\* MCops == <<"Foo3">>  
-\* MCargs ==   << << >> >>
-\* MCexpectedArity == 0
-
-\***** THIS IS A BUG--NOT HANDLED CORRECTLY ******
-\* UU!lab
-\* MCops == <<"UU", "lab">>  
-\* MCargs ==   << << >>, << >> >>
-\* MCexpectedArity == 0
-
-\* Op(2, 2)!(2, 2, 2)
-\* MCops == <<"Op", "null">>
-\* MCargs ==   << <<20, 20>>, <<20, 20, 20 >> >>
-\* MCexpectedArity == 0
-
-\* Foo3!(2)
-\* MCops == <<"Foo3", "null">>
-\* MCargs ==   << << >>, <<20>> >>
-\* MCexpectedArity == 0
-
-\* Op(2, 2)!(2,2,2)!+(2, 2)
-\* MCops == <<"Op", "null", "+">>
-\* MCargs ==   << <<20,20 >>, <<20,20,20>> , <<20,20>>>>
-\* MCexpectedArity == 0
-
-\* MCops == <<"Op">> 
-\* MCargs ==   << << >> >>
-\* MCexpectedArity == -1
-
-\* MCops == <<"Inst", "Op">> 
-\* MCargs ==   << << >> , << >> >>
-\* MCexpectedArity == -1
-
-\* MCops == <<"Inst", "Thm">> 
-\* MCargs ==   << << >> , << >> >>
-\* MCexpectedArity == -1
-
-\* MCops == <<"Inst", "Op", "@", "+">>
-\* MCargs ==   << << >> , << >> , << >> , << >> >>
-\* MCexpectedArity == -1
-
-\* MCops == <<"Op", "lab", ":", "+">>
-\* MCargs ==   << << >> , << >>, << >>,  << >> >>
-\* MCexpectedArity == 7
-
-\* MCops == <<"Op", "lab", ":", "+">>
-\* MCargs ==   << << >> , << >>, << >>,  << >> >>
-\* MCexpectedArity == -1
-
-\* MCops == <<"Foo2">>
-\* MCargs ==   << << >> >> 
-\* MCexpectedArity == 0
-
-\* MCops == <<"Foo3", ":">>
-\* MCargs ==   << << >>, << >> >> 
-\* MCexpectedArity == 0
-
-\* MCops == <<"Op", "lab", ":", "+">>
-\* MCargs ==   << << >>, << >>, << >>, << >> >> 
-\* MCexpectedArity == -1
-
-\* MCops == <<"Inst", "Thm">>
-\* MCargs ==   << <<14 >>,  << >> >>
-\* MCexpectedArity == 0
-
-MCops == <<"Inst", "Thm", ":">>
-MCargs ==   << <<14 >>,  << >>, << >> >>
-MCexpectedArity == 0
-MCdebug == FALSE \* TRUE
-
-(*********************************
-(***************************************************************************)
-(* Module M                                                                *)
-(* CONSTANT C                                                              *)
-(* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> \in {3} :                     *)
-(*                      lab(x,y,z) :: LET a + b == <<Arg(a), b>>           *)
-(*                                    IN  1 + label2 :: p + C              *)
-(*                                                                         *)
-(* Foo(u) == "FooBody(u)"                                                  *)
-(* Inst(w) == INSTANCE M WITH C <- <<w, CONST>>                            *)
-(***************************************************************************)
-
-\* MCNode == 
-\*  1 :> [kind |-> NumeralKind,
-\*        val  |-> 1]
-\* @@
-\*  2 :> [kind  |-> BuiltInKind,         \* \E 
-\*        name  |-> "$UnboundedExists",
-\*        arity |-> -1]
-\*         
-\* @@
-\*  3 :> [kind     |-> OpApplKind,            \* \E x : Node 1
-\*        operands |-> <<1>>,
-\*        operator |-> 2,
-\*        unboundedBoundSymbols |-> <<"x">>,
-\*        boundedBoundSymbols |-> << >>,
-\*        ranges |-> << >>]
-\* @@
-\*  4 :> [kind     |-> UserDefinedOpKind,    \* Inst!UserOp(p1, OpP) == Node 8
-\*        name     |-> "Inst!UserOp",
-\*        body     |-> 8,
-\*        labels   |->  "Label1" :> 7,
-\*        arity    |-> 2,
-\*        params   |-> <<[name |-> "p1",  arity |-> 0],
-\*                       [name |-> "OpP", arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> 40   ]
-\* @@
-\*  5 :> [kind     |-> ModuleInstanceKind,    \* Inst(p1) == INSTANCE
-\*        name     |-> "Inst",
-\*        arity    |-> 1,
-\*        params   |-> <<[name |-> "p1", arity |-> 0] >>,
-\*        defined  |-> TRUE   ]
-\* @@
-\*  6 :> [kind     |-> UserDefinedOpKind,    \* 1ArgOp(1OpP(_, _)) == Node 1
-\*        name     |-> "1ArgOp",
-\*        body     |-> 41,
-\*        labels   |-> << >>,
-\*        arity    |-> 1,
-\*        params   |-> <<[name |-> "1OpP", arity |-> 0]>>,
-\*        defined  |-> TRUE,
-\*        source   |-> null   ]
-\* @@
-\*  7 :> [kind     |-> LabelKind,
-\*        name     |-> "Label1",
-\*        body     |-> 14,
-\*        arity    |-> 1,
-\*        params   |-> <<[name |-> "labParam1", arity |-> 0]>>,
-\*        labels   |-> "Label2" :> 9
-\*       ]
-\* @@
-\*  8 :> [kind  |-> SubstInKind,
-\*        body  |-> 11,
-\*        subst |-> "x <- something(p1)"]
-\* 
-\* @@
-\*  9 :> [kind     |-> LabelKind,
-\*        name     |-> "Label2",
-\*        body     |-> 15,
-\*        arity    |-> 1,
-\*        params   |-> <<[name |-> "labParam2", arity |-> 0]>>,
-\*        labels   |-> << >>
-\*       ]
-\* @@
-\*  10 :> [kind |-> NumeralKind,
-\*         val  |-> 10]
-\* @@
-\*  11 :> [kind  |-> SubstInKind,
-\*         body  |-> 15,
-\*         subst |-> "y <- something(p1)"]
-\* @@
-\*  12 :> [kind |-> StringKind,
-\*         val  |-> "Body of UserOp (p1, OpP)"]
-\* @@
-\*  13 :> [kind |-> StringKind,
-\*         val  |-> "Body of Label2(labParam2)"]
-\* @@
-\*  14 :> [kind |-> StringKind,
-\*         val  |-> "Body of Label1(labParam1)"]
-\* @@
-\*  15 :> [kind    |-> LetInKind,
-\*         context |-> "1ArgOp" :> 6,
-\*         body    |-> 16]
-\* @@
-\*  16 :> [kind |-> StringKind,
-\*         val  |-> "Body of Let/In"]
-\* @@
-\*  17 :> [kind     |-> UserDefinedOpKind,    \* 1ArgOp("1OpP") == Node 1
-\*         name     |-> "Foo",
-\*         body     |-> 18,
-\*         labels   |-> << >>,
-\*         arity    |-> 0,
-\*         params   |-> << >> , \*  <<[name |-> "FooPar1", arity |-> 0]>>,
-\*         defined  |-> TRUE,
-\*         source   |-> null] 
-\* @@
-\*  18 :> [kind     |-> OpApplKind,            
-\*         operands |-> <<42 >>,
-\*         operator |-> 21,
-\*         unboundedBoundSymbols |-> <<>>,
-\*         boundedBoundSymbols |-> << >>,
-\*         ranges |-> << >>]
-\* @@
-\*  19 :> [kind |-> StringKind,
-\*         val  |-> "Arg1"]
-\* @@
-\*  20 :> [kind |-> StringKind,
-\*         val  |-> "Arg2"]
-\* @@
-\*  21 :> [kind    |-> UserDefinedOpKind,    \* Foo2 == [rcd-lab1 |-> rcd-comp1,
-\*         name     |-> "Foo2",              \*            ...
-\*         body     |-> 34,                  \*          rcd-lab3 |-> rcd-comp3]
-\*         labels   |->  << >>,
-\*         arity    |-> 1,
-\*         params   |-> <<[name |-> "p1",  arity |-> 1]>>,
-\*         defined  |-> TRUE,
-\*         source   |-> null   ]
-\* @@ 
-\*  22 :> [kind     |-> OpApplKind,            
-\*         operands |-> <<10 (* 25 *) , 28, 31>>,
-\*         operator |-> 23,
-\*         unboundedBoundSymbols |-> <<>>,
-\*         boundedBoundSymbols |-> << >>,
-\*         ranges |-> << >>]
-\* @@ 
-\*  23 :> [kind  |-> BuiltInKind,
-\*         name  |-> "$Except", \* "$Case", \* "$SetOfRcds", 
-\*         arity |-> -1
-\*        ]
-\* @@ 
-\*  24 :> [kind  |-> BuiltInKind,
-\*         name  |-> "$Pair", 
-\*         arity |-> 2
-\*        ]
-\* @@ 
-\*  25 :> [kind     |-> OpApplKind,            
-\*         operands |-> <<26,27>>,
-\*         operator |-> 24,
-\*         unboundedBoundSymbols |-> <<>>,
-\*         boundedBoundSymbols |-> << >>,
-\*         ranges |-> << >>]
-\* @@ 
-\*  26 :> [kind |-> StringKind,
-\*         val  |-> "rcd-lab1"]
-\* @@ 
-\*  27 :> [kind |-> StringKind,
-\*         val  |-> "rcd-comp1"]
-\* 
-\* @@ 
-\*  28 :> [kind     |-> OpApplKind,            
-\*         operands |-> <<29,30>>,
-\*         operator |-> 24,
-\*         unboundedBoundSymbols |-> <<>>,
-\*         boundedBoundSymbols |-> << >>,
-\*         ranges |-> << >>]
-\* @@ 
-\*  29 :> [kind |-> StringKind,
-\*         val  |-> "rcd-lab2"]
-\* @@ 
-\*  30 :> [kind |-> StringKind,
-\*         val  |-> "rcd-comp2"]
-\* @@ 
-\*  31 :> [kind     |-> OpApplKind,            
-\*         operands |-> <<32,33>>,
-\*         operator |-> 24,
-\*         unboundedBoundSymbols |-> <<>>,
-\*         boundedBoundSymbols |-> << >>,
-\*         ranges |-> << >>]
-\* @@ 
-\*  32 :> [kind |-> StringKind,
-\*         val  |-> "rcd-lab3"]
-\* @@ 
-\*  33 :> [kind |-> StringKind,
-\*         val  |-> "rcd-comp3"]
-\* @@
-\*  34 :> [kind     |-> OpApplKind,            
-\*         operands |-> <<35>>,
-\*         operator |-> 36,
-\*         unboundedBoundSymbols |-> <<>>,
-\*         boundedBoundSymbols |-> << <<"b1", "b2">>, <<"b3">> >>,
-\*         ranges |-> <<37, 38 >>]
-\* @@ 
-\*  35 :> [kind |-> StringKind,
-\*         val  |-> "Fcn of b1, b2, b3"]
-\* @@
-\*  36 :> [kind  |-> BuiltInKind,
-\*         name  |-> "$Pair", 
-\*         arity |-> 2
-\*        ]
-\* @@ 
-\*  37 :> [kind |-> StringKind,
-\*         val  |-> "Range 1"]
-\* @@ 
-\*  38 :> [kind |-> StringKind,
-\*         val  |-> "Range 2"]
-\* @@ 
-\*  39 :> [kind |-> StringKind,
-\*         val  |-> "Arg3"]
-\* @@ 
-\*  40 :> [kind    |-> UserDefinedOpKind,    \* Inst!UserOp(p1, OpP) == Node 8
-\*         name    |-> "UserOp",
-\*         body    |-> 8,
-\*         labels  |->  "Label1" :> 7,
-\*         arity   |-> 1,
-\*         params  |-> <<[name |-> "OpP", arity |-> 1]>>,
-\*         defined |-> TRUE,
-\*         source  |-> null   ]
-\* @@
-\*  41 :> [kind |-> StringKind,
-\*         val  |-> "Body of 1ArgOp may dep on (x, y)"]
-\* @@
-\*  42 :> [kind  |-> OpArgKind,
-\*         op    |-> 43,
-\*         arity |-> 1,
-\*         name  |-> "Foo3"]
-\* @@
-\*  43 :> [kind     |-> UserDefinedOpKind,    \* 1ArgOp("1OpP") == Node 1
-\*         name     |-> "Foo3",
-\*         body     |-> 18,
-\*         labels   |-> << >>,
-\*         arity    |-> 0,
-\*         params   |-> << >>, \* <<[name |-> "FooPar1", arity |-> 0]>>,
-\*         defined  |-> TRUE,
-\*         source   |-> null] 
-\* 
-\* MCNodeId == 1..43
-\* 
-\* MCGlobalContext ==
-\*   "Inst!UserOp" :> 4
-\* @@
-\*   "Inst" :> 5
-\* @@
-\*   "Foo" :> 17
-\* @@
-\*   "Foo2" :> 21
-\* @@
-\*   "Foo3" :> 43
-\* \* @@
-\* \*   "1ArgOp" :> 6
-\* 
-\* 
-\* MCops == <<"Inst", "UserOp">> \* ,  "1">> 
-\* MCargs ==   << <<1 >> , <<1>> >>
-
-\* \* << <<1>>, <<19, 20, 33 >> >> \* <<1>>, <<10>>, << >>, <<4>> >>
-\*      
-\* MCexpectedArity == 0 \* -1 \* 5
-***************************)
-
------------------------------------------------------------------------------
-(***************************************************************************)
-(*                            THE ALGORITHM                                *)
-(***************************************************************************)
-(**************************************  
---algorithm Subexpression
-variables 
-  (*************************************************************************)
-  (* The input variables.                                                  *)
-  (*************************************************************************)
-  ops = MCops,
-  args = MCargs,
-    (***********************************************************************)
-    (* The sequences op and args described above.                          *)
-    (***********************************************************************)
-  expectedArity = MCexpectedArity,
-    (***********************************************************************)
-    (* The arity of the operator expected, or -1 if expecting a definition *)
-    (* name.  It is 0 iff an expression is expected.  The result should be *)
-    (* an OpArg node if expectedArity > 0, it should be an expression node *)
-    (* if expectedArity = 0, and it should be a UserDefinedOpKind or a     *)
-    (* ThmOrAssumpDefKind if expectedArity < 0.                            *)
-    (***********************************************************************)
-    
-  (*************************************************************************)
-  (* The major variables of the algorithm.                                 *)
-  (*************************************************************************)
-  substInPrefix = << >> ,
-    (***********************************************************************)
-    (* The sequence of SubstInNode or APSubstInNode sequence that will be  *)
-    (* appended to body of the resulting OpApplNode                        *)
-    (***********************************************************************)
-  params = << >> ,
-    (***********************************************************************)
-    (* The sequence of FormalParamNode objects that are the formal         *)
-    (* parameters if this produces a Lambda expression.  A Lambda          *)
-    (* expression will be produced iff this is non-empty                   *)
-    (***********************************************************************)
-  allArgs = << >> ,
-    (***********************************************************************)
-    (* The sequence of all arguments.                                      *)
-    (***********************************************************************)
-  curNode = null,
-    (***********************************************************************)
-    (* If params = << >>, then the OpDefNode for the OpApplNode that is    *)
-    (* produced.  If params # << >>, then the Expr node that will form the *)
-    (* body of the Lambda expression.                                      *)
-    (*                                                                     *)
-    (* Note: in the Java implementation, this will actually be a ref to    *)
-    (* the node--in terms of this spec, a NodeId.                          *)
-    (***********************************************************************)
-  subExprOf = "null",
-    (***********************************************************************)
-    (* The node UserDefinedOpDefKind or ThmOrAssumpDefKind within which    *)
-    (* this subexpression is defined.                                      *)
-    (***********************************************************************)
-  result = null,
-    (***********************************************************************)
-    (* The actual output node.                                             *)
-    (***********************************************************************)
-    
-  (*************************************************************************)
-  (* The local variables.                                                  *)
-  (*************************************************************************)
-  idx = 1,
-    (***********************************************************************)
-    (* The element of the arrays op and args that the algorithm is         *)
-    (* currently examining.                                                *)
-    (***********************************************************************)
-  mode = "FindingOpName",
-    (***********************************************************************)
-    (* The current mode describing what kind of selector it is expecting   *)
-    (* next.  Its other possible values are "FollowingLabels" and          *)
-    (* "FindingSubExpr".                                                   *)
-    (***********************************************************************)
-  prevMode = "",
-    (***********************************************************************)
-    (* The mode for the previously examined selector.                      *)
-    (***********************************************************************)
-  curContext = GlobalContext,
-    (***********************************************************************)
-    (* The context for looking up operator or label names.                 *)
-    (***********************************************************************)
-  curName = "" , 
-    (***********************************************************************)
-    (* When looking up an operator name, which may be something like       *)
-    (* "Foo!Bar!Baz", this is the part that has been found so far.         *)
-    (***********************************************************************)
-  opDefArityFound = 0,
-  opDefArgs = << >>,
-    (***********************************************************************)
-    (* The total arity and the sequence of arguments found so far for the  *)
-    (* current operator--for example, opDefArityFound will equal 2 if the  *)
-    (* algorithm has so far processed "Foo(a, b)!Bar"                      *)
-    (***********************************************************************)
-  firstFindingOpName = TRUE,
-    (***********************************************************************)
-    (* True iff have entered the FindingOpName only once (initially).      *)
-    (***********************************************************************)
-  opNode,
-  newName,
-  newNode,
-  nodeArity,
-  temp,
-  tempArgs
-
-
-(***************************************************************************)
-(* A macro used for reporting an error and terminating the execution.      *)
-(***************************************************************************)
-macro Error(msg) begin print msg ;
-                       if debug then (**************************************)
-                                     (* Force the +cal translator to       *)
-                                     (* insert a label so the error trace  *)
-                                     (* shows the final state.             *)
-                                     (**************************************)
-                                     idx := idx ;
-                                     idx := idx ;
-                                     assert FALSE 
-                                else goto Done
-                       end if 
-                 end macro ;
-
- begin
-
-  (*************************************************************************)
-  (* An assertion that checks that ops and args are consistent with each   *)
-  (* other and with the value of expectedArity.  In an implementation,     *)
-  (* some of these should be guaranteed by the context (e.g., Len(ops) =   *)
-  (* Len(args) while others will be checked as part of the processing for  *)
-  (* the particular value of idx.                                          *)
-  (*************************************************************************)
-  assert /\ expectedArity \in Int
-         /\ ops  \in Seq(STRING)
-         /\ args \in Seq(Seq(NodeId))
-         /\ Len(ops) = Len(args)
-         /\ \A i \in 1..Len(ops) :
-              /\ \/ ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp 
-                 \/ expectedArity # 0
-                 => args[i] = << >>  
-              /\ (ops[i] = "null") => (Len(args[i]) > 0) ;
-
-  (*************************************************************************)
-  (* The outer "for" loop on idx.                                          *)
-  (*************************************************************************)
-  while idx <= Len(ops) do
-  if mode = "FindingOpName" then
-
-\* The following code was modified by LL on 23 Sep 2009 to fix the following
-\* bug.  The parser produced a bogus error on a reference to an identifier 
-\* M!N that is imported by an unnamed INSTANCE of a module containing the
-\* statement M == INSTANCE ... .  The original code essentially just 
-\* contained the first iteration of the following while loop, and would
-\* report an error if it found newNode = null.  Thus, it would report
-\* an error in the identifier M!N when it found M to be undefined.
-\*   
-\* Corrected by LL on 9 Nov 2009 to handle arguments of those
-\* undefined operators.  They are put into tempArgs when they are
-\* found.
-
-   newNode := null ;
-   tempArgs := << >> ;
-     (**********************************************************************)
-     (* The number of arguments found in the following loop for the        *)
-     (* undefined operators                                                *)
-     (**********************************************************************)
-   while newNode = null do
-     if idx \geq Len(ops)
-       then Error ("Unknown operator");
-     end if;
-     newName := IF IsName(ops[idx])
-                  THEN IF curName = "" THEN ops[idx] 
-                                       ELSE curName \o "!" \o ops[idx]
-                  ELSE null ;
-     if /\ curName = ""
-        /\ ~ IsName(ops[idx])
-       then (***************************************************************)
-            (* I think that this error can only happen for idx = 1, and    *)
-            (* should never happen in the implementation because the       *)
-            (* parser should not allow a compound name that doesn't begi   *)
-            (* with a name.                                                *)
-            (***************************************************************)
-            Error("Need an operator or label name or step number here")
-       end if;
-     newNode := IF newName # null THEN LookUp(newName, curContext) ELSE null ;
-     if newName = null
-       then tempArgs := tempArgs \o args[idx];
-            idx := idx + 1;
-     end if;
-   end while ;     
-   curNode := newNode ;
-   curName := newName ;
-   if curNode.kind \in 
-          {UserDefinedOpKind, ThmOrAssumpDefKind,
-             ModuleInstanceKind, ConstantDeclKind, VariableDeclKind, 
-             FormalParamKind, BuiltInKind, BoundSymbolKind} 
-     then if curNode.kind \in 
-                  {ConstantDeclKind, VariableDeclKind, FormalParamKind, 
-                   BuiltInKind, BoundSymbolKind}
-             then if idx # 1 
-                    then Error("Abort: Impossible naming of declaration "
-                                \o "or built-in operator.")
-                    else if Len(ops) # 1
-                           then Error("Can't take subexpression of this");
-                         end if
-                 end if                 
-          end if ;
-          nodeArity := Arity(curNode) ;
-          tempArgs := tempArgs \o args[idx];
-          if expectedArity = 0
-            then if opDefArityFound + Len(tempArgs) # nodeArity
-                   then Error("Wrong number of arguments")
-                 end if ;
-                 if \E i \in 1..Len(tempArgs[idx]) :
-                          Arity(Node[tempArgs[idx][i]]) #
-                            ParamArity(curNode, i + opDefArityFound)
-                    then Error("Argument has wrong arity")
-                    else opDefArgs := opDefArgs \o tempArgs; 
-                 end if ;
-            else if expectedArity > 0
-                   then if \E i \in 1..Len(curNode.params) :
-                              curNode.params[i].arity > 0
-                          then Error("Higher-order operator selected " 
-                                       \o "as operator argument") 
-                        end if
-\*                   else Error(
-\*                         "Abort: Don't yet handle expectedArity < 0")
-                 end if
-          end if ;
-          opDefArityFound := nodeArity ;
-          if curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind,
-                               ConstantDeclKind, VariableDeclKind, 
-                               FormalParamKind, BoundSymbolKind} 
-            then if /\ curNode.kind = UserDefinedOpKind
-                    /\ ~ curNode.defined 
-                    /\ ~ Len(ops) = 1
-                   then Error("Can't refer to subexpression of "
-                               \o "operator inside its definition")
-                 end if;
-                 if /\ firstFindingOpName
-                    /\ curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}
-                   then subExprOf := curNode
-                 end if;               
-                 if idx # Len(ops)
-                   then params := params \o curNode.params ;
-                        curName := "" ;
-                        if IsName(ops[idx+1])
-                          then mode := "FollowingLabels"
-                          else mode := "FindingSubExpr" ;
-                        end if ;
-                        allArgs := allArgs \o opDefArgs ;
-                        opDefArityFound := 0 ;
-                        opDefArgs := << >> ;
-                        newNode  := Node[curNode.body] ;
-                        while newNode.kind = SubstInKind do
-                          substInPrefix := substInPrefix \o <<newNode>>;
-                          newNode := Node[newNode.body];  
-                        end while ;
-                        while newNode.kind \in {SubstInKind, APSubstInKind}
-                           (************************************************)
-                           (* Added APSubstInKind test on 13 Nov 2009.     *)
-                           (************************************************)
-                         do
-                          substInPrefix := substInPrefix \o <<newNode>>;
-                          newNode := Node[newNode.body];  
-                        end while ;
-                        (********************************************)
-                        (* If the next op is an OpSel, then need to *)
-                        (* skip over SubstInNodes, which are        *)
-                        (* invisible to the user.                   *)
-                        (********************************************)
-                        if mode = "FindingSubExpr"
-                          then curNode := newNode 
-                        end if;
-                 end if ;
-           else  \* curNode.kind = ModuleInstanceKind
-                 if (idx = Len(ops))
-                   then Error("Operator name incomplete")
-                 end if
-          end if
-   else Error("Unexpected node kind") ;
-   end if ;
-   prevMode := "FindingOpName" ;
-
-        
-  elsif mode = "FollowingLabels" then
-     if prevMode = "FindingOpName"
-       then assert curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} ;
-            newNode := LookUp(ops[idx], curNode.labels) ;
-       else assert curNode.kind = LabelKind ;
-            newNode := LookUp(ops[idx], curNode.labels) ;
-     end if ;
-     if newNode =  null
-       then Error("Label not found")
-     end if;
-     curNode := newNode ;
-     if expectedArity = 0
-       then if Len(args[idx]) # curNode.arity 
-              then Error("bad arity")
-            end if ;
-            if \E i \in 1..Len(args[idx]) :
-                   Arity(Node[args[idx][i]]) # 0
-              then Error("Operator argument given to label")
-            end if;
-            allArgs := allArgs \o args[idx] ;
-     end if;
-     params := params \o curNode.params ;                        
-     if /\ idx < Len(ops)
-        /\ ~IsName(ops[idx+1])
-       then mode := "FindingSubExpr"
-     end if ;
-     if \/ mode = "FindingSubExpr"
-        \/ idx = Len(ops)  
-       then curNode := Node[curNode.body]
-     end if;
-     prevMode := "FollowingLabels";
-
-
-  elsif mode = "FindingSubExpr" then
-    if ops[idx] = ":" then 
-      if \/ prevMode \notin {"FindingOpName", "FollowingLabels"}
-         \/ ~ \/ /\ idx = Len(ops)
-                 /\ prevMode = "FindingOpName"
-              \/ /\ idx < Len(ops)
-                 /\ IsName(ops[idx+1])
-        then Error("`!:' can be used only after a name and either at the " \o
-                   "end after an operator name or before an operator name.")
-      end if 
-
-    elsif curNode.kind = LetInKind then
-      if ArgNum(ops[idx], 1) = 1
-        then curNode := Node[curNode.body]
-        else Error("A Let/In has only a single operand")
-      end if ;
-
-    elsif curNode.kind = OpApplKind then
-      opNode := Node[curNode.operator] ;
-      if opNode.kind 
-              \in {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} then
-         (******************************************************************)
-         (* Selecting an argument from something of the form Op(...).      *)
-         (******************************************************************)
-         temp := ArgNum(ops[idx], opNode.arity) ;
-         if temp = -1
-           then Error("Nonexistent operand specified")
-           else curNode := Node[curNode.operands[temp]]
-         end if
-      elsif opNode.kind = BuiltInKind then 
-              \*  See configuration/ConfigConstants for a list of all
-              \*  BuiltInKind operators.
-        if opNode.name \in {"$RcdConstructor", "$SetOfRcds"} then
-          (*****************************************************************)
-          (* curNode represents an expression                              *)
-          (*   [a_1 |-> e_1, ... , a_n |-> e_n] or                         *)
-          (*   [a_1  :  e_1, ... , a_n  :  e_n]                            *)
-          (* Its i-th argument is the $Pair node (a_i, e_i)                *)
-          (*****************************************************************)
-          temp := ArgNum(ops[idx], Len(curNode.operands)) ;
-          if temp = -1 
-            then Error("Incorrect subexpression number")
-          end if ;
-          curNode := Node[curNode.operands[temp]] ;
-          if \/ curNode.kind # OpApplKind
-             \/ Node[curNode.operator].kind # BuiltInKind
-             \/ Node[curNode.operator].name # "$Pair"
-            then Error ("Expecting $Pair(...)")
-            else curNode := Node[curNode.operands[2]]
-          end if
-
-        elsif opNode.name \in {"$Case"} then
-          (*****************************************************************)
-          (* The i-th clause is a $Pair node, where for the OTHER clause   *)
-          (* the 1st element is null.                                      *)
-          (*****************************************************************)
-          if idx = Len(ops)
-            then Error( "CASE subexpression must be of form !i!j")
-          end if ;
-          temp := ArgNum( ops[idx], Len(curNode.operands)) ;
-          if temp = -1 
-            then Error("Incorrect subexpression name")
-          end if ;
-          curNode := Node[curNode.operands[temp]] ;
-          if \/ curNode.kind # OpApplKind
-             \/ Node[curNode.operator].kind # BuiltInKind
-             \/ Node[curNode.operator].name # "$Pair"
-            then Error ("Expecting $Pair(...)")
-          end if ;
-          idx  := idx+1 ;
-          temp := ArgNum(ops[idx], 2);
-          if temp = -1 
-            then Error("Incorrect subexpression name")
-          end if ;
-          curNode := Node[curNode.operands[temp]] ;
-          if curNode = null
-            then Error("Selecting OTHER")
-          end if
-
-        elsif opNode.name = "$Except" then
-           (****************************************************************)
-           (* For                                                          *)
-           (*   [exp_1 ELSE !... = exp_2, ... , !.. = exp_n]               *)
-           (* argument number i chooses exp_i.  For i > 1, exp_i is the    *)
-           (* second argument of a $Pair operator.                         *)
-           (****************************************************************)
-           temp := ArgNum(ops[idx], Len(curNode.operands));
-           if temp = -1 
-             then Error("Bad argument selector.")
-           end if;
-           curNode := Node[curNode.operands[temp]] ;
-           if temp > 1
-             then if \/ curNode.kind # OpApplKind
-                     \/ Node[curNode.operator].kind # BuiltInKind
-                     \/ Node[curNode.operator].name # "$Pair"
-                    then Error("Unexpected expression node found.")
-                    else curNode := Node[curNode.operands[2]]
-                  end if
-           end if
-
-        else
-          (*****************************************************************)
-          (* Operator handled by standard procedure.                       *)
-          (*****************************************************************)
-          if /\ Len(curNode.unboundedBoundSymbols) = 0 
-             /\ Len(curNode.boundedBoundSymbols)   = 0
-            then (**********************************************************)
-                 (* Current subexpression has no bound variables.          *)
-                 (**********************************************************)
-                 temp := ArgNum(ops[idx], Len(curNode.operands)) ;
-                 if temp = -1 
-                   then Error("Incorrect subexpression selector")
-                 end if;
-                 curNode := Node[curNode.operands[temp]] ;
-            else
-                 (**********************************************************)
-                 (* Current subexpression has bound variables.  If         *)
-                 (* selector is "@" or null, then choosing body and adding *)
-                 (* parameters.  Otherwise, must be selecting one of the   *)
-                 (* bounds.                                                *)
-                 (**********************************************************)
-                 if ops[idx] \in {"null", "@"}
-                   then (***************************************************)
-                        (* Set temp to the sequence of parameters.         *)
-                        (***************************************************)
-                        temp := IF Len(curNode.unboundedBoundSymbols) > 0 
-                                  THEN curNode.unboundedBoundSymbols
-                                  ELSE SeqSeqToSeq(
-                                          curNode.boundedBoundSymbols);
-
-                        params := params \o 
-                                   [i \in 1.. Len(temp) |->
-                                     [name |-> temp[i], arity |-> 0]] ;
-                        allArgs := allArgs \o args[idx] ;
-                        if /\ ops[idx] = "null"
-                           /\ Len(args[idx]) # Len(temp)
-                          then Error("Wrong number of selector arguments");
-                        end if;
-                        curNode := Node[curNode.operands[1]];
-                   else temp := ArgNum(ops[idx], Len(curNode.ranges)) ;
-                        if temp = -1
-                          then Error ("Selecting non-existent range") ;
-                          else curNode := Node[curNode.ranges[temp]] 
-                        end if
-                 end if
-          end if
-        end if  \* opNode.name = ...
-         
-      else Error("Applying subexpression chooser to an expr" \o
-                  " with no choosable subexpressions")
-                            
-      end if \* opNode.kind = ... 
-
-    elsif curNode.kind = AssumeProveKind then
-          temp := ArgNum(ops[idx], 1 + Len(curNode.assumes)) ;
-          if temp = -1 
-            then Error("Illegal argument number")
-            else if temp <= Len(curNode.assumes) 
-                   then curNode := Node[curNode.assumes[temp]]
-                   else curNode := Node[curNode.prove]
-                 end if
-          end if
-
-    elsif curNode.kind = OpArgKind then
-      (*********************************************************************)
-      (* The only kind of OpArgNode that has a subpart is a Lambda         *)
-      (* expression.                                                       *)
-      (*********************************************************************)
-      opNode := Node[curNode.op] ;
-      if \/ opNode.kind # UserDefinedOpKind
-         \/ opNode.name # "LAMBDA"   then 
-        Error("Selecting from operator argument that has no sub-part")
-      elsif ops[idx] \notin {"null", "@"} then
-        Error("Incorrect selection from LAMBDA")
-      elsif /\ ops[idx] = "null"
-            /\ Len(args[idx]) # Len(opNode.params)  then
-        Error("Incorrect number of arguments for LAMBDA")        
-      else params := params \o opNode.params ;
-           allArgs := allArgs \o args[idx] ;
-           curNode := Node[opNode.body] ;
-      end if
-
-    elsif curNode.kind \in {UserDefinedOpKind, BuiltInKind} then
-      Error("Abort: should not have been able to choose this node.")
-
-    elsif curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, StringKind,
-                         FormalParamKind, ConstantDeclKind, VariableDeclKind,
-                         BoundSymbolKind} then
-         Error("Selected part has no subexpression")
-
-    elsif curNode.kind = LabelKind then
-      (*********************************************************************)
-      (* Skip over label.                                                  *)
-      (*********************************************************************)
-      curNode := Node[curNode.body] ;
-      idx := idx - 1  ;
-    else 
-       Error("Unexpected node kind found in expression")
-
-    end if; \* ops[idx] = ":"
-
-    if idx # Len(ops)
-      then if IsName(ops[idx+1])
-             then while curNode.kind = LabelKind do
-                    curNode := Node[curNode.body]
-                  end while ;                    
-                  if curNode.kind = LetInKind
-                    then curContext := curNode.context ;
-                         mode := "FindingOpName" ;
-                         firstFindingOpName := FALSE;
-                    else Error("A name not following the selector of a LET")
-                  end if
-             else if ops[idx+1] = ":"
-                    then Error("!: should not follow an operand selector")
-                  end if ;
-           end if ;
-    end if ; \* ops[idx] = ":"
-    prevMode := "FindingSubExpr";
-
-  else Error("Bad value of mode")
-
-  end if ; \* mode = ...
-
-  idx := idx + 1;  
-  end while ;
-
-  if curNode.kind = AssumeProveKind 
-    then Error("Selecting ASSUME/PROVE instead of expression")
-  end if;
-
-  if expectedArity < 0
-    then if \/ prevMode # "FindingOpName" 
-            \/ curNode.kind \notin {UserDefinedOpKind, ThmOrAssumpDefKind}
-           then Error("Should have selected a definition, but didn't.")
-         end if;
-         result := curNode ;
-         goto Finished 
-  end if;
-
-  if expectedArity > 0 
-     then temp := Len(params) + IF \/ prevMode = "FindingOpName" 
-                                   \/ curNode.kind = OpArgKind 
-                                  THEN Arity(curNode)
-                                  ELSE 0 ;
-          if expectedArity # temp
-            then Error("Expect arity = " \o ToString(expectedArity)
-                           \o ", but found arity = " \o ToString(temp))
-          end if
-  end if;
-
-
-  (*************************************************************************)
-  (* If found an operator def and there are parameters or substitutions,   *)
-  (* then set curNode to the operator applied to new parameters, add those *)
-  (* parameters to params and add the arguments to allArgs.  Note: need to *)
-  (* do this even if operator takes no parameters because we need to put   *)
-  (* the operator in a LAMBDA expression whose body is an expression       *)
-  (*************************************************************************)
-  if /\ prevMode = "FindingOpName"
-     /\ Len(params) + Len(substInPrefix) > 0
-    then temp := [i \in 1..Len(curNode.params) |->
-                             [curNode.params[i] EXCEPT !.name = "New " \o @]] ;
-           (****************************************************************)
-           (* temp := new formal parameters for the operator, with the     *)
-           (*         same arities as the original parameters.             *)
-           (****************************************************************)
-
-         curNode := [kind     |-> OpApplKind,
-                     operands |-> temp,
-                     operator |-> curNode,
-                     unboundedBoundSymbols |-> <<>>,
-                     boundedBoundSymbols |-> << >>,
-                     ranges |-> << >>] ;
-         params := params \o temp ;
-         allArgs := allArgs \o opDefArgs ;
-  end if ;
-
-  if curNode.kind = OpArgKind
-    then if expectedArity = 0 then 
-           Error("Selected Operator Argument when expression expected.")
-         elsif expectedArity # Len(params) + curNode.arity then
-           Error("Selected operator has wrong arity.")
-         else 
-           if Len(params) + Len(substInPrefix) > 0
-              then (********************************************************)
-                   (* If curNode is a LAMBDA, then this will eventually    *)
-                   (* produce an OpArg node whose operator is a LAMBDA     *)
-                   (* whose body is an OpApplNode that applies the         *)
-                   (* curNode's LAMBDA to parameters of the outer LAMBDA.  *)
-                   (* This result can be simplified to a LAMBDA whose body *)
-                   (* is the body of curNode.  However, that               *)
-                   (* simplification is what one will get by selecting the *)
-                   (* body of the LAMBDA, so we keep this complicated      *)
-                   (* expression in this case.                             *)
-                   (********************************************************)
-                   temp := [i \in 1..curNode.arity |-> 
-                             [name |-> "NewParam" \o NumToString(i), 
-                              arity |-> 0]] ;
-                   curNode := [kind     |-> OpApplKind,
-                               operands |-> temp,
-                               operator |-> Node[curNode.op],
-                               unboundedBoundSymbols |-> <<>>,
-                               boundedBoundSymbols |-> << >>,
-                               ranges |-> << >>] ;
-                   params := params \o temp
-           end if
-         end if
-  end if ;
-
-  if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind, VariableDeclKind, 
-                       FormalParamKind, BuiltInKind, BoundSymbolKind,
-                       ThmOrAssumpDefKind}   then 
-    (***********************************************************************)
-    (* There are no params or substitutions, so this is an easy case.      *)
-    (***********************************************************************)
-    if expectedArity > 0 then 
-      result := [kind  |-> OpArgKind,
-                 name  |-> curNode.name,
-                 op    |-> curNode,
-                 arity |-> expectedArity]
-    elsif expectedArity = 0 then
-      result := [kind |-> OpApplKind,
-                 operands |-> opDefArgs,
-                 operator |-> curNode,
-                 unboundedBoundSymbols |-> <<>>,
-                 boundedBoundSymbols |-> << >>,
-                 ranges |-> << >>,
-                 subExprOf |-> subExprOf]
-    end if
-  elsif curNode.kind = OpArgKind then
-    (***********************************************************************)
-    (* There are no params or substitutions, so this is an easy case.      *)
-    (***********************************************************************)
-    result := curNode ;
-  else (******************************************************************)
-       (* curNode should be an expression node.                          *)
-       (******************************************************************)
-       temp := Len(substInPrefix) ;
-       while temp > 0 do
-         curNode := [kind  |-> substInPrefix[temp].kind, 
-                               (********************************************)
-                               (* Changed on 13 Nov 2009 from SubstInKind. *)
-                               (********************************************)
-                     body  |-> curNode ,
-                     subst |-> substInPrefix[temp].subst ] ;
-         temp := temp - 1;
-       end while;
-
-       if expectedArity > 0
-         then if Len(params) # expectedArity
-                then Error ("Selection has wrong arity")
-              end if ;
-              result := [kind |-> OpArgKind,
-                         op   |-> [kind    |-> UserDefinedOpKind,
-                                   name    |-> "LAMBDA",
-                                   body    |-> curNode,
-                                   params  |-> params,
-                                   arity   |-> Len(params),
-                                   defined |-> TRUE,
-                                   source  |-> null],
-                         name |-> "LAMBDA"]
-         else if Len(params) # Len(allArgs)
-                then Error("Abort: number of params # num of args")
-              end if ;
-              if Len(params) = 0
-                then (******************************************************)
-                     (* This is the one case with expectedArity = 0 in     *)
-                     (* which the result is not a newly-constructed node.  *)
-                     (* In this case, we construct a dummy label node so   *)
-                     (* we have a node to which the implementation can     *)
-                     (* attach the syntax node.                            *)
-                     (******************************************************)
-                     result := [kind  |-> LabelKind,
-                                name  |-> "$Subexpression",                  
-                                arity  |-> 0,
-                                params |-> << >>,
-                                body   |-> curNode,
-                                subExprOf |-> subExprOf]
-
-                else result := [kind     |-> OpApplKind,
-                                operator |-> [kind    |-> UserDefinedOpKind,
-                                              name    |-> "LAMBDA",
-                                              body    |-> curNode,
-                                              params  |-> params,
-                                              arity   |-> Len(params),
-                                              defined |-> TRUE,
-                                              source  |-> null],
-                                operands |-> allArgs,
-                                unboundedBoundSymbols |-> <<>>,
-                                boundedBoundSymbols |-> << >>,
-                                ranges |-> << >>,
-                                subExprOf |-> subExprOf]
-              end if
-         end if
-
-  end if;
-
-Finished:
-  print "Result: " ;
-  print result ;
- 
-end algorithm
-*********************************************************************)
-\* BEGIN TRANSLATION
-CONSTANT defaultInitValue
-VARIABLES ops, args, expectedArity, substInPrefix, params, allArgs, curNode, 
-          subExprOf, result, idx, mode, prevMode, curContext, curName, 
-          opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, 
-          newNode, nodeArity, temp, tempArgs, pc
-
-vars == << ops, args, expectedArity, substInPrefix, params, allArgs, curNode, 
-           subExprOf, result, idx, mode, prevMode, curContext, curName, 
-           opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, 
-           newNode, nodeArity, temp, tempArgs, pc >>
-
-Init == (* Global variables *)
-        /\ ops = MCops
-        /\ args = MCargs
-        /\ expectedArity = MCexpectedArity
-        /\ substInPrefix = << >>
-        /\ params = << >>
-        /\ allArgs = << >>
-        /\ curNode = null
-        /\ subExprOf = "null"
-        /\ result = null
-        /\ idx = 1
-        /\ mode = "FindingOpName"
-        /\ prevMode = ""
-        /\ curContext = GlobalContext
-        /\ curName = ""
-        /\ opDefArityFound = 0
-        /\ opDefArgs = << >>
-        /\ firstFindingOpName = TRUE
-        /\ opNode = defaultInitValue
-        /\ newName = defaultInitValue
-        /\ newNode = defaultInitValue
-        /\ nodeArity = defaultInitValue
-        /\ temp = defaultInitValue
-        /\ tempArgs = defaultInitValue
-        /\ pc = "Lbl_1"
-
-Lbl_1 == /\ pc = "Lbl_1"
-         /\ Assert(/\ expectedArity \in Int
-                   /\ ops  \in Seq(STRING)
-                   /\ args \in Seq(Seq(NodeId))
-                   /\ Len(ops) = Len(args)
-                   /\ \A i \in 1..Len(ops) :
-                        /\ \/ ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp
-                           \/ expectedArity # 0
-                           => args[i] = << >>
-                        /\ (ops[i] = "null") => (Len(args[i]) > 0), 
-                   "Failure of assertion at line line 1906, column 3.")
-         /\ pc' = "Lbl_2"
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, idx, mode, 
-                         prevMode, curContext, curName, opDefArityFound, 
-                         opDefArgs, firstFindingOpName, opNode, newName, 
-                         newNode, nodeArity, temp, tempArgs >>
-
-Lbl_2 == /\ pc = "Lbl_2"
-         /\ IF idx <= Len(ops)
-               THEN /\ IF mode = "FindingOpName"
-                          THEN /\ newNode' = null
-                               /\ tempArgs' = << >>
-                               /\ pc' = "Lbl_3"
-                               /\ UNCHANGED << params, allArgs, curNode, idx, 
-                                               opNode, temp >>
-                          ELSE /\ IF mode = "FollowingLabels"
-                                     THEN /\ IF prevMode = "FindingOpName"
-                                                THEN /\ Assert(curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}, 
-                                                               "Failure of assertion at line line 2053, column 13.")
-                                                     /\ newNode' = LookUp(ops[idx], curNode.labels)
-                                                ELSE /\ Assert(curNode.kind = LabelKind, 
-                                                               "Failure of assertion at line line 2055, column 13.")
-                                                     /\ newNode' = LookUp(ops[idx], curNode.labels)
-                                          /\ IF newNode' =  null
-                                                THEN /\ PrintT("Label not found")
-                                                     /\ IF debug
-                                                           THEN /\ idx' = idx
-                                                                /\ pc' = "Lbl_22"
-                                                           ELSE /\ pc' = "Done"
-                                                                /\ UNCHANGED idx
-                                                ELSE /\ pc' = "Lbl_23"
-                                                     /\ UNCHANGED idx
-                                          /\ UNCHANGED << params, allArgs, 
-                                                          curNode, opNode, 
-                                                          temp >>
-                                     ELSE /\ IF mode = "FindingSubExpr"
-                                                THEN /\ IF ops[idx] = ":"
-                                                           THEN /\ IF \/ prevMode \notin {"FindingOpName", "FollowingLabels"}
-                                                                      \/ ~ \/ /\ idx = Len(ops)
-                                                                              /\ prevMode = "FindingOpName"
-                                                                           \/ /\ idx < Len(ops)
-                                                                              /\ IsName(ops[idx+1])
-                                                                      THEN /\ PrintT("`!:' can be used only after a name and either at the " \o
-                                                                                     "end after an operator name or before an operator name.")
-                                                                           /\ IF debug
-                                                                                 THEN /\ idx' = idx
-                                                                                      /\ pc' = "Lbl_29"
-                                                                                 ELSE /\ pc' = "Done"
-                                                                                      /\ UNCHANGED idx
-                                                                      ELSE /\ pc' = "Lbl_63"
-                                                                           /\ UNCHANGED idx
-                                                                /\ UNCHANGED << params, 
-                                                                                allArgs, 
-                                                                                curNode, 
-                                                                                opNode, 
-                                                                                temp >>
-                                                           ELSE /\ IF curNode.kind = LetInKind
-                                                                      THEN /\ IF ArgNum(ops[idx], 1) = 1
-                                                                                 THEN /\ curNode' = Node[curNode.body]
-                                                                                      /\ pc' = "Lbl_63"
-                                                                                      /\ UNCHANGED idx
-                                                                                 ELSE /\ PrintT("A Let/In has only a single operand")
-                                                                                      /\ IF debug
-                                                                                            THEN /\ idx' = idx
-                                                                                                 /\ pc' = "Lbl_30"
-                                                                                            ELSE /\ pc' = "Done"
-                                                                                                 /\ UNCHANGED idx
-                                                                                      /\ UNCHANGED curNode
-                                                                           /\ UNCHANGED << params, 
-                                                                                           allArgs, 
-                                                                                           opNode, 
-                                                                                           temp >>
-                                                                      ELSE /\ IF curNode.kind = OpApplKind
-                                                                                 THEN /\ opNode' = Node[curNode.operator]
-                                                                                      /\ IF opNode'.kind
-                                                                                                 \in {FormalParamKind, ConstantDeclKind, UserDefinedOpKind}
-                                                                                            THEN /\ temp' = ArgNum(ops[idx], opNode'.arity)
-                                                                                                 /\ IF temp' = -1
-                                                                                                       THEN /\ PrintT("Nonexistent operand specified")
-                                                                                                            /\ IF debug
-                                                                                                                  THEN /\ idx' = idx
-                                                                                                                       /\ pc' = "Lbl_31"
-                                                                                                                  ELSE /\ pc' = "Done"
-                                                                                                                       /\ UNCHANGED idx
-                                                                                                            /\ UNCHANGED curNode
-                                                                                                       ELSE /\ curNode' = Node[curNode.operands[temp']]
-                                                                                                            /\ pc' = "Lbl_63"
-                                                                                                            /\ UNCHANGED idx
-                                                                                                 /\ UNCHANGED << params, 
-                                                                                                                 allArgs >>
-                                                                                            ELSE /\ IF opNode'.kind = BuiltInKind
-                                                                                                       THEN /\ IF opNode'.name \in {"$RcdConstructor", "$SetOfRcds"}
-                                                                                                                  THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands))
-                                                                                                                       /\ IF temp' = -1
-                                                                                                                             THEN /\ PrintT("Incorrect subexpression number")
-                                                                                                                                  /\ IF debug
-                                                                                                                                        THEN /\ idx' = idx
-                                                                                                                                             /\ pc' = "Lbl_32"
-                                                                                                                                        ELSE /\ pc' = "Done"
-                                                                                                                                             /\ UNCHANGED idx
-                                                                                                                             ELSE /\ pc' = "Lbl_33"
-                                                                                                                                  /\ UNCHANGED idx
-                                                                                                                       /\ UNCHANGED << params, 
-                                                                                                                                       allArgs, 
-                                                                                                                                       curNode >>
-                                                                                                                  ELSE /\ IF opNode'.name \in {"$Case"}
-                                                                                                                             THEN /\ IF idx = Len(ops)
-                                                                                                                                        THEN /\ PrintT("CASE subexpression must be of form !i!j")
-                                                                                                                                             /\ IF debug
-                                                                                                                                                   THEN /\ idx' = idx
-                                                                                                                                                        /\ pc' = "Lbl_36"
-                                                                                                                                                   ELSE /\ pc' = "Done"
-                                                                                                                                                        /\ UNCHANGED idx
-                                                                                                                                        ELSE /\ pc' = "Lbl_37"
-                                                                                                                                             /\ UNCHANGED idx
-                                                                                                                                  /\ UNCHANGED << params, 
-                                                                                                                                                  allArgs, 
-                                                                                                                                                  curNode, 
-                                                                                                                                                  temp >>
-                                                                                                                             ELSE /\ IF opNode'.name = "$Except"
-                                                                                                                                        THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands))
-                                                                                                                                             /\ IF temp' = -1
-                                                                                                                                                   THEN /\ PrintT("Bad argument selector.")
-                                                                                                                                                        /\ IF debug
-                                                                                                                                                              THEN /\ idx' = idx
-                                                                                                                                                                   /\ pc' = "Lbl_46"
-                                                                                                                                                              ELSE /\ pc' = "Done"
-                                                                                                                                                                   /\ UNCHANGED idx
-                                                                                                                                                   ELSE /\ pc' = "Lbl_47"
-                                                                                                                                                        /\ UNCHANGED idx
-                                                                                                                                             /\ UNCHANGED << params, 
-                                                                                                                                                             allArgs, 
-                                                                                                                                                             curNode >>
-                                                                                                                                        ELSE /\ IF /\ Len(curNode.unboundedBoundSymbols) = 0
-                                                                                                                                                   /\ Len(curNode.boundedBoundSymbols)   = 0
-                                                                                                                                                   THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands))
-                                                                                                                                                        /\ IF temp' = -1
-                                                                                                                                                              THEN /\ PrintT("Incorrect subexpression selector")
-                                                                                                                                                                   /\ IF debug
-                                                                                                                                                                         THEN /\ idx' = idx
-                                                                                                                                                                              /\ pc' = "Lbl_50"
-                                                                                                                                                                         ELSE /\ pc' = "Done"
-                                                                                                                                                                              /\ UNCHANGED idx
-                                                                                                                                                              ELSE /\ pc' = "Lbl_51"
-                                                                                                                                                                   /\ UNCHANGED idx
-                                                                                                                                                        /\ UNCHANGED << params, 
-                                                                                                                                                                        allArgs, 
-                                                                                                                                                                        curNode >>
-                                                                                                                                                   ELSE /\ IF ops[idx] \in {"null", "@"}
-                                                                                                                                                              THEN /\ temp' = (IF Len(curNode.unboundedBoundSymbols) > 0
-                                                                                                                                                                                 THEN curNode.unboundedBoundSymbols
-                                                                                                                                                                                 ELSE SeqSeqToSeq(
-                                                                                                                                                                                         curNode.boundedBoundSymbols))
-                                                                                                                                                                   /\ params' = params \o
-                                                                                                                                                                                 [i \in 1.. Len(temp') |->
-                                                                                                                                                                                   [name |-> temp'[i], arity |-> 0]]
-                                                                                                                                                                   /\ allArgs' = allArgs \o args[idx]
-                                                                                                                                                                   /\ IF /\ ops[idx] = "null"
-                                                                                                                                                                         /\ Len(args[idx]) # Len(temp')
-                                                                                                                                                                         THEN /\ PrintT("Wrong number of selector arguments")
-                                                                                                                                                                              /\ IF debug
-                                                                                                                                                                                    THEN /\ idx' = idx
-                                                                                                                                                                                         /\ pc' = "Lbl_52"
-                                                                                                                                                                                    ELSE /\ pc' = "Done"
-                                                                                                                                                                                         /\ UNCHANGED idx
-                                                                                                                                                                         ELSE /\ pc' = "Lbl_53"
-                                                                                                                                                                              /\ UNCHANGED idx
-                                                                                                                                                                   /\ UNCHANGED curNode
-                                                                                                                                                              ELSE /\ temp' = ArgNum(ops[idx], Len(curNode.ranges))
-                                                                                                                                                                   /\ IF temp' = -1
-                                                                                                                                                                         THEN /\ PrintT("Selecting non-existent range")
-                                                                                                                                                                              /\ IF debug
-                                                                                                                                                                                    THEN /\ idx' = idx
-                                                                                                                                                                                         /\ pc' = "Lbl_54"
-                                                                                                                                                                                    ELSE /\ pc' = "Done"
-                                                                                                                                                                                         /\ UNCHANGED idx
-                                                                                                                                                                              /\ UNCHANGED curNode
-                                                                                                                                                                         ELSE /\ curNode' = Node[curNode.ranges[temp']]
-                                                                                                                                                                              /\ pc' = "Lbl_63"
-                                                                                                                                                                              /\ UNCHANGED idx
-                                                                                                                                                                   /\ UNCHANGED << params, 
-                                                                                                                                                                                   allArgs >>
-                                                                                                       ELSE /\ PrintT("Applying subexpression chooser to an expr" \o
-                                                                                                                       " with no choosable subexpressions")
-                                                                                                            /\ IF debug
-                                                                                                                  THEN /\ idx' = idx
-                                                                                                                       /\ pc' = "Lbl_55"
-                                                                                                                  ELSE /\ pc' = "Done"
-                                                                                                                       /\ UNCHANGED idx
-                                                                                                            /\ UNCHANGED << params, 
-                                                                                                                            allArgs, 
-                                                                                                                            curNode, 
-                                                                                                                            temp >>
-                                                                                 ELSE /\ IF curNode.kind = AssumeProveKind
-                                                                                            THEN /\ temp' = ArgNum(ops[idx], 1 + Len(curNode.assumes))
-                                                                                                 /\ IF temp' = -1
-                                                                                                       THEN /\ PrintT("Illegal argument number")
-                                                                                                            /\ IF debug
-                                                                                                                  THEN /\ idx' = idx
-                                                                                                                       /\ pc' = "Lbl_56"
-                                                                                                                  ELSE /\ pc' = "Done"
-                                                                                                                       /\ UNCHANGED idx
-                                                                                                            /\ UNCHANGED curNode
-                                                                                                       ELSE /\ IF temp' <= Len(curNode.assumes)
-                                                                                                                  THEN /\ curNode' = Node[curNode.assumes[temp']]
-                                                                                                                  ELSE /\ curNode' = Node[curNode.prove]
-                                                                                                            /\ pc' = "Lbl_63"
-                                                                                                            /\ UNCHANGED idx
-                                                                                                 /\ UNCHANGED << params, 
-                                                                                                                 allArgs, 
-                                                                                                                 opNode >>
-                                                                                            ELSE /\ IF curNode.kind = OpArgKind
-                                                                                                       THEN /\ opNode' = Node[curNode.op]
-                                                                                                            /\ IF \/ opNode'.kind # UserDefinedOpKind
-                                                                                                                  \/ opNode'.name # "LAMBDA"
-                                                                                                                  THEN /\ PrintT("Selecting from operator argument that has no sub-part")
-                                                                                                                       /\ IF debug
-                                                                                                                             THEN /\ idx' = idx
-                                                                                                                                  /\ pc' = "Lbl_57"
-                                                                                                                             ELSE /\ pc' = "Done"
-                                                                                                                                  /\ UNCHANGED idx
-                                                                                                                       /\ UNCHANGED << params, 
-                                                                                                                                       allArgs, 
-                                                                                                                                       curNode >>
-                                                                                                                  ELSE /\ IF ops[idx] \notin {"null", "@"}
-                                                                                                                             THEN /\ PrintT("Incorrect selection from LAMBDA")
-                                                                                                                                  /\ IF debug
-                                                                                                                                        THEN /\ idx' = idx
-                                                                                                                                             /\ pc' = "Lbl_58"
-                                                                                                                                        ELSE /\ pc' = "Done"
-                                                                                                                                             /\ UNCHANGED idx
-                                                                                                                                  /\ UNCHANGED << params, 
-                                                                                                                                                  allArgs, 
-                                                                                                                                                  curNode >>
-                                                                                                                             ELSE /\ IF /\ ops[idx] = "null"
-                                                                                                                                        /\ Len(args[idx]) # Len(opNode'.params)
-                                                                                                                                        THEN /\ PrintT("Incorrect number of arguments for LAMBDA")
-                                                                                                                                             /\ IF debug
-                                                                                                                                                   THEN /\ idx' = idx
-                                                                                                                                                        /\ pc' = "Lbl_59"
-                                                                                                                                                   ELSE /\ pc' = "Done"
-                                                                                                                                                        /\ UNCHANGED idx
-                                                                                                                                             /\ UNCHANGED << params, 
-                                                                                                                                                             allArgs, 
-                                                                                                                                                             curNode >>
-                                                                                                                                        ELSE /\ params' = params \o opNode'.params
-                                                                                                                                             /\ allArgs' = allArgs \o args[idx]
-                                                                                                                                             /\ curNode' = Node[opNode'.body]
-                                                                                                                                             /\ pc' = "Lbl_63"
-                                                                                                                                             /\ UNCHANGED idx
-                                                                                                       ELSE /\ IF curNode.kind \in {UserDefinedOpKind, BuiltInKind}
-                                                                                                                  THEN /\ PrintT("Abort: should not have been able to choose this node.")
-                                                                                                                       /\ IF debug
-                                                                                                                             THEN /\ idx' = idx
-                                                                                                                                  /\ pc' = "Lbl_60"
-                                                                                                                             ELSE /\ pc' = "Done"
-                                                                                                                                  /\ UNCHANGED idx
-                                                                                                                       /\ UNCHANGED curNode
-                                                                                                                  ELSE /\ IF curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, StringKind,
-                                                                                                                                            FormalParamKind, ConstantDeclKind, VariableDeclKind,
-                                                                                                                                            BoundSymbolKind}
-                                                                                                                             THEN /\ PrintT("Selected part has no subexpression")
-                                                                                                                                  /\ IF debug
-                                                                                                                                        THEN /\ idx' = idx
-                                                                                                                                             /\ pc' = "Lbl_61"
-                                                                                                                                        ELSE /\ pc' = "Done"
-                                                                                                                                             /\ UNCHANGED idx
-                                                                                                                                  /\ UNCHANGED curNode
-                                                                                                                             ELSE /\ IF curNode.kind = LabelKind
-                                                                                                                                        THEN /\ curNode' = Node[curNode.body]
-                                                                                                                                             /\ idx' = idx - 1
-                                                                                                                                             /\ pc' = "Lbl_63"
-                                                                                                                                        ELSE /\ PrintT("Unexpected node kind found in expression")
-                                                                                                                                             /\ IF debug
-                                                                                                                                                   THEN /\ idx' = idx
-                                                                                                                                                        /\ pc' = "Lbl_62"
-                                                                                                                                                   ELSE /\ pc' = "Done"
-                                                                                                                                                        /\ UNCHANGED idx
-                                                                                                                                             /\ UNCHANGED curNode
-                                                                                                            /\ UNCHANGED << params, 
-                                                                                                                            allArgs, 
-                                                                                                                            opNode >>
-                                                                                                 /\ UNCHANGED temp
-                                                ELSE /\ PrintT("Bad value of mode")
-                                                     /\ IF debug
-                                                           THEN /\ idx' = idx
-                                                                /\ pc' = "Lbl_68"
-                                                           ELSE /\ pc' = "Done"
-                                                                /\ UNCHANGED idx
-                                                     /\ UNCHANGED << params, 
-                                                                     allArgs, 
-                                                                     curNode, 
-                                                                     opNode, 
-                                                                     temp >>
-                                          /\ UNCHANGED newNode
-                               /\ UNCHANGED tempArgs
-               ELSE /\ IF curNode.kind = AssumeProveKind
-                          THEN /\ PrintT("Selecting ASSUME/PROVE instead of expression")
-                               /\ IF debug
-                                     THEN /\ idx' = idx
-                                          /\ pc' = "Lbl_70"
-                                     ELSE /\ pc' = "Done"
-                                          /\ UNCHANGED idx
-                          ELSE /\ pc' = "Lbl_71"
-                               /\ UNCHANGED idx
-                    /\ UNCHANGED << params, allArgs, curNode, opNode, newNode, 
-                                    temp, tempArgs >>
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, 
-                         result, mode, prevMode, curContext, curName, 
-                         opDefArityFound, opDefArgs, firstFindingOpName, 
-                         newName, nodeArity >>
-
-Lbl_69 == /\ pc = "Lbl_69"
-          /\ idx' = idx + 1
-          /\ pc' = "Lbl_2"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_3 == /\ pc = "Lbl_3"
-         /\ IF newNode = null
-               THEN /\ IF idx \geq Len(ops)
-                          THEN /\ PrintT("Unknown operator")
-                               /\ IF debug
-                                     THEN /\ idx' = idx
-                                          /\ pc' = "Lbl_4"
-                                     ELSE /\ pc' = "Done"
-                                          /\ UNCHANGED idx
-                          ELSE /\ pc' = "Lbl_5"
-                               /\ UNCHANGED idx
-                    /\ UNCHANGED << curNode, curName >>
-               ELSE /\ curNode' = newNode
-                    /\ curName' = newName
-                    /\ IF curNode'.kind \in
-                              {UserDefinedOpKind, ThmOrAssumpDefKind,
-                                 ModuleInstanceKind, ConstantDeclKind, VariableDeclKind,
-                                 FormalParamKind, BuiltInKind, BoundSymbolKind}
-                          THEN /\ IF curNode'.kind \in
-                                          {ConstantDeclKind, VariableDeclKind, FormalParamKind,
-                                           BuiltInKind, BoundSymbolKind}
-                                     THEN /\ IF idx # 1
-                                                THEN /\ PrintT("Abort: Impossible naming of declaration "
-                                                                \o "or built-in operator.")
-                                                     /\ IF debug
-                                                           THEN /\ idx' = idx
-                                                                /\ pc' = "Lbl_8"
-                                                           ELSE /\ pc' = "Done"
-                                                                /\ UNCHANGED idx
-                                                ELSE /\ IF Len(ops) # 1
-                                                           THEN /\ PrintT("Can't take subexpression of this")
-                                                                /\ IF debug
-                                                                      THEN /\ idx' = idx
-                                                                           /\ pc' = "Lbl_9"
-                                                                      ELSE /\ pc' = "Done"
-                                                                           /\ UNCHANGED idx
-                                                           ELSE /\ pc' = "Lbl_10"
-                                                                /\ UNCHANGED idx
-                                     ELSE /\ pc' = "Lbl_10"
-                                          /\ UNCHANGED idx
-                          ELSE /\ PrintT("Unexpected node kind")
-                               /\ IF debug
-                                     THEN /\ idx' = idx
-                                          /\ pc' = "Lbl_20"
-                                     ELSE /\ pc' = "Done"
-                                          /\ UNCHANGED idx
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, subExprOf, result, mode, prevMode, 
-                         curContext, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newName, newNode, 
-                         nodeArity, temp, tempArgs >>
-
-Lbl_5 == /\ pc = "Lbl_5"
-         /\ newName' = IF IsName(ops[idx])
-                         THEN IF curName = "" THEN ops[idx]
-                                              ELSE curName \o "!" \o ops[idx]
-                         ELSE null
-         /\ IF /\ curName = ""
-               /\ ~ IsName(ops[idx])
-               THEN /\ PrintT("Need an operator or label name or step number here")
-                    /\ IF debug
-                          THEN /\ idx' = idx
-                               /\ pc' = "Lbl_6"
-                          ELSE /\ pc' = "Done"
-                               /\ UNCHANGED idx
-               ELSE /\ pc' = "Lbl_7"
-                    /\ UNCHANGED idx
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, mode, prevMode, 
-                         curContext, curName, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newNode, nodeArity, temp, 
-                         tempArgs >>
-
-Lbl_6 == /\ pc = "Lbl_6"
-         /\ idx' = idx
-         /\ Assert(FALSE, 
-                   "Failure of assertion at line line 1892, column 38 of macro called at line 1956, column 13.")
-         /\ pc' = "Lbl_7"
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, mode, prevMode, 
-                         curContext, curName, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newName, newNode, 
-                         nodeArity, temp, tempArgs >>
-
-Lbl_7 == /\ pc = "Lbl_7"
-         /\ newNode' = (IF newName # null THEN LookUp(newName, curContext) ELSE null)
-         /\ IF newName = null
-               THEN /\ tempArgs' = tempArgs \o args[idx]
-                    /\ idx' = idx + 1
-               ELSE /\ TRUE
-                    /\ UNCHANGED << idx, tempArgs >>
-         /\ pc' = "Lbl_3"
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, mode, prevMode, 
-                         curContext, curName, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newName, nodeArity, temp >>
-
-Lbl_4 == /\ pc = "Lbl_4"
-         /\ idx' = idx
-         /\ Assert(FALSE, 
-                   "Failure of assertion at line line 1892, column 38 of macro called at line 1942, column 13.")
-         /\ pc' = "Lbl_5"
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, mode, prevMode, 
-                         curContext, curName, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newName, newNode, 
-                         nodeArity, temp, tempArgs >>
-
-Lbl_10 == /\ pc = "Lbl_10"
-          /\ nodeArity' = Arity(curNode)
-          /\ tempArgs' = tempArgs \o args[idx]
-          /\ IF expectedArity = 0
-                THEN /\ IF opDefArityFound + Len(tempArgs') # nodeArity'
-                           THEN /\ PrintT("Wrong number of arguments")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_11"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_12"
-                                /\ UNCHANGED idx
-                ELSE /\ IF expectedArity > 0
-                           THEN /\ IF \E i \in 1..Len(curNode.params) :
-                                         curNode.params[i].arity > 0
-                                      THEN /\ PrintT("Higher-order operator selected "
-                                                       \o "as operator argument")
-                                           /\ IF debug
-                                                 THEN /\ idx' = idx
-                                                      /\ pc' = "Lbl_14"
-                                                 ELSE /\ pc' = "Done"
-                                                      /\ UNCHANGED idx
-                                      ELSE /\ pc' = "Lbl_15"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_15"
-                                /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, temp >>
-
-Lbl_12 == /\ pc = "Lbl_12"
-          /\ IF \E i \in 1..Len(tempArgs[idx]) :
-                      Arity(Node[tempArgs[idx][i]]) #
-                        ParamArity(curNode, i + opDefArityFound)
-                THEN /\ PrintT("Argument has wrong arity")
-                     /\ IF debug
-                           THEN /\ idx' = idx
-                                /\ pc' = "Lbl_13"
-                           ELSE /\ pc' = "Done"
-                                /\ UNCHANGED idx
-                     /\ UNCHANGED opDefArgs
-                ELSE /\ opDefArgs' = opDefArgs \o tempArgs
-                     /\ pc' = "Lbl_15"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_13 == /\ pc = "Lbl_13"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 1990, column 26.")
-          /\ pc' = "Lbl_15"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_11 == /\ pc = "Lbl_11"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 1985, column 25.")
-          /\ pc' = "Lbl_12"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_14 == /\ pc = "Lbl_14"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 1996, column 32.")
-          /\ pc' = "Lbl_15"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_15 == /\ pc = "Lbl_15"
-          /\ opDefArityFound' = nodeArity
-          /\ IF curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind,
-                                  ConstantDeclKind, VariableDeclKind,
-                                  FormalParamKind, BoundSymbolKind}
-                THEN /\ IF /\ curNode.kind = UserDefinedOpKind
-                           /\ ~ curNode.defined
-                           /\ ~ Len(ops) = 1
-                           THEN /\ PrintT("Can't refer to subexpression of "
-                                           \o "operator inside its definition")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_16"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_17"
-                                /\ UNCHANGED idx
-                ELSE /\ IF (idx = Len(ops))
-                           THEN /\ PrintT("Operator name incomplete")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_19"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_21"
-                                /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArgs, firstFindingOpName, 
-                          opNode, newName, newNode, nodeArity, temp, tempArgs >>
-
-Lbl_17 == /\ pc = "Lbl_17"
-          /\ IF /\ firstFindingOpName
-                /\ curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}
-                THEN /\ subExprOf' = curNode
-                ELSE /\ TRUE
-                     /\ UNCHANGED subExprOf
-          /\ IF idx # Len(ops)
-                THEN /\ params' = params \o curNode.params
-                     /\ curName' = ""
-                     /\ IF IsName(ops[idx+1])
-                           THEN /\ mode' = "FollowingLabels"
-                           ELSE /\ mode' = "FindingSubExpr"
-                     /\ allArgs' = allArgs \o opDefArgs
-                     /\ opDefArityFound' = 0
-                     /\ opDefArgs' = << >>
-                     /\ newNode' = Node[curNode.body]
-                     /\ pc' = "Lbl_18"
-                ELSE /\ pc' = "Lbl_21"
-                     /\ UNCHANGED << params, allArgs, mode, curName, 
-                                     opDefArityFound, opDefArgs, newNode >>
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, curNode, 
-                          result, idx, prevMode, curContext, 
-                          firstFindingOpName, opNode, newName, nodeArity, temp, 
-                          tempArgs >>
-
-Lbl_18 == /\ pc = "Lbl_18"
-          /\ IF newNode.kind = SubstInKind
-                THEN /\ substInPrefix' = substInPrefix \o <<newNode>>
-                     /\ newNode' = Node[newNode.body]
-                     /\ pc' = "Lbl_18"
-                     /\ UNCHANGED curNode
-                ELSE /\ IF mode = "FindingSubExpr"
-                           THEN /\ curNode' = newNode
-                           ELSE /\ TRUE
-                                /\ UNCHANGED curNode
-                     /\ pc' = "Lbl_21"
-                     /\ UNCHANGED << substInPrefix, newNode >>
-          /\ UNCHANGED << ops, args, expectedArity, params, allArgs, subExprOf, 
-                          result, idx, mode, prevMode, curContext, curName, 
-                          opDefArityFound, opDefArgs, firstFindingOpName, 
-                          opNode, newName, nodeArity, temp, tempArgs >>
-
-Lbl_16 == /\ pc = "Lbl_16"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2010, column 25.")
-          /\ pc' = "Lbl_17"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_19 == /\ pc = "Lbl_19"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2043, column 25.")
-          /\ pc' = "Lbl_21"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_8 == /\ pc = "Lbl_8"
-         /\ idx' = idx
-         /\ Assert(FALSE, 
-                   "Failure of assertion at line line 1892, column 38 of macro called at line 1974, column 26.")
-         /\ pc' = "Lbl_10"
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, mode, prevMode, 
-                         curContext, curName, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newName, newNode, 
-                         nodeArity, temp, tempArgs >>
-
-Lbl_9 == /\ pc = "Lbl_9"
-         /\ idx' = idx
-         /\ Assert(FALSE, 
-                   "Failure of assertion at line line 1892, column 38 of macro called at line 1977, column 33.")
-         /\ pc' = "Lbl_10"
-         /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                         allArgs, curNode, subExprOf, result, mode, prevMode, 
-                         curContext, curName, opDefArityFound, opDefArgs, 
-                         firstFindingOpName, opNode, newName, newNode, 
-                         nodeArity, temp, tempArgs >>
-
-Lbl_20 == /\ pc = "Lbl_20"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2046, column 9.")
-          /\ pc' = "Lbl_21"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_21 == /\ pc = "Lbl_21"
-          /\ prevMode' = "FindingOpName"
-          /\ pc' = "Lbl_69"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, idx, mode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_23 == /\ pc = "Lbl_23"
-          /\ curNode' = newNode
-          /\ IF expectedArity = 0
-                THEN /\ IF Len(args[idx]) # curNode'.arity
-                           THEN /\ PrintT("bad arity")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_24"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_25"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_28"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_25 == /\ pc = "Lbl_25"
-          /\ IF \E i \in 1..Len(args[idx]) :
-                    Arity(Node[args[idx][i]]) # 0
-                THEN /\ PrintT("Operator argument given to label")
-                     /\ IF debug
-                           THEN /\ idx' = idx
-                                /\ pc' = "Lbl_26"
-                           ELSE /\ pc' = "Done"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_27"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_26 == /\ pc = "Lbl_26"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2068, column 20.")
-          /\ pc' = "Lbl_27"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_27 == /\ pc = "Lbl_27"
-          /\ allArgs' = allArgs \o args[idx]
-          /\ pc' = "Lbl_28"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          curNode, subExprOf, result, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_24 == /\ pc = "Lbl_24"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2064, column 20.")
-          /\ pc' = "Lbl_25"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_28 == /\ pc = "Lbl_28"
-          /\ params' = params \o curNode.params
-          /\ IF /\ idx < Len(ops)
-                /\ ~IsName(ops[idx+1])
-                THEN /\ mode' = "FindingSubExpr"
-                ELSE /\ TRUE
-                     /\ UNCHANGED mode
-          /\ IF \/ mode' = "FindingSubExpr"
-                \/ idx = Len(ops)
-                THEN /\ curNode' = Node[curNode.body]
-                ELSE /\ TRUE
-                     /\ UNCHANGED curNode
-          /\ prevMode' = "FollowingLabels"
-          /\ pc' = "Lbl_69"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, allArgs, 
-                          subExprOf, result, idx, curContext, curName, 
-                          opDefArityFound, opDefArgs, firstFindingOpName, 
-                          opNode, newName, newNode, nodeArity, temp, tempArgs >>
-
-Lbl_22 == /\ pc = "Lbl_22"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2059, column 13.")
-          /\ pc' = "Lbl_23"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_63 == /\ pc = "Lbl_63"
-          /\ IF idx # Len(ops)
-                THEN /\ IF IsName(ops[idx+1])
-                           THEN /\ pc' = "Lbl_64"
-                                /\ UNCHANGED idx
-                           ELSE /\ IF ops[idx+1] = ":"
-                                      THEN /\ PrintT("!: should not follow an operand selector")
-                                           /\ IF debug
-                                                 THEN /\ idx' = idx
-                                                      /\ pc' = "Lbl_66"
-                                                 ELSE /\ pc' = "Done"
-                                                      /\ UNCHANGED idx
-                                      ELSE /\ pc' = "Lbl_67"
-                                           /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_67"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_64 == /\ pc = "Lbl_64"
-          /\ IF curNode.kind = LabelKind
-                THEN /\ curNode' = Node[curNode.body]
-                     /\ pc' = "Lbl_64"
-                     /\ UNCHANGED << idx, mode, curContext, firstFindingOpName >>
-                ELSE /\ IF curNode.kind = LetInKind
-                           THEN /\ curContext' = curNode.context
-                                /\ mode' = "FindingOpName"
-                                /\ firstFindingOpName' = FALSE
-                                /\ pc' = "Lbl_67"
-                                /\ UNCHANGED idx
-                           ELSE /\ PrintT("A name not following the selector of a LET")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_65"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                                /\ UNCHANGED << mode, curContext, 
-                                                firstFindingOpName >>
-                     /\ UNCHANGED curNode
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, prevMode, curName, 
-                          opDefArityFound, opDefArgs, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_65 == /\ pc = "Lbl_65"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2294, column 26.")
-          /\ pc' = "Lbl_67"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_66 == /\ pc = "Lbl_66"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2297, column 26.")
-          /\ pc' = "Lbl_67"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_67 == /\ pc = "Lbl_67"
-          /\ prevMode' = "FindingSubExpr"
-          /\ pc' = "Lbl_69"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, idx, mode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_29 == /\ pc = "Lbl_29"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2091, column 14.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_30 == /\ pc = "Lbl_30"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2098, column 14.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_31 == /\ pc = "Lbl_31"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2110, column 17.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_33 == /\ pc = "Lbl_33"
-          /\ curNode' = Node[curNode.operands[temp]]
-          /\ IF \/ curNode'.kind # OpApplKind
-                \/ Node[curNode'.operator].kind # BuiltInKind
-                \/ Node[curNode'.operator].name # "$Pair"
-                THEN /\ PrintT("Expecting $Pair(...)")
-                     /\ IF debug
-                           THEN /\ idx' = idx
-                                /\ pc' = "Lbl_34"
-                           ELSE /\ pc' = "Done"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_35"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_35 == /\ pc = "Lbl_35"
-          /\ curNode' = Node[curNode.operands[2]]
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_34 == /\ pc = "Lbl_34"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2131, column 18.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_32 == /\ pc = "Lbl_32"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2125, column 18.")
-          /\ pc' = "Lbl_33"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_37 == /\ pc = "Lbl_37"
-          /\ temp' = ArgNum( ops[idx], Len(curNode.operands))
-          /\ IF temp' = -1
-                THEN /\ PrintT("Incorrect subexpression name")
-                     /\ IF debug
-                           THEN /\ idx' = idx
-                                /\ pc' = "Lbl_38"
-                           ELSE /\ pc' = "Done"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_39"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, tempArgs >>
-
-Lbl_38 == /\ pc = "Lbl_38"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2145, column 18.")
-          /\ pc' = "Lbl_39"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_39 == /\ pc = "Lbl_39"
-          /\ curNode' = Node[curNode.operands[temp]]
-          /\ IF \/ curNode'.kind # OpApplKind
-                \/ Node[curNode'.operator].kind # BuiltInKind
-                \/ Node[curNode'.operator].name # "$Pair"
-                THEN /\ PrintT("Expecting $Pair(...)")
-                     /\ IF debug
-                           THEN /\ idx' = idx
-                                /\ pc' = "Lbl_40"
-                           ELSE /\ pc' = "Done"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_41"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_40 == /\ pc = "Lbl_40"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2151, column 18.")
-          /\ pc' = "Lbl_41"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_41 == /\ pc = "Lbl_41"
-          /\ idx' = idx+1
-          /\ temp' = ArgNum(ops[idx'], 2)
-          /\ IF temp' = -1
-                THEN /\ PrintT("Incorrect subexpression name")
-                     /\ IF debug
-                           THEN /\ pc' = "Lbl_42"
-                           ELSE /\ pc' = "Done"
-                ELSE /\ pc' = "Lbl_44"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, tempArgs >>
-
-Lbl_42 == /\ pc = "Lbl_42"
-          /\ idx' = idx
-          /\ pc' = "Lbl_43"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_43 == /\ pc = "Lbl_43"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2156, column 18.")
-          /\ pc' = "Lbl_44"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_44 == /\ pc = "Lbl_44"
-          /\ curNode' = Node[curNode.operands[temp]]
-          /\ IF curNode' = null
-                THEN /\ PrintT("Selecting OTHER")
-                     /\ IF debug
-                           THEN /\ idx' = idx
-                                /\ pc' = "Lbl_45"
-                           ELSE /\ pc' = "Done"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_63"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_45 == /\ pc = "Lbl_45"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2160, column 18.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_36 == /\ pc = "Lbl_36"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2141, column 18.")
-          /\ pc' = "Lbl_37"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_47 == /\ pc = "Lbl_47"
-          /\ curNode' = Node[curNode.operands[temp]]
-          /\ IF temp > 1
-                THEN /\ IF \/ curNode'.kind # OpApplKind
-                           \/ Node[curNode'.operator].kind # BuiltInKind
-                           \/ Node[curNode'.operator].name # "$Pair"
-                           THEN /\ PrintT("Unexpected expression node found.")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_48"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_49"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_63"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_49 == /\ pc = "Lbl_49"
-          /\ curNode' = Node[curNode.operands[2]]
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_48 == /\ pc = "Lbl_48"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2179, column 26.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_46 == /\ pc = "Lbl_46"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2172, column 19.")
-          /\ pc' = "Lbl_47"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_51 == /\ pc = "Lbl_51"
-          /\ curNode' = Node[curNode.operands[temp]]
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_50 == /\ pc = "Lbl_50"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2195, column 25.")
-          /\ pc' = "Lbl_51"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_53 == /\ pc = "Lbl_53"
-          /\ curNode' = Node[curNode.operands[1]]
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_52 == /\ pc = "Lbl_52"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2220, column 32.")
-          /\ pc' = "Lbl_53"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_54 == /\ pc = "Lbl_54"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2225, column 32.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_55 == /\ pc = "Lbl_55"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2232, column 12.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_56 == /\ pc = "Lbl_56"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2240, column 18.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_57 == /\ pc = "Lbl_57"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2255, column 9.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_58 == /\ pc = "Lbl_58"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2257, column 9.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_59 == /\ pc = "Lbl_59"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2260, column 9.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_60 == /\ pc = "Lbl_60"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2267, column 7.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_61 == /\ pc = "Lbl_61"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2272, column 10.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_62 == /\ pc = "Lbl_62"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2281, column 8.")
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_68 == /\ pc = "Lbl_68"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2303, column 8.")
-          /\ pc' = "Lbl_69"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_70 == /\ pc = "Lbl_70"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2311, column 10.")
-          /\ pc' = "Lbl_71"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_71 == /\ pc = "Lbl_71"
-          /\ IF expectedArity < 0
-                THEN /\ IF \/ prevMode # "FindingOpName"
-                           \/ curNode.kind \notin {UserDefinedOpKind, ThmOrAssumpDefKind}
-                           THEN /\ PrintT("Should have selected a definition, but didn't.")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_72"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_73"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_74"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_73 == /\ pc = "Lbl_73"
-          /\ result' = curNode
-          /\ pc' = "Finished"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_72 == /\ pc = "Lbl_72"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2317, column 17.")
-          /\ pc' = "Lbl_73"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_74 == /\ pc = "Lbl_74"
-          /\ IF expectedArity > 0
-                THEN /\ temp' = (Len(params) + IF \/ prevMode = "FindingOpName"
-                                                  \/ curNode.kind = OpArgKind
-                                                 THEN Arity(curNode)
-                                                 ELSE 0)
-                     /\ IF expectedArity # temp'
-                           THEN /\ PrintT("Expect arity = " \o ToString(expectedArity)
-                                              \o ", but found arity = " \o ToString(temp'))
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_75"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ pc' = "Lbl_76"
-                                /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_76"
-                     /\ UNCHANGED << idx, temp >>
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, tempArgs >>
-
-Lbl_75 == /\ pc = "Lbl_75"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2329, column 18.")
-          /\ pc' = "Lbl_76"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_76 == /\ pc = "Lbl_76"
-          /\ IF /\ prevMode = "FindingOpName"
-                /\ Len(params) + Len(substInPrefix) > 0
-                THEN /\ temp' = [i \in 1..Len(curNode.params) |->
-                                            [curNode.params[i] EXCEPT !.name = "New " \o @]]
-                     /\ curNode' = [kind     |-> OpApplKind,
-                                    operands |-> temp',
-                                    operator |-> curNode,
-                                    unboundedBoundSymbols |-> <<>>,
-                                    boundedBoundSymbols |-> << >>,
-                                    ranges |-> << >>]
-                     /\ params' = params \o temp'
-                     /\ allArgs' = allArgs \o opDefArgs
-                ELSE /\ TRUE
-                     /\ UNCHANGED << params, allArgs, curNode, temp >>
-          /\ IF curNode'.kind = OpArgKind
-                THEN /\ IF expectedArity = 0
-                           THEN /\ PrintT("Selected Operator Argument when expression expected.")
-                                /\ IF debug
-                                      THEN /\ idx' = idx
-                                           /\ pc' = "Lbl_77"
-                                      ELSE /\ pc' = "Done"
-                                           /\ UNCHANGED idx
-                           ELSE /\ IF expectedArity # Len(params') + curNode'.arity
-                                      THEN /\ PrintT("Selected operator has wrong arity.")
-                                           /\ IF debug
-                                                 THEN /\ idx' = idx
-                                                      /\ pc' = "Lbl_78"
-                                                 ELSE /\ pc' = "Done"
-                                                      /\ UNCHANGED idx
-                                      ELSE /\ IF Len(params') + Len(substInPrefix) > 0
-                                                 THEN /\ pc' = "Lbl_79"
-                                                 ELSE /\ pc' = "Lbl_80"
-                                           /\ UNCHANGED idx
-                ELSE /\ pc' = "Lbl_80"
-                     /\ UNCHANGED idx
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, 
-                          result, mode, prevMode, curContext, curName, 
-                          opDefArityFound, opDefArgs, firstFindingOpName, 
-                          opNode, newName, newNode, nodeArity, tempArgs >>
-
-Lbl_77 == /\ pc = "Lbl_77"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2363, column 12.")
-          /\ pc' = "Lbl_80"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_78 == /\ pc = "Lbl_78"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2365, column 12.")
-          /\ pc' = "Lbl_80"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_79 == /\ pc = "Lbl_79"
-          /\ temp' = [i \in 1..curNode.arity |->
-                       [name |-> "NewParam" \o NumToString(i),
-                        arity |-> 0]]
-          /\ curNode' = [kind     |-> OpApplKind,
-                         operands |-> temp',
-                         operator |-> Node[curNode.op],
-                         unboundedBoundSymbols |-> <<>>,
-                         boundedBoundSymbols |-> << >>,
-                         ranges |-> << >>]
-          /\ params' = params \o temp'
-          /\ pc' = "Lbl_80"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, allArgs, 
-                          subExprOf, result, idx, mode, prevMode, curContext, 
-                          curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, tempArgs >>
-
-Lbl_80 == /\ pc = "Lbl_80"
-          /\ IF curNode.kind \in {UserDefinedOpKind, ConstantDeclKind, VariableDeclKind,
-                                  FormalParamKind, BuiltInKind, BoundSymbolKind,
-                                  ThmOrAssumpDefKind}
-                THEN /\ IF expectedArity > 0
-                           THEN /\ result' = [kind  |-> OpArgKind,
-                                              name  |-> curNode.name,
-                                              op    |-> curNode,
-                                              arity |-> expectedArity]
-                           ELSE /\ IF expectedArity = 0
-                                      THEN /\ result' = [kind |-> OpApplKind,
-                                                         operands |-> opDefArgs,
-                                                         operator |-> curNode,
-                                                         unboundedBoundSymbols |-> <<>>,
-                                                         boundedBoundSymbols |-> << >>,
-                                                         ranges |-> << >>,
-                                                         subExprOf |-> subExprOf]
-                                      ELSE /\ TRUE
-                                           /\ UNCHANGED result
-                     /\ pc' = "Finished"
-                     /\ UNCHANGED temp
-                ELSE /\ IF curNode.kind = OpArgKind
-                           THEN /\ result' = curNode
-                                /\ pc' = "Finished"
-                                /\ UNCHANGED temp
-                           ELSE /\ temp' = Len(substInPrefix)
-                                /\ pc' = "Lbl_81"
-                                /\ UNCHANGED result
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, tempArgs >>
-
-Lbl_81 == /\ pc = "Lbl_81"
-          /\ IF temp > 0
-                THEN /\ curNode' = [kind  |-> SubstInKind,
-                                    body  |-> curNode ,
-                                    subst |-> substInPrefix[temp].subst ]
-                     /\ temp' = temp - 1
-                     /\ pc' = "Lbl_81"
-                     /\ UNCHANGED idx
-                ELSE /\ IF expectedArity > 0
-                           THEN /\ IF Len(params) # expectedArity
-                                      THEN /\ PrintT("Selection has wrong arity")
-                                           /\ IF debug
-                                                 THEN /\ idx' = idx
-                                                      /\ pc' = "Lbl_82"
-                                                 ELSE /\ pc' = "Done"
-                                                      /\ UNCHANGED idx
-                                      ELSE /\ pc' = "Lbl_83"
-                                           /\ UNCHANGED idx
-                           ELSE /\ IF Len(params) # Len(allArgs)
-                                      THEN /\ PrintT("Abort: number of params # num of args")
-                                           /\ IF debug
-                                                 THEN /\ idx' = idx
-                                                      /\ pc' = "Lbl_84"
-                                                 ELSE /\ pc' = "Done"
-                                                      /\ UNCHANGED idx
-                                      ELSE /\ pc' = "Lbl_85"
-                                           /\ UNCHANGED idx
-                     /\ UNCHANGED << curNode, temp >>
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, tempArgs >>
-
-Lbl_83 == /\ pc = "Lbl_83"
-          /\ result' = [kind |-> OpArgKind,
-                        op   |-> [kind    |-> UserDefinedOpKind,
-                                  name    |-> "LAMBDA",
-                                  body    |-> curNode,
-                                  params  |-> params,
-                                  arity   |-> Len(params),
-                                  defined |-> TRUE,
-                                  source  |-> null],
-                        name |-> "LAMBDA"]
-          /\ pc' = "Finished"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_85 == /\ pc = "Lbl_85"
-          /\ IF Len(params) = 0
-                THEN /\ result' = [kind  |-> LabelKind,
-                                   name  |-> "$Subexpression",
-                                   arity  |-> 0,
-                                   params |-> << >>,
-                                   body   |-> curNode,
-                                   subExprOf |-> subExprOf]
-                ELSE /\ result' = [kind     |-> OpApplKind,
-                                   operator |-> [kind    |-> UserDefinedOpKind,
-                                                 name    |-> "LAMBDA",
-                                                 body    |-> curNode,
-                                                 params  |-> params,
-                                                 arity   |-> Len(params),
-                                                 defined |-> TRUE,
-                                                 source  |-> null],
-                                   operands |-> allArgs,
-                                   unboundedBoundSymbols |-> <<>>,
-                                   boundedBoundSymbols |-> << >>,
-                                   ranges |-> << >>,
-                                   subExprOf |-> subExprOf]
-          /\ pc' = "Finished"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, idx, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_82 == /\ pc = "Lbl_82"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2431, column 22.")
-          /\ pc' = "Lbl_83"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Lbl_84 == /\ pc = "Lbl_84"
-          /\ idx' = idx
-          /\ Assert(FALSE, 
-                    "Failure of assertion at line line 1892, column 38 of macro called at line 2443, column 22.")
-          /\ pc' = "Lbl_85"
-          /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                          allArgs, curNode, subExprOf, result, mode, prevMode, 
-                          curContext, curName, opDefArityFound, opDefArgs, 
-                          firstFindingOpName, opNode, newName, newNode, 
-                          nodeArity, temp, tempArgs >>
-
-Finished == /\ pc = "Finished"
-            /\ PrintT("Result: ")
-            /\ PrintT(result)
-            /\ pc' = "Done"
-            /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, 
-                            allArgs, curNode, subExprOf, result, idx, mode, 
-                            prevMode, curContext, curName, opDefArityFound, 
-                            opDefArgs, firstFindingOpName, opNode, newName, 
-                            newNode, nodeArity, temp, tempArgs >>
-
-Next == Lbl_1 \/ Lbl_2 \/ Lbl_69 \/ Lbl_3 \/ Lbl_5 \/ Lbl_6 \/ Lbl_7
-           \/ Lbl_4 \/ Lbl_10 \/ Lbl_12 \/ Lbl_13 \/ Lbl_11 \/ Lbl_14 \/ Lbl_15
-           \/ Lbl_17 \/ Lbl_18 \/ Lbl_16 \/ Lbl_19 \/ Lbl_8 \/ Lbl_9 \/ Lbl_20
-           \/ Lbl_21 \/ Lbl_23 \/ Lbl_25 \/ Lbl_26 \/ Lbl_27 \/ Lbl_24 \/ Lbl_28
-           \/ Lbl_22 \/ Lbl_63 \/ Lbl_64 \/ Lbl_65 \/ Lbl_66 \/ Lbl_67 \/ Lbl_29
-           \/ Lbl_30 \/ Lbl_31 \/ Lbl_33 \/ Lbl_35 \/ Lbl_34 \/ Lbl_32 \/ Lbl_37
-           \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 \/ Lbl_42 \/ Lbl_43 \/ Lbl_44
-           \/ Lbl_45 \/ Lbl_36 \/ Lbl_47 \/ Lbl_49 \/ Lbl_48 \/ Lbl_46 \/ Lbl_51
-           \/ Lbl_50 \/ Lbl_53 \/ Lbl_52 \/ Lbl_54 \/ Lbl_55 \/ Lbl_56 \/ Lbl_57
-           \/ Lbl_58 \/ Lbl_59 \/ Lbl_60 \/ Lbl_61 \/ Lbl_62 \/ Lbl_68 \/ Lbl_70
-           \/ Lbl_71 \/ Lbl_73 \/ Lbl_72 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76 \/ Lbl_77
-           \/ Lbl_78 \/ Lbl_79 \/ Lbl_80 \/ Lbl_81 \/ Lbl_83 \/ Lbl_85 \/ Lbl_82
-           \/ Lbl_84 \/ Finished
-           \/ (* Disjunct to prevent deadlock on termination *)
-              (pc = "Done" /\ UNCHANGED vars)
-
-Spec == Init /\ [][Next]_vars
-
-Termination == <>(pc = "Done")
-
-\* END TRANSLATION
-
-NotDone == pc # "Done"
-  (*************************************************************************)
-  (* To print a trace of an execution that doesn't produce an error, add   *)
-  (* "INVARIANT NotDone" to Subexpression.cfg .                            *)
-  (*************************************************************************)
-=============================================================================
-
-
-************************* end file Subexpression.tla ****************************/
-
+}
+
+/************************
+ * file Tarjan.tla ***********************************
+ * ------------------------------ MODULE Tarjan --------------------------------
+ * (***************************************************************************)
+ * (* This version of Tarjan's algorithm for computing the strongly *) (*
+ * connected components of a directed graph was adapted from the version *) (*
+ * of 27 Mar 2007 on the Wikipedia page *) (* *) (*
+ * http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
+ * *) (* *) (* One modification is that the set of connected components it
+ * returns *) (* does not contain a singleton {n} unless n has an edge pointing
+ * to *) (* itself. *) (* *) (* This is an implementation of the algorithm in
+ * Tarjan.tla with data *) (* structures that correspond to the ones used in the
+ * algorithm for *) (* constructing dependency components in semantic/Generator.
+ * *) (* *) (* *) (* I have not rigorously verified this algorithm, but I think
+ * I understand *) (* why it works. I have tested it only on the 10 graphs at
+ * the end of *) (* this file. *)
+ * (***************************************************************************)
+ * 
+ * EXTENDS Integers, Sequences, TLC
+ * 
+ * CONSTANT N, \* The number of nodes Neighbors \* A sequence of edges.
+ * 
+ * Node == 1..N
+ * 
+ * ASSUME Neighbors \in Seq(Node \X Node)
+ * 
+ * Min(a, b) == IF a < b THEN a ELSE b
+ * 
+ * Front(s) == [i \in 1 .. (Len(s)-1) |-> s[i]]
+ * (*************************************************************************)
+ * (* The sequence obtained from a sequence s by deleting the last item. *)
+ * (*************************************************************************)
+ * 
+ * Last(s) == s[Len(s)]
+ * (*************************************************************************)
+ * (* The last item in the sequence s. *)
+ * (*************************************************************************)
+ * 
+ * (***************************************************************************)
+ * (* I believe the algorithm works because it maintains the following *) (*
+ * invariants (at reasonable points in the code). We say that node *) (*
+ * nstack[i] precedes nstack[j] iff i < j. *) (* *) (* - A node n is in nstack
+ * iff n.dfs \geq 0 *) (* *) (* - For all nodes n # m in nstack, if n precedes m
+ * then: *) (* /\ n.dfs < m.dfs. *) (* /\ there is a path from n to m in the
+ * graph. *) (* *) (* - For every node n in nstack, *) (* /\ n.lowlink \leq
+ * n.dfs and *) (* /\ there is a node m in nstack such that *) (* 1. n.lowlink =
+ * m.dfs. *) (* 2. if m # n then there is a path from n to m in the graph. *) (*
+ * /\ For every node m in the same connected component as n: *) (* m.examined =
+ * TRUE implies m is in nstack *)
+ * (***************************************************************************)
+ * 
+ * (************************* --algorithm Tarjan2 variables nstack = << >> ;
+ * (***************************************************************) (* The
+ * stack of nodes, where nstack[1] is the bottom of the *) (* stack. *)
+ * (***************************************************************) max_dfs = 0
+ * ; (***************************************************************) (*
+ * Incremented by 1 every time a new element is added to the *) (* stack. *)
+ * (***************************************************************)
+ * 
+ * data = [nd \in Node |-> [lowlink |-> -1, dfs |-> -1, examined |-> FALSE, nbrs
+ * |-> << >>, (*****************************************) (* A sequence of nodes
+ * to which there *) (* are edges from this node. The *) (* same node may appear
+ * multiple *) (* times. *) (*****************************************) nxtDep
+ * |-> -1 ]] ; (***************************************************************)
+ * (* The information maintained for the nodes. Note that *) (* *) (* - Node nd
+ * is on nstack iff data[nd].dfs \geq 0, in *) (* which case nstack[data[nd].dfs
+ * + 1] = nd. *) (* *) (* - nbrs is set from Neighbors by the initialization
+ * code. *) (* *) (* - nxtDep represents the nextDependency field of the *) (*
+ * OpDefNode object, where a value of -1 represents *) (* null. Following nxtDep
+ * pointers leads in a cycle of *) (* the node's connected component. See the
+ * comments on *) (* the nextDependency field in semantic/OpDefNode. *)
+ * (***************************************************************)
+ * 
+ * idx ; \* A counter variable.
+ * 
+ * procedure tarjan(n) variable nxt ; \* A counter variable nxtnbr ; \* An
+ * abbreviation
+ * 
+ * begin data[n].lowlink := max_dfs || data[n].dfs := max_dfs ||
+ * data[n].examined := TRUE ; max_dfs := max_dfs + 1; nstack := Append(nstack,
+ * n) ; nxt := 1 ; while nxt \leq Len(data[n].nbrs) do nxtnbr :=
+ * data[n].nbrs[nxt] ; if ~ data[nxtnbr].examined then
+ * (***************************************************************) (* nxtnbr
+ * is unexamined *)
+ * (***************************************************************) call
+ * tarjan(nxtnbr) ; data[n].lowlink := Min(data[n].lowlink,
+ * data[nxtnbr].lowlink) ;
+ * 
+ * elsif data[nxtnbr].dfs \geq 0 then
+ * (**********************************************************) (* nxtnbr is in
+ * nstack *) (**********************************************************)
+ * data[n].lowlink := Min(data[n].lowlink, data[nxtnbr].dfs) ; end if ; nxt :=
+ * nxt + 1; end while ; if data[n].lowlink = data[n].dfs then
+ * (*****************************************************************) (* The
+ * set of all nodes on nstack from n to the end form a *) (* connected
+ * component. *)
+ * (*****************************************************************) nxtnbr :=
+ * Last(nstack) ; \* save last node on stack. while n # Last(nstack) do
+ * data[Last(nstack)].nxtDep := nstack[Len(nstack)-1] || data[Last(nstack)].dfs
+ * := -1 ; (*************************************************************) (*
+ * Make nxtDep field of last node on stack point to next to *) (* last node. *)
+ * (*************************************************************) nstack :=
+ * Front(nstack) ; end while ; if \/ n # nxtnbr \/ \E i \in 1..Len(data[n].nbrs)
+ * : n = data[n].nbrs[i] then
+ * (**********************************************************) (* Form a
+ * connected component consisting of just n only *) (* if n is a neighbor of
+ * itself. *) (**********************************************************)
+ * data[n].nxtDep := nxtnbr; end if; data[n].dfs := -1 ; nstack := Front(nstack)
+ * ; end if ; data[n].nbrs := << >> ;
+ * (**********************************************************************) (*
+ * Can garbage collect the sequence of neighbors. *)
+ * (**********************************************************************)
+ * return ; end procedure
+ * 
+ * begin
+ * (************************************************************************) (*
+ * Initialize the nbrs field of data elements from Neighbors. *)
+ * (************************************************************************)
+ * idx := 1 ; while idx \leq Len(Neighbors) do with edge = Neighbors[idx] do
+ * data[edge[1]].nbrs := Append(data[edge[1]].nbrs, edge[2]) ; end with ; idx :=
+ * idx + 1; end while ;
+ * 
+ * (************************************************************************) (*
+ * while there is an unexamined node, call tarjan(nd) *) (* for any unexamined
+ * node nd. *)
+ * (************************************************************************)
+ * idx := 1 ; while idx \leq N do if data[idx].lowlink < 0 then
+ * (***************************************************************) (* idx is
+ * unexamined *)
+ * (***************************************************************) call
+ * tarjan(idx) ; end if; idx := idx + 1 ; end while ;
+ * 
+ * (************************************************************************) (*
+ * print result. *)
+ * (************************************************************************)
+ * idx := 1 ; while idx \leq N do if data[idx].nxtDep \geq 0 then print <<idx,
+ * "->", data[idx].nxtDep>> end if ; idx := idx+1; end while end algorithm )
+ * 
+ * \* BEGIN TRANSLATION \* END TRANSLATION
+ * 
+ * \**** Test data
+ * 
+ * Node1 == {1, 2, 3, 4}
+ * 
+ * Nbrs1 == << <<1, 2>>, \* components 1 and 3,4 <<1, 2>>, \* components 1 and
+ * 3,4 <<1, 1>>, <<1, 1>>, <<3, 4>> , <<3, 4>> , <<4, 3>> , <<4, 3>>, <<4, 3>>
+ * >>
+ * 
+ * Nbrs2 == << <<1, 2>>, \* components 1 and 3,4 <<1, 1>>, <<1, 3>>, <<3, 4>>,
+ * <<4, 3>> >> Nbrs3 == << <<1, 2>>, \* components 1 and 3,4 <<1, 1>>, <<3, 1>>,
+ * <<3, 4>>, <<4, 3>> >> Nbrs4 == << <<1, 3>>, \* <<1, 8>>, \* componetns:
+ * 2,3,4,5 and 7,8,9 <<1, 9>>, <<1, 9>>, <<1, 9>>, <<3, 2>>, <<3, 2>>, <<2, 4>>,
+ * <<4, 5>>, <<4, 10>>, <<5, 3>>, <<8, 7>>, <<8, 7>>, <<9, 8>>, <<7, 9>>, <<7,
+ * 9>>, <<9, 10>>, <<9, 10>>, <<9, 10>> >>
+ * 
+ * Nbrs5 == << <<1, 8>>, \* components 7,8,9 <<1, 9>>, <<4, 10>>, <<8, 7>>, <<9,
+ * 8>>, <<7, 9>>, <<9, 10>> >>
+ * 
+ * Nbrs6 == << <<6, 4>>, \* components 2,3,4,5 <<3, 4>>, <<4, 5>>, <<5, 2>>,
+ * <<2, 3>>, <<5, 1>>, <<2, 1>> >>
+ * 
+ * Nbrs7 == << <<1, 7>>, \* 1 component with 1..9 \ {3} <<1, 9>>, <<2, 1>>, <<3,
+ * 2>>, <<3, 6>>, <<4, 6>>, <<5, 2>>, <<6, 5>>, <<6, 8>>, <<7, 5>>, <<7, 8>>,
+ * <<8, 4>>, <<8, 10>>, <<9, 7>>, <<9, 10>> >> Nbrs8 == << <<1, 7>>, \* 1
+ * component with 1..8 \ {3} <<2, 1>>, <<3, 2>>, <<3, 2>>, <<3, 2>>, <<3, 6>>,
+ * <<4, 6>>, <<5, 2>>, <<6, 5>>, <<6, 8>>, <<6, 8>>, <<6, 8>>, <<7, 5>>, <<7,
+ * 8>>, <<8, 4>>, <<8, 10>>, <<9, 7>>, <<9, 10>> >> Nbrs9 == << \* 4,6, 7,8,9,10
+ * <<2, 1>>, <<3, 2>>, <<3, 6>>, <<4, 6>>, <<4, 6>>, <<4, 6>>, <<5, 2>>, <<6,
+ * 5>>, <<6, 8>>, <<7, 5>>, <<7, 5>>, <<7, 5>>, <<7, 5>>, <<7, 8>>, <<8, 4>>,
+ * <<8, 10>>, <<9, 7>>, <<10, 9>> >> Nbrs == << \* 1, 3, 4, 5 and 6 <<1, 4>>,
+ * <<2, 1>>, <<2, 3>>, <<2, 3>>, <<2, 3>>, <<2, 5>>, <<3, 4>>, <<4, 1>>, <<4,
+ * 5>>, <<4, 5>>, <<4, 5>>, <<5, 3>>, <<6, 5>>, <<6, 6>> >>
+ * 
+ * =============================================================================
+ ************************** 
+ * end file Tarjan.tla
+ ******************************/
+
+/*************************
+ * file Subexpression.tla ********************** last modified on Fri 13
+ * November 2009 at 14:11:05 PST by lamport ------------------------ MODULE
+ * Subexpression ---------------------------
+ * 
+ * 
+ * (***************************************************************************)
+ * (* NAMING *) (* *) (* There are four kinds of Nameable Objects: *) (* -
+ * Expressions *) (* - Operators that take an argument *) (* (and that can
+ * appear as operator arguments) *) (* - Operator definitions (named in
+ * BY/USE/HIDE statements) *) (* - ASSUME/PROVEs. *) (* *) (* There are three
+ * relevant kinds of primitive names that appear in a *) (* TLA+ module. *) (*
+ * *) (* Defined Module Name (ModuleName): *) (* It appears to the left of "==
+ * INSTANCE...". It may or may not *) (* contain arguments. *) (* Primitive
+ * Operator Name (PrimOpName): *) (* It appear to the left of the "==" in a
+ * definition, theorem *) (* or assumption. It may or may not contain arguments.
+ * *) (* Label: *) (* It appears before a "::" in an expression. *) (* *) (*
+ * Examples of possible primitive names are "Foo" and "Bar(x+1, 42)". *) (* *)
+ * (* The Built-In Operator names (like "SUBSET") will not be considered to *)
+ * (* be primitive names in the following. *) (* *) (* To these explicit names
+ * we add the following Operand Selector (OpSel) *) (* names: *) (* - numbers,
+ * *) (* - "<<", ">>", *) (* - Things of the form (exp_1, ... , exp_n) where the
+ * exp_i are *) (* expressions or operator arguments and n > 0. *) (* - "@", *)
+ * (* *) (* A Compound Name is a sequence of primitive names, operand selectors,
+ * *) (* and ":"s separated by "!"s. *) (* *) (* In TLA+1, the general way of
+ * naming a non-built-in operator is with *) (* what I will call an Elementary
+ * Operator Name (ElemOpName) which has *) (* the following syntax. For
+ * simplicity, I will ignore the "!"s in the *) (* `formal' syntax. *) (* *) (*
+ * ElemOpName ::= (ModuleName)* PrimOpName *) (* *) (* We consider this to name
+ * both the operator and its definition (the *) (* expression to the right of
+ * the "=="). How the two meanings are *) (* disambiguated will be explained
+ * below. *) (* *) (* If EON is an ElemOpName and Lab_1, ... , Lab_n are a
+ * sequence of *) (* label names, then EON ! Lab_1 ! ... ! Lab_N names the
+ * expression *) (* labeled by Lab_N, where each Lab_(i+1) must be the name of a
+ * labeled *) (* expression within the expression labeled by Lab_i with no
+ * labels *) (* between Lab_i and Lab_(i+1). Thus a name described by *) (* *)
+ * (* ElemOpName (Label)* *) (* *) (* describes an operator definition or a
+ * labeled expression. To such a *) (* name, we can append a sequence of
+ * OperandSelectors. By rules *) (* described later, the resulting name selects
+ * a subexpression (or *) (* ASSUME/PROVE node) or Operator (appearing as an
+ * operator argument) *) (* from the body of the operator definition or labeled
+ * expression. If *) (* the labeled expression is a LET/IN, then to this name
+ * can be appended *) (* an ElemOpName defined in the LET part, and the naming
+ * process can then *) (* be iterated. The full syntax of a General Name is: *)
+ * (* *) (* OperatorName ::= ElemOpName *) (* | ElemOpName (Label)* ((OpSel)+ |
+ * ":") ElemOpName *) (* *) (* GeneralName ::= OperatorName (":" | (Label)*
+ * (OpSeq)* ) *) (* *) (* Note that ":" is used in two ways: *) (* *) (* - An
+ * OperatorName not followed by ":" names the operator; one *) (* followed by
+ * ":" names the operator's definition. *) (* - Suppose a general name GN ending
+ * in a label names a LET/IN *) (* expression and EON is an ElemOpName. Then GN
+ * !: !EON names *) (* an operator defined in the LET. The general name GN ! Foo
+ * would *) (* name an expression labeled by Foo inside the IN clause. *)
+ * (***************************************************************************)
+ * 
+ * 
+ * (***************************************************************************)
+ * (* This +cal program describes an algorithm used in generating the *) (*
+ * semantic tree for a parse tree consisting of a GeneralId node or an *) (*
+ * OpApplication node whose first child is a GeneralId node. Two examples *) (*
+ * are *) (* *) (* Foo(x)!Bar!<<!(a,b)!G *) (* Foo(x)!Bar!<<!(a,b)!G(c) *) (* *)
+ * (* We assume that these are processed to yield two sequences of nodes: *) (*
+ * op and args. The sequences for the first are *) (* *) (* op = << "Foo",
+ * "Bar", "<<", "null", "G" >> *) (* args = << <<x>>, << >>, << >>, <<a, b>>,
+ * <<>> >> *) (* *) (* and for the second are *) (* *) (* op = << "Foo", "Bar",
+ * "<<", "null", "G" >> *) (* args = << <<x>>, << >>, << >>, <<a, b>>, <<c>> >>
+ * *) (* *) (* where each symbol in args represents the semantic node produced
+ * by that *) (* symbol. *) (* *) (* The other input to the algortithm is an
+ * integer expectedArity. There *) (* are three cases: *) (* *) (* expectedArity
+ * = 0 : *) (* The parser is expecting an expression. This is the case *) (*
+ * when the expression occurs in the first part of a USE *) (* or HIDE
+ * statement. *) (* *) (* expectedArity > 0 : The parser is expecting an
+ * operator *) (* argument of this arity. *) (* *) (* expectedArity = -1 : *) (*
+ * The parser is expecting the name of a definition. This is *) (* the case when
+ * the expression occurs in the DEF clause of a USE *) (* or HIDE or BY
+ * statement. *) (* *) (* If expectedArity # 0, then args equals a sequence each
+ * of whose *) (* element is the empty sequence. *)
+ * (***************************************************************************)
+ * 
+ * (***************************************************************************)
+ * (* Note: This spec is assuming that proof-step numbers are treated like *) (*
+ * operator names. That is, a step *) (* *) (* <3>2. foo > bar *) (* *) (* will
+ * be represented by a ThmOrAssumpDefKind node just as if it were *) (* *) (*
+ * THEOREM <3>2 == foo > bar *) (* *) (* If this is not the case, then
+ * additional code needs to be added to deal *) (* with numbered steps. But
+ * however it is handled, the "<3>2" will be a *) (* name in the current symbol
+ * table. *)
+ * (***************************************************************************)
+ * 
+ * EXTENDS Integers, Sequences, TLC
+ * 
+ * (***************************************************************************)
+ * (* SeqSeqToSeq(ss) is defined to be the concatenation of the sequences *) (*
+ * that are the elements of the sequence ss. *)
+ * (***************************************************************************)
+ * RECURSIVE SeqSeqToSeq(_) SeqSeqToSeq(ss) == IF ss = << >> THEN << >> ELSE
+ * Head(ss) \o SeqSeqToSeq(Tail(ss))
+ * 
+ * (***************************************************************************)
+ * (* These are kinds of semantic nodes that can be encountered by the *) (*
+ * algorithm. See tlasany/sematic/ASTConstants.java. *)
+ * (***************************************************************************)
+ * ConstantDeclKind == "ConstantDecl" VariableDeclKind == "VariableDecl"
+ * BoundSymbolKind == "BoundSymbol" UserDefinedOpKind == "UserDefinedOp"
+ * ModuleInstanceKind == "ModuleInstance" BuiltInKind == "BuiltIn" OpArgKind ==
+ * "OpArg" OpApplKind == "OpAppl" LetInKind == "LetIn" FormalParamKind ==
+ * "FormalParam" TheoremKind == "Theorem" SubstInKind == "SubstIn"
+ * AssumeProveKind == "AssumeProve" ProofKind == "Proof" NumeralKind ==
+ * "Numeral" DecimalKind == "Decimal" StringKind == "String" AtNodeKind ==
+ * "AtNode" AssumeKind == "Assume" InstanceKind == "Instance" ThmOrAssumpDefKind
+ * == "ThmOrAssumpDef" LabelKind == "Label" APSubstInKind == "APSubstIn"
+ * 
+ * CONSTANT NodeId, \* The set of all node identifiers. Node, \* A mapping from
+ * NodeId to nodes (which are records). GlobalContext, \* The initial context.
+ * null, \* A special value not equal to anything else. debug \* Setting to TRUE
+ * causes TLC to print a trace if the \* algorithm reports an error.
+ * 
+ * (***************************************************************************)
+ * (* CONTEXTS *) (* *) (* A context is a mapping from names to NodeIds. There
+ * are actually three *) (* different kinds of mappings from names to nodes in
+ * the implementation *) (* that are all represented here as contexts: *) (* -
+ * The symbolTable of the Generator. *) (* - The context field of a LetInNode *)
+ * (* - The labels field of a LabelNode *)
+ * (***************************************************************************)
+ * IsContext(C) ==
+ * (*************************************************************************)
+ * (* True iff C is a context. *)
+ * (*************************************************************************)
+ * /\ DOMAIN C \subseteq STRING /\ \A str \in DOMAIN C : C[str] \in NodeId
+ * 
+ * LookUp(nm, ctxt) ==
+ * (*************************************************************************)
+ * (* The value assigned to name nm by context cxt, or null if there is no *) (*
+ * value assigned. *)
+ * (*************************************************************************)
+ * IF nm \in DOMAIN ctxt THEN Node[ctxt[nm]] ELSE null
+ * 
+ * Param == [name : STRING, arity : Nat]
+ * (*************************************************************************)
+ * (* The set of parameters, which can appear in operator definitions. *)
+ * (*************************************************************************)
+ * 
+ * (***************************************************************************)
+ * (* For simplicity, we assume that argument numbers range from 1 to 9, and *)
+ * (* we define the following mappings from to and from argument numbers to *)
+ * (* their string representations. *)
+ * (***************************************************************************)
+ * NumberOp == {"1", "2", "3", "4", "5", "6", "7", "8", "9"} NumericVal(numOp)
+ * == CASE numOp = "1" -> 1 [] numOp = "2" -> 2 [] numOp = "3" -> 3 [] numOp =
+ * "4" -> 4 [] numOp = "5" -> 5 [] numOp = "6" -> 6 [] numOp = "7" -> 7 [] numOp
+ * = "8" -> 8 [] numOp = "9" -> 9
+ * 
+ * NumToString(num) == CASE num = 1 -> "1" [] num = 2 -> "2" [] num = 3 -> "3"
+ * [] num = 4 -> "4" [] num = 5 -> "5" [] num = 6 -> "6" [] num = 7 -> "7" []
+ * num = 8 -> "8" [] num = 9 -> "9"
+ * 
+ * IsName(op) ==
+ * (*************************************************************************)
+ * (* A name is something other than an argument selector that can appear *) (*
+ * in a compound name. *)
+ * (*************************************************************************)
+ * op \in STRING \ ({"<<", ">>", "@", ":", "null"} \cup NumberOp)
+ * 
+ * 
+ * ArgNum(op, arity) ==
+ * (*************************************************************************)
+ * (* The argument number chosen by argument selector op for an operator *) (*
+ * application with arity arguments. It equals -1 if op is not a legal *) (*
+ * argument selector for this arity. *)
+ * (*************************************************************************)
+ * CASE op \in NumberOp -> IF NumericVal(op) <= arity THEN NumericVal(op) ELSE
+ * -1 [] op = "<<" -> IF arity > 0 THEN 1 ELSE -1 [] op = ">>" -> CASE arity = 1
+ * -> 1 [] arity = 2 -> 2 [] OTHER -> -1 [] OTHER -> -1
+ * 
+ * Arity(node) ==
+ * (*************************************************************************)
+ * (* The arity of a node--that is, the number of arguments it takes. *)
+ * (*************************************************************************)
+ * IF node.kind \in {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind,
+ * ThmOrAssumpDefKind, OpArgKind, LabelKind, ConstantDeclKind, FormalParamKind}
+ * THEN node.arity ELSE 0
+ * 
+ * ParamArity(node, i) ==
+ * (*************************************************************************)
+ * (* The arity of the i-th parameter of the node of kind *) (*
+ * UserDefinedOpKind, ThmOrAssumpDefKind, ModuleInstanceKind, *) (*
+ * ConstantDeclKind, or FormalParamKind *)
+ * (*************************************************************************)
+ * IF node.kind \in {ConstantDeclKind, FormalParamKind} THEN 0 ELSE
+ * node.params[i].arity
+ * -----------------------------------------------------------------------------
+ * 
+ * (***************************************************************************)
+ * (* The following assumptions define the data types--in particular, the *) (*
+ * record components that each node type must contain. *)
+ * (***************************************************************************)
+ * ASSUME IsContext(GlobalContext)
+ * 
+ * ASSUME DOMAIN Node = NodeId
+ * 
+ * ASSUME /\ \A id \in NodeId : LET node == Node[id] IN /\ node.kind \in
+ * {ConstantDeclKind, VariableDeclKind, BoundSymbolKind, UserDefinedOpKind,
+ * ModuleInstanceKind, BuiltInKind, OpArgKind, OpApplKind, LetInKind,
+ * FormalParamKind, TheoremKind, SubstInKind, AssumeProveKind, ProofKind,
+ * NumeralKind, DecimalKind, StringKind, AtNodeKind, AssumeKind, InstanceKind,
+ * ThmOrAssumpDefKind, LabelKind, APSubstInKind}
+ * 
+ * ASSUME /\ \A id \in NodeId : LET node == Node[id] IN /\ node.kind \in
+ * {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind, ThmOrAssumpDefKind,
+ * LabelKind, ConstantDeclKind, FormalParamKind} => /\ node.name \in STRING /\
+ * node.arity \in Nat \cup {-1} /\ (node.arity = -1) => (node.kind =
+ * BuiltInKind)
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN node.kind \in
+ * {UserDefinedOpKind, ThmOrAssumpDefKind} => /\ node.body \in NodeId /\
+ * IsContext(node.labels) /\ node.params \in Seq(Param) /\ Len(node.params) =
+ * node.arity /\ node.defined \in BOOLEAN /\ node.source \in {null} \cup NodeId
+ * \* The original definition (before instantiation) \* or null if this is it.
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = OpApplKind
+ * => /\ node.operands \in Seq(NodeId) /\ node.operator \in NodeId /\
+ * node.unboundedBoundSymbols \in Seq(STRING) /\ node.boundedBoundSymbols \in
+ * Seq(Seq(STRING))
+ * (**************************************************************) (* These are
+ * actually arrays and arrays of arrays of *) (* FormalParamNode objects
+ * (formerly OpDeclNode objects). *)
+ * (**************************************************************) /\
+ * node.ranges \in Seq(NodeId) /\ Len(node.ranges) =
+ * Len(node.boundedBoundSymbols)
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = LetInKind =>
+ * /\ node.body \in NodeId /\ IsContext(node.context)
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = OpArgKind =>
+ * /\ node.op \in NodeId /\ IsName(node.name) /\ (node.arity \in Nat) /\
+ * (node.arity > 0)
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = LabelKind =>
+ * /\ node.body \in NodeId /\ IsContext(node.labels) /\ node.params \in
+ * Seq(Param)
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = SubstInKind
+ * => /\ node.body \in NodeId /\ node.subst \in STRING \* Just to identify it.
+ * 
+ * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind =
+ * AssumeProveKind => /\ node.assumes\in Seq(NodeId) /\ node.prove \in NodeId
+ * 
+ * 
+ * RECURSIVE ExpandNode(_) ExpandNode(node) == CASE node.kind \in
+ * {UserDefinedOpKind, LetInKind} -> [node EXCEPT !.body = ExpandNode(Node[@])]
+ * [] node.kind = OpApplKind -> [node EXCEPT !.operands = [i \in DOMAIN @ |->
+ * ExpandNode(Node[@[i]])], !.operator = ExpandNode(Node[@]), !.ranges = [i \in
+ * DOMAIN @ |-> ExpandNode(Node[@[i]])]] [] OTHER -> node
+ * 
+ * 
+ * -----------------------------------------------------------------------------
+ * (***************************************************************************)
+ * (* Test Data *) (* *) (* The algorithm has been tested on the following sets
+ * of data, where each *) (* operator MCxxx is either substituted for constant
+ * parameter xxx or is *) (* used as initial value of the algorithm's input
+ * variable xxx. *)
+ * (***************************************************************************)
+ * 
+ * 
+ * (*****************************
+ * 
+ * 
+ * (***************************************************************************)
+ * (* ------------------------ MODULE M ------------------------ *) (* CONSTANT
+ * C *) (* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> \in {3} : *) (* lab(x,y,z)
+ * :: LET a + b == <<Arg(a), b>> *) (* IN 1 + label2 :: p + C *) (* Foo(u) ==
+ * "FooBody(u)" *) (*
+ * ============================================================= *) (* *) (*
+ * Inst(d) == INSTANCE M WITH C <- "expr(d)" *)
+ * (***************************************************************************)
+ * 
+ * \* MCNode == \* 1 :> [kind |-> UserDefinedOpKind, \* Op(Arg(_), p) == ... \*
+ * name |-> "Op", \* body |-> 3, \* labels |-> "lab" :> 4, \* arity |-> 2, \*
+ * params |-> <<[name |-> "Arg", arity |-> 1], \* [name |-> "p", arity |-> 0]>>,
+ * \* defined |-> TRUE, \* source |-> null ] \* @@ \* 2 :> [kind |->
+ * BuiltInKind, \* name |-> "$BoundedForall", \* arity |-> -1 \* ] \* @@ \* 3 :>
+ * [kind |-> OpApplKind, \* \A x \in {1,2}, <<y, z>> \in {3} : ... \* operands
+ * |-> << 4 >>, \* operator |-> 2, \* unboundedBoundSymbols |-> <<>>, \*
+ * boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>, \* ranges |-> << 19, 21
+ * >>] \* @@ \* 4 :> [kind |-> LabelKind, \* lab(x,y,z) :: LET a + b ==
+ * <<Arg(a), b>> \* name |-> "lab", \* IN 1 + label2 :: p + C \* body |-> 5, \*
+ * arity |-> 3, \* params |-> <<[name |-> "x", arity |->0], \* [name |-> "y",
+ * arity |->0], \* [name |-> "z", arity |->0] >>, \* labels |-> "label2" :> 15 ]
+ * \* @@ \* 5 :> [kind |-> LetInKind, \* LET a + b == <<Arg(a), b>> \* context
+ * |-> "+" :> 6, \* IN 1 + label2 :: p + C \* body |-> 13 ] \* @@ \* 6 :> [kind
+ * |-> UserDefinedOpKind, \* a + b == <<Arg(a), b>> \* name |-> "+", \* body |->
+ * 7, \* labels |-> << >>, \* arity |-> 2, \* params |-> <<[name |-> "a", arity
+ * |-> 0], \* [name |-> "b", arity |-> 0]>>, \* defined |-> TRUE, \* source |->
+ * null ] \* @@ \* 7 :> [kind |-> OpApplKind, \* <<Arg(a), b>> \* operands |->
+ * << 9, 12 >>, \* operator |-> 8, \* unboundedBoundSymbols |-> <<>>, \*
+ * boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 8 :> [kind |->
+ * BuiltInKind, \* name |-> "$Tuple", \* arity |-> -1 \* ] \* @@ \* 9 :> [kind
+ * |-> OpApplKind, \* Arg(a) \* operands |-> <<11 >>, \* operator |-> 10, \*
+ * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges
+ * |-> << >>] \* @@ \* 10 :> [kind |-> FormalParamKind, \* name |-> "Arg", \*
+ * arity |-> 1] \* \* @@ \* 11 :> [kind |-> FormalParamKind, \* name |-> "a", \*
+ * arity |-> 0] \* @@ \* 12 :> [kind |-> FormalParamKind, \* name |-> "b", \*
+ * arity |-> 0] \* @@ \* 13 :> [kind |-> OpApplKind, \* 1 + label2 :: p + C \*
+ * operands |-> <<14, 15>>, \* operator |-> 6, \* unboundedBoundSymbols |->
+ * <<>>, \* boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 14 :>
+ * [kind |-> NumeralKind, \* val |-> 1] \* @@ \* 15 :> [kind |-> LabelKind, \*
+ * label2 :: p + C \* name |-> "label2", \* body |-> 16, \* arity |-> 0, \*
+ * params |-> <<>>, \* labels |-> << >>] \* @@ \* 16 :> [kind |-> OpApplKind, \*
+ * p + C \* operands |-> <<17, 18>>, \* operator |-> 6, \* unboundedBoundSymbols
+ * |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 17
+ * :> [kind |-> FormalParamKind, \* name |-> "p", \* arity |-> 0] \* @@ \* 18 :>
+ * [kind |-> ConstantDeclKind, \* name |-> "C", \* arity |-> 0 ] \* @@ \* 19 :>
+ * [kind |-> OpApplKind, \* {1,2} \* operands |-> << 14, 20 >>, \* operator |->
+ * 8, \* Actually, specifying <<1, 2>> \* unboundedBoundSymbols |-> << >>, \*
+ * boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 20 :> [kind |->
+ * NumeralKind, \* val |-> 2] \* @@ \* 21 :> [kind |-> OpApplKind, \* {1,2} \*
+ * operands |-> << 22>>, \* operator |-> 8, \* Actually, specifying <<1, 2>> \*
+ * unboundedBoundSymbols |-> << >>, \* boundedBoundSymbols |-> << >>, \* ranges
+ * |-> << >>] \* @@ \* 22 :> [kind |-> NumeralKind, \* val |-> 3] \* @@ \* 23 :>
+ * [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" \* name |-> "Foo", \*
+ * body |-> 24, \* labels |-> << >>, \* arity |-> 1, \* params |-> <<[name |->
+ * "u", arity |-> 0]>>, \* defined |-> TRUE, \* source |-> null ] \* @@ \* 24 :>
+ * [kind |-> StringKind, \* val |-> "FooBody(u)"] \* @@ \* 25 :> [kind |->
+ * StringKind, \* val |-> "string"] \* @@ \* 26 :> [kind |-> ModuleInstanceKind,
+ * \* Inst(d) == INSTANCE M WITH C <- "expr(d)" \* name |-> "Inst", \* arity |->
+ * 1, \* params |-> <<[name |-> "d", arity |-> 0] >>, \* defined |-> TRUE ]
+ * \* @@ \* 27 :> [kind |-> SubstInKind, \* body |-> 24, \* subst |-> "expr(d)"]
+ * \* @@ \* 28 :> [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" \* name
+ * |-> "Inst!Foo", \* body |-> 27, \* labels |-> << >>, \* arity |-> 2, \*
+ * params |-> << [name |-> "d", arity |-> 0], \* [name |-> "u", arity |-> 0]>>,
+ * \* defined |-> TRUE, \* source |-> 23 ] \* @@ \* 29 :> [kind |->
+ * UserDefinedOpKind, \* Op(Arg(_), p) == ... \* name |-> "Inst!Op", \* body |->
+ * 30, \* labels |-> "lab" :> 4, \* arity |-> 3, \* params |-> <<[name |-> "d",
+ * arity |-> 0], \* [name |-> "Arg", arity |-> 1], \* [name |-> "p", arity |->
+ * 0]>>, \* defined |-> TRUE, \* source |-> 1 ] \* @@ \* 30 :> [kind |->
+ * SubstInKind, \* body |-> 3, \* subst |-> "expr(d)"] \* @@ \* 31 :> [kind |->
+ * StringKind, \* val |-> "Argm"] \* \* MCNodeId == 1..31 \* \* MCGlobalContext
+ * == \* "Op" :> 1 \* @@ \* "Foo" :> 23 \* @@ \* "Inst" :> 26 \* @@ \*
+ * "Inst!Foo" :> 28 \* @@ \* "Inst!Op" :> 29 \* \* \* Foo("string") \* \* MCops
+ * == <<"Foo">> \* \* MCargs == << <<25>>>> \* \* MCexpectedArity == 0 \* \* \*
+ * Op(Foo, "string") \* \* MCops == <<"Op">> \* \* MCargs == << <<23, 25>> >> \*
+ * \* MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3) \* \* MCops ==
+ * <<"Op", "lab">> \* \* MCargs == << <<23, 25>> , <<14, 20, 22 >> >> \* \*
+ * MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!<< \* \* MCops ==
+ * <<"Op", "lab", "<<">> \* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >>
+ * >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!<<!>> \*
+ * \* MCops == <<"Op", "lab", "<<", ">>">> \* \* MCargs == << <<23, 25>> , <<14,
+ * 20, 22>> , << >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo,
+ * "string")!lab(1,2,3)!label2 \* \* MCops == <<"Op", "lab", "label2">> \* \*
+ * MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> >> \* \* MCexpectedArity ==
+ * 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!label2!<< \* \* MCops == <<"Op",
+ * "lab", "label2", "<<">> \* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , <<
+ * >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo,
+ * "string")!lab(1,2,3)!:!+(3,2) \* \* MCops == <<"Op", "lab", ":", "+" >> \* \*
+ * MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >> >> \* \*
+ * MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!:!+(3,2)!>> \* \*
+ * MCops == <<"Op", "lab", ":", "+", ">>" >> \* \* MCargs == << <<23, 25>> ,
+ * <<14, 20, 22>> , << >> , <<22, 20 >>, << >> >> \* \* MCexpectedArity == 0 \*
+ * \* \* Op(Foo, "string")!(1,2,3) \* \* MCops == <<"Op", "null" >> \* \* MCargs
+ * == << <<23, 25>> , <<14, 20, 22>> >> \* \* MCexpectedArity == 0 \* \* \*
+ * Op(Foo, "string")!(1,2,3)!>>!>>!<< \* \* MCops == <<"Op", "null", ">>", ">>",
+ * "<<" >> \* \* MCargs == << <<23, 25>> , <<14, 20, 22>>, << >>, << >>, << >>
+ * >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo, "string")!<<!>> \* \* MCops ==
+ * <<"Op", "<<", ">>" >> \* \* MCargs == << <<23, 25>>, << >>, << >> >> \* \*
+ * MCexpectedArity == 0 \* \* \* Inst("Argm")!Foo("string") \* \* MCops ==
+ * <<"Inst", "Foo">> \* \* MCargs == << <<31>>, <<25>>>> \* \* MCexpectedArity
+ * == 0 \* \* \* Inst("Argm")!Op(Foo, "string") \* \* MCops == <<"Inst", "Op">>
+ * \* \* MCargs == << <<31>>, <<23, 25>> >> \* \* MCexpectedArity == 0 \* \* \*
+ * Inst("Argm")!Op(Foo, "string")!lab(1,2,3) \* \* MCops == <<"Inst", "Op",
+ * "lab">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22 >> >> \* \*
+ * MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<< \*
+ * \* MCops == <<"Inst", "Op", "lab", "<<">> \* \* MCargs == << <<31>>, <<23,
+ * 25>> , <<14, 20, 22>> , << >> >> \* \* MCexpectedArity == 0 \* \* \*
+ * Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<<!>> \* \* MCops == <<"Inst",
+ * "Op", "lab", "<<", ">>">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20,
+ * 22>> , << >>, << >> >> \* \* MCexpectedArity == 0 \* \* \*
+ * Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!label2 \* \* MCops == <<"Inst",
+ * "Op", "lab", "label2">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20,
+ * 22>> , << >> >> \* \* MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo,
+ * "string")!lab(1,2,3)!label2!<< \* \* MCops == <<"Inst", "Op", "lab",
+ * "label2", "<<">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , <<
+ * >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo,
+ * "string")!lab(1,2,3)!:!+(3,2) \* \* MCops == <<"Inst", "Op", "lab", ":", "+"
+ * >> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20
+ * >> >> \* \* MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo,
+ * "string")!lab(1,2,3)!:!+(3,2)!>> \* \* MCops == <<"Inst", "Op", "lab", ":",
+ * "+", ">>" >> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> ,
+ * <<22, 20 >>, << >> >> \* \* MCexpectedArity == 0 \* \* \*
+ * Inst("Argm")!Op(Foo, "string")!(1,2,3) \* \* MCops == <<"Inst", "Op", "null"
+ * >> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> >> \* \*
+ * MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, "string")!(1,2,3)!>>!>>!<<
+ * \* \* MCops == <<"Inst", "Op", "null", ">>", ">>", "<<" >> \* \* MCargs == <<
+ * <<31>>, <<23, 25>> , <<14, 20, 22>>, << >>, << >>, << >> >> \* \*
+ * MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, "string")!<<!>> \* \*
+ * MCops == <<"Inst", "Op", "<<", ">>" >> \* \* MCargs == << <<31>>, <<23, 25>>,
+ * << >>, << >> >> \* \* MCexpectedArity == 0
+ ***************************************** 
+ * )
+ * 
+ * (***************************************************************************)
+ * (* ------------------------ MODULE M ------------------------ *) (* CONSTANT
+ * C, Arg1(_) *) (* Op(Arg, p) == \A x \in {1,2}, <<y, z>> \in {3} : *) (*
+ * lab(x,y,z) :: LET a + b == <<Arg1(a), b>> *) (* IN 1 + label2 :: p + C *) (*
+ * Bar(AOp(_)) == AOp(1) *) (* Foo(u) == Bar(Arg1) *) (* Foo2 == Bar(LAMBDA m, n
+ * : <<m, n>>) *) (* THEOREM Thm == ASSUME Arg1(1) , Bar(Arg1) PROVE << C,
+ * Bar(Arg1)>> *) (* UU == lab:: C *) (*
+ * ============================================================= *) (* *) (*
+ * Foo3 == \A x : LET Bar3 == 1 IN 2 *) (* Inst(d) == INSTANCE M WITH C <-
+ * "expr(d)" *)
+ * (***************************************************************************)
+ * 
+ * 
+ * MCNode == 1 :> [kind |-> UserDefinedOpKind, \* Op(Arg, p) == ... name |->
+ * "Op", body |-> 3, labels |-> "lab" :> 4, arity |-> 2, params |-> <<[name |->
+ * "Arg", arity |-> 0], [name |-> "p", arity |-> 0]>>, defined |-> TRUE, source
+ * |-> null ]
+ * 
+ * @@ 2 :> [kind |-> BuiltInKind, name |-> "$BoundedForall", arity |-> -1 ] @@ 3
+ * :> [kind |-> OpApplKind, \* \A x \in {1,2}, <<y, z>> \in {3} : ... operands
+ * |-> << 4 >>, operator |-> 2, unboundedBoundSymbols |-> <<>>,
+ * boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>, ranges |-> << 19, 21
+ * >>] @@ 4 :> [kind |-> LabelKind, \* lab(x,y,z) :: LET a + b == <<Arg(a), b>>
+ * name |-> "lab", \* IN 1 + label2 :: p + C body |-> 5, arity |-> 3, params |->
+ * <<[name |-> "x", arity |->0], [name |-> "y", arity |->0], [name |-> "z",
+ * arity |->0] >>, labels |-> "label2" :> 15 ] @@ 5 :> [kind |-> LetInKind, \*
+ * LET a + b == <<Arg(a), b>> context |-> "+" :> 6, \* IN 1 + label2 :: p + C
+ * body |-> 13 ] @@ 6 :> [kind |-> UserDefinedOpKind, \* a + b == <<Arg(a), b>>
+ * name |-> "+", body |-> 7, labels |-> << >>, arity |-> 2, params |-> <<[name
+ * |-> "a", arity |-> 0], [name |-> "b", arity |-> 0]>>, defined |-> TRUE,
+ * source |-> null ] @@ 7 :> [kind |-> OpApplKind, \* <<Arg(a), b>> operands |->
+ * << 9, 12 >>, operator |-> 8, unboundedBoundSymbols |-> <<>>,
+ * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 8 :> [kind |->
+ * BuiltInKind, name |-> "$Tuple", arity |-> -1 ] @@ 9 :> [kind |-> OpApplKind,
+ * \* Arg1(a) operands |-> <<11 >>, operator |-> 10, unboundedBoundSymbols |->
+ * <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 10 :> [kind |->
+ * ConstantDeclKind, name |-> "Arg1", arity |-> 1]
+ * 
+ * @@ 11 :> [kind |-> FormalParamKind, name |-> "a", arity |-> 0] @@ 12 :> [kind
+ * |-> FormalParamKind, name |-> "b", arity |-> 0] @@ 13 :> [kind |->
+ * OpApplKind, \* 1 + label2 :: p + C operands |-> <<14, 15>>, operator |-> 6,
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>] @@ 14 :> [kind |-> NumeralKind, val |-> 1] @@ 15 :> [kind |-> LabelKind,
+ * \* label2 :: p + C name |-> "label2", body |-> 16, arity |-> 0, params |->
+ * <<>>, labels |-> << >>] @@ 16 :> [kind |-> OpApplKind, \* p + C operands |->
+ * <<17, 18>>, operator |-> 6, unboundedBoundSymbols |-> <<>>,
+ * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 17 :> [kind |->
+ * FormalParamKind, name |-> "p", arity |-> 0] @@ 18 :> [kind |->
+ * ConstantDeclKind, name |-> "C", arity |-> 0 ] @@ 19 :> [kind |-> OpApplKind,
+ * \* {1,2} operands |-> << 14, 20 >>, operator |-> 8, \* Actually, specifying
+ * <<1, 2>> unboundedBoundSymbols |-> << >>, boundedBoundSymbols |-> << >>,
+ * ranges |-> << >>] @@ 20 :> [kind |-> NumeralKind, val |-> 2] @@ 21 :> [kind
+ * |-> OpApplKind, \* {1,2} operands |-> << 22>>, operator |-> 8, \* Actually,
+ * specifying <<1, 2>> unboundedBoundSymbols |-> << >>, boundedBoundSymbols |->
+ * << >>, ranges |-> << >>] @@ 22 :> [kind |-> NumeralKind, val |-> 3] @@ 23 :>
+ * [kind |-> UserDefinedOpKind, \* Foo(u) == Bar(Arg1) name |-> "Foo", body |->
+ * 24, labels |-> << >>, arity |-> 1, params |-> <<[name |-> "u", arity |->
+ * 0]>>, defined |-> TRUE, source |-> null ] @@ 24 :> [kind |-> OpApplKind, \*
+ * Bar(Arg1) operands |-> <<34>>, operator |-> 32, unboundedBoundSymbols |->
+ * <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 25 :> [kind |->
+ * StringKind, val |-> "string"] @@ 26 :> [kind |-> ModuleInstanceKind, \*
+ * Inst(d) == INSTANCE M WITH C <- "expr(d)" name |-> "Inst", arity |-> 1,
+ * params |-> <<[name |-> "d", arity |-> 0] >>, defined |-> TRUE ] @@ 27 :>
+ * [kind |-> SubstInKind, body |-> 24, subst |-> "expr(d)"] @@ 28 :> [kind |->
+ * UserDefinedOpKind, \* Foo(u) == "FooBody(u)" name |-> "Inst!Foo", body |->
+ * 27, labels |-> << >>, arity |-> 2, params |-> << [name |-> "d", arity |-> 0],
+ * [name |-> "u", arity |-> 0]>>, defined |-> TRUE, source |-> 23 ] @@ 29 :>
+ * [kind |-> UserDefinedOpKind, \* Op(Arg, p) == ... name |-> "Inst!Op", body
+ * |-> 30, labels |-> "lab" :> 4, arity |-> 3, params |-> <<[name |-> "d", arity
+ * |-> 0], [name |-> "Arg", arity |-> 0], [name |-> "p", arity |-> 0]>>, defined
+ * |-> TRUE, source |-> 1 ] @@ 30 :> [kind |-> SubstInKind, body |-> 3, subst
+ * |-> "expr(d)"] @@ 31 :> [kind |-> StringKind, val |-> "Argm"] @@ 32 :> [kind
+ * |-> UserDefinedOpKind, \* Bar(AOp(_)) == AOp(1) name |-> "Bar", body |-> 33,
+ * labels |-> << >>, arity |-> 1, params |-> <<[name |-> "AOp", arity |-> 1]>>,
+ * defined |-> TRUE, source |-> null ] @@ 33 :> [kind |-> OpApplKind, \* Arg1(1)
+ * operands |-> <<14>>, operator |-> 10, unboundedBoundSymbols |-> <<>>,
+ * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 34 :> [kind |->
+ * OpArgKind, op |-> 10, arity |-> 1, name |-> "Arg1"] @@ 35 :> [kind |->
+ * UserDefinedOpKind, \* Foo2 == Bar(LAMBDA m, n : <<m, n>>) name |-> "Foo2",
+ * body |-> 36, labels |-> << >>, arity |-> 0, params |-> <<>>, defined |->
+ * TRUE, source |-> null ] @@ 36 :> [kind |-> OpApplKind, \* Bar(LAMBDA m, n :
+ * <<m, n>>) operands |-> <<41>>, operator |-> 32, unboundedBoundSymbols |->
+ * <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 37 :> [kind |->
+ * UserDefinedOpKind, \* LAMBDA m, n : <<m, n>> name |-> "LAMBDA", body |-> 38,
+ * labels |-> << >>, arity |-> 2, params |-> <<[name |-> "m", arity |-> 0],
+ * [name |-> "n", arity |-> 0]>>, defined |-> TRUE, source |-> null ] @@ 38 :>
+ * [kind |-> OpApplKind, \* <<m, n>> operands |-> << 39, 40 >>, operator |-> 8,
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>] @@ 39 :> [kind |-> FormalParamKind, name |-> "m", arity |-> 0] @@ 40 :>
+ * [kind |-> FormalParamKind, name |-> "n", arity |-> 0] @@ 41 :> [kind |->
+ * OpArgKind, \* LAMBDA m, n : <<m, n>> op |-> 37, arity |-> 2, name |->
+ * "LAMBDA"] @@ 42 :> [kind |-> UserDefinedOpKind, \* Inst!Foo2 == Bar(LAMBDA m,
+ * n : <<m, n>>) name |-> "Inst!Foo2", body |-> 43, labels |-> << >>, arity |->
+ * 1, params |-> <<[name |-> "d", arity |-> 0]>>, defined |-> TRUE, source |->
+ * 35 ] @@ 43 :> [kind |-> SubstInKind, body |-> 36, subst |-> "expr(d)"] @@ 44
+ * :> [kind |-> ThmOrAssumpDefKind, \* Thm == ASSUME Arg1(1) ... name |-> "Thm",
+ * body |-> 45, labels |-> << >>, arity |-> 0, params |-> << >> , defined |->
+ * TRUE, source |-> null ] @@ 45 :> [kind |-> AssumeProveKind, assumes |-> <<33,
+ * 24>>, prove |-> 46 ] @@ 46 :> [kind |-> OpApplKind, \* <<c, Bar(Arg1)>>
+ * operands |-> << 59, 24 >>, operator |-> 8, unboundedBoundSymbols |-> <<>>,
+ * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 47 :> [kind |->
+ * ThmOrAssumpDefKind, \* Thm == ASSUME Arg1(1) ... name |-> "Inst!Thm", body
+ * |-> 48, labels |-> << >>, arity |-> 1, params |-> << [name |-> "d", arity |->
+ * 0]>> , defined |-> TRUE, source |-> 44 ] @@ 48 :> [kind |-> SubstInKind, body
+ * |-> 45, subst |-> "expr(d)"] @@ 49 :> [kind |-> UserDefinedOpKind, \* Foo3 ==
+ * \A x : LET ... name |-> "Foo3", body |-> 51, labels |-> << >>, arity |-> 0,
+ * params |-> <<>>, defined |-> TRUE, source |-> null ] @@ 50 :> [kind |->
+ * LetInKind, \* Foo3 == \A x : LET Bar3 == 1 IN 2 context |-> "Bar3" :> 53,
+ * body |-> 13 ] @@ 51 :> [kind |-> OpApplKind, \* \A x : LET Bar3 == 1 IN 2
+ * operands |-> << 50 >>, operator |-> 52, unboundedBoundSymbols |-> <<"x">>,
+ * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 52 :> [kind |->
+ * BuiltInKind, \* \A x : name |-> "$UnboundedForall", arity |-> -1] @@ 53 :>
+ * [kind |-> UserDefinedOpKind, \* Bar3 == 1 name |-> "Bar3", body |-> 14,
+ * labels |-> << >>, arity |-> 0, params |-> <<>>, defined |-> TRUE, source |->
+ * null ] @@ 54 :> [kind |-> FormalParamKind, \* AOp name |-> "AOp", arity |->
+ * 1] @@ 55 :> [kind |-> UserDefinedOpKind, \* Bar(AOp(_)) == AOp(1) name |->
+ * "Inst!Bar", body |-> 56, labels |-> << >>, arity |-> 2, params |-> <<[name
+ * |-> "d", arity |-> 0], [name |-> "AOp", arity |-> 1]>>, defined |-> TRUE,
+ * source |-> null ] @@ 56 :> [kind |-> SubstInKind, body |-> 33, subst |->
+ * "expr(d)"] @@ 57 :> [kind |-> UserDefinedOpKind, \* UU == lab:: C name |->
+ * "UU", body |-> 58, labels |-> "lab":> 58, arity |-> 0, params |-> <<>>,
+ * defined |-> TRUE, source |-> null ] @@ 58 :> [kind |-> LabelKind, \* lab :: C
+ * name |-> "lab", body |-> 59, arity |-> 0, params |-> <<>>, labels |-> << >>]
+ * 
+ * @@ 59 :> [kind |-> OpApplKind, \* C operands |-> <<>>, operator |-> 18,
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>] @@ 60 :> [kind |-> UserDefinedOpKind, \* UU == lab:: C name |->
+ * "Inst!UU", body |-> 58, labels |-> "lab":> 58, arity |-> 1, params |->
+ * <<[name |-> "d", arity |-> 0] >>, defined |-> TRUE, source |-> null ]
+ * 
+ * 
+ * MCNodeId == 1..60
+ * 
+ * MCGlobalContext == "Op" :> 1 @@ "C" :> 18 @@ "UU" :> 57 @@ "Foo" :> 23 @@
+ * "Inst" :> 26 @@ "Inst!Foo" :> 28 @@ "Inst!Bar" :> 55 @@ "Inst!Op" :> 29 @@
+ * "Inst!UU" :> 60 @@ "Foo2" :> 35 @@ "Inst!Foo2" :> 42 @@ "Thm" :> 44 @@
+ * "Inst!Thm" :> 47 @@ "Foo3" :> 49 @@ "Bar" :> 32 @@ "Arg1" :> 10 @@ "AOp" :>
+ * 54 @@ "p" :> 17
+ * 
+ * \* Foo \* MCops == <<"Foo">> \* MCargs == << << >> >> \* MCexpectedArity == 1
+ * 
+ * \* Op() \* MCops == <<"Op">> \* MCargs == << <<>> >> \* MCexpectedArity == 2
+ * 
+ * \* Op!lab \* MCops == <<"Op", "lab">> \* MCargs == << <<>> , <<>> >> \*
+ * MCexpectedArity == 5
+ * 
+ * \* Op!lab!<< \* MCops == <<"Op", "lab", "<<">> \* MCargs == << <<>> , <<>> ,
+ * << >> >> \* MCexpectedArity == 5
+ * 
+ * \* Op!lab!<<!>> \* MCops == <<"Op", "lab", "<<", ">>">> \* MCargs == << << >>
+ * , << >> , << >>, << >> >> \* MCexpectedArity == 5
+ * 
+ * \* Op!lab!label2 \* MCops == <<"Op", "lab", "label2">> \* MCargs == << << >>
+ * , << >> , << >> >> \* MCexpectedArity == 5
+ * 
+ * \* Op!lab!label2!<< \* MCops == <<"Op", "lab", "label2", "<<">> \* MCargs ==
+ * << << >> , << >> , << >>, << >> >> \* MCexpectedArity == 5
+ * 
+ * \* Op!lab!:!+ \* MCops == <<"Op", "lab", ":", "+" >> \* MCargs == << << >> ,
+ * << >> , << >> , << >> >> \* MCexpectedArity == 7
+ * 
+ * \* Op!lab!:!+ \* MCops == <<"Op", "lab", ":", "+", ">>" >> \* MCargs == << <<
+ * >> , << >> , << >> , << >>, << >> >> \* MCexpectedArity == 7
+ * 
+ * \* Op!@ \* MCops == <<"Op", "@" >> \* MCargs == << << >> , << >> >> \*
+ * MCexpectedArity == 5
+ * 
+ * \* Op!@!>>!>>!<< \* MCops == <<"Op", "@", ">>", ">>", "<<" >> \* MCargs == <<
+ * << >> , << >>, << >>, << >>, << >> >> \* MCexpectedArity == 5
+ * 
+ * \* Op!<<!>> \* MCops == <<"Op", "<<", ">>" >> \* MCargs == << << >>, << >>,
+ * << >> >> \* MCexpectedArity == 2
+ * 
+ * \* Inst!Foo \* MCops == <<"Inst", "Foo">> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 2
+ * 
+ * \* Inst!Op \* MCops == <<"Inst", "Op">> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 3
+ * 
+ * \* Inst!Op!lab \* MCops == <<"Inst", "Op", "lab">> \* MCargs == << << >>, <<
+ * >> , << >> >> \* MCexpectedArity == 6
+ * 
+ * \* Inst!Op!lab!<< \* MCops == <<"Inst", "Op", "lab", "<<">> \* MCargs == <<
+ * << >>, << >> , << >> , << >> >> \* MCexpectedArity == 6
+ * 
+ * \* Inst!Op!lab!<<!>> \* MCops == <<"Inst", "Op", "lab", "<<", ">>">> \*
+ * MCargs == << << >>, << >> , << >> , << >>, << >> >> \* MCexpectedArity == 6
+ * 
+ * \* Inst!Op!lab!label2 \* MCops == <<"Inst", "Op", "lab", "label2">> \* MCargs
+ * == << << >>, << >> , << >> , << >> >> \* MCexpectedArity == 6
+ * 
+ * \* Inst!Op!lab!label2!<< \* MCops == <<"Inst", "Op", "lab", "label2", "<<">>
+ * \* MCargs == << << >>, << >> , << >> , << >>, << >> >> \* MCexpectedArity ==
+ * 6
+ * 
+ * \* Inst!Op!lab!:!+ \* MCops == <<"Inst", "Op", "lab", ":", "+" >> \* MCargs
+ * == << << >>, << >> , << >> , << >> , << >> >> \* MCexpectedArity == 8
+ * 
+ * \* Inst!Op!lab!:!+!>> \* MCops == <<"Inst", "Op", "lab", ":", "+", ">>" >> \*
+ * MCargs == << << >>, << >> , << >> , << >> , << >>, << >> >> \*
+ * MCexpectedArity == 8
+ * 
+ * \* Inst!Op!@ \* MCops == <<"Inst", "Op", "@" >> \* MCargs == << << >>, << >>
+ * , << >> >> \* MCexpectedArity == 6
+ * 
+ * \* Inst!Op!@!>>!>>!<< \* MCops == <<"Inst", "Op", "@", ">>", ">>", "<<" >> \*
+ * MCargs == << << >>, << >> , << >>, << >>, << >>, << >> >> \* MCexpectedArity
+ * == 6
+ * 
+ * \* Inst!Op!<<!>> \* MCops == <<"Inst", "Op", "<<", ">>" >> \* MCargs == << <<
+ * >>, << >>, << >>, << >> >> \* MCexpectedArity == 3
+ * 
+ * \* Foo!1 \* MCops == <<"Foo", "1">> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 2
+ * 
+ * \* Inst!Foo!1 \* MCops == <<"Inst", "Foo", "1">> \* MCargs == << << >>, <<
+ * >>, << >> >> \* MCexpectedArity == 3
+ * 
+ * \* Foo2!1 \* MCops == <<"Foo2", "1">> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 2
+ * 
+ * \* Foo2!1!(1, 2) \* MCops == <<"Foo2", "1", "null">> \* MCargs == << << >>,
+ * << >>, <<14, 20>> >> \* MCexpectedArity == 0
+ * 
+ * \* Foo2!1!(1, 2)!2 \* MCops == <<"Foo2", "1", "null", "2">> \* MCargs == <<
+ * << >>, << >>, <<14, 20>>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst!Foo2!1 \* MCops == <<"Inst", "Foo2", "1">> \* MCargs == << << >>, <<
+ * >>, << >> >> \* MCexpectedArity == 3
+ * 
+ * \* Inst!Foo2!1 \* MCops == <<"Inst", "Foo2", "1", "@">> \* MCargs == << <<
+ * >>, << >>, << >>, << >> >> \* MCexpectedArity == 3
+ * 
+ * \* Inst!Foo2!1!2 \* MCops == <<"Inst", "Foo2", "1", "@", "2">> \* MCargs ==
+ * << << >>, << >>, << >>, << >>, << >> >> \* MCexpectedArity == 3
+ * 
+ * \* Inst("Argm")!Foo2!1!(1, 2) \* MCops == <<"Inst", "Foo2", "1", "null">> \*
+ * MCargs == << <<31>>, << >>, << >>, <<14, 20>> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Foo2!1!(1, 2)!2 \* MCops == <<"Inst", "Foo2", "1", "null",
+ * "2">> \* MCargs == << <<31>>, << >>, << >>, <<14, 20>>, << >> >> \*
+ * MCexpectedArity == 0
+ * 
+ * \* Thm!1 \* MCops == <<"Thm", "1" >> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 0
+ * 
+ * \* Thm!2 \* MCops == <<"Thm", "2" >> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 0
+ * 
+ * \* Thm!3!<< \* MCops == <<"Thm", "3", "<<" >> \* MCargs == << << >>, << >>,
+ * << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Thm!1 \* MCops == <<"Inst", "Thm", "1" >> \* MCargs == <<
+ * <<31>>, << >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Thm!2 \* MCops == <<"Inst", "Thm", "2" >> \* MCargs == <<
+ * <<31>>, << >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Thm!3!<< \* MCops == <<"Inst", "Thm", "3", "<<" >> \* MCargs
+ * == << <<31>>, << >>, << >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!UU \* MCops == <<"Inst", "UU">> \* MCargs == << <<31>>, << >>
+ * >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Bar(Arg1)!1<< \* MCops == <<"Inst", "Bar", "1" >> \* MCargs
+ * == << <<31>>, <<10 >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Bar(Arg1)!1<< \* MCops == <<"Inst", "Bar", "1" >> \* MCargs
+ * == << <<31>>, <<10 >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Thm \* Note: doesn't report an error because \* MCops ==
+ * <<"Inst", "Thm" >> \* Inst!Thm isn't expanded \* MCargs == << <<31>>, << >>
+ * >> \* MCexpectedArity == 0
+ * 
+ * \* Inst("Argm")!Thm!: ERROR! \* MCops == <<"Inst", "Thm", ":" >> \* MCargs ==
+ * << <<31>>, << >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * 
+ * \* Foo2 \* MCops == <<"Foo2">> \* MCargs == <<<< >> >> \* MCexpectedArity ==
+ * 0
+ * 
+ * \* Foo3!(3)!:Bar3 \* MCops == <<"Foo3", "null", "Bar3">> \* MCargs == << <<
+ * >>, <<22>>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Bar(Arg1) \* \* MCops == <<"Bar">> \* \* MCargs == << <<10>> >> \* \*
+ * MCexpectedArity == 0
+ * 
+ * \* Arg1(C) \* MCops == <<"Arg1">> \* MCargs == << <<18>> >> \*
+ * MCexpectedArity == 0
+ * 
+ * \* AOp \* MCops == <<"AOp">> \* MCargs == << <<18 >> >> \* MCexpectedArity ==
+ * 0
+ * 
+ * \* C \* MCops == <<"C">> \* MCargs == << << >> >> \* MCexpectedArity == 0
+ * 
+ * \* p \* MCops == <<"p">> \* MCargs == << << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Foo3 \* MCops == <<"Foo3">> \* MCargs == << << >> >> \* MCexpectedArity ==
+ * 0
+ * 
+ * \***** THIS IS A BUG--NOT HANDLED CORRECTLY ****** \* UU!lab \* MCops ==
+ * <<"UU", "lab">> \* MCargs == << << >>, << >> >> \* MCexpectedArity == 0
+ * 
+ * \* Op(2, 2)!(2, 2, 2) \* MCops == <<"Op", "null">> \* MCargs == << <<20,
+ * 20>>, <<20, 20, 20 >> >> \* MCexpectedArity == 0
+ * 
+ * \* Foo3!(2) \* MCops == <<"Foo3", "null">> \* MCargs == << << >>, <<20>> >>
+ * \* MCexpectedArity == 0
+ * 
+ * \* Op(2, 2)!(2,2,2)!+(2, 2) \* MCops == <<"Op", "null", "+">> \* MCargs == <<
+ * <<20,20 >>, <<20,20,20>> , <<20,20>>>> \* MCexpectedArity == 0
+ * 
+ * \* MCops == <<"Op">> \* MCargs == << << >> >> \* MCexpectedArity == -1
+ * 
+ * \* MCops == <<"Inst", "Op">> \* MCargs == << << >> , << >> >> \*
+ * MCexpectedArity == -1
+ * 
+ * \* MCops == <<"Inst", "Thm">> \* MCargs == << << >> , << >> >> \*
+ * MCexpectedArity == -1
+ * 
+ * \* MCops == <<"Inst", "Op", "@", "+">> \* MCargs == << << >> , << >> , << >>
+ * , << >> >> \* MCexpectedArity == -1
+ * 
+ * \* MCops == <<"Op", "lab", ":", "+">> \* MCargs == << << >> , << >>, << >>,
+ * << >> >> \* MCexpectedArity == 7
+ * 
+ * \* MCops == <<"Op", "lab", ":", "+">> \* MCargs == << << >> , << >>, << >>,
+ * << >> >> \* MCexpectedArity == -1
+ * 
+ * \* MCops == <<"Foo2">> \* MCargs == << << >> >> \* MCexpectedArity == 0
+ * 
+ * \* MCops == <<"Foo3", ":">> \* MCargs == << << >>, << >> >> \*
+ * MCexpectedArity == 0
+ * 
+ * \* MCops == <<"Op", "lab", ":", "+">> \* MCargs == << << >>, << >>, << >>, <<
+ * >> >> \* MCexpectedArity == -1
+ * 
+ * \* MCops == <<"Inst", "Thm">> \* MCargs == << <<14 >>, << >> >> \*
+ * MCexpectedArity == 0
+ * 
+ * MCops == <<"Inst", "Thm", ":">> MCargs == << <<14 >>, << >>, << >> >>
+ * MCexpectedArity == 0 MCdebug == FALSE \* TRUE
+ * 
+ * (*********************************
+ * (***************************************************************************)
+ * (* Module M *) (* CONSTANT C *) (* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>>
+ * \in {3} : *) (* lab(x,y,z) :: LET a + b == <<Arg(a), b>> *) (* IN 1 + label2
+ * :: p + C *) (* *) (* Foo(u) == "FooBody(u)" *) (* Inst(w) == INSTANCE M WITH
+ * C <- <<w, CONST>> *)
+ * (***************************************************************************)
+ * 
+ * \* MCNode == \* 1 :> [kind |-> NumeralKind, \* val |-> 1] \* @@ \* 2 :> [kind
+ * |-> BuiltInKind, \* \E \* name |-> "$UnboundedExists", \* arity |-> -1] \*
+ * \* @@ \* 3 :> [kind |-> OpApplKind, \* \E x : Node 1 \* operands |-> <<1>>,
+ * \* operator |-> 2, \* unboundedBoundSymbols |-> <<"x">>, \*
+ * boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 4 :> [kind |->
+ * UserDefinedOpKind, \* Inst!UserOp(p1, OpP) == Node 8 \* name |->
+ * "Inst!UserOp", \* body |-> 8, \* labels |-> "Label1" :> 7, \* arity |-> 2, \*
+ * params |-> <<[name |-> "p1", arity |-> 0], \* [name |-> "OpP", arity |->
+ * 0]>>, \* defined |-> TRUE, \* source |-> 40 ] \* @@ \* 5 :> [kind |->
+ * ModuleInstanceKind, \* Inst(p1) == INSTANCE \* name |-> "Inst", \* arity |->
+ * 1, \* params |-> <<[name |-> "p1", arity |-> 0] >>, \* defined |-> TRUE ]
+ * \* @@ \* 6 :> [kind |-> UserDefinedOpKind, \* 1ArgOp(1OpP(_, _)) == Node 1 \*
+ * name |-> "1ArgOp", \* body |-> 41, \* labels |-> << >>, \* arity |-> 1, \*
+ * params |-> <<[name |-> "1OpP", arity |-> 0]>>, \* defined |-> TRUE, \* source
+ * |-> null ] \* @@ \* 7 :> [kind |-> LabelKind, \* name |-> "Label1", \* body
+ * |-> 14, \* arity |-> 1, \* params |-> <<[name |-> "labParam1", arity |->
+ * 0]>>, \* labels |-> "Label2" :> 9 \* ] \* @@ \* 8 :> [kind |-> SubstInKind,
+ * \* body |-> 11, \* subst |-> "x <- something(p1)"] \* \* @@ \* 9 :> [kind |->
+ * LabelKind, \* name |-> "Label2", \* body |-> 15, \* arity |-> 1, \* params
+ * |-> <<[name |-> "labParam2", arity |-> 0]>>, \* labels |-> << >> \* ] \* @@
+ * \* 10 :> [kind |-> NumeralKind, \* val |-> 10] \* @@ \* 11 :> [kind |->
+ * SubstInKind, \* body |-> 15, \* subst |-> "y <- something(p1)"] \* @@ \* 12
+ * :> [kind |-> StringKind, \* val |-> "Body of UserOp (p1, OpP)"] \* @@ \* 13
+ * :> [kind |-> StringKind, \* val |-> "Body of Label2(labParam2)"] \* @@ \* 14
+ * :> [kind |-> StringKind, \* val |-> "Body of Label1(labParam1)"] \* @@ \* 15
+ * :> [kind |-> LetInKind, \* context |-> "1ArgOp" :> 6, \* body |-> 16] \* @@
+ * \* 16 :> [kind |-> StringKind, \* val |-> "Body of Let/In"] \* @@ \* 17 :>
+ * [kind |-> UserDefinedOpKind, \* 1ArgOp("1OpP") == Node 1 \* name |-> "Foo",
+ * \* body |-> 18, \* labels |-> << >>, \* arity |-> 0, \* params |-> << >> , \*
+ * <<[name |-> "FooPar1", arity |-> 0]>>, \* defined |-> TRUE, \* source |->
+ * null] \* @@ \* 18 :> [kind |-> OpApplKind, \* operands |-> <<42 >>, \*
+ * operator |-> 21, \* unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols
+ * |-> << >>, \* ranges |-> << >>] \* @@ \* 19 :> [kind |-> StringKind, \* val
+ * |-> "Arg1"] \* @@ \* 20 :> [kind |-> StringKind, \* val |-> "Arg2"] \* @@ \*
+ * 21 :> [kind |-> UserDefinedOpKind, \* Foo2 == [rcd-lab1 |-> rcd-comp1, \*
+ * name |-> "Foo2", \* ... \* body |-> 34, \* rcd-lab3 |-> rcd-comp3] \* labels
+ * |-> << >>, \* arity |-> 1, \* params |-> <<[name |-> "p1", arity |-> 1]>>, \*
+ * defined |-> TRUE, \* source |-> null ] \* @@ \* 22 :> [kind |-> OpApplKind,
+ * \* operands |-> <<10 (* 25 *) , 28, 31>>, \* operator |-> 23, \*
+ * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges
+ * |-> << >>] \* @@ \* 23 :> [kind |-> BuiltInKind, \* name |-> "$Except", \*
+ * "$Case", \* "$SetOfRcds", \* arity |-> -1 \* ] \* @@ \* 24 :> [kind |->
+ * BuiltInKind, \* name |-> "$Pair", \* arity |-> 2 \* ] \* @@ \* 25 :> [kind
+ * |-> OpApplKind, \* operands |-> <<26,27>>, \* operator |-> 24, \*
+ * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges
+ * |-> << >>] \* @@ \* 26 :> [kind |-> StringKind, \* val |-> "rcd-lab1"] \* @@
+ * \* 27 :> [kind |-> StringKind, \* val |-> "rcd-comp1"] \* \* @@ \* 28 :>
+ * [kind |-> OpApplKind, \* operands |-> <<29,30>>, \* operator |-> 24, \*
+ * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges
+ * |-> << >>] \* @@ \* 29 :> [kind |-> StringKind, \* val |-> "rcd-lab2"] \* @@
+ * \* 30 :> [kind |-> StringKind, \* val |-> "rcd-comp2"] \* @@ \* 31 :> [kind
+ * |-> OpApplKind, \* operands |-> <<32,33>>, \* operator |-> 24, \*
+ * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges
+ * |-> << >>] \* @@ \* 32 :> [kind |-> StringKind, \* val |-> "rcd-lab3"] \* @@
+ * \* 33 :> [kind |-> StringKind, \* val |-> "rcd-comp3"] \* @@ \* 34 :> [kind
+ * |-> OpApplKind, \* operands |-> <<35>>, \* operator |-> 36, \*
+ * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << <<"b1", "b2">>,
+ * <<"b3">> >>, \* ranges |-> <<37, 38 >>] \* @@ \* 35 :> [kind |-> StringKind,
+ * \* val |-> "Fcn of b1, b2, b3"] \* @@ \* 36 :> [kind |-> BuiltInKind, \* name
+ * |-> "$Pair", \* arity |-> 2 \* ] \* @@ \* 37 :> [kind |-> StringKind, \* val
+ * |-> "Range 1"] \* @@ \* 38 :> [kind |-> StringKind, \* val |-> "Range 2"]
+ * \* @@ \* 39 :> [kind |-> StringKind, \* val |-> "Arg3"] \* @@ \* 40 :> [kind
+ * |-> UserDefinedOpKind, \* Inst!UserOp(p1, OpP) == Node 8 \* name |->
+ * "UserOp", \* body |-> 8, \* labels |-> "Label1" :> 7, \* arity |-> 1, \*
+ * params |-> <<[name |-> "OpP", arity |-> 1]>>, \* defined |-> TRUE, \* source
+ * |-> null ] \* @@ \* 41 :> [kind |-> StringKind, \* val |-> "Body of 1ArgOp
+ * may dep on (x, y)"] \* @@ \* 42 :> [kind |-> OpArgKind, \* op |-> 43, \*
+ * arity |-> 1, \* name |-> "Foo3"] \* @@ \* 43 :> [kind |-> UserDefinedOpKind,
+ * \* 1ArgOp("1OpP") == Node 1 \* name |-> "Foo3", \* body |-> 18, \* labels |->
+ * << >>, \* arity |-> 0, \* params |-> << >>, \* <<[name |-> "FooPar1", arity
+ * |-> 0]>>, \* defined |-> TRUE, \* source |-> null] \* \* MCNodeId == 1..43 \*
+ * \* MCGlobalContext == \* "Inst!UserOp" :> 4 \* @@ \* "Inst" :> 5 \* @@ \*
+ * "Foo" :> 17 \* @@ \* "Foo2" :> 21 \* @@ \* "Foo3" :> 43 \* \* @@ \* \*
+ * "1ArgOp" :> 6 \* \* \* MCops == <<"Inst", "UserOp">> \* , "1">> \* MCargs ==
+ * << <<1 >> , <<1>> >>
+ * 
+ * \* \* << <<1>>, <<19, 20, 33 >> >> \* <<1>>, <<10>>, << >>, <<4>> >> \* \*
+ * MCexpectedArity == 0 \* -1 \* 5 )
+ * 
+ * -----------------------------------------------------------------------------
+ * (***************************************************************************)
+ * (* THE ALGORITHM *)
+ * (***************************************************************************)
+ * (************************************** --algorithm Subexpression variables
+ * (*************************************************************************)
+ * (* The input variables. *)
+ * (*************************************************************************)
+ * ops = MCops, args = MCargs,
+ * (***********************************************************************) (*
+ * The sequences op and args described above. *)
+ * (***********************************************************************)
+ * expectedArity = MCexpectedArity,
+ * (***********************************************************************) (*
+ * The arity of the operator expected, or -1 if expecting a definition *) (*
+ * name. It is 0 iff an expression is expected. The result should be *) (* an
+ * OpArg node if expectedArity > 0, it should be an expression node *) (* if
+ * expectedArity = 0, and it should be a UserDefinedOpKind or a *) (*
+ * ThmOrAssumpDefKind if expectedArity < 0. *)
+ * (***********************************************************************)
+ * 
+ * (*************************************************************************)
+ * (* The major variables of the algorithm. *)
+ * (*************************************************************************)
+ * substInPrefix = << >> ,
+ * (***********************************************************************) (*
+ * The sequence of SubstInNode or APSubstInNode sequence that will be *) (*
+ * appended to body of the resulting OpApplNode *)
+ * (***********************************************************************)
+ * params = << >> ,
+ * (***********************************************************************) (*
+ * The sequence of FormalParamNode objects that are the formal *) (* parameters
+ * if this produces a Lambda expression. A Lambda *) (* expression will be
+ * produced iff this is non-empty *)
+ * (***********************************************************************)
+ * allArgs = << >> ,
+ * (***********************************************************************) (*
+ * The sequence of all arguments. *)
+ * (***********************************************************************)
+ * curNode = null,
+ * (***********************************************************************) (*
+ * If params = << >>, then the OpDefNode for the OpApplNode that is *) (*
+ * produced. If params # << >>, then the Expr node that will form the *) (* body
+ * of the Lambda expression. *) (* *) (* Note: in the Java implementation, this
+ * will actually be a ref to *) (* the node--in terms of this spec, a NodeId. *)
+ * (***********************************************************************)
+ * subExprOf = "null",
+ * (***********************************************************************) (*
+ * The node UserDefinedOpDefKind or ThmOrAssumpDefKind within which *) (* this
+ * subexpression is defined. *)
+ * (***********************************************************************)
+ * result = null,
+ * (***********************************************************************) (*
+ * The actual output node. *)
+ * (***********************************************************************)
+ * 
+ * (*************************************************************************)
+ * (* The local variables. *)
+ * (*************************************************************************)
+ * idx = 1,
+ * (***********************************************************************) (*
+ * The element of the arrays op and args that the algorithm is *) (* currently
+ * examining. *)
+ * (***********************************************************************)
+ * mode = "FindingOpName",
+ * (***********************************************************************) (*
+ * The current mode describing what kind of selector it is expecting *) (* next.
+ * Its other possible values are "FollowingLabels" and *) (* "FindingSubExpr".
+ * *) (***********************************************************************)
+ * prevMode = "",
+ * (***********************************************************************) (*
+ * The mode for the previously examined selector. *)
+ * (***********************************************************************)
+ * curContext = GlobalContext,
+ * (***********************************************************************) (*
+ * The context for looking up operator or label names. *)
+ * (***********************************************************************)
+ * curName = "" ,
+ * (***********************************************************************) (*
+ * When looking up an operator name, which may be something like *) (*
+ * "Foo!Bar!Baz", this is the part that has been found so far. *)
+ * (***********************************************************************)
+ * opDefArityFound = 0, opDefArgs = << >>,
+ * (***********************************************************************) (*
+ * The total arity and the sequence of arguments found so far for the *) (*
+ * current operator--for example, opDefArityFound will equal 2 if the *) (*
+ * algorithm has so far processed "Foo(a, b)!Bar" *)
+ * (***********************************************************************)
+ * firstFindingOpName = TRUE,
+ * (***********************************************************************) (*
+ * True iff have entered the FindingOpName only once (initially). *)
+ * (***********************************************************************)
+ * opNode, newName, newNode, nodeArity, temp, tempArgs
+ * 
+ * 
+ * (***************************************************************************)
+ * (* A macro used for reporting an error and terminating the execution. *)
+ * (***************************************************************************)
+ * macro Error(msg) begin print msg ; if debug then
+ * (**************************************) (* Force the +cal translator to *)
+ * (* insert a label so the error trace *) (* shows the final state. *)
+ * (**************************************) idx := idx ; idx := idx ; assert
+ * FALSE else goto Done end if end macro ;
+ * 
+ * begin
+ * 
+ * (*************************************************************************)
+ * (* An assertion that checks that ops and args are consistent with each *) (*
+ * other and with the value of expectedArity. In an implementation, *) (* some
+ * of these should be guaranteed by the context (e.g., Len(ops) = *) (*
+ * Len(args) while others will be checked as part of the processing for *) (*
+ * the particular value of idx. *)
+ * (*************************************************************************)
+ * assert /\ expectedArity \in Int /\ ops \in Seq(STRING) /\ args \in
+ * Seq(Seq(NodeId)) /\ Len(ops) = Len(args) /\ \A i \in 1..Len(ops) : /\ \/
+ * ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp \/ expectedArity # 0 =>
+ * args[i] = << >> /\ (ops[i] = "null") => (Len(args[i]) > 0) ;
+ * 
+ * (*************************************************************************)
+ * (* The outer "for" loop on idx. *)
+ * (*************************************************************************)
+ * while idx <= Len(ops) do if mode = "FindingOpName" then
+ * 
+ * \* The following code was modified by LL on 23 Sep 2009 to fix the following
+ * \* bug. The parser produced a bogus error on a reference to an identifier \*
+ * M!N that is imported by an unnamed INSTANCE of a module containing the \*
+ * statement M == INSTANCE ... . The original code essentially just \* contained
+ * the first iteration of the following while loop, and would \* report an error
+ * if it found newNode = null. Thus, it would report \* an error in the
+ * identifier M!N when it found M to be undefined. \* \* Corrected by LL on 9
+ * Nov 2009 to handle arguments of those \* undefined operators. They are put
+ * into tempArgs when they are \* found.
+ * 
+ * newNode := null ; tempArgs := << >> ;
+ * (**********************************************************************) (*
+ * The number of arguments found in the following loop for the *) (* undefined
+ * operators *)
+ * (**********************************************************************)
+ * while newNode = null do if idx \geq Len(ops) then Error ("Unknown operator");
+ * end if; newName := IF IsName(ops[idx]) THEN IF curName = "" THEN ops[idx]
+ * ELSE curName \o "!" \o ops[idx] ELSE null ; if /\ curName = "" /\ ~
+ * IsName(ops[idx]) then
+ * (***************************************************************) (* I think
+ * that this error can only happen for idx = 1, and *) (* should never happen in
+ * the implementation because the *) (* parser should not allow a compound name
+ * that doesn't begi *) (* with a name. *)
+ * (***************************************************************) Error("Need
+ * an operator or label name or step number here") end if; newNode := IF newName
+ * # null THEN LookUp(newName, curContext) ELSE null ; if newName = null then
+ * tempArgs := tempArgs \o args[idx]; idx := idx + 1; end if; end while ;
+ * curNode := newNode ; curName := newName ; if curNode.kind \in
+ * {UserDefinedOpKind, ThmOrAssumpDefKind, ModuleInstanceKind, ConstantDeclKind,
+ * VariableDeclKind, FormalParamKind, BuiltInKind, BoundSymbolKind} then if
+ * curNode.kind \in {ConstantDeclKind, VariableDeclKind, FormalParamKind,
+ * BuiltInKind, BoundSymbolKind} then if idx # 1 then Error("Abort: Impossible
+ * naming of declaration " \o "or built-in operator.") else if Len(ops) # 1 then
+ * Error("Can't take subexpression of this"); end if end if end if ; nodeArity
+ * := Arity(curNode) ; tempArgs := tempArgs \o args[idx]; if expectedArity = 0
+ * then if opDefArityFound + Len(tempArgs) # nodeArity then Error("Wrong number
+ * of arguments") end if ; if \E i \in 1..Len(tempArgs[idx]) :
+ * Arity(Node[tempArgs[idx][i]]) # ParamArity(curNode, i + opDefArityFound) then
+ * Error("Argument has wrong arity") else opDefArgs := opDefArgs \o tempArgs;
+ * end if ; else if expectedArity > 0 then if \E i \in 1..Len(curNode.params) :
+ * curNode.params[i].arity > 0 then Error("Higher-order operator selected " \o
+ * "as operator argument") end if \* else Error( \* "Abort: Don't yet handle
+ * expectedArity < 0") end if end if ; opDefArityFound := nodeArity ; if
+ * curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind, ConstantDeclKind,
+ * VariableDeclKind, FormalParamKind, BoundSymbolKind} then if /\ curNode.kind =
+ * UserDefinedOpKind /\ ~ curNode.defined /\ ~ Len(ops) = 1 then Error("Can't
+ * refer to subexpression of " \o "operator inside its definition") end if; if
+ * /\ firstFindingOpName /\ curNode.kind \in {UserDefinedOpKind,
+ * ThmOrAssumpDefKind} then subExprOf := curNode end if; if idx # Len(ops) then
+ * params := params \o curNode.params ; curName := "" ; if IsName(ops[idx+1])
+ * then mode := "FollowingLabels" else mode := "FindingSubExpr" ; end if ;
+ * allArgs := allArgs \o opDefArgs ; opDefArityFound := 0 ; opDefArgs := << >> ;
+ * newNode := Node[curNode.body] ; while newNode.kind = SubstInKind do
+ * substInPrefix := substInPrefix \o <<newNode>>; newNode := Node[newNode.body];
+ * end while ; while newNode.kind \in {SubstInKind, APSubstInKind}
+ * (************************************************) (* Added APSubstInKind
+ * test on 13 Nov 2009. *) (************************************************) do
+ * substInPrefix := substInPrefix \o <<newNode>>; newNode := Node[newNode.body];
+ * end while ; (********************************************) (* If the next op
+ * is an OpSel, then need to *) (* skip over SubstInNodes, which are *) (*
+ * invisible to the user. *) (********************************************) if
+ * mode = "FindingSubExpr" then curNode := newNode end if; end if ; else \*
+ * curNode.kind = ModuleInstanceKind if (idx = Len(ops)) then Error("Operator
+ * name incomplete") end if end if else Error("Unexpected node kind") ; end if ;
+ * prevMode := "FindingOpName" ;
+ * 
+ * 
+ * elsif mode = "FollowingLabels" then if prevMode = "FindingOpName" then assert
+ * curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} ; newNode :=
+ * LookUp(ops[idx], curNode.labels) ; else assert curNode.kind = LabelKind ;
+ * newNode := LookUp(ops[idx], curNode.labels) ; end if ; if newNode = null then
+ * Error("Label not found") end if; curNode := newNode ; if expectedArity = 0
+ * then if Len(args[idx]) # curNode.arity then Error("bad arity") end if ; if \E
+ * i \in 1..Len(args[idx]) : Arity(Node[args[idx][i]]) # 0 then Error("Operator
+ * argument given to label") end if; allArgs := allArgs \o args[idx] ; end if;
+ * params := params \o curNode.params ; if /\ idx < Len(ops) /\
+ * ~IsName(ops[idx+1]) then mode := "FindingSubExpr" end if ; if \/ mode =
+ * "FindingSubExpr" \/ idx = Len(ops) then curNode := Node[curNode.body] end if;
+ * prevMode := "FollowingLabels";
+ * 
+ * 
+ * elsif mode = "FindingSubExpr" then if ops[idx] = ":" then if \/ prevMode
+ * \notin {"FindingOpName", "FollowingLabels"} \/ ~ \/ /\ idx = Len(ops) /\
+ * prevMode = "FindingOpName" \/ /\ idx < Len(ops) /\ IsName(ops[idx+1]) then
+ * Error("`!:' can be used only after a name and either at the " \o "end after
+ * an operator name or before an operator name.") end if
+ * 
+ * elsif curNode.kind = LetInKind then if ArgNum(ops[idx], 1) = 1 then curNode
+ * := Node[curNode.body] else Error("A Let/In has only a single operand") end if
+ * ;
+ * 
+ * elsif curNode.kind = OpApplKind then opNode := Node[curNode.operator] ; if
+ * opNode.kind \in {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} then
+ * (******************************************************************) (*
+ * Selecting an argument from something of the form Op(...). *)
+ * (******************************************************************) temp :=
+ * ArgNum(ops[idx], opNode.arity) ; if temp = -1 then Error("Nonexistent operand
+ * specified") else curNode := Node[curNode.operands[temp]] end if elsif
+ * opNode.kind = BuiltInKind then \* See configuration/ConfigConstants for a
+ * list of all \* BuiltInKind operators. if opNode.name \in {"$RcdConstructor",
+ * "$SetOfRcds"} then
+ * (*****************************************************************) (*
+ * curNode represents an expression *) (* [a_1 |-> e_1, ... , a_n |-> e_n] or *)
+ * (* [a_1 : e_1, ... , a_n : e_n] *) (* Its i-th argument is the $Pair node
+ * (a_i, e_i) *)
+ * (*****************************************************************) temp :=
+ * ArgNum(ops[idx], Len(curNode.operands)) ; if temp = -1 then Error("Incorrect
+ * subexpression number") end if ; curNode := Node[curNode.operands[temp]] ; if
+ * \/ curNode.kind # OpApplKind \/ Node[curNode.operator].kind # BuiltInKind \/
+ * Node[curNode.operator].name # "$Pair" then Error ("Expecting $Pair(...)")
+ * else curNode := Node[curNode.operands[2]] end if
+ * 
+ * elsif opNode.name \in {"$Case"} then
+ * (*****************************************************************) (* The
+ * i-th clause is a $Pair node, where for the OTHER clause *) (* the 1st element
+ * is null. *)
+ * (*****************************************************************) if idx =
+ * Len(ops) then Error( "CASE subexpression must be of form !i!j") end if ; temp
+ * := ArgNum( ops[idx], Len(curNode.operands)) ; if temp = -1 then
+ * Error("Incorrect subexpression name") end if ; curNode :=
+ * Node[curNode.operands[temp]] ; if \/ curNode.kind # OpApplKind \/
+ * Node[curNode.operator].kind # BuiltInKind \/ Node[curNode.operator].name #
+ * "$Pair" then Error ("Expecting $Pair(...)") end if ; idx := idx+1 ; temp :=
+ * ArgNum(ops[idx], 2); if temp = -1 then Error("Incorrect subexpression name")
+ * end if ; curNode := Node[curNode.operands[temp]] ; if curNode = null then
+ * Error("Selecting OTHER") end if
+ * 
+ * elsif opNode.name = "$Except" then
+ * (****************************************************************) (* For *)
+ * (* [exp_1 ELSE !... = exp_2, ... , !.. = exp_n] *) (* argument number i
+ * chooses exp_i. For i > 1, exp_i is the *) (* second argument of a $Pair
+ * operator. *)
+ * (****************************************************************) temp :=
+ * ArgNum(ops[idx], Len(curNode.operands)); if temp = -1 then Error("Bad
+ * argument selector.") end if; curNode := Node[curNode.operands[temp]] ; if
+ * temp > 1 then if \/ curNode.kind # OpApplKind \/ Node[curNode.operator].kind
+ * # BuiltInKind \/ Node[curNode.operator].name # "$Pair" then Error("Unexpected
+ * expression node found.") else curNode := Node[curNode.operands[2]] end if end
+ * if
+ * 
+ * else (*****************************************************************) (*
+ * Operator handled by standard procedure. *)
+ * (*****************************************************************) if /\
+ * Len(curNode.unboundedBoundSymbols) = 0 /\ Len(curNode.boundedBoundSymbols) =
+ * 0 then (**********************************************************) (*
+ * Current subexpression has no bound variables. *)
+ * (**********************************************************) temp :=
+ * ArgNum(ops[idx], Len(curNode.operands)) ; if temp = -1 then Error("Incorrect
+ * subexpression selector") end if; curNode := Node[curNode.operands[temp]] ;
+ * else (**********************************************************) (* Current
+ * subexpression has bound variables. If *) (* selector is "@" or null, then
+ * choosing body and adding *) (* parameters. Otherwise, must be selecting one
+ * of the *) (* bounds. *)
+ * (**********************************************************) if ops[idx] \in
+ * {"null", "@"} then (***************************************************) (*
+ * Set temp to the sequence of parameters. *)
+ * (***************************************************) temp := IF
+ * Len(curNode.unboundedBoundSymbols) > 0 THEN curNode.unboundedBoundSymbols
+ * ELSE SeqSeqToSeq( curNode.boundedBoundSymbols);
+ * 
+ * params := params \o [i \in 1.. Len(temp) |-> [name |-> temp[i], arity |-> 0]]
+ * ; allArgs := allArgs \o args[idx] ; if /\ ops[idx] = "null" /\ Len(args[idx])
+ * # Len(temp) then Error("Wrong number of selector arguments"); end if; curNode
+ * := Node[curNode.operands[1]]; else temp := ArgNum(ops[idx],
+ * Len(curNode.ranges)) ; if temp = -1 then Error ("Selecting non-existent
+ * range") ; else curNode := Node[curNode.ranges[temp]] end if end if end if end
+ * if \* opNode.name = ...
+ * 
+ * else Error("Applying subexpression chooser to an expr" \o " with no choosable
+ * subexpressions")
+ * 
+ * end if \* opNode.kind = ...
+ * 
+ * elsif curNode.kind = AssumeProveKind then temp := ArgNum(ops[idx], 1 +
+ * Len(curNode.assumes)) ; if temp = -1 then Error("Illegal argument number")
+ * else if temp <= Len(curNode.assumes) then curNode :=
+ * Node[curNode.assumes[temp]] else curNode := Node[curNode.prove] end if end if
+ * 
+ * elsif curNode.kind = OpArgKind then
+ * (*********************************************************************) (*
+ * The only kind of OpArgNode that has a subpart is a Lambda *) (* expression.
+ * *) (*********************************************************************)
+ * opNode := Node[curNode.op] ; if \/ opNode.kind # UserDefinedOpKind \/
+ * opNode.name # "LAMBDA" then Error("Selecting from operator argument that has
+ * no sub-part") elsif ops[idx] \notin {"null", "@"} then Error("Incorrect
+ * selection from LAMBDA") elsif /\ ops[idx] = "null" /\ Len(args[idx]) #
+ * Len(opNode.params) then Error("Incorrect number of arguments for LAMBDA")
+ * else params := params \o opNode.params ; allArgs := allArgs \o args[idx] ;
+ * curNode := Node[opNode.body] ; end if
+ * 
+ * elsif curNode.kind \in {UserDefinedOpKind, BuiltInKind} then Error("Abort:
+ * should not have been able to choose this node.")
+ * 
+ * elsif curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, StringKind,
+ * FormalParamKind, ConstantDeclKind, VariableDeclKind, BoundSymbolKind} then
+ * Error("Selected part has no subexpression")
+ * 
+ * elsif curNode.kind = LabelKind then
+ * (*********************************************************************) (*
+ * Skip over label. *)
+ * (*********************************************************************)
+ * curNode := Node[curNode.body] ; idx := idx - 1 ; else Error("Unexpected node
+ * kind found in expression")
+ * 
+ * end if; \* ops[idx] = ":"
+ * 
+ * if idx # Len(ops) then if IsName(ops[idx+1]) then while curNode.kind =
+ * LabelKind do curNode := Node[curNode.body] end while ; if curNode.kind =
+ * LetInKind then curContext := curNode.context ; mode := "FindingOpName" ;
+ * firstFindingOpName := FALSE; else Error("A name not following the selector of
+ * a LET") end if else if ops[idx+1] = ":" then Error("!: should not follow an
+ * operand selector") end if ; end if ; end if ; \* ops[idx] = ":" prevMode :=
+ * "FindingSubExpr";
+ * 
+ * else Error("Bad value of mode")
+ * 
+ * end if ; \* mode = ...
+ * 
+ * idx := idx + 1; end while ;
+ * 
+ * if curNode.kind = AssumeProveKind then Error("Selecting ASSUME/PROVE instead
+ * of expression") end if;
+ * 
+ * if expectedArity < 0 then if \/ prevMode # "FindingOpName" \/ curNode.kind
+ * \notin {UserDefinedOpKind, ThmOrAssumpDefKind} then Error("Should have
+ * selected a definition, but didn't.") end if; result := curNode ; goto
+ * Finished end if;
+ * 
+ * if expectedArity > 0 then temp := Len(params) + IF \/ prevMode =
+ * "FindingOpName" \/ curNode.kind = OpArgKind THEN Arity(curNode) ELSE 0 ; if
+ * expectedArity # temp then Error("Expect arity = " \o ToString(expectedArity)
+ * \o ", but found arity = " \o ToString(temp)) end if end if;
+ * 
+ * 
+ * (*************************************************************************)
+ * (* If found an operator def and there are parameters or substitutions, *) (*
+ * then set curNode to the operator applied to new parameters, add those *) (*
+ * parameters to params and add the arguments to allArgs. Note: need to *) (* do
+ * this even if operator takes no parameters because we need to put *) (* the
+ * operator in a LAMBDA expression whose body is an expression *)
+ * (*************************************************************************)
+ * if /\ prevMode = "FindingOpName" /\ Len(params) + Len(substInPrefix) > 0 then
+ * temp := [i \in 1..Len(curNode.params) |-> [curNode.params[i] EXCEPT !.name =
+ * "New " \o @]] ;
+ * (****************************************************************) (* temp :=
+ * new formal parameters for the operator, with the *) (* same arities as the
+ * original parameters. *)
+ * (****************************************************************)
+ * 
+ * curNode := [kind |-> OpApplKind, operands |-> temp, operator |-> curNode,
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>] ; params := params \o temp ; allArgs := allArgs \o opDefArgs ; end if ;
+ * 
+ * if curNode.kind = OpArgKind then if expectedArity = 0 then Error("Selected
+ * Operator Argument when expression expected.") elsif expectedArity #
+ * Len(params) + curNode.arity then Error("Selected operator has wrong arity.")
+ * else if Len(params) + Len(substInPrefix) > 0 then
+ * (********************************************************) (* If curNode is a
+ * LAMBDA, then this will eventually *) (* produce an OpArg node whose operator
+ * is a LAMBDA *) (* whose body is an OpApplNode that applies the *) (*
+ * curNode's LAMBDA to parameters of the outer LAMBDA. *) (* This result can be
+ * simplified to a LAMBDA whose body *) (* is the body of curNode. However, that
+ * *) (* simplification is what one will get by selecting the *) (* body of the
+ * LAMBDA, so we keep this complicated *) (* expression in this case. *)
+ * (********************************************************) temp := [i \in
+ * 1..curNode.arity |-> [name |-> "NewParam" \o NumToString(i), arity |-> 0]] ;
+ * curNode := [kind |-> OpApplKind, operands |-> temp, operator |->
+ * Node[curNode.op], unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> <<
+ * >>, ranges |-> << >>] ; params := params \o temp end if end if end if ;
+ * 
+ * if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind, VariableDeclKind,
+ * FormalParamKind, BuiltInKind, BoundSymbolKind, ThmOrAssumpDefKind} then
+ * (***********************************************************************) (*
+ * There are no params or substitutions, so this is an easy case. *)
+ * (***********************************************************************) if
+ * expectedArity > 0 then result := [kind |-> OpArgKind, name |-> curNode.name,
+ * op |-> curNode, arity |-> expectedArity] elsif expectedArity = 0 then result
+ * := [kind |-> OpApplKind, operands |-> opDefArgs, operator |-> curNode,
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>, subExprOf |-> subExprOf] end if elsif curNode.kind = OpArgKind then
+ * (***********************************************************************) (*
+ * There are no params or substitutions, so this is an easy case. *)
+ * (***********************************************************************)
+ * result := curNode ; else
+ * (******************************************************************) (*
+ * curNode should be an expression node. *)
+ * (******************************************************************) temp :=
+ * Len(substInPrefix) ; while temp > 0 do curNode := [kind |->
+ * substInPrefix[temp].kind, (********************************************) (*
+ * Changed on 13 Nov 2009 from SubstInKind. *)
+ * (********************************************) body |-> curNode , subst |->
+ * substInPrefix[temp].subst ] ; temp := temp - 1; end while;
+ * 
+ * if expectedArity > 0 then if Len(params) # expectedArity then Error
+ * ("Selection has wrong arity") end if ; result := [kind |-> OpArgKind, op |->
+ * [kind |-> UserDefinedOpKind, name |-> "LAMBDA", body |-> curNode, params |->
+ * params, arity |-> Len(params), defined |-> TRUE, source |-> null], name |->
+ * "LAMBDA"] else if Len(params) # Len(allArgs) then Error("Abort: number of
+ * params # num of args") end if ; if Len(params) = 0 then
+ * (******************************************************) (* This is the one
+ * case with expectedArity = 0 in *) (* which the result is not a
+ * newly-constructed node. *) (* In this case, we construct a dummy label node
+ * so *) (* we have a node to which the implementation can *) (* attach the
+ * syntax node. *) (******************************************************)
+ * result := [kind |-> LabelKind, name |-> "$Subexpression", arity |-> 0, params
+ * |-> << >>, body |-> curNode, subExprOf |-> subExprOf]
+ * 
+ * else result := [kind |-> OpApplKind, operator |-> [kind |->
+ * UserDefinedOpKind, name |-> "LAMBDA", body |-> curNode, params |-> params,
+ * arity |-> Len(params), defined |-> TRUE, source |-> null], operands |->
+ * allArgs, unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>,
+ * ranges |-> << >>, subExprOf |-> subExprOf] end if end if
+ * 
+ * end if;
+ * 
+ * Finished: print "Result: " ; print result ;
+ * 
+ * end algorithm ) \* BEGIN TRANSLATION CONSTANT defaultInitValue VARIABLES ops,
+ * args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf,
+ * result, idx, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs, pc
+ * 
+ * vars == << ops, args, expectedArity, substInPrefix, params, allArgs, curNode,
+ * subExprOf, result, idx, mode, prevMode, curContext, curName, opDefArityFound,
+ * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp,
+ * tempArgs, pc >>
+ * 
+ * Init == (* Global variables *) /\ ops = MCops /\ args = MCargs /\
+ * expectedArity = MCexpectedArity /\ substInPrefix = << >> /\ params = << >> /\
+ * allArgs = << >> /\ curNode = null /\ subExprOf = "null" /\ result = null /\
+ * idx = 1 /\ mode = "FindingOpName" /\ prevMode = "" /\ curContext =
+ * GlobalContext /\ curName = "" /\ opDefArityFound = 0 /\ opDefArgs = << >> /\
+ * firstFindingOpName = TRUE /\ opNode = defaultInitValue /\ newName =
+ * defaultInitValue /\ newNode = defaultInitValue /\ nodeArity =
+ * defaultInitValue /\ temp = defaultInitValue /\ tempArgs = defaultInitValue /\
+ * pc = "Lbl_1"
+ * 
+ * Lbl_1 == /\ pc = "Lbl_1" /\ Assert(/\ expectedArity \in Int /\ ops \in
+ * Seq(STRING) /\ args \in Seq(Seq(NodeId)) /\ Len(ops) = Len(args) /\ \A i \in
+ * 1..Len(ops) : /\ \/ ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp \/
+ * expectedArity # 0 => args[i] = << >> /\ (ops[i] = "null") => (Len(args[i]) >
+ * 0), "Failure of assertion at line line 1906, column 3.") /\ pc' = "Lbl_2" /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_2 == /\ pc = "Lbl_2" /\ IF idx <= Len(ops) THEN /\ IF mode =
+ * "FindingOpName" THEN /\ newNode' = null /\ tempArgs' = << >> /\ pc' = "Lbl_3"
+ * /\ UNCHANGED << params, allArgs, curNode, idx, opNode, temp >> ELSE /\ IF
+ * mode = "FollowingLabels" THEN /\ IF prevMode = "FindingOpName" THEN /\
+ * Assert(curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}, "Failure of
+ * assertion at line line 2053, column 13.") /\ newNode' = LookUp(ops[idx],
+ * curNode.labels) ELSE /\ Assert(curNode.kind = LabelKind, "Failure of
+ * assertion at line line 2055, column 13.") /\ newNode' = LookUp(ops[idx],
+ * curNode.labels) /\ IF newNode' = null THEN /\ PrintT("Label not found") /\ IF
+ * debug THEN /\ idx' = idx /\ pc' = "Lbl_22" ELSE /\ pc' = "Done" /\ UNCHANGED
+ * idx ELSE /\ pc' = "Lbl_23" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs,
+ * curNode, opNode, temp >> ELSE /\ IF mode = "FindingSubExpr" THEN /\ IF
+ * ops[idx] = ":" THEN /\ IF \/ prevMode \notin {"FindingOpName",
+ * "FollowingLabels"} \/ ~ \/ /\ idx = Len(ops) /\ prevMode = "FindingOpName" \/
+ * /\ idx < Len(ops) /\ IsName(ops[idx+1]) THEN /\ PrintT("`!:' can be used only
+ * after a name and either at the " \o "end after an operator name or before an
+ * operator name.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_29" ELSE /\ pc'
+ * = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_63" /\ UNCHANGED idx /\
+ * UNCHANGED << params, allArgs, curNode, opNode, temp >> ELSE /\ IF
+ * curNode.kind = LetInKind THEN /\ IF ArgNum(ops[idx], 1) = 1 THEN /\ curNode'
+ * = Node[curNode.body] /\ pc' = "Lbl_63" /\ UNCHANGED idx ELSE /\ PrintT("A
+ * Let/In has only a single operand") /\ IF debug THEN /\ idx' = idx /\ pc' =
+ * "Lbl_30" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED curNode /\
+ * UNCHANGED << params, allArgs, opNode, temp >> ELSE /\ IF curNode.kind =
+ * OpApplKind THEN /\ opNode' = Node[curNode.operator] /\ IF opNode'.kind \in
+ * {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} THEN /\ temp' =
+ * ArgNum(ops[idx], opNode'.arity) /\ IF temp' = -1 THEN /\ PrintT("Nonexistent
+ * operand specified") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_31" ELSE /\
+ * pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED curNode ELSE /\ curNode' =
+ * Node[curNode.operands[temp']] /\ pc' = "Lbl_63" /\ UNCHANGED idx /\ UNCHANGED
+ * << params, allArgs >> ELSE /\ IF opNode'.kind = BuiltInKind THEN /\ IF
+ * opNode'.name \in {"$RcdConstructor", "$SetOfRcds"} THEN /\ temp' =
+ * ArgNum(ops[idx], Len(curNode.operands)) /\ IF temp' = -1 THEN /\
+ * PrintT("Incorrect subexpression number") /\ IF debug THEN /\ idx' = idx /\
+ * pc' = "Lbl_32" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_33"
+ * /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode >> ELSE /\ IF
+ * opNode'.name \in {"$Case"} THEN /\ IF idx = Len(ops) THEN /\ PrintT("CASE
+ * subexpression must be of form !i!j") /\ IF debug THEN /\ idx' = idx /\ pc' =
+ * "Lbl_36" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_37" /\
+ * UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode, temp >> ELSE /\ IF
+ * opNode'.name = "$Except" THEN /\ temp' = ArgNum(ops[idx],
+ * Len(curNode.operands)) /\ IF temp' = -1 THEN /\ PrintT("Bad argument
+ * selector.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_46" ELSE /\ pc' =
+ * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_47" /\ UNCHANGED idx /\ UNCHANGED
+ * << params, allArgs, curNode >> ELSE /\ IF /\
+ * Len(curNode.unboundedBoundSymbols) = 0 /\ Len(curNode.boundedBoundSymbols) =
+ * 0 THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands)) /\ IF temp' = -1
+ * THEN /\ PrintT("Incorrect subexpression selector") /\ IF debug THEN /\ idx' =
+ * idx /\ pc' = "Lbl_50" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' =
+ * "Lbl_51" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode >> ELSE /\
+ * IF ops[idx] \in {"null", "@"} THEN /\ temp' = (IF
+ * Len(curNode.unboundedBoundSymbols) > 0 THEN curNode.unboundedBoundSymbols
+ * ELSE SeqSeqToSeq( curNode.boundedBoundSymbols)) /\ params' = params \o [i \in
+ * 1.. Len(temp') |-> [name |-> temp'[i], arity |-> 0]] /\ allArgs' = allArgs \o
+ * args[idx] /\ IF /\ ops[idx] = "null" /\ Len(args[idx]) # Len(temp') THEN /\
+ * PrintT("Wrong number of selector arguments") /\ IF debug THEN /\ idx' = idx
+ * /\ pc' = "Lbl_52" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' =
+ * "Lbl_53" /\ UNCHANGED idx /\ UNCHANGED curNode ELSE /\ temp' =
+ * ArgNum(ops[idx], Len(curNode.ranges)) /\ IF temp' = -1 THEN /\
+ * PrintT("Selecting non-existent range") /\ IF debug THEN /\ idx' = idx /\ pc'
+ * = "Lbl_54" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED curNode ELSE /\
+ * curNode' = Node[curNode.ranges[temp']] /\ pc' = "Lbl_63" /\ UNCHANGED idx /\
+ * UNCHANGED << params, allArgs >> ELSE /\ PrintT("Applying subexpression
+ * chooser to an expr" \o " with no choosable subexpressions") /\ IF debug THEN
+ * /\ idx' = idx /\ pc' = "Lbl_55" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\
+ * UNCHANGED << params, allArgs, curNode, temp >> ELSE /\ IF curNode.kind =
+ * AssumeProveKind THEN /\ temp' = ArgNum(ops[idx], 1 + Len(curNode.assumes)) /\
+ * IF temp' = -1 THEN /\ PrintT("Illegal argument number") /\ IF debug THEN /\
+ * idx' = idx /\ pc' = "Lbl_56" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\
+ * UNCHANGED curNode ELSE /\ IF temp' <= Len(curNode.assumes) THEN /\ curNode' =
+ * Node[curNode.assumes[temp']] ELSE /\ curNode' = Node[curNode.prove] /\ pc' =
+ * "Lbl_63" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, opNode >> ELSE /\
+ * IF curNode.kind = OpArgKind THEN /\ opNode' = Node[curNode.op] /\ IF \/
+ * opNode'.kind # UserDefinedOpKind \/ opNode'.name # "LAMBDA" THEN /\
+ * PrintT("Selecting from operator argument that has no sub-part") /\ IF debug
+ * THEN /\ idx' = idx /\ pc' = "Lbl_57" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\
+ * UNCHANGED << params, allArgs, curNode >> ELSE /\ IF ops[idx] \notin {"null",
+ * "@"} THEN /\ PrintT("Incorrect selection from LAMBDA") /\ IF debug THEN /\
+ * idx' = idx /\ pc' = "Lbl_58" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\
+ * UNCHANGED << params, allArgs, curNode >> ELSE /\ IF /\ ops[idx] = "null" /\
+ * Len(args[idx]) # Len(opNode'.params) THEN /\ PrintT("Incorrect number of
+ * arguments for LAMBDA") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_59" ELSE
+ * /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode >>
+ * ELSE /\ params' = params \o opNode'.params /\ allArgs' = allArgs \o args[idx]
+ * /\ curNode' = Node[opNode'.body] /\ pc' = "Lbl_63" /\ UNCHANGED idx ELSE /\
+ * IF curNode.kind \in {UserDefinedOpKind, BuiltInKind} THEN /\ PrintT("Abort:
+ * should not have been able to choose this node.") /\ IF debug THEN /\ idx' =
+ * idx /\ pc' = "Lbl_60" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED
+ * curNode ELSE /\ IF curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind,
+ * StringKind, FormalParamKind, ConstantDeclKind, VariableDeclKind,
+ * BoundSymbolKind} THEN /\ PrintT("Selected part has no subexpression") /\ IF
+ * debug THEN /\ idx' = idx /\ pc' = "Lbl_61" ELSE /\ pc' = "Done" /\ UNCHANGED
+ * idx /\ UNCHANGED curNode ELSE /\ IF curNode.kind = LabelKind THEN /\ curNode'
+ * = Node[curNode.body] /\ idx' = idx - 1 /\ pc' = "Lbl_63" ELSE /\
+ * PrintT("Unexpected node kind found in expression") /\ IF debug THEN /\ idx' =
+ * idx /\ pc' = "Lbl_62" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED
+ * curNode /\ UNCHANGED << params, allArgs, opNode >> /\ UNCHANGED temp ELSE /\
+ * PrintT("Bad value of mode") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_68"
+ * ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs,
+ * curNode, opNode, temp >> /\ UNCHANGED newNode /\ UNCHANGED tempArgs ELSE /\
+ * IF curNode.kind = AssumeProveKind THEN /\ PrintT("Selecting ASSUME/PROVE
+ * instead of expression") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_70" ELSE
+ * /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_71" /\ UNCHANGED idx /\
+ * UNCHANGED << params, allArgs, curNode, opNode, newNode, temp, tempArgs >> /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, result,
+ * mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, newName, nodeArity >>
+ * 
+ * Lbl_69 == /\ pc = "Lbl_69" /\ idx' = idx + 1 /\ pc' = "Lbl_2" /\ UNCHANGED <<
+ * ops, args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_3 == /\ pc = "Lbl_3" /\ IF newNode = null THEN /\ IF idx \geq Len(ops)
+ * THEN /\ PrintT("Unknown operator") /\ IF debug THEN /\ idx' = idx /\ pc' =
+ * "Lbl_4" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_5" /\
+ * UNCHANGED idx /\ UNCHANGED << curNode, curName >> ELSE /\ curNode' = newNode
+ * /\ curName' = newName /\ IF curNode'.kind \in {UserDefinedOpKind,
+ * ThmOrAssumpDefKind, ModuleInstanceKind, ConstantDeclKind, VariableDeclKind,
+ * FormalParamKind, BuiltInKind, BoundSymbolKind} THEN /\ IF curNode'.kind \in
+ * {ConstantDeclKind, VariableDeclKind, FormalParamKind, BuiltInKind,
+ * BoundSymbolKind} THEN /\ IF idx # 1 THEN /\ PrintT("Abort: Impossible naming
+ * of declaration " \o "or built-in operator.") /\ IF debug THEN /\ idx' = idx
+ * /\ pc' = "Lbl_8" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ IF Len(ops) #
+ * 1 THEN /\ PrintT("Can't take subexpression of this") /\ IF debug THEN /\ idx'
+ * = idx /\ pc' = "Lbl_9" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' =
+ * "Lbl_10" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_10" /\ UNCHANGED idx ELSE /\
+ * PrintT("Unexpected node kind") /\ IF debug THEN /\ idx' = idx /\ pc' =
+ * "Lbl_20" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << ops, args,
+ * expectedArity, substInPrefix, params, allArgs, subExprOf, result, mode,
+ * prevMode, curContext, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_5 == /\ pc = "Lbl_5" /\ newName' = IF IsName(ops[idx]) THEN IF curName =
+ * "" THEN ops[idx] ELSE curName \o "!" \o ops[idx] ELSE null /\ IF /\ curName =
+ * "" /\ ~ IsName(ops[idx]) THEN /\ PrintT("Need an operator or label name or
+ * step number here") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_6" ELSE /\
+ * pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_7" /\ UNCHANGED idx /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newNode, nodeArity,
+ * temp, tempArgs >>
+ * 
+ * Lbl_6 == /\ pc = "Lbl_6" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion
+ * at line line 1892, column 38 of macro called at line 1956, column 13.") /\
+ * pc' = "Lbl_7" /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_7 == /\ pc = "Lbl_7" /\ newNode' = (IF newName # null THEN
+ * LookUp(newName, curContext) ELSE null) /\ IF newName = null THEN /\ tempArgs'
+ * = tempArgs \o args[idx] /\ idx' = idx + 1 ELSE /\ TRUE /\ UNCHANGED << idx,
+ * tempArgs >> /\ pc' = "Lbl_3" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, nodeArity, temp >>
+ * 
+ * Lbl_4 == /\ pc = "Lbl_4" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion
+ * at line line 1892, column 38 of macro called at line 1942, column 13.") /\
+ * pc' = "Lbl_5" /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_10 == /\ pc = "Lbl_10" /\ nodeArity' = Arity(curNode) /\ tempArgs' =
+ * tempArgs \o args[idx] /\ IF expectedArity = 0 THEN /\ IF opDefArityFound +
+ * Len(tempArgs') # nodeArity' THEN /\ PrintT("Wrong number of arguments") /\ IF
+ * debug THEN /\ idx' = idx /\ pc' = "Lbl_11" ELSE /\ pc' = "Done" /\ UNCHANGED
+ * idx ELSE /\ pc' = "Lbl_12" /\ UNCHANGED idx ELSE /\ IF expectedArity > 0 THEN
+ * /\ IF \E i \in 1..Len(curNode.params) : curNode.params[i].arity > 0 THEN /\
+ * PrintT("Higher-order operator selected " \o "as operator argument") /\ IF
+ * debug THEN /\ idx' = idx /\ pc' = "Lbl_14" ELSE /\ pc' = "Done" /\ UNCHANGED
+ * idx ELSE /\ pc' = "Lbl_15" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_15" /\
+ * UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, temp >>
+ * 
+ * Lbl_12 == /\ pc = "Lbl_12" /\ IF \E i \in 1..Len(tempArgs[idx]) :
+ * Arity(Node[tempArgs[idx][i]]) # ParamArity(curNode, i + opDefArityFound) THEN
+ * /\ PrintT("Argument has wrong arity") /\ IF debug THEN /\ idx' = idx /\ pc' =
+ * "Lbl_13" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED opDefArgs ELSE /\
+ * opDefArgs' = opDefArgs \o tempArgs /\ pc' = "Lbl_15" /\ UNCHANGED idx /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, mode, prevMode, curContext, curName,
+ * opDefArityFound, firstFindingOpName, opNode, newName, newNode, nodeArity,
+ * temp, tempArgs >>
+ * 
+ * Lbl_13 == /\ pc = "Lbl_13" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 1990, column
+ * 26.") /\ pc' = "Lbl_15" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_11 == /\ pc = "Lbl_11" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 1985, column
+ * 25.") /\ pc' = "Lbl_12" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_14 == /\ pc = "Lbl_14" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 1996, column
+ * 32.") /\ pc' = "Lbl_15" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_15 == /\ pc = "Lbl_15" /\ opDefArityFound' = nodeArity /\ IF curNode.kind
+ * \in {UserDefinedOpKind, ThmOrAssumpDefKind, ConstantDeclKind,
+ * VariableDeclKind, FormalParamKind, BoundSymbolKind} THEN /\ IF /\
+ * curNode.kind = UserDefinedOpKind /\ ~ curNode.defined /\ ~ Len(ops) = 1 THEN
+ * /\ PrintT("Can't refer to subexpression of " \o "operator inside its
+ * definition") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_16" ELSE /\ pc' =
+ * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_17" /\ UNCHANGED idx ELSE /\ IF
+ * (idx = Len(ops)) THEN /\ PrintT("Operator name incomplete") /\ IF debug THEN
+ * /\ idx' = idx /\ pc' = "Lbl_19" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\
+ * pc' = "Lbl_21" /\ UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_17 == /\ pc = "Lbl_17" /\ IF /\ firstFindingOpName /\ curNode.kind \in
+ * {UserDefinedOpKind, ThmOrAssumpDefKind} THEN /\ subExprOf' = curNode ELSE /\
+ * TRUE /\ UNCHANGED subExprOf /\ IF idx # Len(ops) THEN /\ params' = params \o
+ * curNode.params /\ curName' = "" /\ IF IsName(ops[idx+1]) THEN /\ mode' =
+ * "FollowingLabels" ELSE /\ mode' = "FindingSubExpr" /\ allArgs' = allArgs \o
+ * opDefArgs /\ opDefArityFound' = 0 /\ opDefArgs' = << >> /\ newNode' =
+ * Node[curNode.body] /\ pc' = "Lbl_18" ELSE /\ pc' = "Lbl_21" /\ UNCHANGED <<
+ * params, allArgs, mode, curName, opDefArityFound, opDefArgs, newNode >> /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, curNode, result, idx,
+ * prevMode, curContext, firstFindingOpName, opNode, newName, nodeArity, temp,
+ * tempArgs >>
+ * 
+ * Lbl_18 == /\ pc = "Lbl_18" /\ IF newNode.kind = SubstInKind THEN /\
+ * substInPrefix' = substInPrefix \o <<newNode>> /\ newNode' =
+ * Node[newNode.body] /\ pc' = "Lbl_18" /\ UNCHANGED curNode ELSE /\ IF mode =
+ * "FindingSubExpr" THEN /\ curNode' = newNode ELSE /\ TRUE /\ UNCHANGED curNode
+ * /\ pc' = "Lbl_21" /\ UNCHANGED << substInPrefix, newNode >> /\ UNCHANGED <<
+ * ops, args, expectedArity, params, allArgs, subExprOf, result, idx, mode,
+ * prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_16 == /\ pc = "Lbl_16" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2010, column
+ * 25.") /\ pc' = "Lbl_17" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_19 == /\ pc = "Lbl_19" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2043, column
+ * 25.") /\ pc' = "Lbl_21" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_8 == /\ pc = "Lbl_8" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion
+ * at line line 1892, column 38 of macro called at line 1974, column 26.") /\
+ * pc' = "Lbl_10" /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_9 == /\ pc = "Lbl_9" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion
+ * at line line 1892, column 38 of macro called at line 1977, column 33.") /\
+ * pc' = "Lbl_10" /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_20 == /\ pc = "Lbl_20" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2046, column
+ * 9.") /\ pc' = "Lbl_21" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_21 == /\ pc = "Lbl_21" /\ prevMode' = "FindingOpName" /\ pc' = "Lbl_69"
+ * /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, idx, mode, curContext, curName, opDefArityFound,
+ * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp,
+ * tempArgs >>
+ * 
+ * Lbl_23 == /\ pc = "Lbl_23" /\ curNode' = newNode /\ IF expectedArity = 0 THEN
+ * /\ IF Len(args[idx]) # curNode'.arity THEN /\ PrintT("bad arity") /\ IF debug
+ * THEN /\ idx' = idx /\ pc' = "Lbl_24" ELSE /\ pc' = "Done" /\ UNCHANGED idx
+ * ELSE /\ pc' = "Lbl_25" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_28" /\ UNCHANGED
+ * idx /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * subExprOf, result, mode, prevMode, curContext, curName, opDefArityFound,
+ * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp,
+ * tempArgs >>
+ * 
+ * Lbl_25 == /\ pc = "Lbl_25" /\ IF \E i \in 1..Len(args[idx]) :
+ * Arity(Node[args[idx][i]]) # 0 THEN /\ PrintT("Operator argument given to
+ * label") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_26" ELSE /\ pc' = "Done"
+ * /\ UNCHANGED idx ELSE /\ pc' = "Lbl_27" /\ UNCHANGED idx /\ UNCHANGED << ops,
+ * args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_26 == /\ pc = "Lbl_26" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2068, column
+ * 20.") /\ pc' = "Lbl_27" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_27 == /\ pc = "Lbl_27" /\ allArgs' = allArgs \o args[idx] /\ pc' =
+ * "Lbl_28" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params,
+ * curNode, subExprOf, result, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_24 == /\ pc = "Lbl_24" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2064, column
+ * 20.") /\ pc' = "Lbl_25" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_28 == /\ pc = "Lbl_28" /\ params' = params \o curNode.params /\ IF /\ idx
+ * < Len(ops) /\ ~IsName(ops[idx+1]) THEN /\ mode' = "FindingSubExpr" ELSE /\
+ * TRUE /\ UNCHANGED mode /\ IF \/ mode' = "FindingSubExpr" \/ idx = Len(ops)
+ * THEN /\ curNode' = Node[curNode.body] ELSE /\ TRUE /\ UNCHANGED curNode /\
+ * prevMode' = "FollowingLabels" /\ pc' = "Lbl_69" /\ UNCHANGED << ops, args,
+ * expectedArity, substInPrefix, allArgs, subExprOf, result, idx, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_22 == /\ pc = "Lbl_22" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2059, column
+ * 13.") /\ pc' = "Lbl_23" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_63 == /\ pc = "Lbl_63" /\ IF idx # Len(ops) THEN /\ IF IsName(ops[idx+1])
+ * THEN /\ pc' = "Lbl_64" /\ UNCHANGED idx ELSE /\ IF ops[idx+1] = ":" THEN /\
+ * PrintT("!: should not follow an operand selector") /\ IF debug THEN /\ idx' =
+ * idx /\ pc' = "Lbl_66" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' =
+ * "Lbl_67" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_67" /\ UNCHANGED idx /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_64 == /\ pc = "Lbl_64" /\ IF curNode.kind = LabelKind THEN /\ curNode' =
+ * Node[curNode.body] /\ pc' = "Lbl_64" /\ UNCHANGED << idx, mode, curContext,
+ * firstFindingOpName >> ELSE /\ IF curNode.kind = LetInKind THEN /\ curContext'
+ * = curNode.context /\ mode' = "FindingOpName" /\ firstFindingOpName' = FALSE
+ * /\ pc' = "Lbl_67" /\ UNCHANGED idx ELSE /\ PrintT("A name not following the
+ * selector of a LET") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_65" ELSE /\
+ * pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << mode, curContext,
+ * firstFindingOpName >> /\ UNCHANGED curNode /\ UNCHANGED << ops, args,
+ * expectedArity, substInPrefix, params, allArgs, subExprOf, result, prevMode,
+ * curName, opDefArityFound, opDefArgs, opNode, newName, newNode, nodeArity,
+ * temp, tempArgs >>
+ * 
+ * Lbl_65 == /\ pc = "Lbl_65" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2294, column
+ * 26.") /\ pc' = "Lbl_67" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_66 == /\ pc = "Lbl_66" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2297, column
+ * 26.") /\ pc' = "Lbl_67" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_67 == /\ pc = "Lbl_67" /\ prevMode' = "FindingSubExpr" /\ pc' = "Lbl_69"
+ * /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, idx, mode, curContext, curName, opDefArityFound,
+ * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp,
+ * tempArgs >>
+ * 
+ * Lbl_29 == /\ pc = "Lbl_29" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2091, column
+ * 14.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_30 == /\ pc = "Lbl_30" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2098, column
+ * 14.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_31 == /\ pc = "Lbl_31" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2110, column
+ * 17.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_33 == /\ pc = "Lbl_33" /\ curNode' = Node[curNode.operands[temp]] /\ IF
+ * \/ curNode'.kind # OpApplKind \/ Node[curNode'.operator].kind # BuiltInKind
+ * \/ Node[curNode'.operator].name # "$Pair" THEN /\ PrintT("Expecting
+ * $Pair(...)") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_34" ELSE /\ pc' =
+ * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_35" /\ UNCHANGED idx /\ UNCHANGED
+ * << ops, args, expectedArity, substInPrefix, params, allArgs, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_35 == /\ pc = "Lbl_35" /\ curNode' = Node[curNode.operands[2]] /\ pc' =
+ * "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params,
+ * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_34 == /\ pc = "Lbl_34" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2131, column
+ * 18.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_32 == /\ pc = "Lbl_32" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2125, column
+ * 18.") /\ pc' = "Lbl_33" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_37 == /\ pc = "Lbl_37" /\ temp' = ArgNum( ops[idx],
+ * Len(curNode.operands)) /\ IF temp' = -1 THEN /\ PrintT("Incorrect
+ * subexpression name") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_38" ELSE /\
+ * pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_39" /\ UNCHANGED idx /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, result, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, tempArgs >>
+ * 
+ * Lbl_38 == /\ pc = "Lbl_38" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2145, column
+ * 18.") /\ pc' = "Lbl_39" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_39 == /\ pc = "Lbl_39" /\ curNode' = Node[curNode.operands[temp]] /\ IF
+ * \/ curNode'.kind # OpApplKind \/ Node[curNode'.operator].kind # BuiltInKind
+ * \/ Node[curNode'.operator].name # "$Pair" THEN /\ PrintT("Expecting
+ * $Pair(...)") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_40" ELSE /\ pc' =
+ * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_41" /\ UNCHANGED idx /\ UNCHANGED
+ * << ops, args, expectedArity, substInPrefix, params, allArgs, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_40 == /\ pc = "Lbl_40" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2151, column
+ * 18.") /\ pc' = "Lbl_41" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_41 == /\ pc = "Lbl_41" /\ idx' = idx+1 /\ temp' = ArgNum(ops[idx'], 2) /\
+ * IF temp' = -1 THEN /\ PrintT("Incorrect subexpression name") /\ IF debug THEN
+ * /\ pc' = "Lbl_42" ELSE /\ pc' = "Done" ELSE /\ pc' = "Lbl_44" /\ UNCHANGED <<
+ * ops, args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >>
+ * 
+ * Lbl_42 == /\ pc = "Lbl_42" /\ idx' = idx /\ pc' = "Lbl_43" /\ UNCHANGED <<
+ * ops, args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_43 == /\ pc = "Lbl_43" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2156, column
+ * 18.") /\ pc' = "Lbl_44" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_44 == /\ pc = "Lbl_44" /\ curNode' = Node[curNode.operands[temp]] /\ IF
+ * curNode' = null THEN /\ PrintT("Selecting OTHER") /\ IF debug THEN /\ idx' =
+ * idx /\ pc' = "Lbl_45" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' =
+ * "Lbl_63" /\ UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_45 == /\ pc = "Lbl_45" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2160, column
+ * 18.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_36 == /\ pc = "Lbl_36" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2141, column
+ * 18.") /\ pc' = "Lbl_37" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_47 == /\ pc = "Lbl_47" /\ curNode' = Node[curNode.operands[temp]] /\ IF
+ * temp > 1 THEN /\ IF \/ curNode'.kind # OpApplKind \/
+ * Node[curNode'.operator].kind # BuiltInKind \/ Node[curNode'.operator].name #
+ * "$Pair" THEN /\ PrintT("Unexpected expression node found.") /\ IF debug THEN
+ * /\ idx' = idx /\ pc' = "Lbl_48" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\
+ * pc' = "Lbl_49" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_63" /\ UNCHANGED idx /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * subExprOf, result, mode, prevMode, curContext, curName, opDefArityFound,
+ * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp,
+ * tempArgs >>
+ * 
+ * Lbl_49 == /\ pc = "Lbl_49" /\ curNode' = Node[curNode.operands[2]] /\ pc' =
+ * "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params,
+ * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_48 == /\ pc = "Lbl_48" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2179, column
+ * 26.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_46 == /\ pc = "Lbl_46" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2172, column
+ * 19.") /\ pc' = "Lbl_47" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_51 == /\ pc = "Lbl_51" /\ curNode' = Node[curNode.operands[temp]] /\ pc'
+ * = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params,
+ * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_50 == /\ pc = "Lbl_50" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2195, column
+ * 25.") /\ pc' = "Lbl_51" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_53 == /\ pc = "Lbl_53" /\ curNode' = Node[curNode.operands[1]] /\ pc' =
+ * "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params,
+ * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_52 == /\ pc = "Lbl_52" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2220, column
+ * 32.") /\ pc' = "Lbl_53" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_54 == /\ pc = "Lbl_54" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2225, column
+ * 32.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_55 == /\ pc = "Lbl_55" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2232, column
+ * 12.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_56 == /\ pc = "Lbl_56" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2240, column
+ * 18.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_57 == /\ pc = "Lbl_57" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2255, column
+ * 9.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_58 == /\ pc = "Lbl_58" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2257, column
+ * 9.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_59 == /\ pc = "Lbl_59" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2260, column
+ * 9.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_60 == /\ pc = "Lbl_60" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2267, column
+ * 7.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_61 == /\ pc = "Lbl_61" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2272, column
+ * 10.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_62 == /\ pc = "Lbl_62" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2281, column
+ * 8.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_68 == /\ pc = "Lbl_68" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2303, column
+ * 8.") /\ pc' = "Lbl_69" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_70 == /\ pc = "Lbl_70" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2311, column
+ * 10.") /\ pc' = "Lbl_71" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_71 == /\ pc = "Lbl_71" /\ IF expectedArity < 0 THEN /\ IF \/ prevMode #
+ * "FindingOpName" \/ curNode.kind \notin {UserDefinedOpKind,
+ * ThmOrAssumpDefKind} THEN /\ PrintT("Should have selected a definition, but
+ * didn't.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_72" ELSE /\ pc' =
+ * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_73" /\ UNCHANGED idx ELSE /\ pc' =
+ * "Lbl_74" /\ UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_73 == /\ pc = "Lbl_73" /\ result' = curNode /\ pc' = "Finished" /\
+ * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs,
+ * curNode, subExprOf, idx, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_72 == /\ pc = "Lbl_72" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2317, column
+ * 17.") /\ pc' = "Lbl_73" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_74 == /\ pc = "Lbl_74" /\ IF expectedArity > 0 THEN /\ temp' =
+ * (Len(params) + IF \/ prevMode = "FindingOpName" \/ curNode.kind = OpArgKind
+ * THEN Arity(curNode) ELSE 0) /\ IF expectedArity # temp' THEN /\
+ * PrintT("Expect arity = " \o ToString(expectedArity) \o ", but found arity = "
+ * \o ToString(temp')) /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_75" ELSE /\
+ * pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_76" /\ UNCHANGED idx ELSE /\
+ * pc' = "Lbl_76" /\ UNCHANGED << idx, temp >> /\ UNCHANGED << ops, args,
+ * expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, result,
+ * mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >>
+ * 
+ * Lbl_75 == /\ pc = "Lbl_75" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2329, column
+ * 18.") /\ pc' = "Lbl_76" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_76 == /\ pc = "Lbl_76" /\ IF /\ prevMode = "FindingOpName" /\ Len(params)
+ * + Len(substInPrefix) > 0 THEN /\ temp' = [i \in 1..Len(curNode.params) |->
+ * [curNode.params[i] EXCEPT !.name = "New " \o @]] /\ curNode' = [kind |->
+ * OpApplKind, operands |-> temp', operator |-> curNode, unboundedBoundSymbols
+ * |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] /\ params' =
+ * params \o temp' /\ allArgs' = allArgs \o opDefArgs ELSE /\ TRUE /\ UNCHANGED
+ * << params, allArgs, curNode, temp >> /\ IF curNode'.kind = OpArgKind THEN /\
+ * IF expectedArity = 0 THEN /\ PrintT("Selected Operator Argument when
+ * expression expected.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_77" ELSE
+ * /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ IF expectedArity # Len(params') +
+ * curNode'.arity THEN /\ PrintT("Selected operator has wrong arity.") /\ IF
+ * debug THEN /\ idx' = idx /\ pc' = "Lbl_78" ELSE /\ pc' = "Done" /\ UNCHANGED
+ * idx ELSE /\ IF Len(params') + Len(substInPrefix) > 0 THEN /\ pc' = "Lbl_79"
+ * ELSE /\ pc' = "Lbl_80" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_80" /\ UNCHANGED
+ * idx /\ UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf,
+ * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >>
+ * 
+ * Lbl_77 == /\ pc = "Lbl_77" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2363, column
+ * 12.") /\ pc' = "Lbl_80" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_78 == /\ pc = "Lbl_78" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2365, column
+ * 12.") /\ pc' = "Lbl_80" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_79 == /\ pc = "Lbl_79" /\ temp' = [i \in 1..curNode.arity |-> [name |->
+ * "NewParam" \o NumToString(i), arity |-> 0]] /\ curNode' = [kind |->
+ * OpApplKind, operands |-> temp', operator |-> Node[curNode.op],
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>] /\ params' = params \o temp' /\ pc' = "Lbl_80" /\ UNCHANGED << ops, args,
+ * expectedArity, substInPrefix, allArgs, subExprOf, result, idx, mode,
+ * prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >>
+ * 
+ * Lbl_80 == /\ pc = "Lbl_80" /\ IF curNode.kind \in {UserDefinedOpKind,
+ * ConstantDeclKind, VariableDeclKind, FormalParamKind, BuiltInKind,
+ * BoundSymbolKind, ThmOrAssumpDefKind} THEN /\ IF expectedArity > 0 THEN /\
+ * result' = [kind |-> OpArgKind, name |-> curNode.name, op |-> curNode, arity
+ * |-> expectedArity] ELSE /\ IF expectedArity = 0 THEN /\ result' = [kind |->
+ * OpApplKind, operands |-> opDefArgs, operator |-> curNode,
+ * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> <<
+ * >>, subExprOf |-> subExprOf] ELSE /\ TRUE /\ UNCHANGED result /\ pc' =
+ * "Finished" /\ UNCHANGED temp ELSE /\ IF curNode.kind = OpArgKind THEN /\
+ * result' = curNode /\ pc' = "Finished" /\ UNCHANGED temp ELSE /\ temp' =
+ * Len(substInPrefix) /\ pc' = "Lbl_81" /\ UNCHANGED result /\ UNCHANGED << ops,
+ * args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, idx,
+ * mode, prevMode, curContext, curName, opDefArityFound, opDefArgs,
+ * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >>
+ * 
+ * Lbl_81 == /\ pc = "Lbl_81" /\ IF temp > 0 THEN /\ curNode' = [kind |->
+ * SubstInKind, body |-> curNode , subst |-> substInPrefix[temp].subst ] /\
+ * temp' = temp - 1 /\ pc' = "Lbl_81" /\ UNCHANGED idx ELSE /\ IF expectedArity
+ * > 0 THEN /\ IF Len(params) # expectedArity THEN /\ PrintT("Selection has
+ * wrong arity") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_82" ELSE /\ pc' =
+ * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_83" /\ UNCHANGED idx ELSE /\ IF
+ * Len(params) # Len(allArgs) THEN /\ PrintT("Abort: number of params # num of
+ * args") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_84" ELSE /\ pc' = "Done"
+ * /\ UNCHANGED idx ELSE /\ pc' = "Lbl_85" /\ UNCHANGED idx /\ UNCHANGED <<
+ * curNode, temp >> /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, subExprOf, result, mode, prevMode, curContext, curName,
+ * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode,
+ * nodeArity, tempArgs >>
+ * 
+ * Lbl_83 == /\ pc = "Lbl_83" /\ result' = [kind |-> OpArgKind, op |-> [kind |->
+ * UserDefinedOpKind, name |-> "LAMBDA", body |-> curNode, params |-> params,
+ * arity |-> Len(params), defined |-> TRUE, source |-> null], name |-> "LAMBDA"]
+ * /\ pc' = "Finished" /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, idx, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_85 == /\ pc = "Lbl_85" /\ IF Len(params) = 0 THEN /\ result' = [kind |->
+ * LabelKind, name |-> "$Subexpression", arity |-> 0, params |-> << >>, body |->
+ * curNode, subExprOf |-> subExprOf] ELSE /\ result' = [kind |-> OpApplKind,
+ * operator |-> [kind |-> UserDefinedOpKind, name |-> "LAMBDA", body |->
+ * curNode, params |-> params, arity |-> Len(params), defined |-> TRUE, source
+ * |-> null], operands |-> allArgs, unboundedBoundSymbols |-> <<>>,
+ * boundedBoundSymbols |-> << >>, ranges |-> << >>, subExprOf |-> subExprOf] /\
+ * pc' = "Finished" /\ UNCHANGED << ops, args, expectedArity, substInPrefix,
+ * params, allArgs, curNode, subExprOf, idx, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_82 == /\ pc = "Lbl_82" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2431, column
+ * 22.") /\ pc' = "Lbl_83" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Lbl_84 == /\ pc = "Lbl_84" /\ idx' = idx /\ Assert(FALSE, "Failure of
+ * assertion at line line 1892, column 38 of macro called at line 2443, column
+ * 22.") /\ pc' = "Lbl_85" /\ UNCHANGED << ops, args, expectedArity,
+ * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode,
+ * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode,
+ * newName, newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Finished == /\ pc = "Finished" /\ PrintT("Result: ") /\ PrintT(result) /\ pc'
+ * = "Done" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params,
+ * allArgs, curNode, subExprOf, result, idx, mode, prevMode, curContext,
+ * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName,
+ * newNode, nodeArity, temp, tempArgs >>
+ * 
+ * Next == Lbl_1 \/ Lbl_2 \/ Lbl_69 \/ Lbl_3 \/ Lbl_5 \/ Lbl_6 \/ Lbl_7 \/ Lbl_4
+ * \/ Lbl_10 \/ Lbl_12 \/ Lbl_13 \/ Lbl_11 \/ Lbl_14 \/ Lbl_15 \/ Lbl_17 \/
+ * Lbl_18 \/ Lbl_16 \/ Lbl_19 \/ Lbl_8 \/ Lbl_9 \/ Lbl_20 \/ Lbl_21 \/ Lbl_23 \/
+ * Lbl_25 \/ Lbl_26 \/ Lbl_27 \/ Lbl_24 \/ Lbl_28 \/ Lbl_22 \/ Lbl_63 \/ Lbl_64
+ * \/ Lbl_65 \/ Lbl_66 \/ Lbl_67 \/ Lbl_29 \/ Lbl_30 \/ Lbl_31 \/ Lbl_33 \/
+ * Lbl_35 \/ Lbl_34 \/ Lbl_32 \/ Lbl_37 \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41
+ * \/ Lbl_42 \/ Lbl_43 \/ Lbl_44 \/ Lbl_45 \/ Lbl_36 \/ Lbl_47 \/ Lbl_49 \/
+ * Lbl_48 \/ Lbl_46 \/ Lbl_51 \/ Lbl_50 \/ Lbl_53 \/ Lbl_52 \/ Lbl_54 \/ Lbl_55
+ * \/ Lbl_56 \/ Lbl_57 \/ Lbl_58 \/ Lbl_59 \/ Lbl_60 \/ Lbl_61 \/ Lbl_62 \/
+ * Lbl_68 \/ Lbl_70 \/ Lbl_71 \/ Lbl_73 \/ Lbl_72 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76
+ * \/ Lbl_77 \/ Lbl_78 \/ Lbl_79 \/ Lbl_80 \/ Lbl_81 \/ Lbl_83 \/ Lbl_85 \/
+ * Lbl_82 \/ Lbl_84 \/ Finished \/ (* Disjunct to prevent deadlock on
+ * termination *) (pc = "Done" /\ UNCHANGED vars)
+ * 
+ * Spec == Init /\ [][Next]_vars
+ * 
+ * Termination == <>(pc = "Done")
+ * 
+ * \* END TRANSLATION
+ * 
+ * NotDone == pc # "Done"
+ * (*************************************************************************)
+ * (* To print a trace of an execution that doesn't produce an error, add *) (*
+ * "INVARIANT NotDone" to Subexpression.cfg . *)
+ * (*************************************************************************)
+ * =============================================================================
+ ************************* 
+ * 
+ * end file Subexpression.tla
+ ****************************/
diff --git a/tlatools/src/tla2sany/semantic/InstanceNode.java b/tlatools/src/tla2sany/semantic/InstanceNode.java
index e65037bd15c82e0a57074a78e74822dba31618a9..c667f07a84470193596e75174f1a4438198a2b10 100644
--- a/tlatools/src/tla2sany/semantic/InstanceNode.java
+++ b/tlatools/src/tla2sany/semantic/InstanceNode.java
@@ -6,13 +6,15 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 public class InstanceNode extends LevelNode {
 
   /**
@@ -397,16 +399,18 @@ public class InstanceNode extends LevelNode {
       return res;
    }
 
-  public final void walkGaph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  public final void walkGaph(Hashtable<Integer, ExploreNode> semNodesTable, final ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(myUID, this);
+    visitor.preVisit(this);
 
     for (int i = 0; i < params.length; i++) {
-      params[i].walkGraph(semNodesTable);
+      params[i].walkGraph(semNodesTable, visitor);
     }
-    module.walkGraph(semNodesTable);
+    module.walkGraph(semNodesTable, visitor);
+    visitor.postVisit(this);
   }
 
   public final String toString(int depth) {
@@ -449,7 +453,8 @@ public class InstanceNode extends LevelNode {
       }
 
       Element ret = doc.createElement("InstanceNode");
-      ret.appendChild(appendText(doc,"uniquename",name.toString()));
+      if (name != null) ret.appendChild(appendText(doc,"uniquename",name.toString()));
+      ret.appendChild(appendText(doc, "module", module.getName().toString() ));
       ret.appendChild(sbts);
       ret.appendChild(prms);
       return ret;
diff --git a/tlatools/src/tla2sany/semantic/LabelNode.java b/tlatools/src/tla2sany/semantic/LabelNode.java
index 38c6c0db179f194bca9206f6d248a7237def82b4..e3b60bbc063e151b89633b81538c961a1c3abb0e 100644
--- a/tlatools/src/tla2sany/semantic/LabelNode.java
+++ b/tlatools/src/tla2sany/semantic/LabelNode.java
@@ -41,17 +41,19 @@ import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.parser.SyntaxTreeNode;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
+import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 import util.WrongInvocationException;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 public class LabelNode extends ExprNode
                        implements ExploreNode, OpDefOrLabelNode {
 
@@ -214,6 +216,7 @@ public class LabelNode extends ExprNode
   /*************************************************************************
   * Level-Checking.                                                        *
   *************************************************************************/
+  @Override
   public final boolean levelCheck(int iter) {
     if (levelChecked >= iter) {return true ;} ;
     levelChecked = iter;
@@ -224,37 +227,43 @@ public class LabelNode extends ExprNode
     return this.body.levelCheck(iter) && retVal ;
   }
 
+  @Override
   public final int getLevel() {
     if (levelChecked == 0)
       {throw new WrongInvocationException("getLevel called for TheoremNode before levelCheck");};
     return this.body.getLevel();
   }
 
-  public final HashSet getLevelParams() {
+  @Override
+  public final HashSet<SymbolNode> getLevelParams() {
     if (levelChecked == 0)
       {throw new WrongInvocationException("getLevelParams called for ThmNode before levelCheck");};
     return this.body.getLevelParams();
   }
 
-  public final HashSet getAllParams() {
+  @Override
+  public final HashSet<SymbolNode> getAllParams() {
     if (levelChecked == 0)
       {throw new WrongInvocationException("getAllParams called for ThmNode before levelCheck");};
     return this.body.getAllParams();
   }
 
+  @Override
   public final SetOfLevelConstraints getLevelConstraints() {
     if (levelChecked == 0)
        {throw new WrongInvocationException("getLevelConstraints called for ThmNode before levelCheck");};
     return this.body.getLevelConstraints();
   }
 
+  @Override
   public final SetOfArgLevelConstraints getArgLevelConstraints() {
     if (levelChecked == 0)
       {throw new WrongInvocationException("getArgLevelConstraints called for ThmNode before levelCheck");};
     return this.body.getArgLevelConstraints();
   }
 
-  public final HashSet getArgLevelParams() {
+  @Override
+  public final HashSet<ArgLevelParam> getArgLevelParams() {
     if (levelChecked == 0)
       {throw new WrongInvocationException("getArgLevelParams called for ThmNode before levelCheck");};
     return this.body.getArgLevelParams();
@@ -266,22 +275,27 @@ public class LabelNode extends ExprNode
    *
    * @see tla2sany.semantic.SemanticNode#getChildren()
    */
+  @Override
   public SemanticNode[] getChildren() {
       return new SemanticNode[] { this.body };
   }
   /*************************************************************************
   * The methods for implementing the ExploreNode interface.                *
   *************************************************************************/
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
-    if (body != null) body.walkGraph(semNodesTable);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    if (body != null) body.walkGraph(semNodesTable, visitor);
     for (int i = 0 ; i < params.length; i++) {
-      params[i].walkGraph(semNodesTable);
+      params[i].walkGraph(semNodesTable, visitor);
      } ;
+     visitor.postVisit(this);
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     String ret = "\n*LabelNode: " + super.toString(depth);
@@ -319,7 +333,8 @@ public class LabelNode extends ExprNode
     return ret;
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
       Element ret = doc.createElement("LabelNode");
       ret.appendChild(appendText(doc,"uniquename",getName().toString()));
       ret.appendChild(appendText(doc,"arity",Integer.toString(getArity())));
diff --git a/tlatools/src/tla2sany/semantic/LeafProofNode.java b/tlatools/src/tla2sany/semantic/LeafProofNode.java
index 289a17ae259cc8a03ff85e84094c1bd8777dcec9..c4054b40cc00400109bb5d388ec62e17e0bfc7b6 100644
--- a/tlatools/src/tla2sany/semantic/LeafProofNode.java
+++ b/tlatools/src/tla2sany/semantic/LeafProofNode.java
@@ -1,157 +1,167 @@
-// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
-package tla2sany.semantic;
-
-import java.util.Hashtable;
-
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/***************************************************************************
-* This class represents a leaf proof.  It is of kind LeafProffKind         *
-***************************************************************************/
-public class LeafProofNode extends ProofNode {
-
-  /*************************************************************************
-  * The fields.                                                            *
-  *                                                                        *
-  * A leaf proof has the syntax                                            *
-  *                                                                        *
-  *   [PROOF]   BY [ONLY] [facts] [DEF[S] defs].                           *
-  *           | OBVIOUS                                                    *
-  *           | OMITTED                                                    *
-  *                                                                        *
-  * The following two fields are the semantic nodes for the facts and      *
-  * defs.                                                                  *
-  *************************************************************************/
-  LevelNode[]  facts = null ;
-    /***********************************************************************
-    * For each i, facts[i] will be either an ExprNode, a ModuleNode, or    *
-    * an OpDefNode of type ModuleInstanceKind (with no parameters).  A     *
-    * proof management tool will probably put restrictions on the class    *
-    * of expressions that can be used as facts.                            *
-    ***********************************************************************/
-  SymbolNode[] defs  = null ;
-    /***********************************************************************
-    * For each i, defs[i] should be a UserDefinedOpDefKind or              *
-    * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode                 *
-    ***********************************************************************/
-  boolean omitted ;
-    /***********************************************************************
-    * True iff this is a "[PROOF] OMITTED" statement.  In this case, the   *
-    * facts and defs field will be null.  But that is also the case for    *
-    * an "OBVIOUS" proof.                                                  *
-    ***********************************************************************/
-  boolean isOnly ;
-    /***********************************************************************
-    * True iff this is a "BY ONLY" proof.                                  *
-    ***********************************************************************/
-
-  /*************************************************************************
-  * The constructor.                                                       *
-  *************************************************************************/
-  public LeafProofNode(TreeNode stn, LevelNode[] theFacts,
-                   SymbolNode[] theDefs, boolean omit, boolean only) {
-    super(LeafProofKind, stn) ;
-    this.facts   = theFacts ;
-    this.defs    = theDefs ;
-    this.omitted = omit ;
-    this.isOnly = only ;
-   } ;
-
-
-  /*************************************************************************
-  * Methods that return the values of the fields.                          *
-  *************************************************************************/
-  public LevelNode[]  getFacts() {return facts ; } ;
-  public SymbolNode[] getDefs() {return defs ;} ;
-  public boolean getOmitted() {return omitted ;} ;
-  public boolean getOnlyFlag() {return isOnly ;} ;
-
-  public boolean levelCheck(int iter) {
-    /***********************************************************************
-    * Level checking is performed by level-checking the facts.  Since the  *
-    * defs should be defined operators, they have already been level       *
-    * checked.                                                             *
-    ***********************************************************************/
-    if (this.levelChecked >= iter) return this.levelCorrect;
-    return this.levelCheckSubnodes(iter, facts) ;
-   }
-
-  /*
-   * The children are the facts.
-   * @see tla2sany.semantic.SemanticNode#getChildren()
-   */
-  public SemanticNode[] getChildren() {
-      if (this.facts == null || this.facts.length == 0) {
-          return null;
-      }
-      SemanticNode[] res = new SemanticNode[this.facts.length];
-      for (int i = 0; i < facts.length; i++) {
-          res[i] = facts[i];
-      }
-      return res;
-   }
-
-  public void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
-    if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
-    for (int  i = 0; i < facts.length; i++) {
-      facts[i].walkGraph(semNodesTable);
-      } ;
-    /***********************************************************************
-    * Note: there's no need to walk the defs array because all the nodes   *
-    * on it are walked from the nodes under which they appear.             *
-    ***********************************************************************/
-   }
-
-  public String toString(int depth) {
-    if (depth <= 0) return "";
-    String ret = "\n*LeafProofNode:\n"
-                  + super.toString(depth)
-                  + Strings.indent(2, "\nfacts:") ;
-    for (int i = 0 ; i < this.facts.length; i++) {
-        ret += Strings.indent(4, this.facts[i].toString(depth-1)) ;
-      } ;
-    ret += Strings.indent(2, "\ndefs:") ;
-    for (int i = 0 ; i < this.defs.length; i++) {
-        ret += Strings.indent(4, this.defs[i].toString(depth-1)) ;
-      } ;
-    ret += Strings.indent(2, "\nomitted: " + this.omitted)
-            + Strings.indent(2, "\nonlyFlag: " + this.isOnly);
-    return ret;
-   }
-
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
-    Element e;
-
-    if (getOmitted()) {
-      e = doc.createElement("omitted");
-    }
-    else if (getFacts().length == 0 && getDefs().length == 0) {
-      e = doc.createElement("obvious");
-    }
-    else {
-      //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2);
-      e = doc.createElement("by");
-
-      Element factse = doc.createElement("facts");
-      Element definitions = doc.createElement("defs");
-
-      for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context));
-      for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context));
-
-      e.appendChild(factse);
-      e.appendChild(definitions);
-      if(getOnlyFlag()) e.appendChild(doc.createElement("only"));
-      // at the end, we append the context of the symbols used in this node
-      //e.appendChild(context.getContextElement(doc));
-    }
-
-    return e;
-  }
-
-}
+// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
+package tla2sany.semantic;
+
+import java.util.Hashtable;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+
+/***************************************************************************
+* This class represents a leaf proof.  It is of kind LeafProffKind         *
+***************************************************************************/
+public class LeafProofNode extends ProofNode {
+
+  /*************************************************************************
+  * The fields.                                                            *
+  *                                                                        *
+  * A leaf proof has the syntax                                            *
+  *                                                                        *
+  *   [PROOF]   BY [ONLY] [facts] [DEF[S] defs].                           *
+  *           | OBVIOUS                                                    *
+  *           | OMITTED                                                    *
+  *                                                                        *
+  * The following two fields are the semantic nodes for the facts and      *
+  * defs.                                                                  *
+  *************************************************************************/
+  LevelNode[]  facts = null ;
+    /***********************************************************************
+    * For each i, facts[i] will be either an ExprNode, a ModuleNode, or    *
+    * an OpDefNode of type ModuleInstanceKind (with no parameters).  A     *
+    * proof management tool will probably put restrictions on the class    *
+    * of expressions that can be used as facts.                            *
+    ***********************************************************************/
+  SymbolNode[] defs  = null ;
+    /***********************************************************************
+    * For each i, defs[i] should be a UserDefinedOpDefKind or              *
+    * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode                 *
+    ***********************************************************************/
+  boolean omitted ;
+    /***********************************************************************
+    * True iff this is a "[PROOF] OMITTED" statement.  In this case, the   *
+    * facts and defs field will be null.  But that is also the case for    *
+    * an "OBVIOUS" proof.                                                  *
+    ***********************************************************************/
+  boolean isOnly ;
+    /***********************************************************************
+    * True iff this is a "BY ONLY" proof.                                  *
+    ***********************************************************************/
+
+  /*************************************************************************
+  * The constructor.                                                       *
+  *************************************************************************/
+  public LeafProofNode(TreeNode stn, LevelNode[] theFacts,
+                   SymbolNode[] theDefs, boolean omit, boolean only) {
+    super(LeafProofKind, stn) ;
+    this.facts   = theFacts ;
+    this.defs    = theDefs ;
+    this.omitted = omit ;
+    this.isOnly = only ;
+   } ;
+
+
+  /*************************************************************************
+  * Methods that return the values of the fields.                          *
+  *************************************************************************/
+  public LevelNode[]  getFacts() {return facts ; } ;
+  public SymbolNode[] getDefs() {return defs ;} ;
+  public boolean getOmitted() {return omitted ;} ;
+  public boolean getOnlyFlag() {return isOnly ;} ;
+
+  @Override
+  public boolean levelCheck(int iter) {
+    /***********************************************************************
+    * Level checking is performed by level-checking the facts.  Since the  *
+    * defs should be defined operators, they have already been level       *
+    * checked.                                                             *
+    ***********************************************************************/
+    if (this.levelChecked >= iter) return this.levelCorrect;
+    return this.levelCheckSubnodes(iter, facts) ;
+   }
+
+  /*
+   * The children are the facts.
+   * @see tla2sany.semantic.SemanticNode#getChildren()
+   */
+  @Override
+  public SemanticNode[] getChildren() {
+      if (this.facts == null || this.facts.length == 0) {
+          return null;
+      }
+      SemanticNode[] res = new SemanticNode[this.facts.length];
+      for (int i = 0; i < facts.length; i++) {
+          res[i] = facts[i];
+      }
+      return res;
+   }
+
+  @Override
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
+    if (semNodesTable.get(uid) != null) return;
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    for (int  i = 0; i < facts.length; i++) {
+      facts[i].walkGraph(semNodesTable, visitor);
+      } ;
+    /***********************************************************************
+    * Note: there's no need to walk the defs array because all the nodes   *
+    * on it are walked from the nodes under which they appear.             *
+    ***********************************************************************/
+      visitor.postVisit(this);
+   }
+
+  @Override
+  public String toString(int depth) {
+    if (depth <= 0) return "";
+    String ret = "\n*LeafProofNode:\n"
+                  + super.toString(depth)
+                  + Strings.indent(2, "\nfacts:") ;
+    for (int i = 0 ; i < this.facts.length; i++) {
+        ret += Strings.indent(4, this.facts[i].toString(depth-1)) ;
+      } ;
+    ret += Strings.indent(2, "\ndefs:") ;
+    for (int i = 0 ; i < this.defs.length; i++) {
+        ret += Strings.indent(4, this.defs[i].toString(depth-1)) ;
+      } ;
+    ret += Strings.indent(2, "\nomitted: " + this.omitted)
+            + Strings.indent(2, "\nonlyFlag: " + this.isOnly);
+    return ret;
+   }
+
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
+    Element e;
+
+    if (getOmitted()) {
+      e = doc.createElement("omitted");
+    }
+    else if (getFacts().length == 0 && getDefs().length == 0) {
+      e = doc.createElement("obvious");
+    }
+    else {
+      //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2);
+      e = doc.createElement("by");
+
+      Element factse = doc.createElement("facts");
+      Element definitions = doc.createElement("defs");
+
+      for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context));
+      for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context));
+
+      e.appendChild(factse);
+      e.appendChild(definitions);
+      if(getOnlyFlag()) e.appendChild(doc.createElement("only"));
+      // at the end, we append the context of the symbols used in this node
+      //e.appendChild(context.getContextElement(doc));
+    }
+
+    return e;
+  }
+
+}
diff --git a/tlatools/src/tla2sany/semantic/LetInNode.java b/tlatools/src/tla2sany/semantic/LetInNode.java
index 22345cbc902cab6d0ba450d74e4373fde1b772ae..2e94d23d39dd9a9bf514f7071c7196026b3abb75 100644
--- a/tlatools/src/tla2sany/semantic/LetInNode.java
+++ b/tlatools/src/tla2sany/semantic/LetInNode.java
@@ -6,13 +6,15 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
+import tla2sany.xml.SymbolContext;
 
 public class LetInNode extends ExprNode
 implements ExploreNode, LevelConstants {
@@ -106,6 +108,8 @@ implements ExploreNode, LevelConstants {
 //  private SetOfArgLevelConstraints argLevelConstraints;
 //  private HashSet argLevelParams;
 
+  @Override
+  @SuppressWarnings("unchecked")
   public final boolean levelCheck(int itr) {
     if (this.levelChecked >= itr) return this.levelCorrect;
     levelChecked = itr ;
@@ -141,8 +145,8 @@ implements ExploreNode, LevelConstants {
     * the aliasing of the levelParams and allParams fields of this node    *
     * and its body to the same HashSets, but it doesn't hurt to be safe.   *
     ***********************************************************************/
-    this.levelParams = (HashSet) this.body.getLevelParams().clone();
-    this.allParams   = (HashSet) this.body.getAllParams().clone();
+    this.levelParams = (HashSet<SymbolNode>)this.body.getLevelParams().clone();
+    this.allParams   = (HashSet<SymbolNode>) this.body.getAllParams().clone();
 
 //    this.levelConstraints = new SetOfLevelConstraints();
     this.levelConstraints.putAll(this.body.getLevelConstraints());
@@ -173,9 +177,9 @@ implements ExploreNode, LevelConstants {
         if (this.opDefs[i].getKind() != ThmOrAssumpDefKind){
           params = ((OpDefNode) this.opDefs[i]).getParams();
           } ;
-        Iterator iter = this.opDefs[i].getArgLevelParams().iterator();
+        Iterator<ArgLevelParam> iter = this.opDefs[i].getArgLevelParams().iterator();
         while (iter.hasNext()) {
-          ArgLevelParam alp = (ArgLevelParam)iter.next();
+          ArgLevelParam alp = iter.next();
           if (!alp.occur(params)) {
             this.argLevelParams.add(alp);
           }
@@ -219,6 +223,7 @@ implements ExploreNode, LevelConstants {
 //           "ArgLevelParams: "      + this.argLevelParams      + "\n" ;
 //  }
 
+  @Override
   public SemanticNode[] getChildren() {
       SemanticNode[] res =
          new SemanticNode[this.opDefs.length + this.insts.length + 1];
@@ -233,26 +238,30 @@ implements ExploreNode, LevelConstants {
       return res;
    }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
 
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
 
     /***********************************************************************
     * Can now walk LET nodes from context, don't need to use opDefs        *
     * (which is incomplete).                                               *
     ***********************************************************************/
-    if (context != null){context.walkGraph(semNodesTable);} ;
+    if (context != null){context.walkGraph(semNodesTable, visitor);} ;
 //    if (opDefs != null) {
 //      for (int i = 0; i < opDefs.length; i++) {
 //        if (opDefs[i] != null) opDefs[i].walkGraph(semNodesTable);
 //      }
 //    }
-    if (body != null) body.walkGraph(semNodesTable);
+    if (body != null) body.walkGraph(semNodesTable, visitor);
+    visitor.postVisit(this);
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -286,8 +295,8 @@ implements ExploreNode, LevelConstants {
     return ret;
   }
 
-
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element ret = doc.createElement("LetInNode");
     ret.appendChild(appendElement(doc,"body",body.export(doc,context)));
     Element arguments = doc.createElement("opDefs");
diff --git a/tlatools/src/tla2sany/semantic/LevelNode.java b/tlatools/src/tla2sany/semantic/LevelNode.java
index ccd88bc3f782aa6de39290056fb3e4149fc9bdac..189c4139e33df9c44de9f53d5f73814fef0d2977 100644
--- a/tlatools/src/tla2sany/semantic/LevelNode.java
+++ b/tlatools/src/tla2sany/semantic/LevelNode.java
@@ -4,12 +4,13 @@ package tla2sany.semantic;
 import java.util.HashSet;
 import java.util.Iterator;
 
-import tla2sany.st.TreeNode;
-import util.WrongInvocationException;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
+import util.WrongInvocationException;
+
 /***************************************************************************
 * Note: The SANY1 level checking algorithm is specified in the file        *
 * LevelSpec.tla.  The handling of recursive operators is explained in the  *
@@ -63,10 +64,10 @@ public class LevelNode extends SemanticNode {
 ***************************************************************************/
 public boolean                   levelCorrect        = true ;
 public int                       level               = ConstantLevel ;
-public HashSet                   levelParams         = new HashSet() ;
+public HashSet<SymbolNode>       levelParams         = new HashSet<>() ;
 public SetOfLevelConstraints     levelConstraints    = new SetOfLevelConstraints();
 public SetOfArgLevelConstraints  argLevelConstraints = new SetOfArgLevelConstraints();
-public HashSet                   argLevelParams      = new HashSet() ;
+public HashSet<ArgLevelParam>    argLevelParams      = new HashSet<>() ;
 
 /***************************************************************************
 * The following HashSets are used in computing Leibnizity.                 *
@@ -80,8 +81,8 @@ public HashSet                   argLevelParams      = new HashSet() ;
 * See the file leibniz-checking.txt, appended below, for an explanation    *
 * of Leibnizity and Leibniz checking.                                      *
 ***************************************************************************/
-public HashSet                   allParams           = new HashSet() ;
-public HashSet                   nonLeibnizParams    = new HashSet() ;
+public HashSet<SymbolNode>       allParams           = new HashSet<>() ;
+public HashSet<SymbolNode>       nonLeibnizParams    = new HashSet<>() ;
 
 public int levelChecked   = 0 ;
   /*************************************************************************
@@ -178,11 +179,11 @@ public int levelChecked   = 0 ;
   * instantiated by a temporal formula.  Added by LL on 1 Mar 2009         *
   *************************************************************************/
   static void addTemporalLevelConstraintToConstants(
-                 HashSet params,
+                 HashSet<SymbolNode> params,
                  SetOfLevelConstraints constrs ) {
-      Iterator iter = params.iterator();
+      Iterator<SymbolNode> iter = params.iterator();
       while (iter.hasNext()) {
-        LevelNode node = (LevelNode) iter.next() ;
+        SymbolNode node = iter.next() ;
         if (node.getKind() == ConstantDeclKind) {
           constrs.put(node, Levels[ActionLevel]);
          };
@@ -191,6 +192,10 @@ public int levelChecked   = 0 ;
 /***************************************************************************
 * The checks in the following methods should probably be eliminated after  *
 * SANY2 is debugged.                                                       *
+*  <ul><li>0 for constant                                                  *
+*  <li>1 for non-primed variable                                           *
+*  <li>2 for primed variable                                               *
+*  <li>3 for temporal formula</ul>                                         *
 ***************************************************************************/
   public int getLevel(){
     if (this.levelChecked == 0)
@@ -198,7 +203,7 @@ public int levelChecked   = 0 ;
     return this.level;
   }
 
-  public HashSet getLevelParams(){
+  public HashSet<SymbolNode> getLevelParams(){
     /***********************************************************************
     * Seems to return a HashSet of OpDeclNode objects.  Presumably, these  *
     * are the parameters from the local context that contribute to the     *
@@ -209,7 +214,7 @@ public int levelChecked   = 0 ;
     return this.levelParams;
    }
 
-  public HashSet getAllParams(){
+  public HashSet<SymbolNode> getAllParams(){
     /***********************************************************************
     * Returns a HashSet of OpDeclNode objects, which are the parameters    *
     * from the local context that appear within the object.                *
@@ -219,7 +224,7 @@ public int levelChecked   = 0 ;
     return this.allParams;
    }
 
-  public HashSet getNonLeibnizParams(){
+  public HashSet<SymbolNode> getNonLeibnizParams(){
     /***********************************************************************
     * Returns a HashSet of OpDeclNode objects, which is the subset of      *
     * parameters returned by getAllParams() that appear within a           *
@@ -254,7 +259,7 @@ public int levelChecked   = 0 ;
     return this.argLevelConstraints;
    }
 
-  public HashSet getArgLevelParams(){
+  public HashSet<ArgLevelParam> getArgLevelParams(){
     /***********************************************************************
     * Seems to return a HashSet of ArgLevelParam objects.  (See            *
     * ArgLevelParam.java for an explanation of those objects.)             *
@@ -278,32 +283,32 @@ public int levelChecked   = 0 ;
        "NonLeibnizParams: "    + HashSetToString(this.getNonLeibnizParams()) ;
     }
 
-  public static String HashSetToString(HashSet hs) {
+  public static String HashSetToString(HashSet<? extends SymbolNode> hs) {
     /***********************************************************************
     * Converts a HashSet of SymbolNodes to a printable string.             *
     ***********************************************************************/
     String rval = "{" ;
     boolean first = true ;
-    Iterator iter = hs.iterator();
+    final Iterator<? extends SymbolNode> iter = hs.iterator();
     while (iter.hasNext()) {
       if (! first) {rval = rval + ", ";} ;
-      rval = rval + ((SymbolNode) iter.next()).getName() ;
+      rval = rval + iter.next().getName() ;
       first = false ;
      } ;
     rval = rval + "}" ;
     return rval ;
    }
 
-  public static String ALPHashSetToString(HashSet hs) {
+  public static String ALPHashSetToString(HashSet<ArgLevelParam> hs) {
     /***********************************************************************
     * Converts a HashSet of ArgLevelParam objects to a printable string.   *
     ***********************************************************************/
     String rval = "{" ;
     boolean first = true ;
-    Iterator iter = hs.iterator();
+    Iterator<ArgLevelParam> iter = hs.iterator();
     while (iter.hasNext()) {
       if (! first) {rval = rval + ", ";} ;
-      ArgLevelParam alp = (ArgLevelParam) iter.next();
+      ArgLevelParam alp = iter.next();
       rval = rval + "<" + alp.op.getName() + ", " + alp.i + ", " +
                      alp.param.getName() + ">" ;
       first = false;
@@ -322,9 +327,10 @@ public int levelChecked   = 0 ;
     return this.defaultLevelDataToString() ;}
 
 
-  protected Element getSemanticElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getSemanticElement(Document doc, SymbolContext context) {
       // T.L. abstract method used to add data from subclasses
-      Element e = getLevelElement(doc, context);
+      Element e = getLevelElement(doc, context); //SymbolElement.getLevelElement is not supposed to be called
       try {
         Element l = appendText(doc,"level",Integer.toString(getLevel()));
         e.insertBefore(l,e.getFirstChild());
@@ -338,7 +344,7 @@ public int levelChecked   = 0 ;
      * T.L. October 2014
      * Abstract method for subclasses of LevelNode to add their information
      * */
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  protected Element getLevelElement(Document doc, SymbolContext context) {
       throw new UnsupportedOperationException("xml export is not yet supported for: " + getClass() + " with toString: " + toString(100));
     }
 
@@ -607,6 +613,28 @@ public int levelChecked   = 0 ;
 // (* any reasonable case where this will disallow a level-correct expression *)
 // (* that a user is likely to write.                                         *)
 // (*                                                                         *)
+// (* The decision to simplify the bookkeeping results in the following,      *)
+// (* somewhat less unlikely problem.  With the definitions                   *)
+// (*                                                                         *)
+// (*    ApplyToPrime(Op(_)) == Op(x')                                        *)
+// (*    EqualsNoPrime(a) == x                                                *)
+// (*                                                                         *)
+// (* the expression  ApplyToPrime(EqualsNoPrime)' , which equals x', is      *)
+// (* considered to be illegal.  This is because the algorithm to compute the *)
+// (* level makes the assumption that ApplyToPrime will always be applied to  *)
+// (* operators Op for which the level of Op(exp) depends on the level of     *)
+// (* exp.  Hence, SANY's algorithm gives ApplyToPrime(Op) a level of at      *)
+// (* least 2 (primed expression) for any operator Op.  A slightly more       *)
+// (* realistic example can be constructed by modifying ApplyToPrime a bit    *)
+// (* and applying it to ENABLED.                                             *)
+// (* TLC warns users about this bug if it reports an invariant to be         *)
+// (* level-incorrect in tlc2.tool.Spec.processConfigInvariants() with error  *)
+// (* code tlc2.output.EC.TLC_INVARIANT_VIOLATED_LEVEL.                       *)
+// (* A corresponding test can be found in test52. Its invariant "Invariant"  *)
+// (* covers the ENABLED scenario. However, the invariant remains disabled    *)
+// (* for as long as this bug is open. The invariant Invariant can be         *)
+// (* re-enabled in test52.cfg once this bug is closed.                       *)
+// (*                                                                         *)
 // (* To compute the values of op.level, op.weights, and op.minMaxLevel for   *)
 // (* an OpDefNode op corresponding to a definition, a level-checking         *)
 // (* algorithm needs to keep track of the constraints on its formal          *)
@@ -1834,7 +1862,7 @@ public int levelChecked   = 0 ;
 //         (* I believe this shold be OpDeclNode. (LL, Mar 2007)              *)
 //         (*******************************************************************)
 //       param == op.params
-//   IN  /\ n.level = Max(op.level,
+//   IN  /\ n.level = NumMax(op.level,
 //                        SetMax({arg[i].level : i \in 1..p})
 //            (****************************************************************)
 //            (* For an operator parameter, we assume that the weights of     *)
diff --git a/tlatools/src/tla2sany/semantic/ModuleNode.java b/tlatools/src/tla2sany/semantic/ModuleNode.java
index 32173854f8d7de3e902961b30d34014652e700b0..739024b73caea39d5f050969d14ae49e37ddd820 100644
--- a/tlatools/src/tla2sany/semantic/ModuleNode.java
+++ b/tlatools/src/tla2sany/semantic/ModuleNode.java
@@ -12,19 +12,30 @@
 
 package tla2sany.semantic;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.semantic.Context.Pair;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
+import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 import util.WrongInvocationException;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 public class ModuleNode extends SymbolNode {
 
 /***************************************************************************
@@ -174,10 +185,12 @@ public class ModuleNode extends SymbolNode {
     * ModuleNode class.                                                    *
     ***********************************************************************/
 
-  private HashSet allExtendees = null ;
+  private HashMap<Boolean, HashSet<ModuleNode>> depthAllExtendeesMap = new HashMap<>();
     /***********************************************************************
     * The set of all modules that are extended by this module--either      *
-    * directly or indirectly.  Returned by getExtendModules                *
+    * directly or indirectly, keyed a Boolean representing whether the     *
+    * extendees are gathered recursively of not.                           *
+    * Returned by getExtendModules                                         *
     ***********************************************************************/
 
   private OpDeclNode[] constantDecls = null;
@@ -187,7 +200,7 @@ public class ModuleNode extends SymbolNode {
     // VARIABLEs declared in this module
 
 
-  private Vector definitions         = new Vector(8);
+  private ArrayList<SemanticNode> definitions = new ArrayList<>();
     // AssumeNodes, internal ModuleNodes, OpDefNodes, and TheoremNodes, in
     // the exact order they were defined in this module
     /***********************************************************************
@@ -208,7 +221,8 @@ public class ModuleNode extends SymbolNode {
     * It also contains ModuleNodes for inner modules, but not for modules  *
     * nested within them.                                                  *
     *                                                                      *
-    * It appears that this field is never used in SANY1.                   *
+    * It appears that this field is never used in SANY1; it is used by the *
+    * MCParser though.                                                     *
     ***********************************************************************/
 
   Vector recursiveDecls = new Vector(8);
@@ -217,7 +231,7 @@ public class ModuleNode extends SymbolNode {
     * RECURSIVE statements, in the order in which they were created.       *
     ***********************************************************************/
 
-  Vector opDefsInRecursiveSection = new Vector(16);
+  Vector<OpDefNode> opDefsInRecursiveSection = new Vector<>(16);
     /***********************************************************************
     * The list of all OpDefNode objects opd in this module, and in any     *
     * inner modules, with opd.recursiveSection >= 0.  (See the comments    *
@@ -336,8 +350,12 @@ public class ModuleNode extends SymbolNode {
     return (getConstantDecls().length == 0 &&
             getVariableDecls().length == 0);
   }
+  
+  public List<SemanticNode> getDefinitions() {
+	  return definitions;
+  }
 
-  public final void createExtendeeArray(Vector extendeeVec) {
+  public final void createExtendeeArray(Vector<ModuleNode> extendeeVec) {
     /***********************************************************************
     * This is called by Generator.processExtendsList to set the            *
     * ModuleNode's extendees field, which never seems to be used.          *
@@ -345,7 +363,7 @@ public class ModuleNode extends SymbolNode {
     extendees = new ModuleNode[extendeeVec.size()];
 
     for ( int i = 0; i < extendees.length; i++ ) {
-      extendees[i] = (ModuleNode)extendeeVec.elementAt(i);
+      extendees[i] = extendeeVec.elementAt(i);
     }
   }
 
@@ -358,7 +376,7 @@ public class ModuleNode extends SymbolNode {
   public final OpDeclNode[] getConstantDecls() {
     if (constantDecls != null) return constantDecls;
 
-    Vector contextVec = ctxt.getConstantDecls();
+    Vector<SemanticNode> contextVec = ctxt.getConstantDecls();
     constantDecls = new OpDeclNode[contextVec.size()];
     for (int i = 0, j = constantDecls.length - 1; i < constantDecls.length; i++) {
       constantDecls[j--] = (OpDeclNode)contextVec.elementAt(i);
@@ -374,7 +392,7 @@ public class ModuleNode extends SymbolNode {
    public final OpDeclNode[] getVariableDecls() {
     if (variableDecls != null) return variableDecls;
 
-    Vector contextVec = ctxt.getVariableDecls();
+    Vector<SemanticNode> contextVec = ctxt.getVariableDecls();
     variableDecls = new OpDeclNode[contextVec.size()];
     for (int i = 0, j = variableDecls.length - 1; i < variableDecls.length; i++) {
       variableDecls[j--] = (OpDeclNode)contextVec.elementAt(i);
@@ -402,6 +420,14 @@ public class ModuleNode extends SymbolNode {
     return opDefs;
   }
 
+  public final OpDefNode getOpDef(final String name) {
+	  return getOpDef(UniqueString.uniqueStringOf(name));
+  }
+
+  public final OpDefNode getOpDef(final UniqueString name) {
+	  return Stream.of(getOpDefs()).filter(o -> o.getName() == name).findFirst().orElse(null);
+  }
+
   /*************************************************************************
   * Returns an array of all ThmOrAssumpDefNode objects created in the      *
   * current module (but not in inner modules).  They should appear in the  *
@@ -419,14 +445,13 @@ public class ModuleNode extends SymbolNode {
     return thmOrAssDefs;
   }
 
-  /**
-   * Appends to vector of definitions in this module; should only be
-   * called with AssumeNodes, ModuleNodes, OpDefNodes and TheoremNodes
-   * as arguments.
-   */
-  public final void appendDef(SemanticNode s) {
-    definitions.addElement(s);
-  }
+	/**
+	 * Appends to vector of definitions in this module; should only be called with
+	 * AssumeNodes, ModuleNodes, OpDefNodes and TheoremNodes as arguments.
+	 */
+	public final void appendDef(SemanticNode s) {
+		definitions.add(s);
+	}
 
   /**
    * Returns array of the InstanceNode's representing module
@@ -529,6 +554,7 @@ public void setInstantiated(boolean isInstantiated) {
 
 /**
  * @return the isStandard
+ * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule()
  */
 public boolean isStandard() {
 	return isStandard;
@@ -536,6 +562,7 @@ public boolean isStandard() {
 
 /**
  * @param isStandard the isStandard to set
+ * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule()
  */
 public void setStandard(boolean isStandard) {
 	this.isStandard = isStandard;
@@ -592,20 +619,37 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
   }
 
 
-  public final HashSet getExtendedModuleSet() {
-    /***********************************************************************
-    * Returns a Hashset whose elements are ModuleNode objects representing  *
-    * all modules that are extended by this module--either directly or      *
-    * indirectly.                                                           *
-    ***********************************************************************/
-    if (this.allExtendees == null) {
-      this.allExtendees = new HashSet() ;
-      for (int i = 0; i < this.extendees.length; i++) {
-        this.allExtendees.add(this.extendees[i]) ;
-        this.allExtendees.addAll(this.extendees[i].getExtendedModuleSet()) ;
-       } // for
-      }; //if
-    return this.allExtendees ;
+  public final HashSet<ModuleNode> getExtendedModuleSet() {
+	  return getExtendedModuleSet(true);
+  }
+
+  /**
+   * @param recursively if true, the extendees of extendees of extendees of ...
+   * 						will be included; if false, only the direct extendees
+   * 						of this instance will be returned.
+   * @return
+   */
+  public final HashSet<ModuleNode> getExtendedModuleSet(final boolean recursively) {
+		/***********************************************************************
+		 * Returns a Hashset whose elements are ModuleNode objects representing *
+		 * all modules that are extended by this module--either directly or     *
+		 * indirectly.                                                          *
+		 ***********************************************************************/
+	  final Boolean key = Boolean.valueOf(recursively);
+	  HashSet<ModuleNode> extendeesSet = depthAllExtendeesMap.get(key);
+	  if (extendeesSet == null) {
+		  extendeesSet = new HashSet<>();
+		  for (int i = 0; i < this.extendees.length; i++) {
+			  extendeesSet.add(extendees[i]);
+			  if (recursively) {
+				  extendeesSet.addAll(extendees[i].getExtendedModuleSet(true));
+			  }
+		  }
+		  
+		  depthAllExtendeesMap.put(key, extendeesSet);
+	  }
+	  
+	  return extendeesSet;
   }
 
   public boolean extendsModule(ModuleNode mod) {
@@ -680,6 +724,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
 //  private SetOfArgLevelConstraints argLevelConstraints;
 //  private HashSet argLevelParams;
 
+  @Override
   public final boolean levelCheck(int itr) {
 
     if (levelChecked >= itr) return this.levelCorrect;
@@ -733,8 +778,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
       * opDefsInRecursiveSection vector.                                   *
       *********************************************************************/
       int curNodeIdx = firstInSectIdx ;
-      OpDefNode curNode =
-          (OpDefNode) opDefsInRecursiveSection.elementAt(curNodeIdx);
+      OpDefNode curNode = opDefsInRecursiveSection.elementAt(curNodeIdx);
       int curSection = curNode.recursiveSection ;
       boolean notDone = true ;
       while (notDone) {
@@ -758,8 +802,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
          else {curNode.levelChecked = 0 ;};
         curNodeIdx++ ;
         if (curNodeIdx < opDefsInRecursiveSection.size()) {
-          curNode = (OpDefNode)
-                        opDefsInRecursiveSection.elementAt(curNodeIdx);
+          curNode = opDefsInRecursiveSection.elementAt(curNodeIdx);
           notDone = (curNode.recursiveSection == curSection) ;
          }
         else {notDone = false ;} ;
@@ -777,7 +820,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
       HashSet recursiveLevelParams = new HashSet() ;
       HashSet recursiveAllParams = new HashSet() ;
       for (int i = firstInSectIdx ; i < curNodeIdx ; i++) {
-        curNode = (OpDefNode) opDefsInRecursiveSection.elementAt(i) ;
+        curNode = opDefsInRecursiveSection.elementAt(i) ;
         if (curNode.inRecursive) {curNode.levelChecked = 0 ;} ;
         curNode.levelCheck(1) ;
 
@@ -805,7 +848,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
       * for every operator in the recursive section.                       *
       *********************************************************************/
       for (int i = firstInSectIdx ; i < curNodeIdx ; i++) {
-        curNode = (OpDefNode) opDefsInRecursiveSection.elementAt(i) ;
+        curNode = opDefsInRecursiveSection.elementAt(i) ;
         if (curNode.inRecursive) {curNode.levelChecked = 2;} ;
         curNode.level = Math.max(curNode.level, maxRecursiveLevel) ;
         curNode.levelParams.addAll(recursiveLevelParams) ;
@@ -817,7 +860,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
       * recursive section.                                                 *
       *********************************************************************/
       for (int i = firstInSectIdx ; i < curNodeIdx ; i++) {
-        curNode = (OpDefNode) opDefsInRecursiveSection.elementAt(i) ;
+        curNode = opDefsInRecursiveSection.elementAt(i) ;
         if (curNode.inRecursive) {curNode.levelChecked = 1;} ;
         curNode.levelCheck(2) ;
        }; // for i
@@ -948,6 +991,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
     return this.levelCorrect;
   }
 
+  @Override
   public final int getLevel() {
       throw new WrongInvocationException("Internal Error: Should never call ModuleNode.getLevel()");
   }
@@ -1018,11 +1062,29 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
     // Otherwise this module is a constant module
     return true;
   }
+  
+	// TODO Change to take an action/operation that is to be executed on matching
+	// symbols. That way, clients don't have to iterate the result set again.
+	public Collection<SymbolNode> getSymbols(final SymbolMatcher symbolMatcher) {
+		final List<SymbolNode> result = new ArrayList<SymbolNode>(); // TreeSet to order result.
+		
+		final Enumeration<Pair> content = this.ctxt.content();
+		while (content.hasMoreElements()) {
+			final SymbolNode aSymbol = content.nextElement().getSymbol();
+			if (symbolMatcher.matches(aSymbol)) {
+				result.add(aSymbol);
+			}
+		}
+		
+		Collections.sort(result);
+		return result;
+	}
 
   /**
    * walkGraph, levelDataToString, and toString methods to implement
    * ExploreNode interface
    */
+  @Override
   public final String levelDataToString() {
     return "LevelParams: "         + getLevelParams()         + "\n" +
            "LevelConstraints: "    + getLevelConstraints()    + "\n" +
@@ -1031,6 +1093,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
   }
 
   private SemanticNode[] children = null;
+  @Override
   public SemanticNode[] getChildren() {
       if (children != null) {
           return children;
@@ -1047,17 +1110,19 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
       return children;
    }
 
-  public final void walkGraph (Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph (Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
 
     if (semNodesTable.get(uid) != null) return;
 
     semNodesTable.put(uid, this);
+    visitor.preVisit(this);
     if (ctxt != null) {
-      ctxt.walkGraph(semNodesTable);
+      ctxt.walkGraph(semNodesTable, visitor);
     }
     for (int i = 0; i < topLevelVec.size(); i++) {
-      ((LevelNode)(topLevelVec.elementAt(i))).walkGraph(semNodesTable);
+      ((LevelNode)(topLevelVec.elementAt(i))).walkGraph(semNodesTable, visitor);
     }
 //     for (int i = 0; i < instanceVec.size(); i++) {
 //       ((InstanceNode)(instanceVec.elementAt(i))).walkGraph(semNodesTable);
@@ -1068,6 +1133,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
 //     for (int i = 0; i < assumptionVec.size(); i++) {
 //       ((AssumeNode)(assumptionVec.elementAt(i))).walkGraph(semNodesTable);
 //     }
+    visitor.postVisit(this);
   }
 
   public final void print(int indent, int depth, boolean b) {
@@ -1087,6 +1153,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
     }
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -1149,35 +1216,36 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
     return "ModuleNodeRef";
   }
 
-  protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) {
+  protected Element getSymbolElement(Document doc, SymbolContext context) {
     Element ret = doc.createElement("ModuleNode");
     ret.appendChild(appendText(doc, "uniquename", getName().toString()));
 
     SemanticNode[] nodes = null;
     // constants
-    Element constants = doc.createElement("constants");
+    //Element constants = doc.createElement("constants");
     nodes = getConstantDecls();
     for (int i=0; i<nodes.length; i++) {
-      constants.appendChild(nodes[i].export(doc,context));
+      ret.appendChild(nodes[i].export(doc,context));
     }
-    ret.appendChild(constants);
+    //ret.appendChild(constants);
 
     // variables
-    Element variables = doc.createElement("variables");
+    //Element variables = doc.createElement("variables");
     nodes = getVariableDecls();
     for (int i=0; i<nodes.length; i++) {
-      variables.appendChild(nodes[i].export(doc,context));
+      ret.appendChild(nodes[i].export(doc,context));
     }
-    ret.appendChild(variables);
+    //ret.appendChild(variables);
 
     //operators
-    Element operators = doc.createElement("definitions");
+    //Element operators = doc.createElement("definitions");
     nodes = getOpDefs();
     for (int i=0; i<nodes.length; i++) {
-      operators.appendChild(nodes[i].export(doc,context)); //was with true to expand operators
+      ret.appendChild(nodes[i].export(doc,context)); //was with true to expand operators
     }
-    ret.appendChild(operators);
+    //ret.appendChild(operators);
 
+    /*
     //assumptions
     Element assums = doc.createElement("assumptions");
     nodes = getAssumptions();
@@ -1193,6 +1261,12 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st,
       thms.appendChild(nodes[i].export(doc,context));
     }
     ret.appendChild(thms);
+  */
+
+    nodes = getTopLevel();
+    for (int i=0; i<nodes.length; i++) {
+      ret.appendChild(nodes[i].export(doc,context));
+    }
 
     return ret;
   }
diff --git a/tlatools/src/tla2sany/semantic/NewSymbNode.java b/tlatools/src/tla2sany/semantic/NewSymbNode.java
index 81e3d33a002aa8adef04a6d636eafb33b8dbb5c4..f5a5897829c704cc9a7ca54dbb6399596f238c9a 100644
--- a/tlatools/src/tla2sany/semantic/NewSymbNode.java
+++ b/tlatools/src/tla2sany/semantic/NewSymbNode.java
@@ -1,198 +1,208 @@
-// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
-
-// last modified on Fri 23 Nov 2007 at 17:21:52 PST by lamport
-/***************************************************************************
-* The NewSymbNode constructor is invoked in semantic/Generator.java to     *
-* construct the nodes corresponding to a NewSymb syntactic node in an      *
-* AssumeProve.  The NewSymb node represents a declaration in an ASSUME,    *
-* such as "ASSUME CONSTANT x \in S ".                                      *
-*                                                                          *
-* The node has two descendants in the semantic tree, returned              *
-* by these methods:                                                        *
-*   OpDeclNode getOpDeclNode()                                             *
-*     An OpDeclNode for the declaration.                                   *
-*                                                                          *
-*   ExprNode getSet()                                                      *
-*     If the node represents a declaration such as "CONSTANT x \in S",     *
-*     then this returns the ExprNode for S. Otherwise, it returns null.    *
-***************************************************************************/
-
-package tla2sany.semantic;
-
-import java.util.Hashtable;
-
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-public class NewSymbNode extends LevelNode {
-
-  /*************************************************************************
-  * Fields.                                                                *
-  *************************************************************************/
-  private OpDeclNode opDeclNode = null;
-    /***********************************************************************
-    * The OpDeclNode for the declaration represented by the NewSymbNode    *
-    * object.                                                              *
-    ***********************************************************************/
-  private ExprNode  set         = null;
-    /***********************************************************************
-    * The ExprNode for expression S in "CONSTANT id \in S".                *
-    ***********************************************************************/
-// There's now a level field for all LevelNode subclasses, and this.levelChecked
-// indicates if levelCheck has been callsed
-//  private int       level = -1;
-//    /***********************************************************************
-//    * The level.  The value of -1 indicates that levelCheck has not yet    *
-//    * been called.                                                         *
-//    ***********************************************************************/
-
-// Role subsumed by this.levelCorrect
-//  private boolean  theLevelCheck = true ;
-//    /***********************************************************************
-//    * Set by this.levelCheck to the value to be returned on subsequent     *
-//    * calls.                                                               *
-//    ***********************************************************************/
-
-  /*************************************************************************
-  * The Constructor.                                                       *
-  *************************************************************************/
-  public NewSymbNode(
-           OpDeclNode   opDeclNode, // The OpDeclNode for the declaration.
-           ExprNode     set,        // Expression S in "CONSTANT x \in S",
-                                    //   null for other kinds of declaration.
-           TreeNode stn             // The syntax node.
-          ) {
-    super(NewSymbKind, stn) ;
-    this.set        = set;
-    this.opDeclNode = opDeclNode ;
-  }
-
-
-  /*************************************************************************
-  * Methods particular to the NewSymb node.                                *
-  *************************************************************************/
-  public final ExprNode   getSet()         {return set;}
-  public final OpDeclNode getOpDeclNode() {return opDeclNode;}
-
-  /*************************************************************************
-  * The implementation of the LevelNode abstract methods.                  *
-  *                                                                        *
-  * The level of the node is the maximum of opDeclNode's level and the     *
-  * level of the `set' expression, if it's non-null.  Any other level      *
-  * information comes from the `set' expression.                           *
-  *************************************************************************/
-  public boolean levelCheck(int iter)       {
-
-    if (levelChecked < iter) {
-      /*********************************************************************
-      * This is the first call of levelCheck, so the level information     *
-      * must be computed.  Actually, with the current implementation,      *
-      * there's no need to call opDeclNode.levelCheck, since that just     *
-      * returns true.  However, we do it anyway in case the OpDeclNode     *
-      * class is changed to make levelCheck do something.                  *
-      *********************************************************************/
-      levelChecked = iter ;
-      boolean opDeclLevelCheck = opDeclNode.levelCheck(iter) ;
-      level = opDeclNode.getLevel() ;
-      if (set != null) {
-        levelCorrect = set.levelCheck(iter) ;
-        level = Math.max(set.getLevel(), level);
-        if (level == TemporalLevel) {
-          levelCorrect = false;
-          errors.addError(this.stn.getLocation(),
-                          "Level error:\n" +
-                          "Temporal formula used as set.");
-          }
-       }  ;
-      levelCorrect = levelCorrect && opDeclLevelCheck;
-      if (set != null) {
-        levelParams = set.getLevelParams();
-        allParams = set.getAllParams();
-        levelConstraints = set.getLevelConstraints();
-        argLevelConstraints = set.getArgLevelConstraints();
-        argLevelParams      = set.getArgLevelParams();
-       }; // if (set != null)
-      }; // if (levelChecked < iter)
-    return levelCorrect;
-   }
-
-//  public int getLevel()             {return this.level; }
-//
-//  public HashSet getLevelParams()   {
-//    if (set == null) {return EmptySet;}
-//    else return set.getLevelParams();
-//   }
-//
-//  public SetOfLevelConstraints getLevelConstraints() {
-//    if (set == null) {return EmptyLC;}
-//    else return set.getLevelConstraints();
-//   }
-//
-//  public SetOfArgLevelConstraints getArgLevelConstraints() {
-//    if (set == null) {return EmptyALC;}
-//    else return set.getArgLevelConstraints();
-//   }
-//
-//  public HashSet getArgLevelParams() {
-//    if (set == null) {return EmptySet;}
-//    else return set.getArgLevelParams();
-//   }
-
-  /**
-   * toString, levelDataToString and walkGraph methods to implement
-   * ExploreNode interface
-   */
-//  public final String levelDataToString() {
-//    return "Level: "               + this.level                    + "\n" +
-//           "LevelParameters: "     + this.getLevelParams()         + "\n" +
-//           "LevelConstraints: "    + this.getLevelConstraints()    + "\n" +
-//           "ArgLevelConstraints: " + this.getArgLevelConstraints() + "\n" +
-//           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n";
-//  }
-
-  /**
-   * The body is the node's only child.
-   */
-
-  public SemanticNode[] getChildren() {
-    if (this.set == null) {
-        return null;
-    } else {
-      return new SemanticNode[] {this.set};
-    }
-  }
-
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
-    if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
-    if (set != null) { set.walkGraph(semNodesTable); } ;
-   }
-
-  public final String toString(int depth) {
-    if (depth <= 0) return "";
-    String setString = "" ;
-    if (this.set != null) {
-      setString = Strings.indent(2,
-                   "\nSet:" + Strings.indent(2, this.set.toString(depth-1)));
-     }
-    return "\n*NewSymbNode: " +
-	    "  " + super.toString(depth) +
-             Strings.indent(2, "\nKind: " + this.getKind() +
-                          "\nopDeclNode:" + Strings.indent(2,
-                                this.opDeclNode.toString(depth-1)) +
-             setString);
-   }
-
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
-    Element e = doc.createElement("NewSymbNode");
-    e.appendChild(getOpDeclNode().export(doc,context));
-    if (getSet() != null) {
-      e.appendChild(getSet().export(doc,context));
-    }
-    return e;
-  }
-}
+// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
+
+// last modified on Fri 23 Nov 2007 at 17:21:52 PST by lamport
+/***************************************************************************
+* The NewSymbNode constructor is invoked in semantic/Generator.java to     *
+* construct the nodes corresponding to a NewSymb syntactic node in an      *
+* AssumeProve.  The NewSymb node represents a declaration in an ASSUME,    *
+* such as "ASSUME CONSTANT x \in S ".                                      *
+*                                                                          *
+* The node has two descendants in the semantic tree, returned              *
+* by these methods:                                                        *
+*   OpDeclNode getOpDeclNode()                                             *
+*     An OpDeclNode for the declaration.                                   *
+*                                                                          *
+*   ExprNode getSet()                                                      *
+*     If the node represents a declaration such as "CONSTANT x \in S",     *
+*     then this returns the ExprNode for S. Otherwise, it returns null.    *
+***************************************************************************/
+
+package tla2sany.semantic;
+
+import java.util.Hashtable;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+
+public class NewSymbNode extends LevelNode {
+
+  /*************************************************************************
+  * Fields.                                                                *
+  *************************************************************************/
+  private OpDeclNode opDeclNode = null;
+    /***********************************************************************
+    * The OpDeclNode for the declaration represented by the NewSymbNode    *
+    * object.                                                              *
+    ***********************************************************************/
+  private ExprNode  set         = null;
+    /***********************************************************************
+    * The ExprNode for expression S in "CONSTANT id \in S".                *
+    ***********************************************************************/
+// There's now a level field for all LevelNode subclasses, and this.levelChecked
+// indicates if levelCheck has been callsed
+//  private int       level = -1;
+//    /***********************************************************************
+//    * The level.  The value of -1 indicates that levelCheck has not yet    *
+//    * been called.                                                         *
+//    ***********************************************************************/
+
+// Role subsumed by this.levelCorrect
+//  private boolean  theLevelCheck = true ;
+//    /***********************************************************************
+//    * Set by this.levelCheck to the value to be returned on subsequent     *
+//    * calls.                                                               *
+//    ***********************************************************************/
+
+  /*************************************************************************
+  * The Constructor.                                                       *
+  *************************************************************************/
+  public NewSymbNode(
+           OpDeclNode   opDeclNode, // The OpDeclNode for the declaration.
+           ExprNode     set,        // Expression S in "CONSTANT x \in S",
+                                    //   null for other kinds of declaration.
+           TreeNode stn             // The syntax node.
+          ) {
+    super(NewSymbKind, stn) ;
+    this.set        = set;
+    this.opDeclNode = opDeclNode ;
+  }
+
+
+  /*************************************************************************
+  * Methods particular to the NewSymb node.                                *
+  *************************************************************************/
+  public final ExprNode   getSet()         {return set;}
+  public final OpDeclNode getOpDeclNode() {return opDeclNode;}
+
+  /*************************************************************************
+  * The implementation of the LevelNode abstract methods.                  *
+  *                                                                        *
+  * The level of the node is the maximum of opDeclNode's level and the     *
+  * level of the `set' expression, if it's non-null.  Any other level      *
+  * information comes from the `set' expression.                           *
+  *************************************************************************/
+  @Override
+  public boolean levelCheck(int iter)       {
+
+    if (levelChecked < iter) {
+      /*********************************************************************
+      * This is the first call of levelCheck, so the level information     *
+      * must be computed.  Actually, with the current implementation,      *
+      * there's no need to call opDeclNode.levelCheck, since that just     *
+      * returns true.  However, we do it anyway in case the OpDeclNode     *
+      * class is changed to make levelCheck do something.                  *
+      *********************************************************************/
+      levelChecked = iter ;
+      boolean opDeclLevelCheck = opDeclNode.levelCheck(iter) ;
+      level = opDeclNode.getLevel() ;
+      if (set != null) {
+        levelCorrect = set.levelCheck(iter) ;
+        level = Math.max(set.getLevel(), level);
+        if (level == TemporalLevel) {
+          levelCorrect = false;
+          errors.addError(this.stn.getLocation(),
+                          "Level error:\n" +
+                          "Temporal formula used as set.");
+          }
+       }  ;
+      levelCorrect = levelCorrect && opDeclLevelCheck;
+      if (set != null) {
+        levelParams = set.getLevelParams();
+        allParams = set.getAllParams();
+        levelConstraints = set.getLevelConstraints();
+        argLevelConstraints = set.getArgLevelConstraints();
+        argLevelParams      = set.getArgLevelParams();
+       }; // if (set != null)
+      }; // if (levelChecked < iter)
+    return levelCorrect;
+   }
+
+//  public int getLevel()             {return this.level; }
+//
+//  public HashSet getLevelParams()   {
+//    if (set == null) {return EmptySet;}
+//    else return set.getLevelParams();
+//   }
+//
+//  public SetOfLevelConstraints getLevelConstraints() {
+//    if (set == null) {return EmptyLC;}
+//    else return set.getLevelConstraints();
+//   }
+//
+//  public SetOfArgLevelConstraints getArgLevelConstraints() {
+//    if (set == null) {return EmptyALC;}
+//    else return set.getArgLevelConstraints();
+//   }
+//
+//  public HashSet getArgLevelParams() {
+//    if (set == null) {return EmptySet;}
+//    else return set.getArgLevelParams();
+//   }
+
+  /**
+   * toString, levelDataToString and walkGraph methods to implement
+   * ExploreNode interface
+   */
+//  public final String levelDataToString() {
+//    return "Level: "               + this.level                    + "\n" +
+//           "LevelParameters: "     + this.getLevelParams()         + "\n" +
+//           "LevelConstraints: "    + this.getLevelConstraints()    + "\n" +
+//           "ArgLevelConstraints: " + this.getArgLevelConstraints() + "\n" +
+//           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n";
+//  }
+
+  /**
+   * The body is the node's only child.
+   */
+
+  @Override
+  public SemanticNode[] getChildren() {
+    if (this.set == null) {
+        return null;
+    } else {
+      return new SemanticNode[] {this.set};
+    }
+  }
+
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
+    if (semNodesTable.get(uid) != null) return;
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    if (set != null) { set.walkGraph(semNodesTable, visitor); } ;
+    visitor.postVisit(this);
+   }
+
+  @Override
+  public final String toString(int depth) {
+    if (depth <= 0) return "";
+    String setString = "" ;
+    if (this.set != null) {
+      setString = Strings.indent(2,
+                   "\nSet:" + Strings.indent(2, this.set.toString(depth-1)));
+     }
+    return "\n*NewSymbNode: " +
+	    "  " + super.toString(depth) +
+             Strings.indent(2, "\nKind: " + this.getKind() +
+                          "\nopDeclNode:" + Strings.indent(2,
+                                this.opDeclNode.toString(depth-1)) +
+             setString);
+   }
+
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
+    Element e = doc.createElement("NewSymbNode");
+    e.appendChild(getOpDeclNode().export(doc,context));
+    if (getSet() != null) {
+      e.appendChild(getSet().export(doc,context));
+    }
+    return e;
+  }
+}
diff --git a/tlatools/src/tla2sany/semantic/NonLeafProofNode.java b/tlatools/src/tla2sany/semantic/NonLeafProofNode.java
index 67544148b12faed67054ed5fb8718396971a97c1..b9819c21642c6ad63950799f8357d76380cd3f81 100644
--- a/tlatools/src/tla2sany/semantic/NonLeafProofNode.java
+++ b/tlatools/src/tla2sany/semantic/NonLeafProofNode.java
@@ -3,12 +3,15 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
+import tla2sany.xml.SymbolContext;
 
 /***************************************************************************
 * This class represents a non-leaf node in the tree representing a         *
@@ -141,6 +144,7 @@ public class NonLeafProofNode extends ProofNode {
   public LevelNode[] getSteps()   {return steps ;}
   public Context     getContext() {return context ;}
 
+  @Override
   public boolean levelCheck(int iter) {
     /***********************************************************************
     * Level check the steps and the instantiated modules coming from       *
@@ -157,6 +161,7 @@ public class NonLeafProofNode extends ProofNode {
    * The children are the steps.
    * @see tla2sany.semantic.SemanticNode#getChildren()
    */
+  @Override
   public SemanticNode[] getChildren() {
       if (this.steps == null || this.steps.length == 0) {
           return null;
@@ -168,24 +173,28 @@ public class NonLeafProofNode extends ProofNode {
       return res;
    }
 
-  public void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
     for (int  i = 0; i < steps.length; i++) {
-      steps[i].walkGraph(semNodesTable);
+      steps[i].walkGraph(semNodesTable, visitor);
       } ;
     /***********************************************************************
     * Note: there's no need to walk the defs array because all the nodes   *
     * on it are walked from the nodes under which they appear.             *
     ***********************************************************************/
 
-    context.walkGraph(semNodesTable) ;
+    context.walkGraph(semNodesTable, visitor) ;
       /*********************************************************************
       * Walk the ThmOrOpApplDef NumberedProofStepKind nodes.               *
       *********************************************************************/
+    visitor.postVisit(this);
    }
 
+  @Override
   public String toString(int depth) {
     if (depth <= 0) return "";
     String ret = "\n*ProofNode:\n"
@@ -211,7 +220,8 @@ public class NonLeafProofNode extends ProofNode {
     return ret;
    }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("steps");
 
     for (int i=0; i< steps.length; i++) {
diff --git a/tlatools/src/tla2sany/semantic/NumeralNode.java b/tlatools/src/tla2sany/semantic/NumeralNode.java
index 89bce398f95dcef2a5b4dffb8f054873c8f05f31..ff1f369a9c26b20ba5326c6636a252ab69001da3 100644
--- a/tlatools/src/tla2sany/semantic/NumeralNode.java
+++ b/tlatools/src/tla2sany/semantic/NumeralNode.java
@@ -5,12 +5,15 @@ package tla2sany.semantic;
 import java.math.BigInteger;
 import java.util.Hashtable;
 
-import tla2sany.st.TreeNode;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
+
 /**
  * Describes a numeral like 4095.  This number is represented by the
  * values
@@ -64,15 +67,26 @@ public class NumeralNode extends ExprNode {
   public final int val() { return this.value; }
 
   public final BigInteger bigVal() { return this.bigValue; }
+  
+  	/**
+	 * @return true if the numerical value of this instance should be referenced via
+	 *         {@link #val()}, false if it should be referenced via
+	 *         {@link #bigVal()}
+	 */
+  public final boolean useVal() {
+	  return (bigValue == null);
+  }
 
   /**
    * Returns the value as a string--for example, "4095".  This string
    * reflects how the value appeared in the input, so it should be
    * "\O7777" if that's what appears in the source.
    */
+  @Override
   public final String toString() { return this.image; }
 
   /* Level Checking */
+  @Override
   public final boolean levelCheck(int iter) {
     levelChecked = iter;
       /*********************************************************************
@@ -107,13 +121,17 @@ public class NumeralNode extends ExprNode {
 //           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n" ;
 //  }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    visitor.postVisit(this);
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -122,7 +140,8 @@ public class NumeralNode extends ExprNode {
 	   "; image: " + image);
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
       String v = (bigValue != null) ? bigValue.toString() : (Integer.toString(value));
       Element e = doc.createElement("IntValue");
       Node n = doc.createTextNode(v);
diff --git a/tlatools/src/tla2sany/semantic/OpApplNode.java b/tlatools/src/tla2sany/semantic/OpApplNode.java
index 6454ce55d2d31f5fd0c066f7822dbe5989765759..474c05c481283ce2bf7207d3d3b962ea830f6b45 100644
--- a/tlatools/src/tla2sany/semantic/OpApplNode.java
+++ b/tlatools/src/tla2sany/semantic/OpApplNode.java
@@ -19,19 +19,27 @@ package tla2sany.semantic;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.parser.SyntaxTreeNode;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+import tlc2.tool.BuiltInOPs;
+import tlc2.value.ITupleValue;
+import tlc2.value.IValue;
+import tlc2.value.Values;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 /**
  * OpApplNodes represent all kinds of operator applications in TLA+,
- * including the application of builtin operators and user-defined
+ * including the application of builtin
+ * ors and user-defined
  * operators, operators with a variable number of arguments or a fixed
  * number (including, of course, 0), and including quantifiers and
  * choose, which involve bound variables with or without bounding sets.
@@ -262,6 +270,15 @@ public class OpApplNode extends ExprNode implements ExploreNode {
    */
   public final void setArgs(ExprOrOpArgNode[] args) { this.operands = args; }
 
+	public final boolean argsContainOpArgNodes() {
+		for (ExprOrOpArgNode o : operands) {
+			if (o instanceof OpArgNode) {
+				return true;
+			}
+		}
+		return false;
+	}
+  
   /*************************************************************************
   * Despite what you might gather from its 79 character name, this does    *
   * not return the number of bounded bound symbols.  It returns a weird    *
@@ -397,6 +414,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
 //    }
 // }
 
+  @Override
   public final boolean levelCheck(int itr) {
     if (this.levelChecked >= itr) return this.levelCorrect;
     this.levelChecked = itr ;
@@ -491,6 +509,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
           if (opd instanceof OpArgNode &&
               ((OpArgNode)opd).getOp() instanceof AnyDefNode) {
             AnyDefNode opdDef = (AnyDefNode)((OpArgNode)opd).getOp();
+            @SuppressWarnings("unused")  // See below comment block
             boolean opdDefLevelCheck = opdDef.levelCheck(itr) ;
               /*************************************************************
               * Need to call opdDef.levelCheck before using its level      *
@@ -598,7 +617,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
       * Set allBoundSymbols to a hashset containing all the                *
       * FormalParamNodes of the bound symbols.                             *
       *********************************************************************/
-      HashSet allBoundSymbols = new HashSet() ;
+      HashSet<FormalParamNode> allBoundSymbols = new HashSet<>() ;
       if (this.unboundedBoundSymbols != null) {
         for (int i = 0 ; i < this.unboundedBoundSymbols.length; i++){
           allBoundSymbols.add(this.unboundedBoundSymbols[i]);
@@ -618,9 +637,9 @@ public class OpApplNode extends ExprNode implements ExploreNode {
       * Remove bound identifiers from levelParams, allParams, and          *
       * nonLeibnizParams.                                                  *
       *********************************************************************/
-      Iterator absIter = allBoundSymbols.iterator() ;
+      Iterator<FormalParamNode> absIter = allBoundSymbols.iterator() ;
       while (absIter.hasNext()) {
-        Object nextBoundSymbol = absIter.next() ;
+    	  	FormalParamNode nextBoundSymbol = absIter.next() ;
         this.levelParams.remove(nextBoundSymbol) ;
         this.allParams.remove(nextBoundSymbol) ;
         this.nonLeibnizParams.remove(nextBoundSymbol) ;
@@ -649,9 +668,9 @@ public class OpApplNode extends ExprNode implements ExploreNode {
             ***************************************************************/
             SetOfLevelConstraints lcons =
                this.operands[i].getLevelConstraints() ;
-            Iterator iter = lcons.keySet().iterator();
+            Iterator<SymbolNode> iter = lcons.keySet().iterator();
             while (iter.hasNext()) {
-              SymbolNode param = (SymbolNode) iter.next() ;
+              SymbolNode param = iter.next() ;
               if (! allBoundSymbols.contains(param)) {
                 this.levelConstraints.put(param, lcons.get(param)) ;
                 }
@@ -663,9 +682,9 @@ public class OpApplNode extends ExprNode implements ExploreNode {
         this.levelConstraints.putAll(this.ranges[i].getLevelConstraints());
       }
       for (int i = 0; i < this.operands.length; i++) {
-        Integer mlevel = new Integer(opDef.getMaxLevel(i));
+        Integer mlevel = Integer.valueOf(opDef.getMaxLevel(i));
         if (this.operands[i] != null) {
-          Iterator iter = this.operands[i].getLevelParams().iterator();
+          Iterator<SymbolNode> iter = this.operands[i].getLevelParams().iterator();
           while (iter.hasNext()) {
             this.levelConstraints.put(iter.next(), mlevel);
           }
@@ -694,8 +713,8 @@ public class OpApplNode extends ExprNode implements ExploreNode {
           for (int j = 0; j < this.operands.length; j++) {
             for (int k = 0; k < alen; k++) {
               if (opDef.getOpLevelCond(i, j, k)) {
-                Integer mlevel = new Integer(argDef.getMaxLevel(k));
-                Iterator iter = this.operands[j].getLevelParams().iterator();
+                Integer mlevel = Integer.valueOf(argDef.getMaxLevel(k));
+                Iterator<SymbolNode> iter = this.operands[j].getLevelParams().iterator();
                 while (iter.hasNext()) {
                   this.levelConstraints.put(iter.next(), mlevel);
                 }
@@ -722,10 +741,10 @@ public class OpApplNode extends ExprNode implements ExploreNode {
            } ; // if (! argDef.isLeibniz)
         } // if (opdi != null && ...)
       } // for i
-      HashSet alpSet = opDef.getArgLevelParams();
-      Iterator iter = alpSet.iterator();
+      HashSet<ArgLevelParam> alpSet = opDef.getArgLevelParams();
+      Iterator<ArgLevelParam> iter = alpSet.iterator();
       while (iter.hasNext()) {
-        ArgLevelParam alp = (ArgLevelParam)iter.next();
+        ArgLevelParam alp = iter.next();
         ExprOrOpArgNode arg = this.getArg(alp.op);
         // LL changed OpDefNode -> AnyDefNode in the following.
         // See comments in AnyDefNode.java.  (It is only in the
@@ -739,7 +758,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
             /***************************************************************
             * Need to invoke levelCheck before invoking getMaxLevel.       *
             ***************************************************************/
-          Integer mlevel = new Integer(argDef.getMaxLevel(alp.i));
+          Integer mlevel = Integer.valueOf(argDef.getMaxLevel(alp.i));
           this.levelConstraints.put(alp.param, mlevel);
         }
       } // while
@@ -767,14 +786,14 @@ public class OpApplNode extends ExprNode implements ExploreNode {
           int alen = opArg.getArity();
           for (int j = 0; j < alen; j++) {
             ParamAndPosition pap = new ParamAndPosition(opArg, j);
-            Integer mlevel = new Integer(opDef.getMinMaxLevel(i, j));
+            Integer mlevel = Integer.valueOf(opDef.getMinMaxLevel(i, j));
             this.argLevelConstraints.put(pap, mlevel);
           }
           for (int j = 0; j < this.operands.length; j++) {
             for (int k = 0; k < alen; k++) {
               if (opDef.getOpLevelCond(i, j, k)) {
                 ParamAndPosition pap = new ParamAndPosition(opArg, k);
-                Integer mlevel = new Integer(this.operands[j].getLevel());
+                Integer mlevel = Integer.valueOf(this.operands[j].getLevel());
                 this.argLevelConstraints.put(pap, mlevel);
               }
             }
@@ -783,7 +802,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
       } // for i
       iter = alpSet.iterator();
       while (iter.hasNext()) {
-        ArgLevelParam alp = (ArgLevelParam)iter.next();
+        ArgLevelParam alp = iter.next();
         ExprOrOpArgNode arg = this.getArg(alp.op);
         if (arg != null) {
           arg.levelCheck(itr) ;
@@ -791,14 +810,14 @@ public class OpApplNode extends ExprNode implements ExploreNode {
             * Have to invoke levelCheck before invoking getLevel.          *
             ***************************************************************/
           ParamAndPosition pap = new ParamAndPosition(alp.op, alp.i);
-          this.argLevelConstraints.put(pap, new Integer(arg.getLevel()));
+          this.argLevelConstraints.put(pap, Integer.valueOf(arg.getLevel()));
         }
       }
 
       /*********************************************************************
       * Compute this.argLevelParams.                                       *
       *********************************************************************/
-      this.argLevelParams = new HashSet();
+      this.argLevelParams = new HashSet<>();
       for (int i = 0; i < this.operands.length; i++) {
         if (this.operands[i] != null) {
           if (allBoundSymbols.size() == 0) {
@@ -812,9 +831,9 @@ public class OpApplNode extends ExprNode implements ExploreNode {
             * argLevelConstraints the element implied as described above   *
             * if alp.param IS a bound identifier.                          *
             ***************************************************************/
-            Iterator alpiter = this.operands[i].getArgLevelParams().iterator();
-            while (alpiter.hasNext()) {
-              ArgLevelParam alp = (ArgLevelParam) alpiter.next() ;
+            Iterator<ArgLevelParam> alpIter = this.operands[i].getArgLevelParams().iterator();
+            while (alpIter.hasNext()) {
+              ArgLevelParam alp = alpIter.next() ;
               if (!allBoundSymbols.contains(alp.param)) {
                 this.argLevelParams.add(alp) ;
                }
@@ -839,9 +858,9 @@ public class OpApplNode extends ExprNode implements ExploreNode {
               /*************************************************************
               * Need to invoke levelCheck before invoking getLevelParams.  *
               *************************************************************/
-            Iterator iter1 = arg.getLevelParams().iterator();
+            Iterator<SymbolNode> iter1 = arg.getLevelParams().iterator();
             while (iter1.hasNext()) {
-              SymbolNode param = (SymbolNode)iter1.next();
+              SymbolNode param = iter1.next();
               this.argLevelParams.add(new ArgLevelParam(alp.op, alp.i, param));
             }
           }
@@ -868,9 +887,9 @@ public class OpApplNode extends ExprNode implements ExploreNode {
           for (int j = 0; j < this.operands.length; j++) {
             for (int k = 0; k < alen; k++) {
               if (opDef.getOpLevelCond(i, j, k)) {
-                Iterator iter1 = this.operands[j].getLevelParams().iterator();
+                Iterator<SymbolNode> iter1 = this.operands[j].getLevelParams().iterator();
                 while (iter1.hasNext()) {
-                  SymbolNode param = (SymbolNode)iter1.next();
+                  SymbolNode param = iter1.next();
                   this.argLevelParams.add(new ArgLevelParam(opArg, k, param));
                 }
               }
@@ -891,7 +910,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
         this.level = Math.max(this.level, this.operands[i].getLevel());
       } // for
 
-      this.levelParams = new HashSet();
+      this.levelParams = new HashSet<>();
       /*********************************************************************
       * We only add this.operator to the levelParams and allParams.        *
       *********************************************************************/
@@ -935,17 +954,17 @@ public class OpApplNode extends ExprNode implements ExploreNode {
            this.operands[i].getArgLevelConstraints());
       }
 
-      this.argLevelParams = new HashSet();
+      this.argLevelParams = new HashSet<>();
       for (int i = 0; i < this.operands.length; i++) {
         /*******************************************************************
         * For every levelParam p of the i-th operand, add to               *
         * argLevelParams an entry asserting that p appears within an i-th  *
         * operand of this.operator.                                        *
         *******************************************************************/
-        HashSet lpSet = this.operands[i].getLevelParams();
-        Iterator iter = lpSet.iterator();
+        HashSet<SymbolNode> lpSet = this.operands[i].getLevelParams();
+        Iterator<SymbolNode> iter = lpSet.iterator();
         while (iter.hasNext()) {
-          SymbolNode param = (SymbolNode)iter.next();
+          SymbolNode param = iter.next();
           this.argLevelParams.add(
              new ArgLevelParam(this.operator, i, param));
          }; // end while
@@ -1096,6 +1115,12 @@ public class OpApplNode extends ExprNode implements ExploreNode {
 //           "ArgLevelParams: "      + this.argLevelParams      + "\n" ;
 //  }
 
+
+  public boolean hasOpcode(final int opCode) {
+      return opCode == BuiltInOPs.getOpCode(getOperator().getName());
+  }
+
+  @Override
   public SemanticNode[] getChildren() {
       SemanticNode[] res =
          new SemanticNode[this.ranges.length + this.operands.length];
@@ -1113,30 +1138,32 @@ public class OpApplNode extends ExprNode implements ExploreNode {
    * walkGraph finds all reachable nodes in the semantic graph
    * and inserts them in the Hashtable semNodesTable for use by the Explorer tool.
    */
-  public void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
     semNodesTable.put(uid, this);
+    visitor.preVisit(this);
 
     if (operator != null) {
-      operator.walkGraph(semNodesTable);
+      operator.walkGraph(semNodesTable, visitor);
     }
 
     if (unboundedBoundSymbols != null && unboundedBoundSymbols.length > 0) {
       for (int i = 0; i < unboundedBoundSymbols.length; i++)
         if (unboundedBoundSymbols[i] != null)
-           unboundedBoundSymbols[i].walkGraph(semNodesTable);
+           unboundedBoundSymbols[i].walkGraph(semNodesTable, visitor);
     }
 
     if (operands != null && operands.length > 0) {
       for (int i = 0; i < operands.length; i++)
-        if (operands[i] != null) operands[i].walkGraph(semNodesTable);
+        if (operands[i] != null) operands[i].walkGraph(semNodesTable, visitor);
     }
 
     if (ranges.length > 0) {
       for (int i = 0; i < ranges.length; i++)
-        if (ranges[i] != null) ranges[i].walkGraph(semNodesTable);
+        if (ranges[i] != null) ranges[i].walkGraph(semNodesTable, visitor);
     }
 
     if (boundedBoundSymbols != null && boundedBoundSymbols.length > 0) {
@@ -1144,11 +1171,12 @@ public class OpApplNode extends ExprNode implements ExploreNode {
         if (boundedBoundSymbols[i] != null && boundedBoundSymbols[i].length > 0) {
           for (int j = 0; j < boundedBoundSymbols[i].length; j++) {
             if (boundedBoundSymbols[i][j] != null)
-               boundedBoundSymbols[i][j].walkGraph(semNodesTable);
+               boundedBoundSymbols[i][j].walkGraph(semNodesTable, visitor);
           }
         }
       }
     }
+    visitor.postVisit(this);
   }
 
   // Used in implementation of toString() below
@@ -1216,6 +1244,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
    * Displays this node as a String, implementing ExploreNode interface; depth
    * parameter is a bound on the depth of the portion of the tree that is displayed.
    */
+  @Override
   public String toString(int depth) {
     if (depth <= 0) return "";
     String sEO = "" ;
@@ -1228,7 +1257,39 @@ public class OpApplNode extends ExprNode implements ExploreNode {
            + toStringBody(depth) + sEO ;
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+    @Override
+	public String toString(final IValue aValue) {
+		if (aValue instanceof ITupleValue && allParams.size() == ((ITupleValue) aValue).size()) {
+			final StringBuffer result = new StringBuffer();
+			
+			// The values in aValue are ordered by the varloc of the variable names (see
+			// tlc2.tool.TLCStateMut.bind(UniqueString, Value, SemanticNode). Thus, sort
+			// allParams - which are unordered - under same varloc order.
+			final TreeSet<SymbolNode> s = new TreeSet<SymbolNode>(new java.util.Comparator<SymbolNode>() {
+				@Override
+				public int compare(SymbolNode o1, SymbolNode o2) {
+					return Integer.compare(o1.getName().getVarLoc(), o2.getName().getVarLoc());
+				}
+			});
+			s.addAll(allParams);
+			
+			int idx = 0;
+			for (final SymbolNode sn : s) {
+				result.append("/\\ ");
+				result.append(sn.getName().toString());
+
+				final IValue value = ((ITupleValue) aValue).getElem(idx++);
+				result.append(" = ");
+				result.append(Values.ppr(value));
+				result.append("\n");
+			}
+			return result.toString();
+		}
+		return super.toString(aValue);
+	}
+  
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("OpApplNode");
 
     // TL 2014 - A fix for detecting null representing OTHER inside a case (please refrain from using null as a semantical object),
@@ -1242,8 +1303,8 @@ public class OpApplNode extends ExprNode implements ExploreNode {
           // indeed the OTHER case
           if (other.getOperator().getName().toString().equals("$Pair") && other.getArgs()[0] == null) {
             // we pass a flag that tells any future OpApplNode that a null operand in 0 position should be replaced by the string $Other
-            context = new tla2sany.xml.SymbolContext(context);
-            context.setFlag(tla2sany.xml.SymbolContext.OTHER_BUG);
+            context = new SymbolContext(context);
+            context.setFlag(SymbolContext.OTHER_BUG);
           }
        }
 
@@ -1258,7 +1319,7 @@ public class OpApplNode extends ExprNode implements ExploreNode {
     Element ope = doc.createElement("operands");
     for (int i=0; i< operands.length; i++) {
       // dealing with the $Case OTHER null bug
-      if (i == 0 && operands[0] == null && context.hasFlag(tla2sany.xml.SymbolContext.OTHER_BUG)) {
+      if (i == 0 && operands[0] == null && context.hasFlag(SymbolContext.OTHER_BUG)) {
         ope.appendChild(appendText(doc,"StringNode","$Other"));
       }
       else {
diff --git a/tlatools/src/tla2sany/semantic/OpArgNode.java b/tlatools/src/tla2sany/semantic/OpArgNode.java
index 8f7f32f38025ff7cfc6194913377178534e5871c..1f50c9b21241874ce42cbfc34a78960a7e97a38c 100644
--- a/tlatools/src/tla2sany/semantic/OpArgNode.java
+++ b/tlatools/src/tla2sany/semantic/OpArgNode.java
@@ -7,13 +7,16 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.parser.SyntaxTreeNode;
 import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 /**
  * This class represents operators of arity > 0 used as arguments to
  * other operators.  Such operator instances are used in syntactic
@@ -65,6 +68,7 @@ public class OpArgNode extends ExprOrOpArgNode {
   public final ModuleNode   getModule()    { return this.mn; }
 
   /* Level check */
+  @Override
   public final boolean levelCheck(int iter) {
     if (levelChecked >= iter) {return this.levelCorrect; } ;
     levelChecked = iter ;
@@ -108,11 +112,13 @@ public class OpArgNode extends ExprOrOpArgNode {
 //           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n" ;
 //  }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
 
     /***********************************************************************
     * Modified on 28 Mar 2007 by LL to walk the operator node of the       *
@@ -122,9 +128,11 @@ public class OpArgNode extends ExprOrOpArgNode {
     * walking the node representing the declaration or definition of the   *
     * operator.                                                            *
     ***********************************************************************/
-    if (op != null) {op.walkGraph(semNodesTable) ;} ;
+    if (op != null) {op.walkGraph(semNodesTable, visitor) ;} ;
+    visitor.postVisit(this);
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -134,11 +142,17 @@ public class OpArgNode extends ExprOrOpArgNode {
       "  op: " + (op != null ? "" + ((SemanticNode)op).getUid() : "null" );
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("OpArgNode");
-
-    e.appendChild(appendText(doc,"uniquename",getName().toString()));
-    e.appendChild(appendText(doc,"arity", Integer.toString(getArity())));
+    Element n = doc.createElement("argument");
+    //Element ope = op.getSymbolElement(doc, context);
+    Element ope = op.export(doc, context);
+    n.appendChild(ope);
+    e.appendChild(n);
+
+    //e.appendChild(appendText(doc,"uniquename",getName().toString()));
+    //e.appendChild(appendText(doc,"arity", Integer.toString(getArity())));
 
     return e;
   }
diff --git a/tlatools/src/tla2sany/semantic/OpDeclNode.java b/tlatools/src/tla2sany/semantic/OpDeclNode.java
index a0cb9c0c9b7c2262fd5eb9d3f9a2505c303bde24..7475d966ad8ccf21952c38afc7093be71e616b70 100644
--- a/tlatools/src/tla2sany/semantic/OpDeclNode.java
+++ b/tlatools/src/tla2sany/semantic/OpDeclNode.java
@@ -7,12 +7,15 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
-import tla2sany.st.TreeNode;
-import util.UniqueString;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
+import util.UniqueString;
+
 /**
  * An OpDeclNode can have one of the following kinds:
  *
@@ -85,6 +88,7 @@ public class OpDeclNode extends OpDefOrDeclNode {
 
 //  private HashSet levelParams;
 
+  @Override
   public final boolean levelCheck(int iter) {
     /***********************************************************************
     * Level information set by constructor.                                *
@@ -136,12 +140,26 @@ public class OpDeclNode extends OpDefOrDeclNode {
 //           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n";
 //  }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    visitor.postVisit(this);
   }
 
+	@Override
+	public String getHumanReadableImage() {
+		if (getKind() == 2) {
+			return super.getName().toString() + " CONSTANT";
+		} else if (getKind() == 3) {
+			return super.getName().toString() + " VARIABLE";
+		}
+		return super.getHumanReadableImage();
+	}
+
+  @Override
   public final String toString (int depth) {
     if (depth <= 0) return "";
     return "\n*OpDeclNode: " + this.getName() + "  " + super.toString(depth)
@@ -156,7 +174,7 @@ public class OpDeclNode extends OpDefOrDeclNode {
     return "OpDeclNodeRef";
   }
 
-  protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) {
+  protected Element getSymbolElement(Document doc, SymbolContext context) {
     Element e = doc.createElement("OpDeclNode");
     e.appendChild(appendText(doc,"uniquename",getName().toString()));
     e.appendChild(appendText(doc,"arity",Integer.toString(getArity())));
diff --git a/tlatools/src/tla2sany/semantic/OpDefNode.java b/tlatools/src/tla2sany/semantic/OpDefNode.java
index 59ee96d97099dc368e031025ba53c81b40ea34f2..3b5c9f958fc89d8caada229ac2d81b1107ba8eec 100644
--- a/tlatools/src/tla2sany/semantic/OpDefNode.java
+++ b/tlatools/src/tla2sany/semantic/OpDefNode.java
@@ -35,17 +35,21 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.parser.SyntaxTreeNode;
 import tla2sany.st.Location;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
+import tla2sany.xml.SymbolContext;
+import tlc2.tool.BuiltInOPs;
 import util.UniqueString;
 import util.WrongInvocationException;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
   /**
    * An OpDefNode can have one of the following kinds:
    *
@@ -620,11 +624,52 @@ public class OpDefNode extends OpDefOrDeclNode
 
   /**
    * This method tests whether an operand is a legal instance of an
-   * operator being passed as argument to another operator
+   * operator being passed as argument to another operator.
+   * 
+   * Bug discovered in July 2017: This method was testing if the
+   * arg is an operator argument that may be used as parameter 
+   * number i of the operator described by this OpDefNode.  For example,
+   * if
+   * 
+   *     D(Op(_)) == Op(42)
+   *     
+   * then the argument of D must be an operator that takes a single
+   * expression as an argument.  However, this method returned true
+   * if the argument is an operator like D, that takes a single
+   * OPERATOR as an argument.  LL rewrote the code on 22 July 2017 
+   * to catch this case.
+   * 
+   * @see IllegalOperatorTest
    */
   private boolean matchingOpArgOperand (ExprOrOpArgNode arg, int i) {
-    return ((arg instanceof OpArgNode) &&
-            params[i].getArity() == ((OpArgNode)arg).getArity());
+	  // Set result to true iff arg is an operator argument of the
+	  // correct arity.
+	  boolean result = (arg instanceof OpArgNode) &&
+			           (params[i].getArity() == ((OpArgNode)arg).getArity()) ;
+	  
+	  // We now must check that arg does not expect an operator as any
+	  // of its arguments.  The comments in OpArgNode.java state that 
+	  // OpArgNode.op is an OpDefNode, OpDeclNode, or FormalParamNode.  Of
+	  // those, only an OpDefNode represents an operator that can have 
+	  // an argument that is an operator.  Note that an argument j of an
+	  // OpDefNode is an operator iff its arity is  0.
+	  if (result) {
+		  OpArgNode argOpArg = (OpArgNode) arg ;
+		  if (argOpArg.getOp() instanceof OpDefNode) {
+			  OpDefNode opdefArg = (OpDefNode) argOpArg.getOp() ;
+			  for (int j = 0; j < opdefArg.getArity() ; j++) {
+				  if (opdefArg.getParams()[j].getArity() > 0) {
+					  result = false ;
+				  }
+			  }
+		  }
+	  }
+		  
+      return result ;
+	  	           
+   // Code before 22 July 2017 change.
+   //    return ((arg instanceof OpArgNode) &&
+   //              params[i].getArity() == ((OpArgNode)arg).getArity());
   }
 
   /* This method shortens the match() method right after it */
@@ -904,6 +949,7 @@ public class OpDefNode extends OpDefOrDeclNode
 //    this.argLevelParams      = EmptySet;
   }
 
+  @Override
   public final boolean levelCheck(int itr) {
     if (   (this.levelChecked >= itr)
         || (    (! inRecursiveSection)
@@ -1103,6 +1149,7 @@ public class OpDefNode extends OpDefOrDeclNode
    * toString, levelDataToString, and walkGraph methods to implement
    * ExploreNode interface
    */
+  @Override
   public final String levelDataToString() {
     if (   (this.getKind() == ModuleInstanceKind)
         || (this.getKind() == NumberedProofStepKind)) {return "";} ;
@@ -1165,10 +1212,15 @@ public class OpDefNode extends OpDefOrDeclNode
   }
 
 
+  public boolean hasOpcode(final int opCode) {
+      return opCode == BuiltInOPs.getOpCode(getName());
+  }
+
   /**
    * The body is the node's only child.
    */
 
+  @Override
   public SemanticNode[] getChildren() {
     return new SemanticNode[] {this.body};
   }
@@ -1178,24 +1230,53 @@ public class OpDefNode extends OpDefOrDeclNode
    * and inserts them in the Hashtable semNodesTable for use by
    * the Explorer tool.
    */
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
     semNodesTable.put(uid, this);
+    visitor.preVisit(this);
     if (params != null && params.length > 0) {
       for (int i = 0; i < params.length; i++) {
-        if (params[i] != null) params[i].walkGraph(semNodesTable);
+        if (params[i] != null) params[i].walkGraph(semNodesTable, visitor);
       }
     }
-    if (body != null) body.walkGraph(semNodesTable);
-    if (stepNode != null) stepNode.walkGraph(semNodesTable);
+    if (body != null) body.walkGraph(semNodesTable, visitor);
+    if (stepNode != null) stepNode.walkGraph(semNodesTable, visitor);
+    visitor.postVisit(this);
   }
 
+	@Override
+	public String getSignature() {
+		final StringBuffer buf = new StringBuffer();
+		buf.append(getName().toString());
+		if (getArity() > 0 && getKind() != ASTConstants.BuiltInKind) {
+			buf.append("(");
+			//TODO This hack doesn't work for infix operators
+			final FormalParamNode[] params = getParams();
+			for (int i = 0; i < params.length - 1; i++) {
+				final FormalParamNode formalParamNode = params[i];
+				if (formalParamNode.getTreeNode() != null) {
+					final SyntaxTreeNode treeNode = (SyntaxTreeNode) formalParamNode.getTreeNode();
+					buf.append(treeNode.getHumanReadableImage());
+					buf.append(", ");
+				}
+			}
+			if (params[params.length - 1].getTreeNode() != null) {
+				final TreeNode treeNode = params[params.length - 1].getTreeNode();
+				buf.append(treeNode.getHumanReadableImage());
+			}
+			buf.append(")");
+		}
+		return buf.toString();
+	}
+
   /**
    * Displays this node as a String, implementing ExploreNode
    * interface; depth parameter is a bound on the depth of the portion
    * of the tree that is displayed.
    */
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -1270,6 +1351,25 @@ public class OpDefNode extends OpDefOrDeclNode
     return ret;
   }
 
+	@Override
+	public String getHumanReadableImage() {
+		// This ony gets pre-comments (directly in front of the operator definition).
+		// In-line comments - e.g. found in the standard modules - are not pre-comments.
+		final String comment = getComment();
+		
+		final StringBuffer buf = new StringBuffer(comment);
+		buf.append("\n");
+		
+		TreeNode[] ones = getTreeNode().one();
+		for (TreeNode treeNode : ones) {
+			// TODO This omits all whitespaces from the definition and is thus hard to read.
+			// I couldn't figure out a better way to obtain the original image though.
+			buf.append(treeNode.getHumanReadableImage());
+			buf.append(" ");
+		}
+		return buf.toString().trim();
+	}
+  
   protected String getNodeRef() {
     switch (getKind()) {
       case UserDefinedOpKind:
@@ -1282,7 +1382,7 @@ public class OpDefNode extends OpDefOrDeclNode
     }
   }
 
-  protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) {
+  protected Element getSymbolElement(Document doc, SymbolContext context) {
     Element ret = null;
     switch (getKind()) {
       case UserDefinedOpKind:
@@ -1325,4 +1425,4 @@ public class OpDefNode extends OpDefOrDeclNode
     }
     return ret;
   }
-}
+}
\ No newline at end of file
diff --git a/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java b/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java
index d174b3fb54c84c53fd893b410d87893319374437..adb581946e9dc2a60ff1dbd6ddca1892568c7132 100644
--- a/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java
+++ b/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java
@@ -81,5 +81,17 @@ public abstract class OpDefOrDeclNode extends SymbolNode {
                              ? originallyDefinedInModule.getName().toString() 
                              : "<null>" );
   }
-  
+
+	public String getComment() {
+		final StringBuffer buf = new StringBuffer();
+		
+		//TODO Support in-line comments found in the standard modules.
+		final String[] preComments = getPreComments();
+		for (String string : preComments) {
+			buf.append(string);
+			buf.append("\n");
+		}
+		
+		return buf.toString().replace("\n$", "").trim();
+	}
 }
diff --git a/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java b/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java
index d7eb889c0253dba0b5f92e8358d972e24dc07157..fe64c21a815c930ee0c6f34705b675da139afb6b 100644
--- a/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java
+++ b/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java
@@ -1,66 +1,66 @@
-// Last modified on Sat 28 Apr 2007 at 14:37:16 PST by lamport
-
-/***************************************************************************
-* This interface is implemented by LabelNode, OpDefNode, and               *
-* ThmOrAssumpDefNode.  It contains methods for accessing the set of        *
-* LabelNode objects for labels that occur immediately within this          *
-* node--that is, with no intervening LabelNode or OpDefNode in the         *
-* semantic tree.  This set is represented by a Hashtable, and by the null  *
-* object if the set is empty.  The Hashtable key of a LabelNode ln is      *
-* ln.getName(), which is a UniqueString.                                   *
-*                                                                          *
-* WARNING: If an OpDefNode odn represents an operator obtained by          *
-* instanting a module M, then each LabelNode object lab returned by        *
-* getLabel() and getLabels() is the same one as in the OpDefNode           *
-* from module M. Therefore, lab.body is an expression in module M, not in  *
-* the current module.  The meaning of this label must be interpreted as    *
-* lab.body under the substitutions of the instantiation.  This             *
-* substitution is described by the SubstInNode odn.body.  Hence, to        *
-* interpret a label in the label set of an OpDefNode odn, one must check   *
-* if odn.body is a SubstInNode, in which case that substitution must be    *
-* applied to the body of the label.  In general, odn is the result of a    *
-* series of k instantiations iff odn.body.body. ... .body is a SubstIn     *
-* node, for up to k ".body"s.                                              *
-*                                                                          *
-* A similar warning applies to ThmOrAssumpDefNode objects, except they     *
-* use APSubstInNode objects instead of SubstInNode objects for the         *
-* instantiation.                                                           *
-***************************************************************************/
-
-package tla2sany.semantic;
-
-import java.util.Hashtable;
-
-import util.UniqueString;
-
-interface OpDefOrLabelNode {
-
-public abstract void setLabels(Hashtable ht) ;
-  /*************************************************************************
-  * Set the set of labels to ht.                                           *
-  *************************************************************************/
-  
-public abstract LabelNode getLabel(UniqueString us) ;
-  /*************************************************************************
-  * If the set contains an OpDefNode with name `us', then that OpDefNode   *
-  * is returned; otherwise null is returned.                               *
-  *************************************************************************/
-
-public abstract boolean addLabel(LabelNode odn) ;
-  /*************************************************************************
-  * If the set contains no LabelNode with the same name as odn, then odn   *
-  * is added to the set and true is return; else the set is unchanged and  *
-  * false is returned.                                                     *
-  *************************************************************************/
-  
-public abstract LabelNode[] getLabels() ;
-  /*************************************************************************
-  * Returns an array containing the Label objects in the set.              *
-  *************************************************************************/
-  
-public abstract int getArity();
-  /*************************************************************************
-  * Returns the arity of the label or operator.                            *
-  *************************************************************************/
-  
+// Last modified on Sat 28 Apr 2007 at 14:37:16 PST by lamport
+
+/***************************************************************************
+* This interface is implemented by LabelNode, OpDefNode, and               *
+* ThmOrAssumpDefNode.  It contains methods for accessing the set of        *
+* LabelNode objects for labels that occur immediately within this          *
+* node--that is, with no intervening LabelNode or OpDefNode in the         *
+* semantic tree.  This set is represented by a Hashtable, and by the null  *
+* object if the set is empty.  The Hashtable key of a LabelNode ln is      *
+* ln.getName(), which is a UniqueString.                                   *
+*                                                                          *
+* WARNING: If an OpDefNode odn represents an operator obtained by          *
+* instanting a module M, then each LabelNode object lab returned by        *
+* getLabel() and getLabels() is the same one as in the OpDefNode           *
+* from module M. Therefore, lab.body is an expression in module M, not in  *
+* the current module.  The meaning of this label must be interpreted as    *
+* lab.body under the substitutions of the instantiation.  This             *
+* substitution is described by the SubstInNode odn.body.  Hence, to        *
+* interpret a label in the label set of an OpDefNode odn, one must check   *
+* if odn.body is a SubstInNode, in which case that substitution must be    *
+* applied to the body of the label.  In general, odn is the result of a    *
+* series of k instantiations iff odn.body.body. ... .body is a SubstIn     *
+* node, for up to k ".body"s.                                              *
+*                                                                          *
+* A similar warning applies to ThmOrAssumpDefNode objects, except they     *
+* use APSubstInNode objects instead of SubstInNode objects for the         *
+* instantiation.                                                           *
+***************************************************************************/
+
+package tla2sany.semantic;
+
+import java.util.Hashtable;
+
+import util.UniqueString;
+
+interface OpDefOrLabelNode {
+
+public abstract void setLabels(Hashtable<UniqueString, LabelNode> ht) ;
+  /*************************************************************************
+  * Set the set of labels to ht.                                           *
+  *************************************************************************/
+  
+public abstract LabelNode getLabel(UniqueString us) ;
+  /*************************************************************************
+  * If the set contains an OpDefNode with name `us', then that OpDefNode   *
+  * is returned; otherwise null is returned.                               *
+  *************************************************************************/
+
+public abstract boolean addLabel(LabelNode odn) ;
+  /*************************************************************************
+  * If the set contains no LabelNode with the same name as odn, then odn   *
+  * is added to the set and true is return; else the set is unchanged and  *
+  * false is returned.                                                     *
+  *************************************************************************/
+  
+public abstract LabelNode[] getLabels() ;
+  /*************************************************************************
+  * Returns an array containing the Label objects in the set.              *
+  *************************************************************************/
+  
+public abstract int getArity();
+  /*************************************************************************
+  * Returns the arity of the label or operator.                            *
+  *************************************************************************/
+  
 }
\ No newline at end of file
diff --git a/tlatools/src/tla2sany/semantic/SemanticNode.java b/tlatools/src/tla2sany/semantic/SemanticNode.java
index 78b1334ca4511e79c085247bb3d8fecc1813c630..fc265c0b64c071eab9e836ab7dccc20b95ce787d 100644
--- a/tlatools/src/tla2sany/semantic/SemanticNode.java
+++ b/tlatools/src/tla2sany/semantic/SemanticNode.java
@@ -4,19 +4,21 @@
 // last modified on Fri 16 Mar 2007 at 17:22:54 PST by lamport
 package tla2sany.semantic;
 
-import java.util.Comparator;
 import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.parser.SyntaxTreeNode;
 import tla2sany.st.Location;
 import tla2sany.st.TreeNode;
-import util.ToolIO;
-
 import tla2sany.xml.XMLExportable;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
+import tlc2.value.IValue;
+import tlc2.value.Values;
 
 /**
  * SemanticNode is the (abstract) superclass of all nodes in the
@@ -26,15 +28,15 @@ import org.w3c.dom.Node;
  * used to check for equality.
  */
 public abstract class SemanticNode
- implements ASTConstants, ExploreNode, LevelConstants, Comparable, XMLExportable /* interface for exporting into XML */ {
+ implements ASTConstants, ExploreNode, LevelConstants, Comparable<SemanticNode>, XMLExportable /* interface for exporting into XML */ {
 
   private static final Object[] EmptyArr = new Object[0];
 
-  private static int uid = 0;  // the next unique ID for any semantic node
+  private static final AtomicInteger uid = new AtomicInteger();  // the next unique ID for any semantic node
 
   protected static Errors errors;
 
-  public    int      myUID;    // the unique ID of THIS semantic node
+  public    final int      myUID;    // the unique ID of THIS semantic node
   public    TreeNode stn;      // the concrete syntax tree node associated with THIS semantic node
   private   Object[] tools;    // each tool has a location in this array where
                                //   it may store an object for its own purposes
@@ -42,7 +44,7 @@ public abstract class SemanticNode
                                //   strongly correlated with the Java type of the node
 
   public SemanticNode(int kind, TreeNode stn) {
-    myUID = uid++;
+    myUID = uid.getAndIncrement();
     this.kind = kind;
     this.stn = stn;
     this.tools = EmptyArr;
@@ -88,7 +90,6 @@ public abstract class SemanticNode
       this.tools = newTools;
     }
     this.tools[toolId] = obj;
-    ToolIO.registerSemanticNode(this, toolId);
   }
 
   /**
@@ -177,10 +178,12 @@ public abstract class SemanticNode
    * of walkgraph is to find all reachable nodes in the semantic graph
    * and insert them in a Hashtable for use by the Explorer tool.
    */
-  public void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    visitor.postVisit(this);
   }
 
   /**
@@ -194,10 +197,25 @@ public abstract class SemanticNode
 	    "  kind: " + (kind == -1 ? "<none>" : kinds[kind])
 	    + getPreCommentsAsString());
   }
+  
+  public boolean isBuiltIn() {
+	  return Context.isBuiltIn(this);
+  }
 
+  /**
+   * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule()
+   * @see StandardModules.isDefinedInStandardModule()
+   */
+  public boolean isStandardModule() {
+	  return StandardModules.isDefinedInStandardModule(this);
+  }
+  
   // YY's code
   public final Location getLocation() {
-    return this.stn.getLocation();
+	  if (this.stn != null) {
+		  return this.stn.getLocation();
+	  }
+	  return Location.nullLoc;
   }
 
   /**
@@ -212,9 +230,9 @@ public abstract class SemanticNode
    * @param s2
    * @return
    */
-  public int compareTo(Object s) {
+  public int compareTo(SemanticNode s) {
        Location loc1 = this.stn.getLocation();
-       Location loc2 = ((SemanticNode) s).stn.getLocation();
+       Location loc2 = s.stn.getLocation();
        if (loc1.beginLine() < loc2.beginLine())
         {
            return -1;
@@ -228,18 +246,70 @@ public abstract class SemanticNode
        return (loc1.beginColumn() < loc2.beginColumn())?-1:1;
   }
 
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + kind;
+		result = prime * result + myUID;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		if (getClass() != obj.getClass()) {
+			return false;
+		}
+		SemanticNode other = (SemanticNode) obj;
+		if (kind != other.kind) {
+			return false;
+		}
+		if (myUID != other.myUID) {
+			return false;
+		}
+		return true;
+	}
+
 /***************************************************************************
 * XXXXX A test for getLocation() returning null should be added            *
 *       to the following two toString methods.                             *
 ***************************************************************************/
   public final void toString(StringBuffer sb, String padding) {
-    sb.append(this.getLocation());
+	  TreeNode treeNode = getTreeNode();
+		if (treeNode instanceof SyntaxTreeNode
+				&& System.getProperty(SemanticNode.class.getName() + ".showPlainFormulae") != null) {
+		  SyntaxTreeNode stn = (SyntaxTreeNode) treeNode;
+		  sb.append(stn.getHumanReadableImage());
+	  } else {
+		  sb.append(this.getLocation());
+	  }
   }
 
+  @Override
   public String toString() {
+	  TreeNode treeNode = getTreeNode();
+		if (treeNode instanceof SyntaxTreeNode
+				&& System.getProperty(SemanticNode.class.getName() + ".showPlainFormulae") != null) {
+			  SyntaxTreeNode stn = (SyntaxTreeNode) treeNode;
+		  return stn.getHumanReadableImage();
+	  }
     return this.getLocation().toString();
   }
 
+  public String getHumanReadableImage() {
+	  return getLocation().toString();
+  }
+  
+  public String toString(final IValue aValue) {
+	return Values.ppr(aValue.toString());
+  }
+
     /**
      * August 2014 - TL
      * All nodes inherit from semantic node, which just attach location to the returned node
diff --git a/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java b/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java
index 08d295e3f3a10f7c4586626d3806c51f969bec08..edafe574a290a855dbd488667f1fdcc38fe32c7e 100644
--- a/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java
+++ b/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java
@@ -6,7 +6,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-class SetOfArgLevelConstraints extends HashMap implements LevelConstants {
+class SetOfArgLevelConstraints extends HashMap<ParamAndPosition, Integer> implements LevelConstants {
   // Implements a map mapping arg-level parameters (ParamAndPosition)
   // to levels (Integer). An entry <pap,x> means that the argument
   // pap.position of the operator pap.param must have a level >= x.
@@ -23,16 +23,17 @@ class SetOfArgLevelConstraints extends HashMap implements LevelConstants {
    * it with another one for the same parameter if one is there, or
    * ignoring the constraint if it is vacuous.
    */
-  public final Object put(Object pap, Object level) {
-    int newLevel = ((Integer)level).intValue();
-    Object old = this.get(pap);
+  @Override
+  public final Integer put(ParamAndPosition pap, Integer level) {
+    int newLevel = level.intValue();
+    Integer old = this.get(pap);
 
-    int oldLevel = (old == null) ? MinLevel : ((Integer)old).intValue();
+    int oldLevel = (old == null) ? MinLevel : old.intValue();
     super.put(pap, new Integer(Math.max(newLevel, oldLevel)));
     return old;
   }
 
-  public final Object put(SymbolNode param, int position, int level) {
+  public final Integer put(SymbolNode param, int position, int level) {
     ParamAndPosition pap = new ParamAndPosition(param, position);
     return this.put(pap, new Integer(level));
   }
@@ -42,17 +43,19 @@ class SetOfArgLevelConstraints extends HashMap implements LevelConstants {
    * map, in each case "subsuming" it with any constraint already
    * present for the same parameter if one is there.
    */
-  public final void putAll(Map s) {
-    for (Iterator iter = s.keySet().iterator(); iter.hasNext(); ) {
-      Object key = iter.next();
-      put(key, s.get(key));
+  @Override
+  public final void putAll(Map<? extends ParamAndPosition, ? extends Integer> s) {
+    for (Iterator<? extends ParamAndPosition> iter = s.keySet().iterator(); iter.hasNext(); ) {
+    	  ParamAndPosition key = iter.next();
+      this.put(key, s.get(key));
     }
   }
 
+  @Override
   public final String toString() {
     StringBuffer sb = new StringBuffer("{ ");
-    for (Iterator iter = this.keySet().iterator(); iter.hasNext(); ) {
-      Object pap = iter.next();
+    for (Iterator<ParamAndPosition> iter = this.keySet().iterator(); iter.hasNext(); ) {
+      ParamAndPosition pap = iter.next();
       sb.append(pap.toString() + " -> " + this.get(pap));
       if (iter.hasNext()) sb.append(", ");
     }
diff --git a/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java b/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java
index 8f3c4dfec2ce722c8523218d25068c643d7a0265..676bb196cb14fb49f9aea86788468a4939f8e261 100644
--- a/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java
+++ b/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java
@@ -6,7 +6,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-class SetOfLevelConstraints extends HashMap implements LevelConstants {
+class SetOfLevelConstraints extends HashMap<SymbolNode, Integer> implements LevelConstants {
   // Implements a map mapping parameters to levels. An entry <p,x> in
   // the set means that p must have a level <= x.
   /*************************************************************************
@@ -15,17 +15,16 @@ class SetOfLevelConstraints extends HashMap implements LevelConstants {
   * that the key/parameter must have a level <= the value/int.             *
   *************************************************************************/
   
-
-
   /**
    * This method adds <param, level> into this map. It subsumes
    * any existing one. 
    */
-  public final Object put(Object param, Object level) {
-    int newLevel = ((Integer)level).intValue();
-    Object old = this.get(param);
+  @Override
+  public final Integer put(SymbolNode param, Integer level) {
+    int newLevel = level.intValue();
+    Integer old = this.get(param);
 
-    int oldLevel = (old == null) ? MaxLevel : ((Integer)old).intValue();
+    int oldLevel = (old == null) ? MaxLevel : old.intValue();
     super.put(param, new Integer(Math.min(newLevel, oldLevel)));
     return old;
   }
@@ -35,17 +34,19 @@ class SetOfLevelConstraints extends HashMap implements LevelConstants {
    * map, in each case "subsuming" it with any constraint already
    * present for the same parameter if one is there.
    */
-  public final void putAll(Map s) {
-    for (Iterator iter = s.keySet().iterator(); iter.hasNext(); ) {
-      Object key = iter.next();
+  @Override
+  public final void putAll(Map<? extends SymbolNode, ? extends Integer> s) {
+    for (Iterator<? extends SymbolNode> iter = s.keySet().iterator(); iter.hasNext(); ) {
+      SymbolNode key = iter.next();
       this.put(key, s.get(key));
     }
   }
   
+  @Override
   public final String toString() {
     StringBuffer sb = new StringBuffer("{ ");
-    for (Iterator iter = this.keySet().iterator(); iter.hasNext(); ) {
-      SymbolNode param = (SymbolNode)iter.next();
+    for (Iterator<SymbolNode> iter = this.keySet().iterator(); iter.hasNext(); ) {
+      SymbolNode param = iter.next();
       sb.append(param.getName() + " -> " + this.get(param));
       if (iter.hasNext()) sb.append(", ");
     }
diff --git a/tlatools/src/tla2sany/semantic/StandardModules.java b/tlatools/src/tla2sany/semantic/StandardModules.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c8634a2685a5294c61850ab2bb9b76ef503def8
--- /dev/null
+++ b/tlatools/src/tla2sany/semantic/StandardModules.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tla2sany.semantic;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class StandardModules {
+	private static final Set<String> STANDARD_MODULES = new HashSet<>();
+	
+	static {
+		// Quick'n'Dirty hack! This breaks if any one of the operators is override by
+		// the model or a complete standard module has been replaced by a user-defined
+		// module with the same name.
+		STANDARD_MODULES.add("FiniteSets");
+		STANDARD_MODULES.add("Sequences");
+		STANDARD_MODULES.add("Bags");
+		STANDARD_MODULES.add("Naturals");
+		STANDARD_MODULES.add("Integers");
+		STANDARD_MODULES.add("Reals");
+		STANDARD_MODULES.add("RealTime");
+		STANDARD_MODULES.add("Randomization");
+		STANDARD_MODULES.add("TLC");
+	}
+
+	private StandardModules() {
+		// no instances
+	}
+	
+	public static boolean isDefinedInStandardModule(SemanticNode sn) {
+		if ((sn != null) && (sn.getLocation() != null)) {
+			return isDefinedInStandardModule(sn.getLocation().source()); // source might be null
+		}
+		return false;
+	}
+	
+	public static boolean isDefinedInStandardModule(final String moduleName) {
+		return STANDARD_MODULES.contains(moduleName);
+	}
+	
+	public static void filterNonStandardModulesFromSet(final Set<String> listOfModules) {
+		listOfModules.retainAll(STANDARD_MODULES);
+	}
+}
diff --git a/tlatools/src/tla2sany/semantic/StringNode.java b/tlatools/src/tla2sany/semantic/StringNode.java
index 054c5077b397d17aebf4683d9b247cc0f49f9c50..80746864ee8e21279d624291cd9c6c005d855d1c 100644
--- a/tlatools/src/tla2sany/semantic/StringNode.java
+++ b/tlatools/src/tla2sany/semantic/StringNode.java
@@ -4,14 +4,16 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
-import tla2sany.explorer.ExploreNode;
-import tla2sany.st.TreeNode;
-import util.UniqueString;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.xml.SymbolContext;
+import util.UniqueString;
+
 
 
 /**
@@ -47,6 +49,7 @@ public class StringNode extends ExprNode implements ExploreNode {
   public final UniqueString getRep() { return this.value; }
 
   /* Level Checking */
+  @Override
   public final boolean levelCheck(int iter) {
     levelChecked = iter;
       /*********************************************************************
@@ -81,11 +84,14 @@ public class StringNode extends ExprNode implements ExploreNode {
 //           "ArgLevelParams: "      + this.getArgLevelParams()      + "\n" ;
 //  }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    visitor.postVisit(this);
   }
 
   final String PrintVersion(String str) {
@@ -118,6 +124,7 @@ public class StringNode extends ExprNode implements ExploreNode {
     return buf.toString();
    }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     return "\n*StringNode: " + super.toString(depth)
@@ -125,7 +132,8 @@ public class StringNode extends ExprNode implements ExploreNode {
                              "'" + " Length: " + value.length();
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
       Element e = doc.createElement("StringValue");
       Node n = doc.createTextNode(value.toString());
       e.appendChild(n);
diff --git a/tlatools/src/tla2sany/semantic/Subst.java b/tlatools/src/tla2sany/semantic/Subst.java
index 4e91afb6866dbb8c81e954b5644973b6999e83e8..8f3bd4de55010c74d99707e149c317534cb5a859 100644
--- a/tlatools/src/tla2sany/semantic/Subst.java
+++ b/tlatools/src/tla2sany/semantic/Subst.java
@@ -6,13 +6,16 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
 import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
-
+import tla2sany.xml.SymbolContext;
 import tla2sany.xml.XMLExportable;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
+import tlc2.tool.coverage.CostModel;
 
 public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExportable /* interface for exporting into XML */ {
 
@@ -24,6 +27,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
   private ExprOrOpArgNode  expr;
   private TreeNode         exprSTN;
   private boolean          implicit;
+  private CostModel        cm = CostModel.DO_NOT_RECORD;
 
   /* Constructors */
   public Subst(OpDeclNode odn, ExprOrOpArgNode exp, TreeNode exSTN, boolean imp) {
@@ -49,6 +53,13 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
   public final void setExprSTN(TreeNode stn) { this.exprSTN = stn; }
 
   public final boolean isImplicit() { return this.implicit; }
+  
+  public final CostModel getCM() { return this.cm; }
+  
+  public final Subst setCM(final CostModel cm) {
+	  this.cm = cm;
+	  return this;
+  }
 
   public static ExprOrOpArgNode getSub(Object param, Subst[] subs) {
     for (int i = 0; i < subs.length; i++) {
@@ -59,7 +70,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
     return null;
   }
 
-  public static HashSet paramSet(Object param, Subst[] subs) {
+  public static HashSet<SymbolNode> paramSet(SymbolNode param, Subst[] subs) {
     /***********************************************************************
     * If subs[i] is of the form `parm <- expr', then it returns the        *
     * expr.levelParams.  Otherwise, it returns the HashSet containing      *
@@ -76,12 +87,12 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
       return subs[idx].getExpr().getLevelParams();
     }
 
-    HashSet res = new HashSet();
+    HashSet<SymbolNode> res = new HashSet<>();
     res.add(param);
     return res;
   }
 
-  public static HashSet allParamSet(Object param, Subst[] subs) {
+  public static HashSet<SymbolNode> allParamSet(SymbolNode param, Subst[] subs) {
     /***********************************************************************
     * This is exactly like paramSet, except it returns the allParams       *
     * HashSet instead of levelParams.                                      *
@@ -94,7 +105,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
       return subs[idx].getExpr().getAllParams();
     }
 
-    HashSet res = new HashSet();
+    HashSet<SymbolNode> res = new HashSet<>();
     res.add(param);
     return res;
   }
@@ -110,10 +121,10 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
     ***********************************************************************/
     SetOfLevelConstraints res = new SetOfLevelConstraints();
     SetOfLevelConstraints lcSet = body.getLevelConstraints();
-    Iterator iter = lcSet.keySet().iterator();
+    Iterator<SymbolNode> iter = lcSet.keySet().iterator();
     while (iter.hasNext()) {
-      SymbolNode param = (SymbolNode)iter.next();
-      Object plevel = lcSet.get(param);
+      SymbolNode param = iter.next();
+      Integer plevel = lcSet.get(param);
       if (!isConstant) {
 	if (param.getKind() == ConstantDeclKind) {
 	  plevel = Levels[ConstantLevel];
@@ -122,15 +133,15 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
 	  plevel = Levels[VariableLevel];
 	}
       }
-      Iterator iter1 = paramSet(param, subs).iterator();
+      Iterator<SymbolNode> iter1 = paramSet(param, subs).iterator();
       while (iter1.hasNext()) {
 	res.put(iter1.next(), plevel);
       }
     }
-    HashSet alpSet = body.getArgLevelParams();
-    iter = alpSet.iterator();
-    while (iter.hasNext()) {
-      ArgLevelParam alp = (ArgLevelParam)iter.next();
+    HashSet<ArgLevelParam> alpSet = body.getArgLevelParams();
+    Iterator<ArgLevelParam> alpIter = alpSet.iterator();
+    while (alpIter.hasNext()) {
+      ArgLevelParam alp = alpIter.next();
       OpArgNode sub = (OpArgNode)getSub(alp.op, subs);
       if (sub != null &&
 	  sub.getOp() instanceof OpDefNode) {
@@ -145,7 +156,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
           * argument of this method.                                       *
           *****************************************************************/
 	Integer mlevel = new Integer(subDef.getMaxLevel(alp.i));
-	Iterator iter1 = paramSet(alp.param, subs).iterator();
+	Iterator<SymbolNode> iter1 = paramSet(alp.param, subs).iterator();
 	while (iter1.hasNext()) {
 	  res.put(iter1.next(), mlevel);
 	}
@@ -162,10 +173,10 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
     ***********************************************************************/
     SetOfArgLevelConstraints res = new SetOfArgLevelConstraints();
     SetOfArgLevelConstraints alcSet = body.getArgLevelConstraints();
-    Iterator iter = alcSet.keySet().iterator();
+    Iterator<ParamAndPosition> iter = alcSet.keySet().iterator();
     while (iter.hasNext()) {
-      ParamAndPosition pap = (ParamAndPosition)iter.next();
-      Object plevel = alcSet.get(pap);
+      ParamAndPosition pap = iter.next();
+      Integer plevel = alcSet.get(pap);
       ExprOrOpArgNode sub = getSub(pap.param, subs);
       if (sub == null) {
 	res.put(pap, plevel);
@@ -178,10 +189,10 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
 	}
       }
     }
-    HashSet alpSet = body.getArgLevelParams();
-    iter = alpSet.iterator();
-    while (iter.hasNext()) {
-      ArgLevelParam alp = (ArgLevelParam)iter.next();
+    HashSet<ArgLevelParam> alpSet = body.getArgLevelParams();
+    Iterator<ArgLevelParam> alpIter = alpSet.iterator();
+    while (alpIter.hasNext()) {
+      ArgLevelParam alp = alpIter.next();
       ExprOrOpArgNode subParam = getSub(alp.param, subs);
       if (subParam != null) {
 	ExprOrOpArgNode subOp = getSub(alp.op, subs);
@@ -200,16 +211,16 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
     return res;
   }
 
-  public static HashSet getSubALPSet(LevelNode body, Subst[] subs) {
+  public static HashSet<ArgLevelParam> getSubALPSet(LevelNode body, Subst[] subs) {
     /***********************************************************************
     * This should only be called after level checking has been called on   *
     * body and on all subs[i].getExpr().                                   *
     ***********************************************************************/
-    HashSet res = new HashSet();
-    HashSet alpSet = body.getArgLevelParams();
-    Iterator iter = alpSet.iterator();
+    HashSet<ArgLevelParam> res = new HashSet<>();
+    HashSet<ArgLevelParam> alpSet = body.getArgLevelParams();
+    Iterator<ArgLevelParam> iter = alpSet.iterator();
     while (iter.hasNext()) {
-      ArgLevelParam alp = (ArgLevelParam)iter.next();
+      ArgLevelParam alp = iter.next();
       ExprOrOpArgNode sub = getSub(alp.op, subs);
       if (sub == null) {
 	res.add(alp);
@@ -217,9 +228,9 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
       else {
 	SymbolNode subOp = ((OpArgNode)sub).getOp();
 	if (subOp.isParam()) {
-	  Iterator iter1 = paramSet(alp.param, subs).iterator();
+	  Iterator<SymbolNode> iter1 = paramSet(alp.param, subs).iterator();
 	  while (iter1.hasNext()) {
-	    res.add(new ArgLevelParam(subOp, alp.i, (SymbolNode)iter1.next()));
+	    res.add(new ArgLevelParam(subOp, alp.i, iter1.next()));
 	  }
 	}
       }
@@ -229,9 +240,11 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
 
   public final String levelDataToString() { return "Dummy level string"; }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    if (op != null) op.walkGraph(semNodesTable);
-    if (expr != null) expr.walkGraph(semNodesTable);
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+	visitor.preVisit(this);
+    if (op != null) op.walkGraph(semNodesTable, visitor);
+    if (expr != null) expr.walkGraph(semNodesTable, visitor);
+    visitor.postVisit(this);
   }
 
   public final String toString(int depth) {
@@ -240,7 +253,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo
            "\nExpr: " + Strings.indent(2,(expr!=null ? expr.toString(depth-1) : "<null>"));
   }
 
-  public Element export(Document doc, tla2sany.xml.SymbolContext context) {
+  public Element export(Document doc, SymbolContext context) {
       Element ret = doc.createElement("Subst");
       ret.appendChild(op.export(doc,context));
       ret.appendChild(expr.export(doc,context));
diff --git a/tlatools/src/tla2sany/semantic/SubstInNode.java b/tlatools/src/tla2sany/semantic/SubstInNode.java
index 07230da85a00f4a1062a90e26390665170f0c78f..e1de6219a3995def4cf6e237be98cfe8144804c0 100644
--- a/tlatools/src/tla2sany/semantic/SubstInNode.java
+++ b/tlatools/src/tla2sany/semantic/SubstInNode.java
@@ -24,14 +24,17 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
 import tla2sany.utilities.Vector;
+import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 public class SubstInNode extends ExprNode {
   /**
    * For a SubstInNode object s that has the WITH clause
@@ -80,7 +83,7 @@ public class SubstInNode extends ExprNode {
    * substitutions is to be produced.
    */
   public SubstInNode(TreeNode treeNode, SymbolTable instancerST,
-		     Vector instanceeDecls, ModuleNode ingmn, ModuleNode edmn)
+		     Vector<OpDeclNode> instanceeDecls, ModuleNode ingmn, ModuleNode edmn)
   throws AbortException {
     super(SubstInKind, treeNode);
     this.instantiatingModule = ingmn;
@@ -130,17 +133,17 @@ public class SubstInNode extends ExprNode {
    * OpApplNode or an OpArgNode substituted for each CONSTANT of
    * VARIABLE OpDeclNode in vector v.
    */
-  final void constructSubst(Vector instanceeDecls, SymbolTable instancerST,
+  final void constructSubst(Vector<OpDeclNode> instanceeDecls, SymbolTable instancerST,
 			    TreeNode treeNode)
   throws AbortException {
-    Vector vtemp = new Vector();
+    Vector<Subst> vtemp = new Vector<>();
 
     // for each CONSTANT or VARIABLE declared in module being
     // instantiated (the instancee)
     for ( int i = 0; i < instanceeDecls.size(); i++ ) {
       // Get the OpDeclNode for the CONSTANT or VARIABLE being
       // substituted for, i.e. "c" in" c <- e"
-      OpDeclNode decl = (OpDeclNode)instanceeDecls.elementAt(i);
+      OpDeclNode decl = instanceeDecls.elementAt(i);
 
       // Try to resolve the name in the instancer module so we can see
       // if it is recognized as an operator, and if so, what kind of
@@ -196,7 +199,8 @@ public class SubstInNode extends ExprNode {
    * to this method can contain a mixture of explicit and implicit
    * substitutions
    */
-  final void addExplicitSubstitute(Context instanceeCtxt, UniqueString lhs,
+  @SuppressWarnings("unused")	// TODO final else block is dead code 
+  final void addExplicitSubstitute(Context instanceCtxt, UniqueString lhs,
                                    TreeNode stn, ExprOrOpArgNode sub) {
     int index;
     for (index = 0; index < this.substs.length; index++) {
@@ -224,7 +228,7 @@ public class SubstInNode extends ExprNode {
       // the instancee context
 
       // look up the lhs symbol in the instancee context
-      SymbolNode lhsSymbol = instanceeCtxt.getSymbol(lhs);
+      SymbolNode lhsSymbol = instanceCtxt.getSymbol(lhs);
 
       // lhs must be an OpDeclNode; if not just return, as this error
       // will have been earlier, though semantic analysis was allowed
@@ -261,10 +265,10 @@ public class SubstInNode extends ExprNode {
    * possible, because X is not defined in the instantiating module,
    * then we have an error.
    */
-  final void matchAll(Vector decls) {
+  final void matchAll(Vector<OpDeclNode> decls) {
     for (int i = 0; i < decls.size(); i++) {
       // Get the name of the i'th operator that must be substituted for
-      UniqueString opName = ((OpDeclNode)decls.elementAt(i)).getName();
+      UniqueString opName = decls.elementAt(i).getName();
 
       // See if it is represented in the substitutions array
       int j;
@@ -276,7 +280,7 @@ public class SubstInNode extends ExprNode {
       if ( j >= this.substs.length ) {
         errors.addError(stn.getLocation(),
 			"Substitution missing for symbol " + opName + " declared at " +
-			((OpDeclNode)(decls.elementAt(i))).getTreeNode().getLocation() +
+			decls.elementAt(i).getTreeNode().getLocation() +
 			" \nand instantiated in module " + instantiatingModule.getName() + "." );
       }
     }
@@ -291,6 +295,8 @@ public class SubstInNode extends ExprNode {
 //  private SetOfArgLevelConstraints argLevelConstraints;
 //  private HashSet argLevelParams;
 
+  @Override
+  @SuppressWarnings("unchecked")
   public final boolean levelCheck(int itr) {
     if (this.levelChecked >= itr) return this.levelCorrect;
     this.levelChecked = itr ;
@@ -311,7 +317,7 @@ public class SubstInNode extends ExprNode {
 
     // Calculate the level information
     this.level = this.body.getLevel();
-    HashSet lpSet = this.body.getLevelParams();
+    HashSet<SymbolNode> lpSet = this.body.getLevelParams();
     for (int i = 0; i < this.substs.length; i++) {
       if (lpSet.contains(this.getSubFor(i))) {
 	this.level = Math.max(level, this.getSubWith(i).getLevel());
@@ -319,10 +325,9 @@ public class SubstInNode extends ExprNode {
     }
 
 //    this.levelParams = new HashSet();
-    Iterator iter = lpSet.iterator();
+    Iterator<SymbolNode> iter = lpSet.iterator();
     while (iter.hasNext()) {
-      Object param = iter.next();
-      this.levelParams.addAll(Subst.paramSet(param, this.substs));
+      this.levelParams.addAll(Subst.paramSet(iter.next(), this.substs));
         /*******************************************************************
         * At this point, levelCheck(itr) has been invoked on              *
         * this.substs[i].getExpr() (which equals this.getSubWith(i)).      *
@@ -353,8 +358,8 @@ public class SubstInNode extends ExprNode {
     * 23 February 2009: Added ".clone" to the following statements to fix  *
     * bug.                                                                 *
     ***********************************************************************/
-    this.allParams        = (HashSet) this.body.getAllParams().clone() ;
-    this.nonLeibnizParams = (HashSet) this.body.getNonLeibnizParams().clone() ;
+    this.allParams        = (HashSet<SymbolNode>)this.body.getAllParams().clone() ;
+    this.nonLeibnizParams = (HashSet<SymbolNode>)this.body.getNonLeibnizParams().clone() ;
     for (int i = 0 ; i < this.substs.length ; i++) {
       OpDeclNode param = substs[i].getOp() ;
       if (this.allParams.contains(param)) {
@@ -460,6 +465,7 @@ public class SubstInNode extends ExprNode {
 //           "ArgLevelParams: "      + this.argLevelParams      + "\n" ;
 //  }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
 
@@ -489,6 +495,7 @@ public class SubstInNode extends ExprNode {
    * The children of this node are the body and the expressions
    * being substituted for symbols.
    */
+  @Override
   public SemanticNode[] getChildren() {
      SemanticNode[] res = new SemanticNode[this.substs.length + 1];
      res[0] = this.body;
@@ -498,31 +505,46 @@ public class SubstInNode extends ExprNode {
      return res;
   }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
 
-    semNodesTable.put(new Integer(myUID), this);
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
 
     if (this.substs != null) {
       for (int i = 0; i < this.substs.length; i++) {
-        if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable);
+        if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable, visitor);
       }
     }
-    if (this.body != null) this.body.walkGraph(semNodesTable);
+    if (this.body != null) this.body.walkGraph(semNodesTable, visitor);
+    visitor.postVisit(this);
     return;
   }
 
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
       Element sbts = doc.createElement("substs");
       for (int i=0; i<substs.length; i++) {
         sbts.appendChild(substs[i].export(doc,context));
       }
       Element bdy = doc.createElement("body");
       bdy.appendChild(body.export(doc,context));
+
+      Element from = doc.createElement("instFrom");
+      Element fromchild = this.instantiatingModule.export(doc, context);
+      from.appendChild(fromchild);
+
+      Element to = doc.createElement("instTo");
+      Element tochild = instantiatedModule.export(doc,context);
+      to.appendChild(tochild);
+
       Element ret = doc.createElement("SubstInNode");
       ret.appendChild(sbts);
       ret.appendChild(bdy);
+      ret.appendChild(from);
+      ret.appendChild(to);
       // at the end, we append the context of the symbols used in this node
       //ret.appendChild(instanceeCtxt.export(doc));
 
diff --git a/tlatools/src/tla2sany/semantic/SymbolMatcher.java b/tlatools/src/tla2sany/semantic/SymbolMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..2866761da704b34b4371e7acd8e0a1ce77c6e0f9
--- /dev/null
+++ b/tlatools/src/tla2sany/semantic/SymbolMatcher.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tla2sany.semantic;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public interface SymbolMatcher {
+	
+	/**
+	 * @return true if the given {@link SymbolNode} matches this predicate.
+	 */
+	boolean matches(final SymbolNode aSymbol);
+
+	public static class NameAndTypeMatcher implements SymbolMatcher {
+	
+		private String prefix;
+		
+		//** Invoked by clients of ModuleNode#getSymbols **//
+		
+		public NameAndTypeMatcher setPrefix(final String aPrefix) {
+			this.prefix = aPrefix;
+			return this;
+		}
+		
+		//** Invoked by ModuleNode, overridden by clients **//
+		
+		/* (non-Javadoc)
+		 * @see tla2sany.semantic.SymbolMatcher#matches(tla2sany.semantic.SymbolNode)
+		 */
+		@Override
+		public boolean matches(final SymbolNode aSymbol) {
+			if (!matchesAnyType() && !matchTypes().contains(aSymbol.getClass())) {
+				// TODO Better test for isAssignableFrom(aSymbol.getClass()), but would require
+				// looping over matchTypes.
+				return false;
+			}
+			if (aSymbol.getKind() == ASTConstants.BuiltInKind && aSymbol.getName().toString().startsWith("$")) {
+				// Do not match internal built-in operators.
+				return false;
+			}
+
+			final String symbolName = aSymbol.getName().toString();
+			if (matchCaseSensitive() && !symbolName.startsWith(getPrefix())) {
+				return false;
+			} else if (!symbolName.toLowerCase().startsWith(getPrefix().toLowerCase())){
+				return false;
+			}
+			
+			return true;
+		}
+		
+		//** Invoked by matches, subclasses may override **//
+		
+		/**
+		 * @return A Set of SymbolNodes to match.
+		 */
+		protected Set<Class<? extends SymbolNode>> matchTypes() {
+			return new HashSet<>();
+		}
+		
+		protected boolean matchesAnyType() {
+			return matchTypes().isEmpty();
+		}
+		
+		protected boolean matchCaseSensitive() {
+			return false;
+		}
+	
+		protected String getPrefix() {
+			return prefix;
+		}
+	}
+}
diff --git a/tlatools/src/tla2sany/semantic/SymbolNode.java b/tlatools/src/tla2sany/semantic/SymbolNode.java
index fd5a442d8fc79f78c1834cdc64fb0524cec9c51c..b5ca60576480ad4a822342723d10a1171422bf52 100644
--- a/tlatools/src/tla2sany/semantic/SymbolNode.java
+++ b/tlatools/src/tla2sany/semantic/SymbolNode.java
@@ -2,12 +2,12 @@
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
 package tla2sany.semantic;
 
-import tla2sany.st.TreeNode;
-import util.UniqueString;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import tla2sany.st.TreeNode;
+import util.UniqueString;
+
 /**
  * Abstract class extended by classes that represent the meaning of an
  * identifier, including generalized IDs.
@@ -73,6 +73,10 @@ public abstract class SymbolNode extends LevelNode {
     return (this instanceof OpDeclNode ||
 	    this instanceof FormalParamNode);
   }
+  
+  public String getSignature() {
+	  return getName().toString();
+  }
 
   /**
    * Returns true iff this node and otherNode are both OpDefOrDeclNode objects or
@@ -127,6 +131,9 @@ public abstract class SymbolNode extends LevelNode {
    * we need to add location and level information here.
    */
   public Element exportDefinition(Document doc, tla2sany.xml.SymbolContext context) {
+    if (!context.isTop_level_entry())
+      throw new IllegalArgumentException("Exporting definition "+getName()+" ref "+getNodeRef()+" twice!");
+    context.resetTop_level_entry();
     try {
       Element e = getSymbolElement(doc, context);
       // level
@@ -158,7 +165,7 @@ public abstract class SymbolNode extends LevelNode {
    * we also override getLevelElement as it should never be called
    */
   protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
-    throw new UnsupportedOperationException("This should not be possible and therefore a bug");
+    throw new UnsupportedOperationException("implementation Error: A symbol node may not be called for its level element.");
   }
 
   /** TL
diff --git a/tlatools/src/tla2sany/semantic/SymbolTable.java b/tlatools/src/tla2sany/semantic/SymbolTable.java
index 167d1e75a82ec6284e7f6d56ae959449e6f7f6c7..864320ad87eac4f6fc269b5ff57c610ac7fb5344 100644
--- a/tlatools/src/tla2sany/semantic/SymbolTable.java
+++ b/tlatools/src/tla2sany/semantic/SymbolTable.java
@@ -88,14 +88,15 @@ public class SymbolTable implements ASTConstants {
   * Looks up `name' in the symbol table and returns the node it finds, or  *
   * null if there is no entry for `name'.                                  *
   *************************************************************************/
-  public final SymbolNode resolveSymbol(UniqueString name) {
-    for (int c = contextStack.size()-1; c >= 0; c-- ) {
-      Context ct = (Context)contextStack.elementAt(c);
-      SymbolNode r = ct.getSymbol( name );
-      if (r != null) return r;
-    }
-    return null;
-  }
+	public final SymbolNode resolveSymbol(UniqueString name) {
+		for (int c = contextStack.size() - 1; c >= 0; c--) {
+			Context ct = (Context) contextStack.elementAt(c);
+			SymbolNode r = ct.getSymbol(name);
+			if (r != null)
+				return r;
+		}
+		return null;
+	}
 
   public final ModuleNode resolveModule(UniqueString name) {
     ModuleName modName = new ModuleName(name);
diff --git a/tlatools/src/tla2sany/semantic/TheoremNode.java b/tlatools/src/tla2sany/semantic/TheoremNode.java
index ded540740327eb836f937973f7adbfc3c4ae27eb..7b07e72372603313e677ad6f34bac6b7e69c2c92 100644
--- a/tlatools/src/tla2sany/semantic/TheoremNode.java
+++ b/tlatools/src/tla2sany/semantic/TheoremNode.java
@@ -15,13 +15,17 @@ package tla2sany.semantic;
 
 import java.util.Hashtable;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
 import tla2sany.st.TreeNode;
 import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 /**
  * This class represents a theorem
  */
@@ -70,6 +74,10 @@ public class TheoremNode extends LevelNode {
     this.def = opd;
     this.proof = pf;
     if (opd != null) opd.thmOrAssump = this;
+
+    // make sure that definition and statemtent agree
+    if (def != null)
+      assert(def.getBody() == theoremExprOrAssumeProve);
   }
 
   /* Returns the statement of the theorem  */
@@ -107,7 +115,8 @@ public class TheoremNode extends LevelNode {
   /* (non-Javadoc)
  * @see tla2sany.semantic.LevelNode#levelCheck(int)
  */
-public final boolean levelCheck(int iter) {
+  @Override
+  public final boolean levelCheck(int iter) {
     if (levelChecked >= iter) {return true ;} ;
     levelChecked = iter;
     LevelNode sub[] ;
@@ -313,6 +322,7 @@ public final boolean levelCheck(int iter) {
    * toString, levelDataToString, and walkGraph methods to implement
    * ExploreNode interface
    */
+  @Override
   public final String levelDataToString() {
     return "Level: "               + this.getLevel()               + "\n" +
            "LevelParameters: "     + this.getLevelParams()         + "\n" +
@@ -321,6 +331,7 @@ public final boolean levelCheck(int iter) {
            "ArgLevelParams: "      + this.getArgLevelParams()      + "\n";
   }
 
+  @Override
   public final String toString(int depth) {
     if (depth <= 0) return "";
     String res =
@@ -353,6 +364,7 @@ public final boolean levelCheck(int iter) {
    * The children are the statement and the proof (if there is one).
    */
 
+  @Override
   public SemanticNode[] getChildren() {
     if (this.proof == null) {
     return new SemanticNode[] {this.theoremExprOrAssumeProve};
@@ -361,30 +373,90 @@ public final boolean levelCheck(int iter) {
                                this.proof};
   }
 
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
     if (semNodesTable.get(uid) != null) return;
     semNodesTable.put(uid, this);
+    visitor.preVisit(this);
     if (theoremExprOrAssumeProve != null)
-      {theoremExprOrAssumeProve.walkGraph(semNodesTable);} ;
-    if (proof != null) {proof.walkGraph(semNodesTable);} ;
+      {theoremExprOrAssumeProve.walkGraph(semNodesTable, visitor);} ;
+    if (proof != null) {proof.walkGraph(semNodesTable, visitor);} ;
+    visitor.postVisit(this);
   }
 
+  /* MR: this does not do anything
   public Element export(Document doc, tla2sany.xml.SymbolContext context) {
-    if (getDef() == null)
-      // we export the definition of the theorem
-      return super.export(doc,context);
-    else
-      // we export its name only, named theorem will be exported through the ThmOrAss..
-      return getDef().export(doc,context);
+    Element e = super.export(doc, context);
+    return e;
+  }
+  */
+
+  /* MR: This is the same as SymbolNode.exportDefinition. Exports the actual theorem content, not only a reference.
+   */
+  public Element exportDefinition(Document doc, SymbolContext context) {
+    //makes sure that the we are creating an entry in the database
+    if (!context.isTop_level_entry())
+      throw new IllegalArgumentException("Exporting theorem ref "+getNodeRef()+" twice!");
+    context.resetTop_level_entry();
+
+    try {
+      Element e = getLevelElement(doc, context);
+      // level
+      try {
+        Element l = appendText(doc,"level",Integer.toString(getLevel()));
+        e.insertBefore(l,e.getFirstChild());
+      } catch (RuntimeException ee) {
+        // not sure it is legal for a LevelNode not to have level, debug it!
+      }
+      //location
+      try {
+        Element loc = getLocationElement(doc);
+        e.insertBefore(loc,e.getFirstChild());
+      } catch (RuntimeException ee) {
+        // do nothing if no location
+      }
+      return e;
+    } catch (RuntimeException ee) {
+      System.err.println("failed for node.toString(): " + toString() + "\n with error ");
+      ee.printStackTrace();
+      throw ee;
+    }
+  }
+
+  protected String getNodeRef() {
+    return "TheoremNodeRef";
   }
 
   protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
     Element e = doc.createElement("TheoremNode");
-    e.appendChild(getTheorem().export(doc,context));
+
+    //the theorem name is now contained in the definition, if it exists
+    Node n = doc.createElement("body");
+    if (def != null) {
+      //if there is a definition, export it too
+      Node d = doc.createElement("definition");
+      d.appendChild(def.export(doc, context));
+      e.appendChild(d);
+      assert( def.getBody() == getTheorem() ); //make sure theorem and definition body agree before export
+    }
+
+    n.appendChild(getTheorem().export(doc,context));
+    e.appendChild( n );
+
     if (getProof() != null)  e.appendChild(getProof().export(doc,context));
     if (isSuffices()) e.appendChild(doc.createElement("suffices"));
     return e;
   }
+
+  /* overrides LevelNode.export and exports a UID reference instad of the full version*/
+  @Override
+  public Element export(Document doc, SymbolContext context) {
+    // first add symbol to context
+    context.put(this, doc);
+    Element e = doc.createElement(getNodeRef());
+    e.appendChild(appendText(doc,"UID",Integer.toString(myUID)));
+    return e;
+  }
 }
 
diff --git a/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java b/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java
index 1079a1216b60aba137083fd0ff2b73daa60b6ab4..3e64c84fc28e76e9371ddc7ff9b9e6a8fa8e0bd0 100644
--- a/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java
+++ b/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java
@@ -1,647 +1,676 @@
-// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
-
-// last modified on Thu  2 July 2009 at 15:44:33 PST by lamport
-/***************************************************************************
-* The ThmOrAssumpDefNode constructor is invoked in                         *
-* semantic/Generator.java to construct the nodes corresponding to the      *
-* definition in something like                                             *
-*                                                                          *
-*   THEOREM Foo == ...                                                     *
-*                                                                          *
-* The following are the only public methods that a tool is likely to       *
-* call.  (Search for the method to find an explanation of what it does.)   *
-*                                                                          *
-*    LevelNode getBody()                                                   *
-*    ModuleNode getOriginallyDefinedInModuleNode()                         *
-*    boolean  isTheorem()                                                  *
-*    boolean isSuffices()                                                  *
-*    ProofNode getProof()                                                  *
-*    FormalParamNode[] getParams()                                         *
-*    ModuleNode getInstantiatedFrom()                                      *
-***************************************************************************/
-
-package tla2sany.semantic;
-
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Iterator;
-
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-import tla2sany.utilities.Vector;
-import util.UniqueString;
-import util.WrongInvocationException;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/***************************************************************************
-* This node represents the definition of Foo in                            *
-*                                                                          *
-*    THEOREM Foo == ...                                                    *
-*    ASSUME  Foo == ...                                                    *
-***************************************************************************/
-
-public class ThmOrAssumpDefNode extends SymbolNode
-         implements OpDefOrLabelNode, AnyDefNode {
-
-  // T.L. 2014 - the actual theorem or assumption associated with this def
-  // this is required in order to simplify presentation. After all normalizations
-  // we dont really need this def any more
-  // this field is being set by TheoremNode or AssumeNode
-  protected LevelNode thmOrAssump  = null;
-  private LevelNode body        = null;
-  private ModuleNode originallyDefinedInModule = null;
-  private boolean theorem       = true;
-    /***********************************************************************
-    * True if a theorem, false if an assumption.                           *
-    ***********************************************************************/
-  private boolean suffices = false ;
-    /***********************************************************************
-    * If this represents a theorem, then this is the value of the          *
-    * TheoremNode's suffices field (which is false unless the TheoremNode  *
-    * represents a SUFFICES proof step).  This is a kludge added for the   *
-    * following reason.  To check if a reference to a subexpression of an  *
-    * ASSUME/PROVE is legal, we need to know whether or not the            *
-    * ASSUME/PROVE lies within a SUFFICES step.  However, the reference    *
-    * accesses the ThmOrAssumpDefNode, while it's the TheoremNode that     *
-    * contains the suffices field.  It might seem sensible to have the     *
-    * ThmOrAssumpDefNode contain a pointer to the TheoremNode.  However,   *
-    * that's problematic.  For one thing, instantiation creates a new      *
-    * ThmOrAssumpDefNode for the imported definition, but does not create  *
-    * a new TheoremNode.  So, we copy just the suffices field (which will  *
-    * be false for an instantiated node because proof steps don't get      *
-    * instantiated).                                                       *
-    *                                                                      *
-    * This field is set by setSuffices() and read by isSuffices() ;        *
-    ***********************************************************************/
-
-
-  /*************************************************************************
-  * The following field is used to implement the OpDefOrLabel interface.   *
-  * It is a hashtable of OpDefNode objects representing labels within      *
-  * the body that are not within the scope of an inner label or LET        *
-  * definition.                                                            *
-  *************************************************************************/
-  private Hashtable labels = null ;
-  public Hashtable  getLabelsHT() {
-      /***********************************************************************
-      * Return the labels field.  Used to "clone" an OpDefNode for module    *
-      * instantiation.                                                       *
-      ***********************************************************************/
-      return labels ;
-     }
-
-  public int arity = 0 ;
-  private FormalParamNode[] params = new FormalParamNode[0];
-    /***********************************************************************
-    * A theorem or assumption definition like                              *
-    *                                                                      *
-    *   THEOREM foo == ...                                                 *
-    *                                                                      *
-    * has no parameters.  However, it can acquire parameters when          *
-    * the module it's in is instantiated.                                  *
-    ***********************************************************************/
-   ProofNode proof;
-     /**********************************************************************
-     * The proof, if there is one; else null.  Obviously, only a theorem   *
-     * can have a proof.                                                   *
-     **********************************************************************/
-
-  private ModuleNode instantiatedFrom = null ;
-    /***********************************************************************
-    * If the theorem or assumption was obtained by instantiation, then     *
-    * this is the module from which it was instantiated.                   *
-    ***********************************************************************/
-
-  /**
-   *  The source field and its methods was added by LL on 15 June 2010.
-   *  I don't know why it wasn't added to this class when it was added
-   *  to OpDefNode.  However, it's needed by the Toolbox, so it's being
-   *  added now by trying to duplicate what was done for OpDefNode
-   */
-  private ThmOrAssumpDefNode source = null;
-
-  public ThmOrAssumpDefNode getSource() {
-      if (source == null) {
-          return this;
-      }
-      return source;
-  }
-
-  /*************************************************************************
-  * The Constructor.                                                      *
-  *************************************************************************/
-  public ThmOrAssumpDefNode(
-           UniqueString name,       // The thm/assump name.
-           boolean      thm,        // true if a theorem, false if an assump.
-           LevelNode    exp,        // The body
-           ModuleNode   oModNode,   // Name of module.
-           SymbolTable  st,         // The current value of
-                                    // Generator.symbolTable
-           TreeNode stn,            // The syntax node.
-           FormalParamNode[] parms, // The parameters, or null if none.
-           ModuleNode   iFrom,      // The instantiatedFrom field.
-           ThmOrAssumpDefNode src   // The source field.
-                           ) {
-    super(ThmOrAssumpDefKind, stn, name) ;
-    this.theorem    = thm;
-    this.body       = exp;
-    this.originallyDefinedInModule = oModNode;
-      /*********************************************************************
-      * Note that when this theorem or assumption is instantiated with a   *
-      * parameter substitution, then the newly created ThmOrAssumpDefNode  *
-      * has this field set to the instantiating module.  Thus, this field  *
-      * is not useful to track down the module of origin of a theorem.     *
-      *********************************************************************/
-    // On 1 Nov 2012,, LL moved the following two statements to here from
-    // the end of the constructor.  It's necessary for the source field to be
-    // set before addSymbol is called.
-    this.instantiatedFrom = iFrom ;
-    this.source = src;
-
-    if (st != null) {st.addSymbol(name, this);} ;
-      /*********************************************************************
-      * By some magic, this eventually puts the name into the current      *
-      * module's context.                                                  *
-      *********************************************************************/
-    if (parms != null) {
-      this.params = parms;
-      this.arity = parms.length;
-     } ;
-  }
-
-  /*************************************************************************
-  * A place-holder constructor and a method for setting the remaining      *
-  * fields.  It is used in Generator.processTheorem, where the object      *
-  * needs to be created before its body is generated.  This method is      *
-  * not used when created a ThmOrAssumpDefNode by instantiation, so        *
-  * getInstantitedFrom will be null for this object.                       *
-  *************************************************************************/
-  public ThmOrAssumpDefNode(UniqueString name, TreeNode stn) {
-    super(ThmOrAssumpDefKind, stn, name) ;
-   }
-
-  public void construct(
-           boolean      thm,       // true if a theorem, false if an assump.
-           LevelNode    exp,       // The body
-           ModuleNode   oModNode,  // Name of module.
-           SymbolTable  st,        // The current value of
-                                   // Generator.symbolTable
-           FormalParamNode[] parms // The parameters, or null if none.
-                           ) {
-    this.theorem    = thm;
-    this.body       = exp;
-    this.originallyDefinedInModule = oModNode;
-    if (st != null) {st.addSymbol(name, this);} ;
-      /*********************************************************************
-      * By some magic, this eventually puts the name into the current      *
-      * module's context.                                                  *
-      *********************************************************************/
-    if (parms != null) {
-      this.params = parms;
-      this.arity = parms.length;
-     } ;
-  }
-
-  /*************************************************************************
-  * The methods.                                                           *
-  *************************************************************************/
-  public LevelNode getBody() {return this.body; } ;
-    /***********************************************************************
-    * Note: this is a LevelNode rather than an ExprNode because it could   *
-    * be either an ExprNode, AssumeProve node, or an APSubstInNode whose   *
-    * body is either an ExprNode or an AssumeProve node.  To check if a    *
-    * node N is an ExprNode, use the test                                  *
-    *                                                                      *
-    *    N instanceof ExprNode                                             *
-    ***********************************************************************/
-
-  public ModuleNode getOriginallyDefinedInModuleNode()
-    /***********************************************************************
-    * This is the module for which the node was created, which for a       *
-    * theorem or assumption instantiated with substitution is not the      *
-    * module from which the theorem or assumption came.                    *
-    ***********************************************************************/
-    { return originallyDefinedInModule; }
-
-  public ModuleNode getInstantiatedFrom() { return this.instantiatedFrom ; }
-    /***********************************************************************
-    * If the theorem or assumption was obtained by instantiation, then     *
-    * this is the module from which it was instantiated.  Otherwise, it    *
-    * is null.                                                             *
-    ***********************************************************************/
-
-  public boolean  isTheorem()  {return this.theorem ;}
-  public boolean isSuffices()  {return this.suffices ;};
-         void    setSuffices() {this.suffices = true;};
-
-  /*************************************************************************
-  * Return the proof of the theorem, which is null unless this is a        *
-  * theorem and it has a proof.                                            *
-  *************************************************************************/
-  public final ProofNode getProof() {return this.proof;}
-
-  public final FormalParamNode[] getParams() { return this.params; }
-
-  public final boolean isExpr() { return this.body instanceof ExprNode; }
-    /***********************************************************************
-    * Returns true iff the body is an ExprNode (rather than an             *
-    * AssumeProveNode or APSubstInNode).                                   *
-    ***********************************************************************/
-
-
-  /*************************************************************************
-  * Implementations of the abstract methods of the SymbolNode superclass.  *
-  *************************************************************************/
-  public final int getArity() { return this.arity;}
-    /***********************************************************************
-    * The name of a theorem or assumption has no parameters.               *
-    ***********************************************************************/
-
-  // Modified by LL on 30 Oct 2012 to handle locally instantiated theorems
-  // and assumptions.
-  private boolean local = false ;
-  public final boolean isLocal() { return local; }
-    /***********************************************************************
-    * Theorem and assumption definitions are local iff imported with a     *
-    * LOCAL instance.                                                      *
-    ***********************************************************************/
-  public final void setLocal(boolean localness) {
-      local = localness ;
-  }
-
-//  public final ModuleNode getModuleNode() { return this.moduleNode; }
-
-  public final boolean match( OpApplNode test, ModuleNode mn ) {
-    /***********************************************************************
-    * True iff the current object has the same arity as the node operator  *
-    * of the OpApplNode test.                                              *
-    ***********************************************************************/
-    SymbolNode odn = test.getOperator();
-    return odn.getArity() == 0;
-  }
-
-
-  /*************************************************************************
-  * The following methods implement the OpDefOrLabel interface.            *
-  *                                                                        *
-  * These are the same as the other classes that implement the interface.  *
-  * There doesn't seem to be any easy way to write these methods only      *
-  * once.                                                                  *
-  *************************************************************************/
-  public void setLabels(Hashtable ht) {labels = ht; }
-    /***********************************************************************
-    * Sets the set of labels.                                              *
-    ***********************************************************************/
-
-  public LabelNode getLabel(UniqueString us) {
-    /***********************************************************************
-    * If the hashtable `labels' contains a LabelNode with name `us',       *
-    * then that LabelNode is returned; otherwise null is returned.         *
-    ***********************************************************************/
-    if (labels == null) {return null;} ;
-    return (LabelNode) labels.get(us) ;
-   }
-
-  public boolean addLabel(LabelNode odn) {
-    /***********************************************************************
-    * If the hashtable `labels' contains no OpDefNode with the same name   *
-    * as odn, then odn is added to the set and true is return; else the    *
-    * set is unchanged and false is returned.                              *
-    ***********************************************************************/
-    if (labels == null) {labels = new Hashtable(); } ;
-    if (labels.containsKey(odn)) {return false ;} ;
-    labels.put(odn.getName(), odn) ;
-    return true;
-   }
-
-  public LabelNode[] getLabels() {
-    /***********************************************************************
-    * Returns an array containing the Label objects in the hashtable       *
-    * `labels'.                                                            *
-    ***********************************************************************/
-    if (labels == null) {return new LabelNode[0];} ;
-    Vector v = new Vector() ;
-    Enumeration e = labels.elements() ;
-    while (e.hasMoreElements()) { v.addElement(e.nextElement()); } ;
-    LabelNode[] retVal = new LabelNode[v.size()] ;
-    for (int i = 0 ; i < v.size() ; i++)
-      {retVal[i] = (LabelNode) v.elementAt(i); } ;
-    return retVal ;
-   }
-
-// On 24 Oct 2012 replaced the following levelCheck method with the current
-// one, which was obtained by cloning the method from OpDefNode with some
-// simplifications that were possible because a theorem or assumption, unlike
-// and ordinary definition, cannot appear in the scope of a RECURSIVE declaration.
-// He also made the ThmOrAssumpDefNode implement the AnyNode interface.
-// See the comments in AnyDefNode.java for an explanation of why.
-
-//  /*************************************************************************
-//  * Level checking.                                                        *
-//  *************************************************************************/
-//  public boolean levelCheck(int iter) {
-//    if (levelChecked >= iter) {return levelCorrect;} ;
-//    levelChecked = iter ;
-//    levelCorrect        = this.body.levelCheck(iter) ;
-//    level               = this.body.level ;
-//    levelParams         = this.body.levelParams ;
-//    allParams           = this.body.allParams ;
-//    levelConstraints    = this.body.levelConstraints ;
-//    argLevelConstraints = this.body.argLevelConstraints ;
-//    argLevelParams      = this.body.argLevelParams ;
-//    return levelCorrect ;
-//   }
-//
-////  /*************************************************************************
-////  * The implementation of the LevelNode abstract methods.  They simply     *
-////  * return the corresponding values for the body.                          *
-////  *************************************************************************/
-////  public final LevelNode getBody()  {return this.body;}
-////  public boolean levelCheck()       {return this.body.levelCheck();}
-////  public int getLevel()             {return this.body.getLevel();}
-////  public HashSet getLevelParams()   {return this.body.getLevelParams();}
-////  public SetOfLevelConstraints getLevelConstraints()
-////    {return this.body.getLevelConstraints() ;}
-////  public SetOfArgLevelConstraints getArgLevelConstraints()
-////    {return this.body.getArgLevelConstraints() ; }
-////  public HashSet getArgLevelParams()
-////    {return this.body.getArgLevelParams() ; }
-
-  /*************************************************************************
-  * The fields used for level checking.                                    *
-  *************************************************************************/
-// These fields are now present in all LevelNode subclasses
-//  private boolean levelCorrect;
-//  private int level;
-//  private HashSet levelParams;
-//  private SetOfLevelConstraints levelConstraints;
-//  private SetOfArgLevelConstraints argLevelConstraints;
-//  private HashSet argLevelParams;
-
-
-  int[] maxLevels;
-  int[] weights;
-  int[][] minMaxLevel;
-    /***********************************************************************
-    * According to LevelSpec.tla, if this is the OpDefNode for the         *
-    * definition of op, then op.minMaxLevel[i][k] is the minimum value of  *
-    * oparg.maxLevels[k] for the i-th argument of Op.  Thus,               *
-    * op.minMaxLevels[i] is a sequence whose length is the number of       *
-    * arguments taken by the i-th argument of Op.  (It equals 0 if the     *
-    * i-th argument of Op is not an operator argument.)                    *
-    ***********************************************************************/
-
-  boolean[] isLeibnizArg;
-  boolean   isLeibniz;
-    /***********************************************************************
-    * If this is the OpDefNode for the definition of op, then              *
-    * isLeibnizArg[i] is true iff the i-th argument of op is Leibniz, and  *
-    * isLeibniz = \A i : isLeibnizArg[i]                                   *
-    ***********************************************************************/
-  public boolean[] getIsLeibnizArg() {
-      return isLeibnizArg;
-  }
-  public boolean getIsLeibniz() {
-      return isLeibniz;
-  }
-  private boolean[][][] opLevelCond;
-    /***********************************************************************
-    * According to LevelSpec.tla, if thisnode defines op,                  *
-    * then op.opLevelCond[i][j][k] is true iff                             *
-    * the i-th argument of op is an operator argument opArg, and the       *
-    * definition of op contains an expression in which the j-th formal     *
-    * parameter of the definition of op appears within the k-th argument   *
-    * of opArg.                                                            *
-    ***********************************************************************/
-  public final boolean levelCheck(int itr) {
-      if (this.levelChecked >= itr) { return this.levelCorrect; }
-      this.levelChecked = itr ;
-
-      /***********************************************************************
-      * Initialize maxLevels to the least restrictive value and weights to 0.*
-      * Initialize isLeibniz and all isLeibniz[i] to true.                   *
-      ***********************************************************************/
-      this.maxLevels    = new int[this.params.length];
-      this.weights      = new int[this.params.length];
-      for (int i = 0 ; i < this.params.length ; i++) {
-          this.maxLevels[i] = MaxLevel ;
-          this.weights[i] = 0 ;
-          this.isLeibniz    = true;
-          this.isLeibnizArg = new boolean[this.params.length];
-          this.isLeibnizArg[i] = true ;
-        } ;
-
-
-   // Level check the body:
-      this.levelCorrect = this.body.levelCheck(itr);
-      this.level = this.body.getLevel();
-
-      SetOfLevelConstraints lcSet = this.body.getLevelConstraints();
-      for (int i = 0; i < this.params.length; i++) {
-         Object plevel = lcSet.get(params[i]);
-         if (plevel != null) {
-             this.maxLevels[i] = ((Integer)plevel).intValue();
-           }
-      }
-
-      for (int i = 0; i < this.params.length; i++) {
-          if (this.body.getLevelParams().contains(this.params[i])) {
-            this.weights[i] = this.weights[i];
-          }
-        }
-
-      this.minMaxLevel = new int[this.params.length][];
-      SetOfArgLevelConstraints alcSet = this.body.getArgLevelConstraints();
-      for (int i = 0; i < this.params.length; i++) {
-        int alen = this.params[i].getArity();
-        this.minMaxLevel[i] = new int[alen];
-        for (int j = 0; j < alen; j++) {
-          Object alevel = alcSet.get(new ParamAndPosition(this.params[i], j));
-          if (alevel == null) {
-            this.minMaxLevel[i][j] = MinLevel;
-          }
-          else {
-            this.minMaxLevel[i][j] = ((Integer)alevel).intValue();
-          }
-        }
-      }
-
-      this.opLevelCond = new boolean[this.params.length][this.params.length][];
-      HashSet alpSet = this.body.getArgLevelParams();
-      for (int i = 0; i < this.params.length; i++) {
-        for (int j = 0; j < this.params.length; j++) {
-          this.opLevelCond[i][j] = new boolean[this.params[i].getArity()];
-          for (int k = 0; k < this.params[i].getArity(); k++) {
-            ArgLevelParam alp = new ArgLevelParam(this.params[i], k, this.params[j]);
-            this.opLevelCond[i][j][k] = alpSet.contains(alp);
-          }
-        }
-      }
-
-      this.levelParams.addAll(this.body.getLevelParams());
-      this.allParams.addAll(this.body.getAllParams());
-      this.nonLeibnizParams.addAll(this.body.getNonLeibnizParams());
-      for (int i = 0; i < this.params.length; i++) {
-        this.levelParams.remove(this.params[i]);
-        this.allParams.remove(this.params[i]);
-        if (this.nonLeibnizParams.contains(this.params[i])) {
-          /*******************************************************************
-          * The i-th argument is non-Leibniz if this.params[i] is in the     *
-          * body's nonLeibnizParams hashset (and hence now in this node's    *
-          * nonLeibnizParams hashset.                                        *
-          *******************************************************************/
-          this.nonLeibnizParams.remove(this.params[i]) ;
-          this.isLeibnizArg[i] = false ;
-          this.isLeibniz = false ;
-         } ;
-      }
-
-      this.levelConstraints = (SetOfLevelConstraints)lcSet.clone();
-      for (int i = 0; i < this.params.length; i++) {
-        this.levelConstraints.remove(this.params[i]);
-      }
-
-      this.argLevelConstraints = (SetOfArgLevelConstraints)alcSet.clone();
-      for (int i = 0; i < this.params.length; i++) {
-        int alen = this.params[i].getArity();
-        for (int j = 0; j < alen; j++) {
-          this.argLevelConstraints.remove(new ParamAndPosition(this.params[i], j));
-        }
-      }
-
-      Iterator iter = alpSet.iterator();
-      while (iter.hasNext()) {
-        ArgLevelParam alp = (ArgLevelParam)iter.next();
-        if (!alp.op.occur(this.params) ||
-            !alp.param.occur(this.params)) {
-          this.argLevelParams.add(alp);
-        }
-      }
-
-      return levelCorrect ;
-  }
-
-  /***************************************************************************
-  * The following Asserts can be removed after debugging.                    *
-  ***************************************************************************/
-    public final int getMaxLevel(int i) {
-      if (this.levelChecked == 0)
-        {throw new WrongInvocationException("getMaxLevel called before levelCheck");};
-      int idx = (this.getArity() == -1) ? 0 : i;
-      return this.maxLevels[idx];
-    }
-
-    public final int getWeight(int i) {
-      if (this.levelChecked == 0)
-        {throw new WrongInvocationException("getWeight called before levelCheck");};
-      int idx = (this.getArity() == -1) ? 0 : i;
-      return this.weights[idx];
-    }
-
-    public final int getMinMaxLevel(int i, int j) {
-      if (this.levelChecked == 0)
-        {throw new WrongInvocationException("getMinMaxLevel called before levelCheck");};
-      if (this.minMaxLevel == null) {
-        return ConstantLevel;
-      }
-      return this.minMaxLevel[i][j];
-    }
-
-    public final boolean getOpLevelCond(int i, int j, int k) {
-      if (this.levelChecked == 0)
-        {throw new WrongInvocationException("getOpLevelCond called before levelCheck");};
-      if (this.opLevelCond == null) {
-        return false;
-      }
-      return this.opLevelCond[i][j][k];
-    }
-
-  /**
-   * toString, levelDataToString and walkGraph methods to implement
-   * ExploreNode interface
-   */
-//  public final String levelDataToString() {
-//    return this.body.levelDataToString();
-//   }
-
-
-
-  /**
-   *  The body is the node's only child.
-   */
-  public SemanticNode[] getChildren() {
-    return new SemanticNode[] {this.body};
-  }
-
-  public final void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
-    if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
-    if(this.body != null) {this.body.walkGraph(semNodesTable) ;} ;
-   }
-
-  public final String toString(int depth) {
-    if (depth <= 0) return "";
-    String ret =
-          "\n*ThmOrAssumpDefNode: " + this.getName().toString() +
-            "  " + super.toString(depth) +
-            " arity: " + this.arity +
-            " module: " + (originallyDefinedInModule != null
-                             ? originallyDefinedInModule.getName().toString()
-                             : "<null>" ) ;
-    if (instantiatedFrom != null) {ret += " instantiatedFrom: " +
-                                          instantiatedFrom.getName() ; } ;
-    if (params != null) {
-      String tempString = "\nFormal params: " + params.length;
-      for (int i = 0; i < params.length; i++) {
-        tempString += Strings.indent(2, ((params[i] != null)
-                                        ? params[i].toString(depth-1)
-                                         : "\nnull"));
-        } ;
-      ret += Strings.indent(2,tempString);
-     } ;
-    if (body != null) {
-        ret += Strings.indent(2,
-                             "\nisTheorem(): " + theorem +
-                             "\nBody:" +
-                              Strings.indent(2, this.body.toString(depth-1)) +
-                             "\nsuffices: " + this.isSuffices());
-      } // if
-    /***********************************************************************
-    * The following is the same for all classes that implement the         *
-    * OpDefOrLabelNode interface.                                          *
-    ***********************************************************************/
-    if (labels != null) {
-       ret += "\n  Labels: " ;
-       Enumeration list = labels.keys() ;
-       while (list.hasMoreElements()) {
-          ret += ((UniqueString) list.nextElement()).toString() + "  " ;
-         } ;
-      }
-    else {ret += "\n  Labels: null";};
-
-    return ret ;
-   }
-
-  /**
-   * most of the information in this class is a duplication of the information
-   * in the TheoremNode.
-   *
-   * We care to export only the name of the theorem (CHECK what hppens when instantiated).
-   */
-  protected String getNodeRef() {
-    if (theorem)
-      return "TheoremNodeRef";
-    else
-      return "AssumeNodeRef";
-  }
-
-  protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) {
-    // since this element doesnt seem to contain any additional information
-    // over theorems or assumptions, we just refer to them
-    return thmOrAssump.getLevelElement(doc,context);
-  }
-}
+// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
+
+// last modified on Thu  2 July 2009 at 15:44:33 PST by lamport
+/***************************************************************************
+* The ThmOrAssumpDefNode constructor is invoked in                         *
+* semantic/Generator.java to construct the nodes corresponding to the      *
+* definition in something like                                             *
+*                                                                          *
+*   THEOREM Foo == ...                                                     *
+*                                                                          *
+* The following are the only public methods that a tool is likely to       *
+* call.  (Search for the method to find an explanation of what it does.)   *
+*                                                                          *
+*    LevelNode getBody()                                                   *
+*    ModuleNode getOriginallyDefinedInModuleNode()                         *
+*    boolean  isTheorem()                                                  *
+*    boolean isSuffices()                                                  *
+*    ProofNode getProof()                                                  *
+*    FormalParamNode[] getParams()                                         *
+*    ModuleNode getInstantiatedFrom()                                      *
+***************************************************************************/
+
+package tla2sany.semantic;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.utilities.Vector;
+import tla2sany.xml.SymbolContext;
+import util.UniqueString;
+import util.WrongInvocationException;
+
+/***************************************************************************
+* This node represents the definition of Foo in                            *
+*                                                                          *
+*    THEOREM Foo == ...                                                    *
+*    ASSUME  Foo == ...                                                    *
+***************************************************************************/
+
+public class ThmOrAssumpDefNode extends SymbolNode
+         implements OpDefOrLabelNode, AnyDefNode {
+
+  // T.L. 2014 - the actual theorem or assumption associated with this def
+  // this is required in order to simplify presentation. After all normalizations
+  // we dont really need this def any more
+  // this field is being set by TheoremNode or AssumeNode
+  protected LevelNode thmOrAssump  = null;
+  private LevelNode body        = null;
+  private ModuleNode originallyDefinedInModule = null;
+  private boolean theorem       = true;
+    /***********************************************************************
+    * True if a theorem, false if an assumption.                           *
+    ***********************************************************************/
+  private boolean suffices = false ;
+    /***********************************************************************
+    * If this represents a theorem, then this is the value of the          *
+    * TheoremNode's suffices field (which is false unless the TheoremNode  *
+    * represents a SUFFICES proof step).  This is a kludge added for the   *
+    * following reason.  To check if a reference to a subexpression of an  *
+    * ASSUME/PROVE is legal, we need to know whether or not the            *
+    * ASSUME/PROVE lies within a SUFFICES step.  However, the reference    *
+    * accesses the ThmOrAssumpDefNode, while it's the TheoremNode that     *
+    * contains the suffices field.  It might seem sensible to have the     *
+    * ThmOrAssumpDefNode contain a pointer to the TheoremNode.  However,   *
+    * that's problematic.  For one thing, instantiation creates a new      *
+    * ThmOrAssumpDefNode for the imported definition, but does not create  *
+    * a new TheoremNode.  So, we copy just the suffices field (which will  *
+    * be false for an instantiated node because proof steps don't get      *
+    * instantiated).                                                       *
+    *                                                                      *
+    * This field is set by setSuffices() and read by isSuffices() ;        *
+    ***********************************************************************/
+
+
+  /*************************************************************************
+  * The following field is used to implement the OpDefOrLabel interface.   *
+  * It is a hashtable of OpDefNode objects representing labels within      *
+  * the body that are not within the scope of an inner label or LET        *
+  * definition.                                                            *
+  *************************************************************************/
+  private Hashtable<UniqueString, LabelNode> labels = null ;
+  public Hashtable<UniqueString, LabelNode>  getLabelsHT() {
+      /***********************************************************************
+      * Return the labels field.  Used to "clone" an OpDefNode for module    *
+      * instantiation.                                                       *
+      ***********************************************************************/
+      return labels ;
+     }
+
+  public int arity = 0 ;
+  private FormalParamNode[] params = new FormalParamNode[0];
+    /***********************************************************************
+    * A theorem or assumption definition like                              *
+    *                                                                      *
+    *   THEOREM foo == ...                                                 *
+    *                                                                      *
+    * has no parameters.  However, it can acquire parameters when          *
+    * the module it's in is instantiated.                                  *
+    ***********************************************************************/
+   ProofNode proof;
+     /**********************************************************************
+     * The proof, if there is one; else null.  Obviously, only a theorem   *
+     * can have a proof.                                                   *
+     **********************************************************************/
+
+  private ModuleNode instantiatedFrom = null ;
+    /***********************************************************************
+    * If the theorem or assumption was obtained by instantiation, then     *
+    * this is the module from which it was instantiated.                   *
+    ***********************************************************************/
+
+  /**
+   *  The source field and its methods was added by LL on 15 June 2010.
+   *  I don't know why it wasn't added to this class when it was added
+   *  to OpDefNode.  However, it's needed by the Toolbox, so it's being
+   *  added now by trying to duplicate what was done for OpDefNode
+   */
+  private ThmOrAssumpDefNode source = null;
+
+  public ThmOrAssumpDefNode getSource() {
+      if (source == null) {
+          return this;
+      }
+      return source;
+  }
+
+  /*************************************************************************
+  * The Constructor.                                                      *
+  *************************************************************************/
+  public ThmOrAssumpDefNode(
+           UniqueString name,       // The thm/assump name.
+           boolean      thm,        // true if a theorem, false if an assump.
+           LevelNode    exp,        // The body
+           ModuleNode   oModNode,   // Name of module.
+           SymbolTable  st,         // The current value of
+                                    // Generator.symbolTable
+           TreeNode stn,            // The syntax node.
+           FormalParamNode[] parms, // The parameters, or null if none.
+           ModuleNode   iFrom,      // The instantiatedFrom field.
+           ThmOrAssumpDefNode src   // The source field.
+                           ) {
+    super(ThmOrAssumpDefKind, stn, name) ;
+    this.theorem    = thm;
+    this.body       = exp;
+    this.originallyDefinedInModule = oModNode;
+      /*********************************************************************
+      * Note that when this theorem or assumption is instantiated with a   *
+      * parameter substitution, then the newly created ThmOrAssumpDefNode  *
+      * has this field set to the instantiating module.  Thus, this field  *
+      * is not useful to track down the module of origin of a theorem.     *
+      *********************************************************************/
+    // On 1 Nov 2012,, LL moved the following two statements to here from
+    // the end of the constructor.  It's necessary for the source field to be
+    // set before addSymbol is called.
+    this.instantiatedFrom = iFrom ;
+    this.source = src;
+
+    if (st != null) {st.addSymbol(name, this);} ;
+      /*********************************************************************
+      * By some magic, this eventually puts the name into the current      *
+      * module's context.                                                  *
+      *********************************************************************/
+    if (parms != null) {
+      this.params = parms;
+      this.arity = parms.length;
+     } ;
+  }
+
+  /*************************************************************************
+  * A place-holder constructor and a method for setting the remaining      *
+  * fields.  It is used in Generator.processTheorem, where the object      *
+  * needs to be created before its body is generated.  This method is      *
+  * not used when created a ThmOrAssumpDefNode by instantiation, so        *
+  * getInstantitedFrom will be null for this object.                       *
+  *************************************************************************/
+  public ThmOrAssumpDefNode(UniqueString name, TreeNode stn) {
+    super(ThmOrAssumpDefKind, stn, name) ;
+   }
+
+  public void construct(
+           boolean      thm,       // true if a theorem, false if an assump.
+           LevelNode    exp,       // The body
+           ModuleNode   oModNode,  // Name of module.
+           SymbolTable  st,        // The current value of
+                                   // Generator.symbolTable
+           FormalParamNode[] parms // The parameters, or null if none.
+                           ) {
+    this.theorem    = thm;
+    this.body       = exp;
+    this.originallyDefinedInModule = oModNode;
+    if (st != null) {st.addSymbol(name, this);} ;
+      /*********************************************************************
+      * By some magic, this eventually puts the name into the current      *
+      * module's context.                                                  *
+      *********************************************************************/
+    if (parms != null) {
+      this.params = parms;
+      this.arity = parms.length;
+     } ;
+  }
+
+  /*************************************************************************
+  * The methods.                                                           *
+  *************************************************************************/
+  public LevelNode getBody() {return this.body; } ;
+    /***********************************************************************
+    * Note: this is a LevelNode rather than an ExprNode because it could   *
+    * be either an ExprNode, AssumeProve node, or an APSubstInNode whose   *
+    * body is either an ExprNode or an AssumeProve node.  To check if a    *
+    * node N is an ExprNode, use the test                                  *
+    *                                                                      *
+    *    N instanceof ExprNode                                             *
+    ***********************************************************************/
+
+  public ModuleNode getOriginallyDefinedInModuleNode()
+    /***********************************************************************
+    * This is the module for which the node was created, which for a       *
+    * theorem or assumption instantiated with substitution is not the      *
+    * module from which the theorem or assumption came.                    *
+    ***********************************************************************/
+    { return originallyDefinedInModule; }
+
+  public ModuleNode getInstantiatedFrom() { return this.instantiatedFrom ; }
+    /***********************************************************************
+    * If the theorem or assumption was obtained by instantiation, then     *
+    * this is the module from which it was instantiated.  Otherwise, it    *
+    * is null.                                                             *
+    ***********************************************************************/
+
+  public boolean  isTheorem()  {return this.theorem ;}
+  public boolean isSuffices()  {return this.suffices ;};
+         void    setSuffices() {this.suffices = true;};
+
+  /*************************************************************************
+  * Return the proof of the theorem, which is null unless this is a        *
+  * theorem and it has a proof.                                            *
+  *************************************************************************/
+  public final ProofNode getProof() {return this.proof;}
+
+  public final FormalParamNode[] getParams() { return this.params; }
+
+  public final boolean isExpr() { return this.body instanceof ExprNode; }
+    /***********************************************************************
+    * Returns true iff the body is an ExprNode (rather than an             *
+    * AssumeProveNode or APSubstInNode).                                   *
+    ***********************************************************************/
+
+
+  /*************************************************************************
+  * Implementations of the abstract methods of the SymbolNode superclass.  *
+  *************************************************************************/
+  public final int getArity() { return this.arity;}
+    /***********************************************************************
+    * The name of a theorem or assumption has no parameters.               *
+    ***********************************************************************/
+
+  // Modified by LL on 30 Oct 2012 to handle locally instantiated theorems
+  // and assumptions.
+  private boolean local = false ;
+  public final boolean isLocal() { return local; }
+    /***********************************************************************
+    * Theorem and assumption definitions are local iff imported with a     *
+    * LOCAL instance.                                                      *
+    ***********************************************************************/
+  public final void setLocal(boolean localness) {
+      local = localness ;
+  }
+
+//  public final ModuleNode getModuleNode() { return this.moduleNode; }
+
+  public final boolean match( OpApplNode test, ModuleNode mn ) {
+    /***********************************************************************
+    * True iff the current object has the same arity as the node operator  *
+    * of the OpApplNode test.                                              *
+    ***********************************************************************/
+    SymbolNode odn = test.getOperator();
+    return odn.getArity() == 0;
+  }
+
+
+  /*************************************************************************
+  * The following methods implement the OpDefOrLabel interface.            *
+  *                                                                        *
+  * These are the same as the other classes that implement the interface.  *
+  * There doesn't seem to be any easy way to write these methods only      *
+  * once.                                                                  *
+  *************************************************************************/
+  public void setLabels(Hashtable<UniqueString, LabelNode> ht) {labels = ht; }
+    /***********************************************************************
+    * Sets the set of labels.                                              *
+    ***********************************************************************/
+
+  public LabelNode getLabel(UniqueString us) {
+    /***********************************************************************
+    * If the hashtable `labels' contains a LabelNode with name `us',       *
+    * then that LabelNode is returned; otherwise null is returned.         *
+    ***********************************************************************/
+    if (labels == null) {return null;} ;
+    return (LabelNode) labels.get(us) ;
+   }
+
+  public boolean addLabel(LabelNode odn) {
+    /***********************************************************************
+    * If the hashtable `labels' contains no OpDefNode with the same name   *
+    * as odn, then odn is added to the set and true is return; else the    *
+    * set is unchanged and false is returned.                              *
+    ***********************************************************************/
+    if (labels == null) {labels = new Hashtable<>(); } ;
+    if (labels.containsKey(odn.getName())) {return false ;} ;
+    labels.put(odn.getName(), odn) ;
+    return true;
+   }
+
+  public LabelNode[] getLabels() {
+    /***********************************************************************
+    * Returns an array containing the Label objects in the hashtable       *
+    * `labels'.                                                            *
+    ***********************************************************************/
+    if (labels == null) {return new LabelNode[0];} ;
+    Vector<LabelNode> v = new Vector<>() ;
+    Enumeration<LabelNode> e = labels.elements() ;
+    while (e.hasMoreElements()) { v.addElement(e.nextElement()); } ;
+    LabelNode[] retVal = new LabelNode[v.size()] ;
+    for (int i = 0 ; i < v.size() ; i++)
+      {retVal[i] = v.elementAt(i); } ;
+    return retVal ;
+   }
+
+// On 24 Oct 2012 replaced the following levelCheck method with the current
+// one, which was obtained by cloning the method from OpDefNode with some
+// simplifications that were possible because a theorem or assumption, unlike
+// and ordinary definition, cannot appear in the scope of a RECURSIVE declaration.
+// He also made the ThmOrAssumpDefNode implement the AnyNode interface.
+// See the comments in AnyDefNode.java for an explanation of why.
+
+//  /*************************************************************************
+//  * Level checking.                                                        *
+//  *************************************************************************/
+//  public boolean levelCheck(int iter) {
+//    if (levelChecked >= iter) {return levelCorrect;} ;
+//    levelChecked = iter ;
+//    levelCorrect        = this.body.levelCheck(iter) ;
+//    level               = this.body.level ;
+//    levelParams         = this.body.levelParams ;
+//    allParams           = this.body.allParams ;
+//    levelConstraints    = this.body.levelConstraints ;
+//    argLevelConstraints = this.body.argLevelConstraints ;
+//    argLevelParams      = this.body.argLevelParams ;
+//    return levelCorrect ;
+//   }
+//
+////  /*************************************************************************
+////  * The implementation of the LevelNode abstract methods.  They simply     *
+////  * return the corresponding values for the body.                          *
+////  *************************************************************************/
+////  public final LevelNode getBody()  {return this.body;}
+////  public boolean levelCheck()       {return this.body.levelCheck();}
+////  public int getLevel()             {return this.body.getLevel();}
+////  public HashSet getLevelParams()   {return this.body.getLevelParams();}
+////  public SetOfLevelConstraints getLevelConstraints()
+////    {return this.body.getLevelConstraints() ;}
+////  public SetOfArgLevelConstraints getArgLevelConstraints()
+////    {return this.body.getArgLevelConstraints() ; }
+////  public HashSet getArgLevelParams()
+////    {return this.body.getArgLevelParams() ; }
+
+  /*************************************************************************
+  * The fields used for level checking.                                    *
+  *************************************************************************/
+// These fields are now present in all LevelNode subclasses
+//  private boolean levelCorrect;
+//  private int level;
+//  private HashSet levelParams;
+//  private SetOfLevelConstraints levelConstraints;
+//  private SetOfArgLevelConstraints argLevelConstraints;
+//  private HashSet argLevelParams;
+
+
+  int[] maxLevels;
+  int[] weights;
+  int[][] minMaxLevel;
+    /***********************************************************************
+    * According to LevelSpec.tla, if this is the OpDefNode for the         *
+    * definition of op, then op.minMaxLevel[i][k] is the minimum value of  *
+    * oparg.maxLevels[k] for the i-th argument of Op.  Thus,               *
+    * op.minMaxLevels[i] is a sequence whose length is the number of       *
+    * arguments taken by the i-th argument of Op.  (It equals 0 if the     *
+    * i-th argument of Op is not an operator argument.)                    *
+    ***********************************************************************/
+
+  boolean[] isLeibnizArg;
+  boolean   isLeibniz;
+    /***********************************************************************
+    * If this is the OpDefNode for the definition of op, then              *
+    * isLeibnizArg[i] is true iff the i-th argument of op is Leibniz, and  *
+    * isLeibniz = \A i : isLeibnizArg[i]                                   *
+    ***********************************************************************/
+  public boolean[] getIsLeibnizArg() {
+      return isLeibnizArg;
+  }
+  public boolean getIsLeibniz() {
+      return isLeibniz;
+  }
+  private boolean[][][] opLevelCond;
+    /***********************************************************************
+    * According to LevelSpec.tla, if thisnode defines op,                  *
+    * then op.opLevelCond[i][j][k] is true iff                             *
+    * the i-th argument of op is an operator argument opArg, and the       *
+    * definition of op contains an expression in which the j-th formal     *
+    * parameter of the definition of op appears within the k-th argument   *
+    * of opArg.                                                            *
+    ***********************************************************************/
+  @Override
+  public final boolean levelCheck(int itr) {
+      if (this.levelChecked >= itr) { return this.levelCorrect; }
+      this.levelChecked = itr ;
+
+      /***********************************************************************
+      * Initialize maxLevels to the least restrictive value and weights to 0.*
+      * Initialize isLeibniz and all isLeibniz[i] to true.                   *
+      ***********************************************************************/
+      this.maxLevels    = new int[this.params.length];
+      this.weights      = new int[this.params.length];
+      for (int i = 0 ; i < this.params.length ; i++) {
+          this.maxLevels[i] = MaxLevel ;
+          this.weights[i] = 0 ;
+          this.isLeibniz    = true;
+          this.isLeibnizArg = new boolean[this.params.length];
+          this.isLeibnizArg[i] = true ;
+        } ;
+
+
+   // Level check the body:
+      this.levelCorrect = this.body.levelCheck(itr);
+      this.level = this.body.getLevel();
+
+      SetOfLevelConstraints lcSet = this.body.getLevelConstraints();
+      for (int i = 0; i < this.params.length; i++) {
+         Object plevel = lcSet.get(params[i]);
+         if (plevel != null) {
+             this.maxLevels[i] = ((Integer)plevel).intValue();
+           }
+      }
+
+      for (int i = 0; i < this.params.length; i++) {
+          if (this.body.getLevelParams().contains(this.params[i])) {
+            this.weights[i] = this.weights[i];
+          }
+        }
+
+      this.minMaxLevel = new int[this.params.length][];
+      SetOfArgLevelConstraints alcSet = this.body.getArgLevelConstraints();
+      for (int i = 0; i < this.params.length; i++) {
+        int alen = this.params[i].getArity();
+        this.minMaxLevel[i] = new int[alen];
+        for (int j = 0; j < alen; j++) {
+          Object alevel = alcSet.get(new ParamAndPosition(this.params[i], j));
+          if (alevel == null) {
+            this.minMaxLevel[i][j] = MinLevel;
+          }
+          else {
+            this.minMaxLevel[i][j] = ((Integer)alevel).intValue();
+          }
+        }
+      }
+
+      this.opLevelCond = new boolean[this.params.length][this.params.length][];
+      HashSet alpSet = this.body.getArgLevelParams();
+      for (int i = 0; i < this.params.length; i++) {
+        for (int j = 0; j < this.params.length; j++) {
+          this.opLevelCond[i][j] = new boolean[this.params[i].getArity()];
+          for (int k = 0; k < this.params[i].getArity(); k++) {
+            ArgLevelParam alp = new ArgLevelParam(this.params[i], k, this.params[j]);
+            this.opLevelCond[i][j][k] = alpSet.contains(alp);
+          }
+        }
+      }
+
+      this.levelParams.addAll(this.body.getLevelParams());
+      this.allParams.addAll(this.body.getAllParams());
+      this.nonLeibnizParams.addAll(this.body.getNonLeibnizParams());
+      for (int i = 0; i < this.params.length; i++) {
+        this.levelParams.remove(this.params[i]);
+        this.allParams.remove(this.params[i]);
+        if (this.nonLeibnizParams.contains(this.params[i])) {
+          /*******************************************************************
+          * The i-th argument is non-Leibniz if this.params[i] is in the     *
+          * body's nonLeibnizParams hashset (and hence now in this node's    *
+          * nonLeibnizParams hashset.                                        *
+          *******************************************************************/
+          this.nonLeibnizParams.remove(this.params[i]) ;
+          this.isLeibnizArg[i] = false ;
+          this.isLeibniz = false ;
+         } ;
+      }
+
+      this.levelConstraints = (SetOfLevelConstraints)lcSet.clone();
+      for (int i = 0; i < this.params.length; i++) {
+        this.levelConstraints.remove(this.params[i]);
+      }
+
+      this.argLevelConstraints = (SetOfArgLevelConstraints)alcSet.clone();
+      for (int i = 0; i < this.params.length; i++) {
+        int alen = this.params[i].getArity();
+        for (int j = 0; j < alen; j++) {
+          this.argLevelConstraints.remove(new ParamAndPosition(this.params[i], j));
+        }
+      }
+
+      Iterator iter = alpSet.iterator();
+      while (iter.hasNext()) {
+        ArgLevelParam alp = (ArgLevelParam)iter.next();
+        if (!alp.op.occur(this.params) ||
+            !alp.param.occur(this.params)) {
+          this.argLevelParams.add(alp);
+        }
+      }
+
+      return levelCorrect ;
+  }
+
+  /***************************************************************************
+  * The following Asserts can be removed after debugging.                    *
+  ***************************************************************************/
+    public final int getMaxLevel(int i) {
+      if (this.levelChecked == 0)
+        {throw new WrongInvocationException("getMaxLevel called before levelCheck");};
+      int idx = (this.getArity() == -1) ? 0 : i;
+      return this.maxLevels[idx];
+    }
+
+    public final int getWeight(int i) {
+      if (this.levelChecked == 0)
+        {throw new WrongInvocationException("getWeight called before levelCheck");};
+      int idx = (this.getArity() == -1) ? 0 : i;
+      return this.weights[idx];
+    }
+
+    public final int getMinMaxLevel(int i, int j) {
+      if (this.levelChecked == 0)
+        {throw new WrongInvocationException("getMinMaxLevel called before levelCheck");};
+      if (this.minMaxLevel == null) {
+        return ConstantLevel;
+      }
+      return this.minMaxLevel[i][j];
+    }
+
+    public final boolean getOpLevelCond(int i, int j, int k) {
+      if (this.levelChecked == 0)
+        {throw new WrongInvocationException("getOpLevelCond called before levelCheck");};
+      if (this.opLevelCond == null) {
+        return false;
+      }
+      return this.opLevelCond[i][j][k];
+    }
+
+  /**
+   * toString, levelDataToString and walkGraph methods to implement
+   * ExploreNode interface
+   */
+//  public final String levelDataToString() {
+//    return this.body.levelDataToString();
+//   }
+
+
+
+  /**
+   *  The body is the node's only child.
+   */
+  @Override
+  public SemanticNode[] getChildren() {
+    return new SemanticNode[] {this.body};
+  }
+
+  @Override
+  public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
+    if (semNodesTable.get(uid) != null) return;
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    if(this.body != null) {this.body.walkGraph(semNodesTable, visitor) ;} ;
+    visitor.postVisit(this);
+   }
+
+  @Override
+  public final String toString(int depth) {
+    if (depth <= 0) return "";
+    String ret =
+          "\n*ThmOrAssumpDefNode: " + this.getName().toString() +
+            "  " + super.toString(depth) +
+            " arity: " + this.arity +
+            " module: " + (originallyDefinedInModule != null
+                             ? originallyDefinedInModule.getName().toString()
+                             : "<null>" ) ;
+    if (instantiatedFrom != null) {ret += " instantiatedFrom: " +
+                                          instantiatedFrom.getName() ; } ;
+    if (params != null) {
+      String tempString = "\nFormal params: " + params.length;
+      for (int i = 0; i < params.length; i++) {
+        tempString += Strings.indent(2, ((params[i] != null)
+                                        ? params[i].toString(depth-1)
+                                         : "\nnull"));
+        } ;
+      ret += Strings.indent(2,tempString);
+     } ;
+    if (body != null) {
+        ret += Strings.indent(2,
+                             "\nisTheorem(): " + theorem +
+                             "\nBody:" +
+                              Strings.indent(2, this.body.toString(depth-1)) +
+                             "\nsuffices: " + this.isSuffices());
+      } // if
+    /***********************************************************************
+    * The following is the same for all classes that implement the         *
+    * OpDefOrLabelNode interface.                                          *
+    ***********************************************************************/
+    if (labels != null) {
+       ret += "\n  Labels: " ;
+       Enumeration<UniqueString> list = labels.keys() ;
+       while (list.hasMoreElements()) {
+          ret += list.nextElement().toString() + "  " ;
+         } ;
+      }
+    else {ret += "\n  Labels: null";};
+
+    return ret ;
+   }
+
+  /**
+   *
+   */
+  protected String getNodeRef() {
+    if (theorem) {
+      assert(thmOrAssump instanceof TheoremNode);
+      return "TheoremDefRef";
+    }
+    else {
+      assert(thmOrAssump instanceof  AssumeNode);
+      return "AssumeDefRef";
+    }
+  }
+
+  protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) {
+    assert(this.body != null); //A theorem or assumption definition without a body does not make sense.
+    Element e = null;
+    if (theorem) {
+      e = doc.createElement("TheoremDefNode");
+    }
+    else {
+      e = doc.createElement("AssumeDef");
+    }
+
+    e.appendChild(appendText(doc, "uniquename", getName().toString() ));
+    e.appendChild(body.export(doc, context));
+    return e;
+  }
+
+  /* overrides LevelNode.export and exports a UID reference instad of the full version*/
+  @Override
+  public Element export(Document doc, SymbolContext context) {
+    // first add symbol to context
+    context.put(this, doc);
+    Element e = doc.createElement(getNodeRef());
+    e.appendChild(appendText(doc,"UID",Integer.toString(myUID)));
+    return e;
+  }
+}
diff --git a/tlatools/src/tla2sany/semantic/UseOrHideNode.java b/tlatools/src/tla2sany/semantic/UseOrHideNode.java
index 7ed500c0b87ab41a9b85b2866ab65808e094ae02..1aaf34381d3942ed06a7cffeaa614b7f5c0331ec 100644
--- a/tlatools/src/tla2sany/semantic/UseOrHideNode.java
+++ b/tlatools/src/tla2sany/semantic/UseOrHideNode.java
@@ -1,183 +1,193 @@
-// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
-// last modified on Fri  3 July 2009 at 12:41:45 PST by lamport
-package tla2sany.semantic;
-
-import java.util.Hashtable;
-
-import tla2sany.st.TreeNode;
-import tla2sany.utilities.Strings;
-import util.UniqueString;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/***************************************************************************
-* This class represents a USE or HIDE statement.  It is of kind            *
-* UseKind or HideKind.                                                     *
-***************************************************************************/
-public class UseOrHideNode extends LevelNode {
-
-  /*************************************************************************
-  * The fields.                                                            *
-  *                                                                        *
-  * A use or hide has the syntax USE/HIDE [facts] [DEF[S] defs].  The      *
-  * following two fields are the semantic nodes for the facts and defs.    *
-  *************************************************************************/
-  public LevelNode[]  facts = null ;
-    /***********************************************************************
-    * For each i, facts[i] will be either an ExprNode, a ModuleNode, or    *
-    * an OpDefNode of type ModuleInstanceKind (with no parameters).  A     *
-    * proof management tool will probably put restrictions on the class    *
-    * of expressions that can be used as facts.                            *
-    *                                                                      *
-    * 4 Mar 2009: implemented a restriction that arbitrary expressions     *
-    * can't be used as facts.  The only allowable expressions are the      *
-    * names of theorems, assumptions, and steps.                           *
-    ***********************************************************************/
-  public SymbolNode[] defs  = null ;
-    /***********************************************************************
-    * For each i, defs[i] should be a UserDefinedOpDefKind or              *
-    * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode                 *
-    ***********************************************************************/
-
-  public boolean isOnly ;
-    /***********************************************************************
-    * True iff this node was formed from an "ONLY" step.  This is          *
-    * possible only if the node is of kind UseKind or if it was            *
-    * temporarily constructed for making a LeafProofNode for a "BY ONLY"   *
-    * proof.  However, the "ONLY BY" construct might be disabled.          *
-    ***********************************************************************/
-
-  /**
-   * If the UseOrHideNode is a proof step, this is the step number.  It
-   * is made a UniqueString for consistency; there's no need to make
-   * comparison efficient.
-   * Added by LL on 6 June 2010.
-   */
-  private UniqueString stepName = null;
-
-    public void setStepName(UniqueString stepName)
-    {
-        this.stepName = stepName;
-    }
-
-    /**
-     * @return the stepName
-     */
-    public UniqueString getStepName()
-    {
-        return stepName;
-    }
-  /*************************************************************************
-  * The constructor.                                                       *
-  *************************************************************************/
-  public UseOrHideNode(int kind, TreeNode stn, LevelNode[] theFacts,
-                   SymbolNode[] theDefs, boolean only) {
-    super(kind, stn) ;
-    this.facts = theFacts ;
-    this.defs = theDefs ;
-    this.isOnly = only ;
-  } ;
-
-  /*************************************************************************
-  * The following method was added 4 Mar 2009 to check the restriction     *
-  * that only the names of facts (and of modules) can be used as facts in  *
-  * a USE or HIDE.                                                         *
-  *                                                                        *
-  * It was modified on 1 Jul 2009 to allow the use of expressions as       *
-  * facts in a USE.                                                        *
-  *************************************************************************/
-  public void factCheck() {
-    if (this.facts == null || this.getKind() == UseKind) { return; };
-    for (int i = 0; i < this.facts.length; i++) {
-      if (    (this.facts[i].getKind() == OpApplKind)
-           && (((OpApplNode) this.facts[i]).operator.getKind()
-                   != ThmOrAssumpDefKind)) {
-          errors.addError(
-             this.facts[i].stn.getLocation(),
-               "The only expression allowed as a fact in a HIDE " +
-               "is \nthe name of a theorem, assumption, or step.");
-      } ;
-    } // for
-  }
-
-  public boolean levelCheck(int iter) {
-    /***********************************************************************
-    * Level checking is performed by level-checking the facts.  Since the  *
-    * defs should be defined operators, they have already been level       *
-    * checked.                                                             *
-    ***********************************************************************/
-    if (this.levelChecked >= iter) return this.levelCorrect;
-    return this.levelCheckSubnodes(iter, facts) ;
-   }
-
-  public void walkGraph(Hashtable semNodesTable) {
-    Integer uid = new Integer(myUID);
-    if (semNodesTable.get(uid) != null) return;
-    semNodesTable.put(new Integer(myUID), this);
-    for (int  i = 0; i < facts.length; i++) {
-      facts[i].walkGraph(semNodesTable);
-      } ;
-    /***********************************************************************
-    * Note: there's no need to walk the defs array because all the nodes   *
-    * on it are walked from the nodes under which they appear.             *
-    ***********************************************************************/
-   }
-
-  /*
-   * The children are the facts.
-   * @see tla2sany.semantic.SemanticNode#getChildren()
-   */
-  public SemanticNode[] getChildren() {
-      if (this.facts == null || this.facts.length == 0) {
-          return null;
-      }
-      SemanticNode[] res = new SemanticNode[this.facts.length];
-      for (int i = 0; i < facts.length; i++) {
-          res[i] = facts[i];
-      }
-      return res;
-   }
-
-  public String toString(int depth) {
-    if (depth <= 0) return "";
-    String ret = "\n*UseOrHideNode:\n"
-                  + super.toString(depth)
-                  + Strings.indent(2, "\nisOnly: " + this.isOnly)
-                  + Strings.indent(2, "\nfacts:") ;
-    for (int i = 0 ; i < this.facts.length; i++) {
-        ret += Strings.indent(4, this.facts[i].toString(1)) ;
-      } ;
-    ret += Strings.indent(2, "\ndefs:") ;
-    for (int i = 0 ; i < this.defs.length; i++) {
-        ret += Strings.indent(4, this.defs[i].toString(1)) ;
-      } ;
-    return ret;
-   }
-
-  protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) {
-    //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2);
-    Element e = doc.createElement("UseOrHideNode");
-
-    Element factse = doc.createElement("facts");
-    Element definitions = doc.createElement("defs");
-
-    for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context));
-    for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context));
-
-    e.appendChild(factse);
-    e.appendChild(definitions);
-    if(isOnly) e.appendChild(doc.createElement("only"));
-    if(getKind() == HideKind) e.appendChild(doc.createElement("hide"));
-
-
-/*    if (stepName != null)
-      e.setAttribute("step_name", stepName.toString());
-*/
-
-    // at the end, we append the context of the symbols used in this node
-    //e.appendChild(context.getContextElement(doc));
-
-    return e;
-  }
-}
+// Copyright (c) 2007 Microsoft Corporation.  All rights reserved.
+// last modified on Fri  3 July 2009 at 12:41:45 PST by lamport
+package tla2sany.semantic;
+
+import java.util.Hashtable;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.st.TreeNode;
+import tla2sany.utilities.Strings;
+import tla2sany.xml.SymbolContext;
+import util.UniqueString;
+
+/***************************************************************************
+* This class represents a USE or HIDE statement.  It is of kind            *
+* UseKind or HideKind.                                                     *
+***************************************************************************/
+public class UseOrHideNode extends LevelNode {
+
+  /*************************************************************************
+  * The fields.                                                            *
+  *                                                                        *
+  * A use or hide has the syntax USE/HIDE [facts] [DEF[S] defs].  The      *
+  * following two fields are the semantic nodes for the facts and defs.    *
+  *************************************************************************/
+  public LevelNode[]  facts = null ;
+    /***********************************************************************
+    * For each i, facts[i] will be either an ExprNode, a ModuleNode, or    *
+    * an OpDefNode of type ModuleInstanceKind (with no parameters).  A     *
+    * proof management tool will probably put restrictions on the class    *
+    * of expressions that can be used as facts.                            *
+    *                                                                      *
+    * 4 Mar 2009: implemented a restriction that arbitrary expressions     *
+    * can't be used as facts.  The only allowable expressions are the      *
+    * names of theorems, assumptions, and steps.                           *
+    ***********************************************************************/
+  public SymbolNode[] defs  = null ;
+    /***********************************************************************
+    * For each i, defs[i] should be a UserDefinedOpDefKind or              *
+    * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode                 *
+    ***********************************************************************/
+
+  public boolean isOnly ;
+    /***********************************************************************
+    * True iff this node was formed from an "ONLY" step.  This is          *
+    * possible only if the node is of kind UseKind or if it was            *
+    * temporarily constructed for making a LeafProofNode for a "BY ONLY"   *
+    * proof.  However, the "ONLY BY" construct might be disabled.          *
+    ***********************************************************************/
+
+  /**
+   * If the UseOrHideNode is a proof step, this is the step number.  It
+   * is made a UniqueString for consistency; there's no need to make
+   * comparison efficient.
+   * Added by LL on 6 June 2010.
+   */
+  private UniqueString stepName = null;
+
+    public void setStepName(UniqueString stepName)
+    {
+        this.stepName = stepName;
+    }
+
+    /**
+     * @return the stepName
+     */
+    public UniqueString getStepName()
+    {
+        return stepName;
+    }
+  /*************************************************************************
+  * The constructor.                                                       *
+  *************************************************************************/
+  public UseOrHideNode(int kind, TreeNode stn, LevelNode[] theFacts,
+                   SymbolNode[] theDefs, boolean only) {
+    super(kind, stn) ;
+    this.facts = theFacts ;
+    this.defs = theDefs ;
+    this.isOnly = only ;
+  } ;
+
+  /*************************************************************************
+  * The following method was added 4 Mar 2009 to check the restriction     *
+  * that only the names of facts (and of modules) can be used as facts in  *
+  * a USE or HIDE.                                                         *
+  *                                                                        *
+  * It was modified on 1 Jul 2009 to allow the use of expressions as       *
+  * facts in a USE.                                                        *
+  *************************************************************************/
+  public void factCheck() {
+    if (this.facts == null || this.getKind() == UseKind) { return; };
+    for (int i = 0; i < this.facts.length; i++) {
+      if (    (this.facts[i].getKind() == OpApplKind)
+           && (((OpApplNode) this.facts[i]).operator.getKind()
+                   != ThmOrAssumpDefKind)) {
+          errors.addError(
+             this.facts[i].stn.getLocation(),
+               "The only expression allowed as a fact in a HIDE " +
+               "is \nthe name of a theorem, assumption, or step.");
+      } ;
+    } // for
+  }
+
+  @Override
+  public boolean levelCheck(int iter) {
+    /***********************************************************************
+    * Level checking is performed by level-checking the facts.  Since the  *
+    * defs should be defined operators, they have already been level       *
+    * checked.                                                             *
+    ***********************************************************************/
+    if (this.levelChecked >= iter) return this.levelCorrect;
+    return this.levelCheckSubnodes(iter, facts) ;
+   }
+
+  @Override
+  public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) {
+    Integer uid = Integer.valueOf(myUID);
+    if (semNodesTable.get(uid) != null) return;
+    semNodesTable.put(uid, this);
+    visitor.preVisit(this);
+    for (int  i = 0; i < facts.length; i++) {
+      facts[i].walkGraph(semNodesTable, visitor);
+      } ;
+    /***********************************************************************
+    * Note: there's no need to walk the defs array because all the nodes   *
+    * on it are walked from the nodes under which they appear.             *
+    ***********************************************************************/
+      visitor.postVisit(this);
+   }
+
+  /*
+   * The children are the facts.
+   * @see tla2sany.semantic.SemanticNode#getChildren()
+   */
+  @Override
+  public SemanticNode[] getChildren() {
+      if (this.facts == null || this.facts.length == 0) {
+          return null;
+      }
+      SemanticNode[] res = new SemanticNode[this.facts.length];
+      for (int i = 0; i < facts.length; i++) {
+          res[i] = facts[i];
+      }
+      return res;
+   }
+
+  @Override
+  public String toString(int depth) {
+    if (depth <= 0) return "";
+    String ret = "\n*UseOrHideNode:\n"
+                  + super.toString(depth)
+                  + Strings.indent(2, "\nisOnly: " + this.isOnly)
+                  + Strings.indent(2, "\nfacts:") ;
+    for (int i = 0 ; i < this.facts.length; i++) {
+        ret += Strings.indent(4, this.facts[i].toString(1)) ;
+      } ;
+    ret += Strings.indent(2, "\ndefs:") ;
+    for (int i = 0 ; i < this.defs.length; i++) {
+        ret += Strings.indent(4, this.defs[i].toString(1)) ;
+      } ;
+    return ret;
+   }
+
+  @Override
+  protected Element getLevelElement(Document doc, SymbolContext context) {
+    //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2);
+    Element e = doc.createElement("UseOrHideNode");
+
+    Element factse = doc.createElement("facts");
+    Element definitions = doc.createElement("defs");
+
+    for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context));
+    for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context));
+
+    e.appendChild(factse);
+    e.appendChild(definitions);
+    if(isOnly) e.appendChild(doc.createElement("only"));
+    if(getKind() == HideKind) e.appendChild(doc.createElement("hide"));
+
+
+/*    if (stepName != null)
+      e.setAttribute("step_name", stepName.toString());
+*/
+
+    // at the end, we append the context of the symbols used in this node
+    //e.appendChild(context.getContextElement(doc));
+
+    return e;
+  }
+}
diff --git a/tlatools/src/tla2sany/st/Location.java b/tlatools/src/tla2sany/st/Location.java
index beb23da1221988e279612e478a6a02565447798c..a56c755a474eb05cda2bc31fcdf41bffc2ebd419 100644
--- a/tlatools/src/tla2sany/st/Location.java
+++ b/tlatools/src/tla2sany/st/Location.java
@@ -8,9 +8,8 @@ import java.util.regex.Pattern;
 
 import pcal.PCalLocation;
 import pcal.Region;
-
-import tlc2.output.EC;
 import util.Assert;
+import util.TLAConstants;
 import util.UniqueString;
 
 /**
@@ -21,10 +20,10 @@ import util.UniqueString;
  * @author Leslie Lamport, Simon Zambrovski
  * @version $Id$                                                              
  */
-public final class Location
+public final class Location implements Comparable<Location>
 {
     // strings used in toString() and Regex
-    private static final String LINE = "line ";
+    private static final String LINE = TLAConstants.LINE;
     private static final String LINE_CAP = "Line ";
     private static final String TO_LINE = " to line ";
     private static final String COL = ", col ";
@@ -36,7 +35,7 @@ public final class Location
     private static final String NATURAL = "([0-9]+)";
     private static final String MODULE_ID = "([A-Za-z_0-9]+)";
     private static final String CLOSE_ACTION = ">";
-    private static final String OPEN_ACTION = "<Action ";
+    private static final String OPEN_ACTION = "<[A-Za-z_0-9]+ "; // The regex used to just be "Action" but the most recent TLC prints the name of the action instead of just the location.
 
     private static final UniqueString unknown = UniqueString.uniqueStringOf("--unknown--");
 
@@ -94,7 +93,12 @@ public final class Location
     protected UniqueString name;
     protected int bLine, bColumn, eLine, eColumn;
 
-    /**
+	public Location(final String fName, final String bl, final String bc, final String el, final String ec) {
+		this(UniqueString.uniqueStringOf(fName), Integer.valueOf(bl), Integer.valueOf(bc), Integer.valueOf(el),
+				Integer.valueOf(ec));
+	}
+    
+   /**
      * Constructs a location
      * @param fName name of the source
      * @param bl begin line
@@ -218,6 +222,11 @@ public final class Location
         }
     }
 
+    public static Location parseCoordinates(final String source, final String coordinates) {
+    	final String[] c = coordinates.split(" ");
+		return new Location(source, c[0], c[1], c[2], c[3]);
+    }
+    
     /**
      * Returns an array of {@link Location} that can be parsed
      * from the input. More precisely, this method returns all
@@ -295,6 +304,10 @@ public final class Location
         return this.eLine;
     }
 
+    public final int[] getCoordinates() {
+		return new int[] { bLine, bColumn, eLine, eColumn };
+    }
+    
     /** 
      * gets the end column number of this location. 
      */
@@ -308,7 +321,7 @@ public final class Location
      */
     public final String source()
     {
-        return name.toString();
+        return name != null ? name.toString() : null;
     }
 
     /**
@@ -357,6 +370,21 @@ public final class Location
         return false;
     }
 
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + bColumn;
+		result = prime * result + bLine;
+		result = prime * result + eColumn;
+		result = prime * result + eLine;
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		return result;
+	}
+
 	/**
 	 * Translates the {@link Location} into a PCal {@link Region} adjusting the
 	 * 1-based offset to a 0-based one.
@@ -367,4 +395,72 @@ public final class Location
 		return new pcal.Region(new PCalLocation(bLine - 1, bColumn - 1),
 				new PCalLocation(eLine - 1, eColumn - 1));
 	}
+	
+	/**
+	 * @param line
+	 * @return returns true if line is in [beginLine, endLine]
+	 */
+	public boolean containsLine(final int line) {
+		return ((line >= bLine) && (line <= eLine));
+	}
+
+	@Override
+	public int compareTo(final Location other) {
+		if (this.equals(other)) {
+			return 0;
+		}
+		if (this.name.compareTo(other.name) != 0) {
+			return this.name.compareTo(other.name);
+		}
+
+		if (this.bLine > other.bLine) {
+			return 1;
+		} else if (this.bLine < other.bLine) {
+			return -1;
+		}
+
+		if (this.bColumn > other.bColumn) {
+			return 1;
+		} else if (this.bColumn < other.bColumn) {
+			return -1;
+		}
+		
+		if (this.eLine > other.eLine) {
+			return 1;
+		} else if (this.eLine < other.eLine) {
+			return -1;
+		}
+
+		if (this.eColumn < other.eColumn) {
+			return -1;
+		}
+		return 1;
+	}
+	
+	/**
+	 * @return true if other is a location within this location (same module file
+	 *         and the range of chars is within this range of chars).
+	 */
+	public boolean includes(final Location other) {
+		if (this.name != other.name) {
+			return false;
+		}
+		if (this.bLine > other.bLine) {
+			return false;
+		}
+		if (this.eLine < other.eLine) {
+			return false;
+		}
+		if (this.bColumn > other.bColumn) {
+			return false;
+		}
+		if (this.eColumn < other.eColumn) {
+			return false;
+		}
+		return true;
+	}
+
+	public String linesAndColumns() {
+		return toString().replaceAll(OF_MODULE + ".*", "");
+	}
 }
diff --git a/tlatools/src/tla2sany/st/TreeNode.java b/tlatools/src/tla2sany/st/TreeNode.java
index 7c7bb106ab5f1872619fced4462a2157563d6a3d..e80cc8c547ea2fdaaa8edc2702e06b57168ed117 100644
--- a/tlatools/src/tla2sany/st/TreeNode.java
+++ b/tlatools/src/tla2sany/st/TreeNode.java
@@ -20,4 +20,5 @@ public interface TreeNode {
 //  public String[]               getPostComments(); // always returns an array, never null
   public boolean                local();
   public void                   printST(int indentation);
+  public String					getHumanReadableImage();
 }
diff --git a/tlatools/src/tla2sany/utilities/Vector.java b/tlatools/src/tla2sany/utilities/Vector.java
index f22e094a477f634a421763319b4c0c72d2d9a8ab..21705a210ab01b86b225f8d3f642441d2e33c96d 100644
--- a/tlatools/src/tla2sany/utilities/Vector.java
+++ b/tlatools/src/tla2sany/utilities/Vector.java
@@ -2,7 +2,7 @@
 package tla2sany.utilities;
 import java.util.Enumeration;
 
-public class Vector {
+public class Vector<E> {
   static int defaultSize = 10;
   
   protected Object info[];
@@ -38,7 +38,7 @@ public class Vector {
   }
   */
 
-  public final void addElement( Object obj ) {
+  public final void addElement( E obj ) {
     if (size == capacity) {
       Object next[] = new Object[ capacity + increment ];
       System.arraycopy( info, 0, next, 0, capacity );
@@ -49,19 +49,22 @@ public class Vector {
     size++;
   }
 
-  public final Object firstElement() {
-    return info[0];
+  @SuppressWarnings("unchecked")
+  public final E firstElement() {
+    return (E)info[0];
   }
 
-  public final Object lastElement() {
-    return info[ size-1 ];
+  @SuppressWarnings("unchecked")
+  public final E lastElement() {
+    return (E)info[ size-1 ];
   }
 
-  public final Object elementAt(int i) {
+  @SuppressWarnings("unchecked")
+  public final E elementAt(int i) {
     if (i < 0 || i >= size )
       throw new ArrayIndexOutOfBoundsException();
     else
-      return info[ i ];
+      return (E)info[ i ];
   }
 
   public final void removeAllElements() {
@@ -82,7 +85,7 @@ public class Vector {
     }
   }
 
-  public final void insertElementAt( Object obj, int i ) {
+  public final void insertElementAt( E obj, int i ) {
     if (i < 0 || i >= size )
       throw new ArrayIndexOutOfBoundsException();
     else if (size == capacity) {
@@ -100,25 +103,25 @@ public class Vector {
     size++;
   }
 
-  public final void setElementAt( Object obj, int i ) {
+  public final void setElementAt( E obj, int i ) {
     if (i < 0 || i >= size )
       throw new ArrayIndexOutOfBoundsException();
     else 
       info[ i ] = obj;
   }
 
-  public final boolean contains (Object obj) {
+  public final boolean contains (E obj) {
     for (int i = 0; i < size; i++) {
       if ( info[ i ] == obj ) return true;
     }
     return false; 
   }
 
-  public final Enumeration elements() {
-    return new VectorEnumeration( info, size );
+  public final Enumeration<E> elements() {
+    return new VectorEnumeration<E>( info, size );
   }
 
-  public final void append( Vector v ) {
+  public final void append( Vector<E> v ) {
     if ( v.size + size  > capacity ) {
       Object neo[] = new Object[ capacity + v.capacity ];
       capacity += v.capacity;
@@ -132,12 +135,13 @@ public class Vector {
   // Like the append method above, but elements of v will not be added to THIS Vector 
   // if they are already present at least once; repeated elements already in 
   // THIS Vector, however, will not be removed.
-  public final void appendNoRepeats(Vector v) {
+  public final void appendNoRepeats(Vector<E> v) {
     for (int i = 0; i < v.size(); i++) {
       if ( ! this.contains(v.elementAt(i)) ) this.addElement(v.elementAt(i));
     }
   }
 
+  @Override
   public final String toString() {
     String ret;
     ret = "[ ";
diff --git a/tlatools/src/tla2sany/utilities/VectorEnumeration.java b/tlatools/src/tla2sany/utilities/VectorEnumeration.java
index 2d3dbe35010cce11f6471d1155fd19069c8f8002..9ab1fe506cc05ac61276392df4f6fae3dd457890 100644
--- a/tlatools/src/tla2sany/utilities/VectorEnumeration.java
+++ b/tlatools/src/tla2sany/utilities/VectorEnumeration.java
@@ -2,7 +2,7 @@
 package tla2sany.utilities;
 import java.util.Enumeration;
 
-final class VectorEnumeration implements Enumeration {
+final class VectorEnumeration<E> implements Enumeration<E> {
   int index = 0;
   Object data[];
 
@@ -15,9 +15,10 @@ final class VectorEnumeration implements Enumeration {
     return index < data.length;
   }
 
-  public final Object nextElement() {
+  @SuppressWarnings("unchecked")
+  public final E nextElement() {
     if (index < data.length)
-      return data[index++];
+      return (E)data[index++];
     else
       throw new java.util.NoSuchElementException();
   }
diff --git a/tlatools/src/tla2sany/xml/SymbolContext.java b/tlatools/src/tla2sany/xml/SymbolContext.java
index ff58215c3d02003c932639f38717d7a4784dea53..c4100a4aca75d7dd705c861e728c1115e2b5e44f 100644
--- a/tlatools/src/tla2sany/xml/SymbolContext.java
+++ b/tlatools/src/tla2sany/xml/SymbolContext.java
@@ -1,9 +1,11 @@
 package tla2sany.xml;
 
-import tla2sany.semantic.SymbolNode;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.Node;
+
+import tla2sany.semantic.AssumeNode;
+import tla2sany.semantic.SymbolNode;
+import tla2sany.semantic.TheoremNode;
 
 /* TL
  * This class is used to track the occurrence of SymbolNodes
@@ -16,18 +18,21 @@ import org.w3c.dom.Node;
 public class SymbolContext {
   private java.util.Map<Integer,Element> context;
   private java.util.Set<Integer> keys; // we need this set since the generated element might spawn new keys
+  private boolean top_level_entry;  // used to detect if a symbol is exported twice.
+                                    // only set in put() and reset in SymbolNode.getDefinitionElement
 
   // flags list
   public static final int OTHER_BUG = 0;
 
   // some semantic objects are represented using null. this flags array
-  // is used to tell nodes tio expect them so xml exporting will be done properly
+  // is used to tell nodes to expect them so xml exporting will be done properly
   private boolean[] flagArray;
 
   public SymbolContext() {
     context = new java.util.HashMap<Integer,Element>();
     keys = new java.util.HashSet<Integer>();
     flagArray = new boolean[1];
+    top_level_entry = false;
   }
 
   // copy concstructor
@@ -35,6 +40,7 @@ public class SymbolContext {
     context = other.context;
     keys = other.keys;
     flagArray = other.flagArray;
+    top_level_entry = other.top_level_entry;
   }
 
   public void setFlag(int flag) {
@@ -50,6 +56,27 @@ public class SymbolContext {
     if (!keys.contains(k)) {
       // first add the key as it might be mentioned again inside the definition
       keys.add(k);
+      setTop_level_entry();
+      context.put(k,nd.exportDefinition(doc,this));
+    }
+  }
+
+  public void put(TheoremNode nd, Document doc) {
+    Integer k = new Integer(nd.myUID);
+    if (!keys.contains(k)) {
+      // first add the key as it might be mentioned again inside the definition
+      keys.add(k);
+      setTop_level_entry();
+      context.put(k,nd.exportDefinition(doc,this));
+    }
+  }
+
+  public void put(AssumeNode nd, Document doc) {
+    Integer k = new Integer(nd.myUID);
+    if (!keys.contains(k)) {
+      // first add the key as it might be mentioned again inside the definition
+      keys.add(k);
+      setTop_level_entry();
       context.put(k,nd.exportDefinition(doc,this));
     }
   }
@@ -66,4 +93,13 @@ public class SymbolContext {
     }
     return ret;
   }
+
+  public int getContextSize() {
+    return context.size();
+  }
+
+  public boolean isTop_level_entry() { return top_level_entry; }
+  public void setTop_level_entry() { top_level_entry = true; }
+  public void resetTop_level_entry() { top_level_entry = false; }
+
 }
diff --git a/tlatools/src/tla2sany/xml/XMLExportable.java b/tlatools/src/tla2sany/xml/XMLExportable.java
index dfa93113d97fd4671ebff6cc25b222e2e4e4f69b..95bd1b99b6c65a3755291dd58f0c9cc3d5c951b2 100644
--- a/tlatools/src/tla2sany/xml/XMLExportable.java
+++ b/tlatools/src/tla2sany/xml/XMLExportable.java
@@ -2,12 +2,13 @@
 
 package tla2sany.xml;
 
+import org.w3c.dom.Document;
+
 /**
  * an interface for being able to export XML content
  */
 
 import org.w3c.dom.Element;
-import org.w3c.dom.Document;
 
 public interface XMLExportable {
 
diff --git a/tlatools/src/tla2sany/xml/XMLExporter.java b/tlatools/src/tla2sany/xml/XMLExporter.java
index 65b0bc6da0a8792dc918b85aa064a1ab23670412..7ab4275c31657024f600c117e3552a69748bbed8 100644
--- a/tlatools/src/tla2sany/xml/XMLExporter.java
+++ b/tlatools/src/tla2sany/xml/XMLExporter.java
@@ -3,48 +3,16 @@
 
 package tla2sany.xml;
 
+import java.io.ByteArrayOutputStream;
+
 /**
  * a tool for exporting the loaded modules to XML format
  */
 
 import java.io.PrintStream;
-import java.io.ByteArrayOutputStream;
-import java.util.Iterator;
+import java.net.URL;
 import java.util.LinkedList;
 
-import tla2sany.drivers.SANY;
-import tla2sany.drivers.FrontEndException;
-import tla2sany.configuration.Configuration;
-import tla2sany.explorer.Explorer;
-import tla2sany.explorer.ExplorerQuitException;
-import tla2sany.modanalyzer.ParseUnit;
-import tla2sany.modanalyzer.SpecObj;
-import tla2sany.parser.ParseException;
-import tla2sany.semantic.AbortException;
-import tla2sany.semantic.BuiltInLevel;
-import tla2sany.semantic.Context;
-import tla2sany.semantic.Errors;
-import tla2sany.semantic.ExternalModuleTable;
-import tla2sany.semantic.Generator;
-import tla2sany.semantic.ModuleNode;
-import tla2sany.semantic.SemanticNode;
-import tla2sany.st.Location;
-import tla2sany.semantic.ModuleNode;
-import util.FileUtil;
-import util.ToolIO;
-import util.UniqueString;
-import util.FilenameToStream;
-import util.SimpleFilenameToStream;
-import tla2sany.semantic.SymbolNode;
-import tla2sany.semantic.AssumeNode;
-import tla2sany.semantic.TheoremNode;
-import tla2sany.semantic.InstanceNode;
-import tla2sany.semantic.OpDeclNode;
-import tla2sany.semantic.OpDefNode;
-
-
-// XML packages
-import java.io.File;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -53,16 +21,23 @@ import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
-import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Validator;
-import org.xml.sax.SAXException;
-import java.net.URL;
-import java.net.MalformedURLException;
 
-import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import tla2sany.drivers.FrontEndException;
+import tla2sany.drivers.SANY;
+import tla2sany.modanalyzer.SpecObj;
+import tla2sany.semantic.ExternalModuleTable;
+import tla2sany.semantic.ModuleNode;
+import util.FileUtil;
+import util.FilenameToStream;
+import util.SimpleFilenameToStream;
+import util.ToolIO;
 
 public class XMLExporter {
 
@@ -86,25 +61,28 @@ public class XMLExporter {
 
     boolean offline_mode = false;
     int lastarg = -1; // lastarg will be incremented, initialize at -1
-    for (int i = 0; i<args.length-1; i++) {
-        if ("-o".equals(args[i])) {
-            offline_mode = true;
-            lastarg = i;
-        } else if ("-I".equals(args[i])) {
-            i++;
-            if (i > args.length-2) throw new IllegalArgumentException("the -I flag must be followed by a directory and at least one .tla file");
-            pathsLs.addLast(args[i]);
-            lastarg = i;
-        }
+    for (int i = 0; i < args.length - 1; i++) {
+      if ("-o".equals(args[i])) {
+        offline_mode = true;
+        lastarg = i;
+      } else if ("-I".equals(args[i])) {
+        i++;
+        if (i > args.length - 2)
+          throw new IllegalArgumentException("the -I flag must be followed by a directory and at least one .tla file");
+        pathsLs.addLast(args[i]);
+        lastarg = i;
+      }
     }
 
     lastarg++;
 
     String[] paths = new String[pathsLs.size()];
-    for (int i=0; i<paths.length; i++) paths[i] = (String)pathsLs.get(i);
+    for (int i = 0; i < paths.length; i++) paths[i] = (String) pathsLs.get(i);
 
-    String[] tlas = new String[args.length - lastarg];
-    for (int i=0; i<args.length-lastarg; i++) tlas[i] = args[lastarg++];
+    if (args.length - lastarg != 1)
+      throw new IllegalArgumentException("Only one TLA file to check allowed!");
+
+    String tla_name = args[lastarg++];
 
     FilenameToStream fts = new SimpleFilenameToStream(paths);
 
@@ -112,53 +90,40 @@ public class XMLExporter {
     PrintStream out = System.out;
     System.setOut(new PrintStream(new ByteArrayOutputStream()));
 
-    SpecObj[] specs = new SpecObj[tlas.length];
-    for (int i=0; i<tlas.length; i++) specs[i] = new SpecObj(tlas[i], fts);
-
-    // For each file named on the command line (i.e. in the tlas
-    // array) (re)initialize the entire system and process that file
-    // as the root of a new specification.
-    for (int i = 0; i < tlas.length; i++) {
-      // continue the loop where the last one left off
-      // Print documentation line on System.out
-      ToolIO.out.println("\n****** SANY2 " + SANY.version + "\n") ;
-
-      // Get next file name from command line; then parse,
-      // semantically analyze, and level check the spec started in
-      // file Filename leaving the result (normally) in Specification
-      // spec.
-      // check if file exists
-      if (FileUtil.createNamedInputStream(tlas[i], specs[i].getResolver()) != null)
-      {
-          try {
-              SANY.frontEndMain(specs[i], tlas[i], System.err);
-              if (specs[i].getExternalModuleTable() == null)
-                throw new XMLExportingException("spec " + specs[i].getName() + " is malformed - does not have an external module table", null);
-              if (specs[i].getExternalModuleTable().getRootModule() == null)
-                throw new XMLExportingException("spec " + specs[i].getName() + " is malformed - does not have a root module", null);
-            }
-            catch (FrontEndException fe) {
-              // For debugging
-              fe.printStackTrace();
-              ToolIO.out.println(fe);
-              return;
-            }
-      } else
-      {
-          ToolIO.out.println("Cannot find the specified file " + tlas[i] + ".");
+    SpecObj spec = new SpecObj(tla_name, fts);
+
+    // Print documentation line on System.out
+    ToolIO.out.println("\n****** SANY2 " + SANY.version + "\n");
+
+    // Get next file name from command line; then parse,
+    // semantically analyze, and level check the spec started in
+    // file Filename leaving the result (normally) in Specification
+    // spec.
+    // check if file exists
+    //ToolIO.out.println("Processing: "+tlas[i]+"\n"+(tlas[i] == null));
+    if (FileUtil.createNamedInputStream(tla_name, spec.getResolver()) != null) {
+      try {
+        SANY.frontEndMain(spec, tla_name, System.err);
+        if (spec.getExternalModuleTable() == null)
+          throw new XMLExportingException("spec " + spec.getName() + " is malformed - does not have an external module table", null);
+        if (spec.getExternalModuleTable().getRootModule() == null)
+          throw new XMLExportingException("spec " + spec.getName() + " is malformed - does not have a root module", null);
+      } catch (FrontEndException fe) {
+        // For debugging
+        fe.printStackTrace();
+        ToolIO.out.println(fe);
+        return;
       }
+    } else {
+      ToolIO.out.println("Cannot find the specified file " + tla_name + ".");
+      return;
     }
 
+
     try {
 
       DocumentBuilderFactory docFactory =
-            DocumentBuilderFactory.newInstance();
-      /* validation is being done later
-      docFactory.setNamespaceAware(true);
-      docFactory.setValidating(true);
-      docFactory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
-      docFactory.setAttribute(JAXP_SCHEMA_SOURCE, TLA_SCHEMA);
-      */
+              DocumentBuilderFactory.newInstance();
 
       // write XML
       DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
@@ -167,31 +132,44 @@ public class XMLExporter {
       Document doc = docBuilder.newDocument();
       Element rootElement = doc.createElement("modules");
       doc.appendChild(rootElement);
+      // Create symbol context. It will be filled by all symbol references during module export.
       SymbolContext context = new SymbolContext();
-      for (int i=0; i<specs.length; i++) {
-        Element e = specs[i].getExternalModuleTable().getRootModule().exportDefinition(doc,context);
-        rootElement.appendChild(e);
+
+      //Export all the external modules
+      ExternalModuleTable table = spec.getExternalModuleTable();
+      //Element e = table.getRootModule().exportDefinition(doc,context);
+      //rootElement.appendChild(e);
+      ModuleNode[] externalModules = table.getModuleNodes();
+      for (int j = 0; j < externalModules.length; j++) {
+        //Element ext_e = externalModules[j].exportDefinition(doc, context);
+        Element ext_e = externalModules[j].export(doc, context);
+        rootElement.appendChild(ext_e);
       }
-      // at the beginning, we append the context of the symbols used in this node
+
+      // Insert the symbol table into the beginning of the XML DOM
       rootElement.insertBefore(context.getContextElement(doc), rootElement.getFirstChild());
 
+      //Insert name of root module
+      insertRootName(doc, rootElement, spec);
+
+      //Create XML file
       TransformerFactory transformerFactory = TransformerFactory.newInstance();
       Transformer transformer = transformerFactory.newTransformer();
       DOMSource source = new DOMSource(doc);
 
       // validate the file, do not fail if there is a URL connection error
-      if (! offline_mode) { //skip validation in offline mode
-          try {
-              SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA);
-              Schema schema = factory.newSchema(new URL(TLA_SCHEMA));
-              // create a Validator instance, which can be used to validate an instance document
-              Validator validator = schema.newValidator();
-              //validate the DOM tree
-              validator.validate(source);
-          } catch (java.io.IOException ioe) {
-              // do nothing if there is no internet connection
-              // but fail for other errors
-          }
+      if (!offline_mode) { //skip validation in offline mode
+        try {
+          SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA);
+          Schema schema = factory.newSchema(new URL(TLA_SCHEMA));
+          // create a Validator instance, which can be used to validate an instance document
+          Validator validator = schema.newValidator();
+          //validate the DOM tree
+          validator.validate(source);
+        } catch (java.io.IOException ioe) {
+          // do nothing if there is no internet connection
+          // but fail for other errors
+        }
           /*catch (org.xml.sax.SAXParseException spe) {
             // do nothing if there is no internet connection
             // but fail for other errors
@@ -200,13 +178,19 @@ public class XMLExporter {
       StreamResult result = new StreamResult(out);
 
       transformer.transform(source, result);
-    }
-    catch (ParserConfigurationException pce) {
+    } catch (ParserConfigurationException pce) {
       throw new XMLExportingException("failed to write XML", pce);
     } catch (TransformerException tfe) {
       throw new XMLExportingException("failed to write XML", tfe);
     } catch (SAXException se) {
       throw new XMLExportingException("failed to validate XML", se);
     }
+
+  }
+
+  static void insertRootName(Document doc, Element rootElement, SpecObj spec) {
+    Element el = doc.createElement("RootModule");
+    el.appendChild(doc.createTextNode(spec.getName()));
+    rootElement.insertBefore(el, rootElement.getFirstChild());
   }
 }
diff --git a/tlatools/src/tla2sany/xml/XMLExportingException.java b/tlatools/src/tla2sany/xml/XMLExportingException.java
index f988a2db7b49462ca5de510d5a44a7747b5d724d..50521ff22ec8bb8932745d9abe7f764058b02a30 100644
--- a/tlatools/src/tla2sany/xml/XMLExportingException.java
+++ b/tlatools/src/tla2sany/xml/XMLExportingException.java
@@ -2,7 +2,8 @@
 // Copyright (c) 2013 INRIA-MSR.  All rights reserved.
 
 package tla2sany.xml;
-import java.io.*;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 
 /**
  * an exception for erroes in the exporter
diff --git a/tlatools/src/tla2sany/xml/sany.xsd b/tlatools/src/tla2sany/xml/sany.xsd
index d12701d1cb919105976f322f18d4cdb3df46ac94..6201603de0c611cf2ca2301f97aa0bdecdadafff 100644
--- a/tlatools/src/tla2sany/xml/sany.xsd
+++ b/tlatools/src/tla2sany/xml/sany.xsd
@@ -10,18 +10,22 @@
   <xs:element name="modules">
     <xs:complexType>
       <xs:sequence>
-        <xs:element ref="context"/> 
+        <xs:element name="RootModule" type="xs:string"/>
+        <xs:element ref="context"/>
         <xs:sequence maxOccurs="unbounded">
-          <xs:element ref="ModuleNode"/>
+          <xs:choice>
+            <xs:element ref="ModuleNode"/>
+            <xs:element ref="ModuleNodeRef"/>
+          </xs:choice>
         </xs:sequence>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- contextes and symbols references -->
   <!-- everything which has a uniquename is defined only once, here, and is referred by its name.
        this element resides within a certain scope and affects only this scope -->
-  <xs:element name="context"> 
+  <xs:element name="context">
     <xs:complexType>
       <xs:sequence minOccurs="0" maxOccurs="unbounded">
         <xs:element name="entry">
@@ -35,6 +39,9 @@
                 <xs:group ref="OpDefNode"/>
                 <xs:element ref="TheoremNode"/>
                 <xs:element ref="AssumeNode"/>
+                <xs:element ref="APSubstInNode"/>
+                <xs:element ref="TheoremDefNode"/>
+                <xs:element ref="AssumeDef"/>
               </xs:choice>
             </xs:sequence>
           </xs:complexType>
@@ -42,12 +49,14 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-              
+
   <xs:group name="OpDefNodeRef">
     <xs:choice>
       <xs:element ref="ModuleInstanceKindRef"/>
       <xs:element ref="UserDefinedOpKindRef"/>
       <xs:element ref="BuiltInKindRef"/>
+      <xs:element ref="TheoremDefRef"/>
+      <xs:element ref="AssumeDefRef"/>
     </xs:choice>
   </xs:group>
   <xs:element name="ModuleInstanceKindRef">
@@ -71,7 +80,7 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-        
+
   <xs:element name="FormalParamNodeRef">
     <xs:complexType>
       <xs:sequence>
@@ -79,7 +88,7 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="ModuleNodeRef">
     <xs:complexType>
       <xs:sequence>
@@ -87,7 +96,7 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="TheoremNodeRef">
     <xs:complexType>
       <xs:sequence>
@@ -95,7 +104,15 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
+  <xs:element name="TheoremDefRef">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="UID"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
   <xs:element name="AssumeNodeRef">
     <xs:complexType>
       <xs:sequence>
@@ -103,7 +120,15 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
+  <xs:element name="AssumeDefRef">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="UID"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
   <xs:element name="OpDeclNodeRef">
     <xs:complexType>
       <xs:sequence>
@@ -111,7 +136,7 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- A level node is a wrapper around all other nodes containing location and level information -->
   <xs:group name="node">
     <xs:sequence>
@@ -119,7 +144,7 @@
       <xs:element minOccurs="0"  ref="level"/>
     </xs:sequence>
   </xs:group>
-  
+
   <!-- ExprNode is a grouping of the following nodes -->
   <xs:group name="ExprNode">
     <xs:choice>
@@ -131,9 +156,11 @@
       <xs:element ref="OpApplNode"/>
       <xs:element ref="StringNode"/>
       <xs:element ref="SubstInNode"/>
+      <xs:element ref="TheoremDefRef"/>
+      <xs:element ref="AssumeDefRef"/>
     </xs:choice>
   </xs:group>
-  
+
   <!-- Location -->
   <xs:element name="location">
     <xs:complexType>
@@ -163,7 +190,7 @@
   <xs:element name="filename" type="xs:string"/>
   <xs:element name="begin" type="xs:integer"/>
   <xs:element name="end" type="xs:integer"/>
-  
+
   <!-- Level -->
   <xs:element name="level">
     <xs:simpleType>
@@ -171,16 +198,16 @@
       <xs:minInclusive value="0"/>
       <xs:maxInclusive value="3"/>
       <!--
-      ActionLevel = 2
-      ConstantLevel = 0
-      TemporalLevel = 3
-      VariableLevel = 1
+      ConstantLevel = 0 (Constant)
+      VariableLevel = 1 (State)
+      ActionLevel = 2   (Transition)
+      TemporalLevel = 3 (Temporal)
       -->
     </xs:restriction>
   </xs:simpleType>
  </xs:element>
- 
- 
+
+
   <!-- Instantiations and substitutions -->
   <!-- Operator substitution? -->
   <!-- A list of substitutions and the expression they apply to -->
@@ -198,28 +225,29 @@
         <xs:element name="body">
           <xs:complexType>
             <xs:choice>
-              <xs:element ref="APSubstInNode"/>
-              <xs:element ref="AssumeProveNode"/>
-              <xs:element ref="DefStepNode"/>
               <xs:group ref="ExprNode"/>
-              <xs:element ref="OpArgNode"/>
-              <xs:element ref="InstanceNode"/>
-              <xs:element ref="NewSymbNode"/>
-              <xs:group ref="ProofNode"/>
-              <xs:element ref="FormalParamNodeRef"/>
-              <xs:element ref="ModuleNodeRef"/>
-              <xs:element ref="OpDeclNodeRef"/>
-              <xs:group ref="OpDefNodeRef"/>
-              <xs:element ref="AssumeNodeRef"/>
-              <xs:element ref="TheoremNodeRef"/>
-              <xs:element ref="UseOrHideNode"/>
+              <xs:group ref="AssumeProveLike"/>
             </xs:choice>
           </xs:complexType>
         </xs:element>
+        <xs:element name="instFrom">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element ref="ModuleNodeRef"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="instTo">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element ref="ModuleNodeRef"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Expr substitution -->
   <!-- A list of substitutions and the expression they apply to -->
   <xs:element name="SubstInNode">
@@ -233,28 +261,45 @@
             </xs:sequence>
           </xs:complexType>
         </xs:element>
-        <xs:element name="body">       
+        <xs:element name="body">
           <xs:complexType>
             <xs:sequence>
               <xs:group ref="ExprNode"/>
             </xs:sequence>
           </xs:complexType>
         </xs:element>
+        <xs:element name="instFrom">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element ref="ModuleNodeRef"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="instTo">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element ref="ModuleNodeRef"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Module substitution -->
   <!-- A list of substitutions, the name of the instance and list of instance params -->
   <xs:element name="InstanceNode">
     <xs:complexType>
       <xs:sequence>
         <xs:group ref="node"/>
-        <xs:element ref="uniquename"/>
+        <xs:sequence>
+          <xs:element minOccurs="0" maxOccurs="1" ref="uniquename"/>
+        </xs:sequence>
+        <xs:element name="module" type="xs:string"/>
         <xs:element name="substs">
           <xs:complexType>
             <xs:sequence>
-              <xs:element maxOccurs="unbounded" ref="Subst"/>
+              <xs:element minOccurs="0" maxOccurs="unbounded" ref="Subst"/>
             </xs:sequence>
           </xs:complexType>
         </xs:element>
@@ -268,7 +313,7 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Mapping from the OpDeclNode to either of the others -->
   <xs:element name="Subst">
     <xs:complexType>
@@ -281,33 +326,72 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- This is a unique name in the hierarchy -->
   <xs:element name="uniquename" type="xs:string"/>
-  
+
   <!-- This is a unique ID in the system -->
   <xs:element name="UID" type="xs:integer"/>
-  
+
   <!-- Assumptions and Theorems -->
   <!-- An assumption named by name and described by ExprNode -->
   <xs:element name="AssumeNode">
     <xs:complexType>
       <xs:sequence>
         <xs:group ref="node"/>
-        <xs:group ref="ExprNode"/>
+        <xs:element minOccurs="0" name="definition">
+          <xs:complexType>
+            <xs:choice>
+              <xs:element ref="AssumeDefRef"/>
+            </xs:choice>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="body"> <!-- Invariant: definition body is assumption body, if definition exists -->
+          <xs:complexType>
+            <xs:choice>
+              <xs:group ref="ExprNode"/>
+              <xs:element ref="AssumeDef"/>
+            </xs:choice>
+          </xs:complexType>
+        </xs:element>
+        <!-- reference to AssumeDef included in ExprNode -->
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
-  <!-- Similar to assumption but may contain a proof. It can also refer to a proof step -->
-  <xs:element name="TheoremNode">
+
+  <xs:element name="AssumeDef">
     <xs:complexType>
       <xs:sequence>
         <xs:group ref="node"/>
+        <xs:element name="uniquename" type="xs:string"/>
         <xs:choice>
           <xs:group ref="ExprNode"/>
-          <xs:element ref="AssumeProveNode"/>
+          <xs:group ref="AssumeProveLike"/>
         </xs:choice>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <!-- Similar to assumption but may contain a proof. It can also refer to a proof step -->
+  <xs:element name="TheoremNode">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:group ref="node"/>
+        <xs:element minOccurs="0" name="definition">
+          <xs:complexType>
+            <xs:choice>
+              <xs:element ref="TheoremDefRef"/>
+            </xs:choice>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="body"> <!-- Invariant: definition body is theorem body, if definition exists -->
+          <xs:complexType>
+            <xs:choice>
+              <xs:group ref="ExprNode"/>
+              <xs:group ref="AssumeProveLike"/>
+            </xs:choice>
+          </xs:complexType>
+        </xs:element>
         <xs:group minOccurs="0" ref="ProofNode"/>
         <xs:element minOccurs="0" ref="suffices"/>
       </xs:sequence>
@@ -316,37 +400,57 @@
   <xs:element name="suffices">
     <xs:complexType/>
   </xs:element>
-  
+
+  <xs:element name="TheoremDefNode">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:group ref="node"/>
+        <xs:element name="uniquename" type="xs:string"/>
+        <xs:choice>
+          <xs:group ref="ExprNode"/>
+          <xs:group ref="AssumeProveLike"/>
+        </xs:choice>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
   <!-- A non empty list of assumptions and a prove expressions with possible suffices and boxed -->
   <xs:element name="AssumeProveNode">
     <xs:complexType>
       <xs:sequence>
-        <xs:group ref="node"/>       
+        <xs:group ref="node"/>
         <xs:element name="assumes">
           <xs:complexType>
             <xs:choice maxOccurs="unbounded">
-              <xs:element ref="AssumeProveNode"/>
+              <xs:group ref="AssumeProveLike"/>
               <xs:group ref="ExprNode"/>
               <xs:element ref="NewSymbNode"/>
             </xs:choice>
           </xs:complexType>
-        </xs:element>   
+        </xs:element>
         <xs:element name="prove">
-          <xs:complexType>     
+          <xs:complexType>
             <xs:group ref="ExprNode"/>
           </xs:complexType>
-        </xs:element>   
+        </xs:element>
         <xs:element minOccurs="0" ref="suffices"/>
         <xs:element minOccurs="0" ref="boxed"/>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
+  <xs:group name="AssumeProveLike">
+    <xs:choice>
+      <xs:element ref="AssumeProveNode"/>
+      <xs:element ref="APSubstInNode"/>
+    </xs:choice>
+  </xs:group>
+
   <!--  Signals a boxed scope (not clear if it is implemented in SANY) -->
   <xs:element name="boxed">
     <xs:complexType/>
   </xs:element>
-  
+
   <!-- Represents a new declaration + possible domain -->
   <xs:element name="NewSymbNode">
     <xs:complexType>
@@ -357,8 +461,8 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
-  
+
+
   <!-- Operator definitions -->
   <!--  OpDefNode is classified according to its kinds (is NumberedProofStepKind relevant, since it is never referenced in by or use?) -->
   <xs:group name="OpDefNode">
@@ -368,7 +472,7 @@
       <xs:element ref="BuiltInKind"/>
     </xs:choice>
   </xs:group>
-  
+
   <!-- Reprensets the name of an instantiated module -->
   <xs:element name="ModuleInstanceKind">
     <xs:complexType>
@@ -378,12 +482,12 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- An operator and its definition, arity and list of arguments -->
   <xs:element name="UserDefinedOpKind">
     <xs:complexType>
-      <xs:sequence>      
-        <xs:group ref="node"/>    
+      <xs:sequence>
+        <xs:group ref="node"/>
         <xs:element ref="uniquename"/>
         <xs:element ref="arity"/>
         <xs:element name="body">
@@ -402,13 +506,13 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="recursive">
     <xs:complexType/>
   </xs:element>
-  
+
   <xs:element name="arity" type="xs:integer"/>
-  
+
   <xs:element name="leibnizparam">
     <xs:complexType>
       <xs:sequence>
@@ -417,16 +521,16 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="leibniz">
     <xs:complexType/>
   </xs:element>
-  
+
   <!--  This node represents all builtins, including quantifiers, set operatios, etc. -->
   <xs:element name="BuiltInKind">
     <xs:complexType>
       <xs:sequence>
-        <xs:group ref="node"/>              
+        <xs:group ref="node"/>
         <xs:element ref="uniquename"/>
         <xs:element ref="arity"/>
         <xs:element minOccurs="0" name="params">
@@ -444,12 +548,23 @@
     <xs:complexType>
       <xs:sequence>
         <xs:group ref="node"/>
-        <xs:element ref="uniquename"/>
-        <xs:element ref="arity"/>
+        <xs:element name="argument">
+          <xs:complexType>
+            <xs:choice>
+              <xs:element ref="FormalParamNodeRef"/>
+              <xs:element ref="ModuleNodeRef"/>
+              <xs:element ref="OpDeclNodeRef"/>
+              <xs:group ref="OpDefNodeRef"/>
+              <xs:element ref="TheoremNodeRef"/>
+              <xs:element ref="AssumeNodeRef"/>
+              <xs:element ref="APSubstInNode"/>
+            </xs:choice>
+          </xs:complexType>
+        </xs:element>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Represents params of user definitions -->
   <xs:element name="FormalParamNode">
     <xs:complexType>
@@ -460,7 +575,7 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Represents constants, variables, bound variables and new symbols -->
   <xs:element name="OpDeclNode">
     <xs:complexType>
@@ -481,12 +596,12 @@
               <xs:enumeration value="28"/> <!--NewTemporalKind-->
             </xs:restriction>
           </xs:simpleType>
-        </xs:element> 
+        </xs:element>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
-  
+
+
   <!-- Proofs -->
   <!-- The first three correspond to LeafProofNode, the last to NonLeafProofNode -->
   <xs:group name="ProofNode">
@@ -499,7 +614,7 @@
         <xs:element name="steps">
           <xs:complexType>
             <xs:sequence>
-              <xs:group ref="node"/>                       
+              <xs:group ref="node"/>
               <xs:sequence maxOccurs="unbounded">
                 <xs:group ref="step"/>
               </xs:sequence>
@@ -507,21 +622,21 @@
           </xs:complexType>
         </xs:element>
       </xs:choice>
-      </xs:sequence>     
+      </xs:sequence>
   </xs:group>
-  
+
   <xs:element name="omitted">
     <xs:complexType>
       <xs:group ref="node"/>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="obvious">
     <xs:complexType>
       <xs:group ref="node"/>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Facts and Defs are defined to be of these kinds according to comments in the Java code, seems step names cannot be referenced? -->
   <xs:element name="by">
     <xs:complexType>
@@ -541,20 +656,20 @@
             <xs:choice minOccurs="0" maxOccurs="unbounded">
               <xs:element ref="UserDefinedOpKindRef"/>
               <xs:element ref="ModuleInstanceKindRef"/>
-              <xs:element ref="TheoremNodeRef"/>
-              <xs:element ref="AssumeNodeRef"/>
+              <xs:element ref="TheoremDefRef"/>
+              <xs:element ref="AssumeDefRef"/>
             </xs:choice>
           </xs:complexType>
-        </xs:element>        
+        </xs:element>
         <xs:element minOccurs="0" ref="only"/>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="only">
     <xs:complexType/>
   </xs:element>
-  
+
   <!-- Steps are defined to be of this types according to comments in the Java code -->
   <xs:group name="step">
     <xs:choice>
@@ -565,7 +680,7 @@
       <xs:element ref="TheoremNode"/> <!-- in case of unnamed theorem -->
     </xs:choice>
   </xs:group>
-  
+
   <xs:element name="DefStepNode">
     <xs:complexType>
       <xs:sequence>
@@ -573,10 +688,10 @@
         <xs:group maxOccurs="unbounded" ref="OpDefNodeRef"/>
       </xs:sequence>
     </xs:complexType>
-<!--        <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) 
-when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> 
+<!--        <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs)
+when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)-->
   </xs:element>
-  
+
   <xs:element name="UseOrHideNode">
     <xs:complexType>
       <xs:sequence>
@@ -602,19 +717,19 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
         </xs:element>
         <xs:element minOccurs="0" ref="only"/>
         <xs:element minOccurs="0" ref="hide"/>
-<!--        <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) 
-when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> 
+<!--        <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs)
+when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)-->
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="hide">
     <xs:complexType/>
   </xs:element>
-  
+
   <xs:element name="stepnumber" type="xs:string"/>
-  
-  
+
+
   <!-- Expressions -->
   <!-- Occurrences of @ by supplying the innermost enclosing $Except and the innermost $Pair containing this @ -->
   <xs:element name="AtNode">
@@ -632,7 +747,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- A decimal number (read Java code and java API for more information)-->
   <xs:element name="DecimalNode">
     <xs:complexType>
@@ -642,7 +757,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- Represents a labelled expression nm(x,y): (ExprNode|AssumeProveNode) -->
   <xs:element name="LabelNode">
     <xs:complexType>
@@ -651,10 +766,10 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
         <xs:element ref="uniquename"/>
         <xs:element ref="arity"/>
         <xs:element name="body">
-          <xs:complexType>        
+          <xs:complexType>
             <xs:choice>
               <xs:group ref="ExprNode"/>
-              <xs:element ref="AssumeProveNode"/>
+              <xs:group ref="AssumeProveLike"/>
             </xs:choice>
           </xs:complexType>
         </xs:element>
@@ -668,14 +783,14 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- A list of definitions and the body -->
   <xs:element name="LetInNode">
     <xs:complexType>
       <xs:sequence>
-        <xs:group ref="node"/>           
+        <xs:group ref="node"/>
         <xs:element name="body">
-          <xs:complexType>        
+          <xs:complexType>
             <xs:group ref="ExprNode"/>
           </xs:complexType>
         </xs:element>
@@ -693,7 +808,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- An integer number -->
   <xs:element name="NumeralNode">
     <xs:complexType>
@@ -703,7 +818,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
         </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- An string -->
   <xs:element name="StringNode">
     <xs:complexType>
@@ -713,12 +828,12 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
         </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <!-- A general node for applications. FIrst the operator and then the operands -->
   <xs:element name="OpApplNode">
     <xs:complexType>
       <xs:sequence>
-        <xs:group ref="node"/>            
+        <xs:group ref="node"/>
         <xs:element name="operator">
           <xs:complexType>
             <xs:choice>
@@ -750,7 +865,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="unbound">
     <xs:complexType>
       <xs:sequence>
@@ -759,7 +874,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
   <xs:element name="bound">
     <xs:complexType>
       <xs:sequence>
@@ -774,52 +889,25 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s
   <xs:element name="tuple">
     <xs:complexType/>
   </xs:element>
-  
+
   <!-- Module(constants,variables,operators,assumptions,theorems) -->
   <xs:element name="ModuleNode">
     <xs:complexType>
       <xs:sequence>
-        <xs:group ref="node"/>             
+        <xs:group ref="node"/>
         <xs:element ref="uniquename"/>
-        <xs:element name="constants">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:element minOccurs="0" maxOccurs="unbounded" ref="OpDeclNodeRef"/>
-            </xs:sequence>
-          </xs:complexType>
-          </xs:element>
-        <xs:element name="variables">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:element minOccurs="0" maxOccurs="unbounded" ref="OpDeclNodeRef"/>
-            </xs:sequence>
-          </xs:complexType>
-          </xs:element>
-        <xs:element name="definitions">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:group minOccurs="0" maxOccurs="unbounded" ref="OpDefNodeRef"/>
-            </xs:sequence>
-          </xs:complexType>
-          </xs:element>
-        <xs:element name="assumptions">
-          <xs:complexType>
-            <xs:choice minOccurs="0" maxOccurs="unbounded">
-              <xs:element ref="AssumeNodeRef"/>
-              <xs:element ref="AssumeNode"/>
-            </xs:choice>
-          </xs:complexType>
-          </xs:element>
-        <xs:element name="theorems">
-          <xs:complexType>
-            <xs:choice minOccurs="0" maxOccurs="unbounded">
-              <xs:element ref="TheoremNodeRef"/>
-              <xs:element ref="TheoremNode"/>
-            </xs:choice>
-          </xs:complexType>
-        </xs:element>
+        <xs:sequence minOccurs="0" maxOccurs="unbounded">
+        <xs:choice>
+          <xs:element ref="OpDeclNodeRef"/>
+          <xs:group   ref="OpDefNodeRef"/>
+          <xs:element ref="AssumeNodeRef"/>
+          <xs:element ref="InstanceNode"/>
+          <xs:element ref="UseOrHideNode"/>
+          <xs:element ref="TheoremNodeRef"/>
+        </xs:choice>
+        </xs:sequence>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-  
+
 </xs:schema>
diff --git a/tlatools/src/tla2tex/BuiltInSymbols.java b/tlatools/src/tla2tex/BuiltInSymbols.java
index 55f5f07afb08e540e91bdf5f019cdc75f120aceb..f378c7042dd5e7220be2b62a074640765db56b15 100644
--- a/tlatools/src/tla2tex/BuiltInSymbols.java
+++ b/tlatools/src/tla2tex/BuiltInSymbols.java
@@ -33,6 +33,8 @@ package tla2tex;
 import java.util.Enumeration;
 import java.util.Hashtable;
 
+import util.TLAConstants;
+
 public final class BuiltInSymbols
   { 
     /***********************************************************************
@@ -145,7 +147,7 @@ public final class BuiltInSymbols
       } ;
 
     public static boolean IsStringChar(char ch)
-      { return stringCharTable.containsKey("" + ch) ;
+      { return stringCharTable.containsKey(String.valueOf(ch)) ;
       } ;
 
     public static boolean CanPrecedeLabel(String str) {
@@ -164,7 +166,7 @@ public final class BuiltInSymbols
                   + "0123456789" ;
         int n = 0 ;
         while (n < legalChars.length())
-          { stringCharTable.put("" + legalChars.charAt(n), nullString);
+          { stringCharTable.put(String.valueOf(legalChars.charAt(n)), nullString);
             n = n + 1 ;
           }
       } ;
@@ -224,7 +226,7 @@ public final class BuiltInSymbols
              // Changed to INFIX from KEYWORD by LL on 21 July 2012 to allow 
              // left-aligning with [].  It produces something reasonable when
              // a bunch of [] symbols are right-aligned with CASE as well.
-        add("CONSTANT",   "{\\CONSTANT}",    Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.CONSTANT,   "{\\CONSTANT}",    Symbol.KEYWORD, 0);
         add("CONSTANTS",  "{\\CONSTANTS}",   Symbol.KEYWORD, 0);
         add("EXCEPT",     "{\\EXCEPT}",      Symbol.KEYWORD, 0);
         add("EXTENDS",    "{\\EXTENDS}",     Symbol.KEYWORD, 0);
@@ -237,7 +239,7 @@ public final class BuiltInSymbols
         add("STRING",     "{\\STRING}",      Symbol.KEYWORD, 0);
         add("THEOREM",    "{\\THEOREM}",     Symbol.KEYWORD, 0);
         add("TRUE",       "{\\TRUE}",        Symbol.KEYWORD, 0);
-        add("VARIABLE",   "{\\VARIABLE}",    Symbol.KEYWORD, 0);
+        add(TLAConstants.KeyWords.VARIABLE,   "{\\VARIABLE}",    Symbol.KEYWORD, 0);
         add("VARIABLES",  "{\\VARIABLES}",   Symbol.KEYWORD, 0);
         add("WITH",       "{\\WITH}",        Symbol.KEYWORD, 0);
 // The following added for tla2tex
diff --git a/tlatools/src/tla2tex/FindAlignments.java b/tlatools/src/tla2tex/FindAlignments.java
index c540837b07ae55bc4f563cd3bf1ec5d85a78c2fe..19ceb885f8fad9d0253dfc7a1156891cc8abedeb 100644
--- a/tlatools/src/tla2tex/FindAlignments.java
+++ b/tlatools/src/tla2tex/FindAlignments.java
@@ -1,1100 +1,1104 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-//
-// This is the version of FindAlignments modified to be consistent
-// with the versions of TokenizeSpec, BuiltInSymbols, etc. that have
-// been modified to handle PlusCal, but with no special PlusCal alignments
-// added.  It is also being modified to add a special rule for 
-// aligning labels -- or more precisely, aligning tokens that
-// follow labels with other tokens.
-
-/***************************************************************************
-* CLASS FindAlignments                                                     *
-*                                                                          *
-* Contains the one public method                                           *
-*                                                                          *
-*    public static void FindAlignments(Token[][] spec)                     *
-*                                                                          *
-* that sets the aboveAlign, belowAlign, and isAlignmentPoint fields for    *
-* all tokens in the tokenized spec spec--except for NULL and MULTI         *
-* comment tokens, for which the aboveAlign field is set by                 *
-* CommentToken.processComments.  (This method should be called after       *
-* CommentToken.processComments.)                                           *
-*                                                                          *
-* There are six kinds of alignments, illustrated by the following          *
-* example:                                                                 *
-*                                                                          *
-*    Alignment Type         Tokens so aligned                              *
-*    --------------         -----------------                              *
-*    FirstNonLeftComment   foo, x, + , /\ , y/comment r, u/comment k       *
-*                                           ^^^^^^^^^^^                    *
-*                                I think this should be +/comment r        *
-*    LeftComment           comments j                                      *
-*    CommentInner          comments p                                      *
-*    InfixInner            ==, => , >                                      *
-*    AfterInfixInner       m, c,                                           *
-*    InfixArg              a, z/z, y/z                                     *
-*                                                                          *
-*             foo == LET x == /\ a  =>  m * n >  c                         *
-*             |   |      | |  |  |  |   |     |  |                         *
-*    (* j *)  |   |      | |  /\ a  =>  m / q >  c  (* p *)                *
-*    |        |   |      | |                        |                      *
-*    (* j *)  |   |      x ==   z                   |                      *
-*             |   |             |                   |                      *
-*             |   |           + z                   \* p                   *
-*             |   |           | |                   |                      *
-*             |   |           | |                   (* p *)                *
-*             |   |           | |                   |                      *
-*             |   |           + y                   (* p *)                *
-*             |   |           |                                            *
-*             |   |           \* r                                         *
-*             |   |                                                        *
-*             foo ==                                                       *
-*              |                                                           *
-*              (* k *)                                                     *
-*              |                                                           *
-*              u + v                                                       *
-*                                                                          *
-* For InfixInner and CommentInner alignment, a token's belowAlign          *
-* field points to the token directly below it with which it is             *
-* aligned.                                                                 *
-*                                                                          *
-* For all alignment types, if a token t1 is aligned with a token           *
-* t2 above it, then t1.aboveAlign points to:                               *
-*                                                                          *
-*   IF t2 has no aboveAlign pointer THEN t2                                *
-*                                   ELSE t2.abovealign.                    *
-*                                                                          *
-* Thus, in the example above, the aboveAlign pointer for all the p         *
-* comments point to the first p comment.                                   *
-*                                                                          *
-* To define the types of alignment, we use the following definitions:      *
-*                                                                          *
-* - A LEFT-COMMENT is a comment that is the first token on its line but    *
-*   not the last token on its line.  (The comments (* j *) in the          *
-*   example are left-comments.)  For all the types of alignment except     *
-*   CommentInner, left-comments are treated as if they didn't exist.       *
-*                                                                          *
-* - A RIGHT-COMMENT is a comment that is the last token on its line.       *
-*                                                                          *
-* Any comment other than a left-comment or a right-comment is treated      *
-* like any other non-built-in token.  We also define:                      *
-*                                                                          *
-* - The COVERING TOKEN of a token t is the right-most token ct that        *
-*   covers t on the last line (one with biggest line number) containing    *
-*   a token that covers t, where a token t1 COVERS a token t2 if t1        *
-*   lies on an earlier line than t2 and t1.column \leq t2.column.          *
-*   However, if there is a DASH between ct and t, or                       *
-*   on the same line as ct, then t has no covering token.                  *
-*   (This definition has two versions, depending on whether or not         *
-*   left-comments are ignored.)                                            *
-*                                                                          *
-* - The BLOCKING TOKEN of a token t is the left-most token s with          *
-*   s.column \geq t.column on the last line before t's line containing     *
-*   such a non-left-comment token s.                                       *
-*                                                                          *
-* Here are the descriptions of the different types of alignment.           *
-*                                                                          *
-*                                                                          *
-* CommentInner:                                                            *
-*    Token t at position pos is CommentInner above-aligned with its        *
-*    blocking token bt at position bpos iff:                               *
-*       /\ t is a right-comment                                            *
-*       /\ bt is a right-comment                                           *
-*       /\ t.column = bt.column                                            *
-*       /\ \/ bt is not above-aligned with anything                        *
-*          \/ bt is CommentInner aligned with a token above it             *
-*                                                                          *
-* FirstNonLeftComment                                                      *
-*    If t is the first token on a line that is not a left-comment, and is  *
-*    not a right-comment that is CommentInner aligned with a token above   *
-*    it, then t is FirstNonLeftComment aligned with its covering token.    *
-*                                                                          *
-* LeftComment:                                                             *
-*    If t is a left-comment token, then it is LeftComment aligned          *
-*    with its covering token (where left-comments are visible).            *
-*                                                                          *
-* InfixInner:                                                              *
-*    Token t at position pos is InfixInner aligned with its covering       *
-*    token ct at position cpos iff t is not FirstNonLeftComment aligned,   *
-*    and both t and ct are built-in symbols with the same nonzero alignment  *
-*    class.  (The name InfixInner is misleading because non-infix symbols  *
-*    like ":" get aligned by this mechanism.)                              *
-*                                                                          *
-* AfterInfixInner:                                                         *
-*    Token t is After InfixInner aligned with the token above it iff       *
-*                                                                          *
-*       LET lt  == the token to the left of t                              *
-*           alt == the token at position lt.aboveAlign                     *
-*           at  == the token to the right of alt                           *
-*       IN  /\ t is not the first token on the line                        *
-*           /\ lt is InnerAligned with token alt above it                  *
-*           /\ alt is not the last token on its line.                      *
-*           /\ at is the covering token of t                               *
-*           /\ t.column = at.column                                        *
-*           /\ at is not a comment                                         *
-*    (The name AfterInfixInner means "after InfixInner alignment".         *
-*    Remember that some non-infix symbols get InfixInner aligned.)         *
-*                                                                          *
-* InfixArg:                                                                *
-*    Token t is InfixArg aligned with its covering token ct iff:           *
-*       LET lt == the token to the left of t                               *
-*           alt == the token at position lt.aboveAlign                     *
-*       IN  /\ t is not the first token on the line                        *
-*           /\ lt is the first non-left-comment token                      *
-*           /\ lt is an infix operator                                     *
-*           /\ lt is not InfixInner aligned with any token above it.       *
-*           /\ t.column = ct.column                                        *
-*           /\ alt is the token to the left of ct                          *
-*           /\ lt is either aligned with or lies entirely to the           *
-*              right of alt.                                               *
-*                                                                          *
-*   Note: The large number of conditions are an attempt to rule            *
-*   out spurious alignments.                                               *
-* ----------------------------------------------------------------------   *
-*                                                                          *
-* PlusCal Alignment                                                        *
-* -----------------                                                        *
-*                                                                          *
-* The following rule is added for PlusCal.                                 *
-*                                                                          *
-* Define a token t to be a FIRST NON-LABEL if either                       *
-*     (a) it is the first token on the line, or                            *
-*     (b) it is the 2nd token on its line and the first token on           *
-*         the line is a label.                                             *
-*                                                                          *
-* AfterLabel:                                                              *
-*   A token t is AfterLabel aligned with a token ta iff:                   *
-*     /\ t is a first non-label that is the 2nd token on its line          *
-*     /\ t and ta are left-aligned                                         *
-*     /\ there is no line between t and ta containing any token            *
-*        to the left of t other than a label that is the line's            *
-*        first token.                                                      *
-*     /\ \/ ta is a first non-label                                        *
-*        \/ /\ ta is above t                                               *
-*           /\ \/ ta is the first token on the line                        *
-*              \/ the token to the left of ta is a label or                *
-*                 a PlusCal symbol                                         *
-*                                                                          *
-* Here's an example of AfterLabel alignment.  Token b and all the x        *
-* tokens are AfterLabel aligned with token A. Tokens A and B and all the   *
-* other x tokens are also AfterLabel aligned with the first and last x     *
-* tokens.                                                                  *
-*                                                                          *
-*            { B                                                           *
-*          l:  x                                                           *
-*          m:    z                                                         *
-*              x                                                           *
-*          l:  A                                                           *
-*                z                                                         *
-*              x                                                           *
-*        l:    x                                                           *
-*            } z                                                           *
-*                                                                          *
-* Note that B is the only token whose alignment is due to the second       *
-* disjunction of the last conjunction.  That disjunct is designed to try   *
-* to rule out bogus alignments such as the alignment of the y's with the   *
-* 0 in                                                                     *
-*                                                                          *
-*     if (x = 0) {                                                         *
-*       lab:  y := 1;                                                      *
-*       lab:  y := 2  }                                                    *
-*                                                                          *
-* while aligning the the y's in                                            *
-*                                                                          *
-*      if (x = 0) {     y := 0;                                            *
-*                  lab: y := 1;                                            *
-*                  lab: y := 2  }                                          *
-*                                                                          *           
-* and in                                                                   *
-*                                                                          *
-*      if (x = 0) { y := 0;                                                *
-*   lab:            y := 1;                                                *
-*   lab:            y := 2  }                                              *
-*                                                                          *
-* ----------------------------------------------------------------------   *
-*                                                                          *
-* Note: A possible addition is a special case to recognize alignments      *
-* after a comma, as in                                                     *
-*                                                                          *
-*        <<abcdef,  ghi + 7, jklmn>>                                       *
-*                   |        |                                             *
-*        <<a,       bc,      def>>                                         *
-*                                                                          *
-* However, that should be done only if there's a clear intention to align  *
-* them--as evidenced here by the extra spaces between the "a,", the "cb,"  *
-* and the "def".  Otherwise, there would be too many chance alignments.    *
-***************************************************************************/
-package tla2tex;
-
-public class FindAlignments
-{ public static void FindAlignments(Token[][] spec)
-    { setSubscriptField(spec) ;
-        /*******************************************************************
-        * Set the subscript fields of the tokens.                          *
-        *******************************************************************/
-      int line = 0 ;
-        
-      /*********************************************************************
-      * Skip all lines beginning with a prolog token.  This means that if  *
-      * the module begins on the same line as a prolog token, then the     *
-      * first line of the spec is ignored for alignment.  Tant pis!        *
-      *********************************************************************/
-      boolean inProlog = true ;
-      while (inProlog && (line < spec.length))
-       { if (    (spec[line].length > 0)
-              && (spec[line][0].type != Token.PROLOG))
-           { inProlog = false;}
-         else
-           { line = line + 1;}
-       };
-      
-      /*********************************************************************
-      * Set aboveAlign and belowAlign "pointers".                          *
-      *********************************************************************/
-      while (line < spec.length)
-       { int item = 0 ;
-         boolean prevInfixInner = false ;
-           /****************************************************************
-           * True iff the previous item on the line was InfixInner         *
-           * aligned.                                                      *
-           ****************************************************************/
-         while (item < spec[line].length)
-          { 
-            /***************************************************************
-            * Set token, pos to the current token and its position.        *
-            ***************************************************************/
-            Position pos = new Position(line, item) ;
-            Token    token = spec[line][item] ;
-
-            if (! token.subscript)
-             {/*************************************************************
-              * Do not align subscript tokens.                             *
-              *************************************************************/
-              if (isRightComment(spec, pos))
-              { /***********************************************************
-                * This is a RightComment.  First check if it should be     *
-                * CommentInner aligned.  If not, check if it should be     *
-                * FirstNonLeftComment aligned.                             *
-                ***********************************************************/
-
-                /***********************************************************
-                * Set btoken, bpos to the blocking token of `token' and    *
-                * its position.                                            *
-                ***********************************************************/
-                Position bpos   = blockingPosition(spec, pos);              
-                Token    btoken =  null ;
-                if (bpos.line != -1) {btoken = bpos.toToken(spec);};
-                
-                /***********************************************************
-                * Set ctok to be a CommentToken alias for token.           *
-                ***********************************************************/
-                CommentToken ctok = (CommentToken) token ;
-                if (   (ctok.subtype == CommentToken.MULTI)
-                    || (ctok.subtype == CommentToken.NULL))
-                 { /********************************************************
-                   * This is the continuation of a multi-column comment.   *
-                   ********************************************************/
-                   Debug.Assert(   (btoken != null) 
-                                && (btoken.type == Token.COMMENT),
-                      "Bad blocking token for a MULTI or NULL token");
-                   
-                   /********************************************************
-                   * Set token's aboveAlign to point to the BEGIN_MULTI    *
-                   * token beginning the commnet.                          *
-                   ********************************************************/
-                   if ( ((CommentToken) btoken).subtype 
-                            == CommentToken.BEGIN_MULTI)
-                     { token.aboveAlign = bpos ; }
-                   else
-                     { token.aboveAlign = btoken.aboveAlign ; };
-
-                   /********************************************************
-                   * Make the blocking token's belowAlign pointer point    *
-                   * to token.                                             *
-                   ********************************************************/
-                   btoken.belowAlign = pos ;
-                 } // END then OF if  if ((ctok.subtype == .. ))
-                else
-                 { /********************************************************
-                   * This is not the continuation of a multi-line          *
-                   * comment.  Try to CommentInner align it.               *
-                   ********************************************************/
-
-                   if (   (bpos.line != -1)
-                       && isRightComment(spec, bpos)
-                       && (btoken.column == token.column)
-                       && (   (   (btoken.aboveAlign.line == -1)
-                               && (bpos.item > 0))
-                           || (   (btoken.aboveAlign.line != -1)
-                               && (btoken.aboveAlign.
-                                     toToken(spec).belowAlign.line != -1))
-                                /**********************************************
-                                * Asserts that bpos is aboveAligned with a    *
-                                * token that is belowAligned with something,  *
-                                * which is possible only if bpos is           *
-                                * CommentInner aligned.                       *
-                                **********************************************/
-                          )     
-                      )
-                     { /****************************************************
-                       * CommentInner align.                               *
-                       ****************************************************/
-                       btoken.belowAlign = pos ; 
-                       if (btoken.aboveAlign.line == -1)
-                         { token.aboveAlign = bpos; }
-                       else
-                         { token.aboveAlign = btoken.aboveAlign ;};
-                     }  // END then OF if ((bpos.line != -1)...)
-                   else
-                     { /*******************************************************
-                       * FirstNonLeftComment align iff it is the first        *
-                       * non-left comment.                                    *
-                       *******************************************************/
-                       if (   (item == 0)
-                           || (   (item == 1)
-                               && (spec[line][0].type == Token.COMMENT)))
-                         { pos.toToken(spec).aboveAlign = 
-                             coveringPosition(spec, pos, true) ;
-                         } ;
-                     }; // END else OF if ((bpos.line != -1)...)
-
-
-                 } // END else OF if  if ((ctok.subtype == .. ))
-                prevInfixInner = false ;
-              } // END then OF if (isRightComment(spec, pos))                
-
-              { /***********************************************************
-                * This is not a right-comment.  Check for every kind of    *
-                * alignment except CommentInner.                           *
-                ***********************************************************/
-                if (prevInfixInner)
-                 { /********************************************************
-                   * Check for AfterInfixInner alignment.                  *
-                   *                                                       *
-                   * In the following, the positions lPos, alPos, and      *
-                   * aPos are defined by                                   *
-                   *                            alPos --- aPos             *
-                   *                              |                        *
-                   *                              |                        *
-                   *                             lPos --- pos              *
-                   ********************************************************/
-                   Debug.Assert(pos.item > 0, 
-                      "prevInfixInner true for first item on line");
-                   Position lPos  = new Position(pos.line, pos.item - 1);
-                   Debug.Assert(lPos.toToken(spec).aboveAlign.line != -1,
-                     "prevInfixInner true, but token to left not aligned");
-                   Position alPos = lPos.toToken(spec).aboveAlign ;
-                   Token alToken = alPos.toToken(spec);
-                   if (alPos.item + 1 < spec[alPos.line].length)
-                    { Position aPos   = 
-                          new Position(alPos.line, alPos.item + 1) ;
-                      Token    atoken = aPos.toToken(spec);
-                      Position cPos   = coveringPosition(spec, pos, true);
-                      if (   (cPos.line == aPos.line)
-                          && (cPos.item == aPos.item)
-                          && (token.column == atoken.column)
-                          && (atoken.type != Token.COMMENT) )
-                        { /*************************************************
-                          * AfterInfixInner aligned.                       *
-                          *************************************************/
-                           token.aboveAlign = aPos;
-                        } ;
-                    } // END if (alPos.item + 1 < spec[alPos.line].length)
-                   prevInfixInner = false;
-                 } // END then OF if (prevInfixInner)
-               else
-                 { /********************************************************
-                   * Check for every kind of alignment except              *
-                   * AfterInfixInner and CommentInner.                     *
-                   *                                                       *
-                   * First, check if FirstNonLeftComment aligned.          *
-                   ********************************************************/
-                   if (   ((item == 0) && (token.type != Token.COMMENT))
-                       || ((item == 1) && (spec[line][0].type 
-                                              == Token.COMMENT)))
-                    { /*****************************************************
-                      * FirstNonLeftCommentAligned.                        *
-                      *****************************************************/
-                      token.aboveAlign = 
-                            coveringPosition(spec, pos, true);
-                    }
-                   else
-                    { /*****************************************************
-                      * Not FirstNonLeftCommentAligned.                    *
-                      *                                                    *
-                      * Next, check if LeftComment aligned.                *
-                      *****************************************************/
-                      if (isLeftComment(spec, pos))
-                        { token.aboveAlign = 
-                            coveringPosition(spec, pos, false);
-                        }
-                      else
-                        { /*************************************************
-                          * Next, check if InfixInner aligned.             *
-                          *************************************************/
-                          Position cpos = coveringPosition(spec, pos, true) ;
-                          Token ctoken = null ;
-                            /***********************************************
-                            * cpos and ctoken are the covering position    *
-                            * and covering token of the current token.     *
-                            ***********************************************/
-                          if (cpos.line != -1)
-                            { ctoken = cpos.toToken(spec);};
-                          int alignClass = 0 ;  // The alignment classes
-                          int calignClass = 0;  //   of pos and cpos.
-                          if (token.type == Token.BUILTIN)
-                           { alignClass = 
-                                BuiltInSymbols.GetBuiltInSymbol(
-                                   token.string, true).alignmentType ; } ;
-                          if (   (ctoken != null)
-                              && (ctoken.type == Token.BUILTIN))
-                           { calignClass = 
-                                BuiltInSymbols.GetBuiltInSymbol(
-                                   ctoken.string, true).alignmentType ; } ;
-                          if (   (ctoken != null)
-                              && (token.column == ctoken.column)
-                              && (alignClass != 0)
-                              && (alignClass == calignClass))
-                           { /**********************************************
-                             * InfixInner alignment.                       *
-                             **********************************************/
-                             ctoken.belowAlign = pos ;
-                             if (ctoken.aboveAlign.line == -1)
-                              {token.aboveAlign = cpos ; }
-                             else
-                              { token.aboveAlign = ctoken.aboveAlign ; };
-                             prevInfixInner = true;
-                           }  // END then OF if ((token.column == ...))
-                          else
-                           { /**********************************************
-                             * Not InfixInner alignment.  Check last       *
-                             * possibility, which is InfixArg alignment.   *
-                             **********************************************/
-                             if (   (   (item == 1)
-                                     || (   (item == 2)
-                                         && (spec[line][0].type == 
-                                               Token.COMMENT)))
-                                 && (spec[line][item-1].type == Token.BUILTIN)
-                                 && (BuiltInSymbols.GetBuiltInSymbol(
-                                       spec[line][item-1].string, true).symbolType
-                                      == Symbol.INFIX)
-                                 /******************************************
-                                 * Correction made 7 Nov 2001.             *
-                                 * The conjunct above replaced the         *
-                                 * following.                              *
-                                 *                                         *
-                                 * && (BuiltInSymbols.GetBuiltInSymbol(    *
-                                 *       spec[line][item-1].string         *
-                                 *        ).alignmentType != 0)            *
-                                 *                                         *
-                                 * It seems reasonable that this           *
-                                 * alignment should be performed only      *
-                                 * when the token to the left is an infix  *
-                                 * operator.                               *
-                                 ******************************************/
-                                 && (ctoken != null) 
-                                 && (token.column == ctoken.column)
-                                 && (spec[line][item-1].aboveAlign.line
-                                       != -1)
-                               )
-                              { /*******************************************
-                                * Possible InfixArg alignment.             *
-                                *******************************************/
-                             // This can happen and seems harmless.
-                             //   Debug.Assert(ctoken.belowAlign.line == -1,
-                             //       "Trying to InfixArg align with token "
-                             //   + "that is not aligned with token below it");
-
-                                 Token lTok = spec[line][item-1] ;
-                                   /****************************************
-                                   * The token to the left of the current  *
-                                   * token.                                *
-                                   ****************************************/
-                                Position alPos = lTok.aboveAlign ;
-                                Token alTok = alPos.toToken(spec);
-                                  /*****************************************
-                                  * The token with which lTok is aligned   *
-                                  * above.                                 *
-                                  *****************************************/
-                                if (   (alPos.line == cpos.line)
-                                    && (alPos.item == cpos.item - 1)
-                                    )
-                                 { /****************************************
-                                   * InfixArg alignment with a token       *
-                                   * having a token to its left, as in     *
-                                   *                                       *
-                                   *   x ==   a                            *
-                                   *          |                            *
-                                   *        + b                            *
-                                   ****************************************/
-                                   token.aboveAlign = cpos; 
-                                   // following fixes the mis-alignment that occurs if
-                                   // the "x == " occupies less space than the "+".
-                                   // However, for safety, I'm only fixing it for the
-                                   // case of labels, which should be more common.
-                                   // However, I'm not doing it until I find
-                                   // some example of the bug for fear that it
-                                   // might break something else.
-                                   //
-                                   //    ctoken.belowAlign = pos ;
-                                 } 
-                               else
-                                 { if (   (cpos.item == 0)
-                                       || (   (cpos.item == 1)
-                                           && (spec[cpos.line][0].type 
-                                                        == Token.COMMENT)))
-                                    { /*****************************************
-                                      * InfixArg alignment with a token        *
-                                      * having no token to its left.  In       *
-                                      * this case, we can have a               *
-                                      * situation like                         *
-                                      *                                        *
-                                      *    x ==                                *
-                                      *     a                                  *
-                                      *   + b                                  *
-                                      *                                        *
-                                      * where a is both above-aligned          *
-                                      * with x and below-aligned with b.       *
-                                      * I hope this doesn't cause              *
-                                      * problems.                              *
-                                      *****************************************/
-                                      token.aboveAlign = cpos;           
-                                      ctoken.belowAlign = pos ;
-                                    } ;
-                                 }
-                              };
-                           };// END else OF if ((token.column == ...))
-                        }; // END else of if (isLeftComment(spec, pos))
-                    }; // END else OF if (   ((item == 0) && ... ))
-                 }; // END else OF if (prevInfixInner)
-              }; // END else OF (isRightComment(spec, pos))                   
-             }   // END then OF if (! token.subscript)
-            else
-             { prevInfixInner = false ;
-                 /**********************************************************
-                 * Need to reset prevInfixInner for the tokens following   *
-                 * a ^ or _.                                               *
-                 **********************************************************/
-             } ;  
-           item = item + 1 ;
-          } ; // END while (item < spec[line].length)
-
-         line = line + 1;
-         /******************************************************************
-         * Skip over epilog lines.                                         *
-         ******************************************************************/
-         if (    (line < spec.length) 
-              && (spec[line].length > 0)
-              && (spec[line][0].type == Token.EPILOG))
-           { line = spec.length ;} ;
-       }; // END while (line < spec.length)
-
-       // Add the AfterLabel alignments.
-       FindLabelAlignments(spec) ;
-
-       /*********************************************************************
-      * Set isAlignmentPoint flags.  For simplicity, it is set true for    *
-      * any token that is not the first on the line and is either the      *
-      * source or target of a belowAlign pointer                           *
-      *********************************************************************/
-      line = 0 ;
-      while (line < spec.length)
-       { int item = 0 ;
-         while (item < spec[line].length)
-          { Token token = spec[line][item] ;
-            if (token.aboveAlign.line != -1)
-              { if (item > 0)
-                  {token.isAlignmentPoint = true ;
-                  };
-                if (token.aboveAlign.item != 0)
-                   /********************************************************
-                   * Corrected apparent bug on 16 Jan 00: This if          *
-                   * condition was: token.aboveAlign.line != 0.            *
-                   ********************************************************/
-                  { token.aboveAlign.toToken(spec).isAlignmentPoint 
-                            = true; 
-                  };
-              } ;
-
-            if (token.belowAlign.line != -1)
-              { if (item > 0)
-                  {token.isAlignmentPoint = true ;
-                  };
-                if (token.belowAlign.line != 0)
-                  { token.belowAlign.toToken(spec).isAlignmentPoint 
-                            = true; 
-                  };
-              } ;
-
-            item = item + 1 ;
-          } ; // END while (item < spec[line].length)
-
-         line = line + 1;
-       }; // END while (line < spec.length)
-    } ;
-
-  /**
-   * Adds AfterLabel alignments to spec.  More precisely, for something like
-   * 
-   *  if (x) { stmt
-   *    label: stmt
-   *           stmt
-   *    label: stmt
-   *           stmt
-   *           stmt
-   *        }
-   * 
-   * it makes the belowAlign field of all stmts but the last one point to the
-   * stmt below it, and the aboveAlign field of all but the first stmt point to
-   * the one above it.  In this case
-   * 
-   *    while x
-   *           stmt
-   *    label: stmt
-   * 
-   * the first stmt's aboveAlign field should point to x by the FirstNonLeftComment
-   * alignment rule.  For
-   * 
-   *    while x
-   *    label: stmt
-   *    label: stmt
-   * 
-   * the method sets the first stmt's aboveAlign field
-   *  
-   * @param spec
-   */
-  public static void FindLabelAlignments(Token[][] spec) {
-      /*
-       * Do nothing if there is no PlusCal algorithm.
-       */
-      if (!TokenizeSpec.hasPcal) {
-          return ;
-      }
-      
-      /*
-       * We get the first and last line that may begin with a label or a 
-       * PlusCal statement.  Since the algorithm begins with --algorithm
-       * or --fair, we skip the algorithm's first line.
-       */
-      int pcalStartLine = TokenizeSpec.pcalStart.line + 1 ;
-      int pcalEndLine   = TokenizeSpec.pcalEnd.line ;
-      
-      /*
-       * The algorithm works by repeatedly searching for the next line beginning
-       * with a label followed by a token tok.  It then performs the following
-       * two steps
-       * 
-       * 1. Searche downward for tokens AfterLabel aligned with tok, setting
-       *    aboveAlign/belowAlign fields as it finds them. It stops when it reaches a 
-       *    line in which tok is not not aligned with the first token on the
-       *    line.  If there's a token etok on that line that is AfterLabel
-       *    aligned with tok, so it must follow a label, then the aboveAlign
-       *    field of etok is set to point to the AfterLabel aligned token above
-       *    it, whose belowAlign field points to etok.
-       *    
-       * 2. IF tok's aboveAlign field points to a token whose belowAlign field
-       *       points to a field whose belowAlign field points to tok
-       *     THEN do nothing.  I think this is possible only if those fields were set
-       *          by step 1 for a previous label.  If I'm wrong and there's some weird
-       *          situation that caused this alignment (which I think can only be
-       *          the case if tok is a comment), then not stopping will probably
-       *          do more harm than good.
-       *     ELSE search upwards to set aboveAlign/belowAlign fields of tokens 
-       *          AfterLabel aligned with tok. As indicated above, if there
-       *          are no tokens above tok that are AfterLabel aligned with it,
-       *          then tok's aboveAlign field must be set to point to its
-       *          covering token.   
-       */
-      int curLabelLine = pcalStartLine ;
-      
-      while (   (curLabelLine <= pcalEndLine)
-             && (curLabelLine < spec.length) ) {
-          /* 
-           * set curLabelLine to the first line at or below its current
-           * position that begins with a label.
-           */
-          if (   (spec[curLabelLine].length > 1)
-              && (spec[curLabelLine][0].type == Token.PCAL_LABEL) ) {
-              
-              Token tok = spec[curLabelLine][1] ;
-              int alignCol = tok.column ;
-
-              // Perform step 1 for token tok
-              int curLine = curLabelLine + 1 ;
-              
-              // spec[alignLine][alignItem] is the token to which the
-              // next token to be aligned is aligned with.
-              int alignLine = curLabelLine ;
-              int alignItem = 1 ;
-              boolean notDone = true ;
-              while (notDone) {
-                  int curItem = 0 ;
-                     // If spec[alignLine][alignItem] is to be aligned with a token
-                     // on this line, then the token is spec[curLine][nextItem] 
-                     // 
-                  boolean shouldSkip = false ;
-                  if (spec[curLine].length != 0) {
-                      if (spec[curLine][0].type == Token.PCAL_LABEL) {
-                          // line begins with label
-                          if (spec[curLine].length > 1) {
-                              curItem = 1 ;
-                          }
-                          else {
-                              // The label is the only token on the line.  Stop
-                              // iff the label is to the left of the alignment
-                              // column.
-                              notDone = (spec[curLine][0].column >= alignCol) ;
-                              shouldSkip = true ;
-                          }
-                      }
-                      else {
-                          // line doesn't begin with label
-                      }
-                      if (!shouldSkip) {
-                          if (spec[curLine][curItem].column < alignCol) {
-                              notDone = false ;
-                          }
-                          else if (spec[curLine][curItem].column == alignCol) {
-                              spec[alignLine][alignItem].belowAlign = 
-                                      new Position(curLine, curItem) ;
-                              spec[curLine][curItem].aboveAlign =
-                                      new Position(alignLine, alignItem) ;
-                              alignLine = curLine ;
-                              alignItem = curItem ;
-                          }
-                      }
-                  }
-                  curLine++ ;
-                  if (   (curLine > pcalEndLine)
-                      || (curLine >= spec.length)) {
-                      notDone = false ;
-                  }
-              }
-              
-              // Perform step 2 for token tok
-              
-              if (    (tok.aboveAlign.line != -1)
-                   && (tok.aboveAlign.toToken(spec).belowAlign.equals(new Position(curLabelLine, 1)))) {
-                  // already aligned with above tokens so do nothing
-              }
-              else {
-                  curLine = curLabelLine - 1 ;
-                  alignLine = curLabelLine ;
-                  alignItem = 1 ;
-                  notDone = true ;
-                  while (notDone) {
-                     if (   (spec[curLine].length > 0 )
-                         && (spec[curLine][0].column <= alignCol)
-                         && (   (spec[curLine][0].type != Token.PCAL_LABEL)
-                             || (   (spec[curLine].length > 1)
-                                  && (spec[curLine][1].column <= alignCol) ) )
-                            // the conjunct above causes the line to be skipped
-                            // if the only token to the left of tok on this
-                            // line is an initial label followed by a token
-                            // to the right of tok.
-                        ) {
-                         if (spec[curLine][0].column == alignCol) {
-                             //  spec[curLine][0] is to be aligned with 
-                             //  spec[alignLine][alignItem] 
-                             spec[alignLine][alignItem].aboveAlign = 
-                                     new Position(curLine, 0) ;
-                             spec[curLine][0].belowAlign =
-                                     new Position(alignLine, alignItem) ;
-                             alignLine = curLine ;
-                             alignItem = 0 ; 
-                         }
-                         else if (spec[alignLine][alignItem].aboveAlign.line == -1) {        
-                             // we need to align spec[alignLine][alignItem] with the
-                             // right-most token on line curLine with column \leq alignCol
-                             int item = 0 ;
-                             while (   (item < spec[curLine].length)
-                                    && (spec[curLine][item].column <= alignCol)) {
-                                 item++;
-                             }
-                             // item is now either off the line or pointing to a token
-                             // to the right of the alignment token ;
-                             item-- ;
-                             
-                             // We  set the aboveAlign pointer of 
-                             // to point to spec[curLine][item] if the latter token is either
-                             // the first one on its line, or else the token to its left is
-                             // either a label or a PlusCal token.  
-                             // If that's the case, we also set the belowAlign pointer of 
-                             // spec[curLine][item] if that token is in the same column
-                             // as spec[alignLine][alignItem].
-                             // 
-                             Token altok = null ;
-                             if (item > 0) {
-                                 altok = spec[curLine][item-1] ;
-                             }
-                             if (   (altok != null)
-                                 && (   (altok.type == Token.PCAL_LABEL)
-                                     || (   (altok.type == Token.BUILTIN)
-                                         && BuiltInSymbols.IsBuiltInSymbol(altok.string, true)
-                                         && ! BuiltInSymbols.IsBuiltInSymbol(altok.string, false)
-                                        )
-                                    )
-                                ) {
-                                 spec[alignLine][alignItem].aboveAlign = 
-                                            new Position(curLine, item) ;
-                                
-                                 if (spec[curLine][item].column == alignCol) {
-                                     spec[curLine][item].belowAlign =
-                                             new Position(alignLine, alignItem) ;
-                                 }
-                             }
-                             // This ends step 2
-                             notDone = false ;
-                         }
-                         else {
-                             notDone = false ;
-                         }
-                     }
-                     curLine-- ;
-                     if (curLine < pcalStartLine) {
-                         notDone = false ;
-                     }
-                  }
-              }     
-          }
-          curLabelLine++ ;
-      }       
-  }
-  /*************************************************************************
-  * The following are functions used in FindAlignments.                    *
-  *************************************************************************/
-  private static boolean isLeftComment(Token[][] spec, Position p)
-    /***********************************************************************
-    * A left-comment is a comment token that is the first token on a line  *
-    * and has another token to its right.  This method returns true iff    *
-    * the token at position p of spec is a left-comment.                   *
-    ***********************************************************************/
-    { return    (p.item == 0)
-             && (spec[p.line][p.item].type == Token.COMMENT) 
-             && (spec[p.line].length > 1) ;
-    } ;
-
-  private static boolean isRightComment(Token[][] spec, Position p)
-    /***********************************************************************
-    * A right-comment is a comment token that is the last token on its     *
-    * line.  This method returns true iff the token at position p in spec  *
-    * is a right-comment.                                                  *
-    ***********************************************************************/
-    { return    (p.item == spec[p.line].length - 1)
-             && (spec[p.line][p.item].type == Token.COMMENT) ;
-    } ;
-
-  private static Position coveringPosition( 
-                          Token[][] spec, Position p, boolean ignore)
-    /***********************************************************************
-    * A token t1 COVERS a token t2 if t1 lies on an earlier line than t2   *
-    * and t1.column \leq t2.column.  This method searches upwards to find  *
-    * the first line with a token that covers the token at position p,     *
-    * and then returns the position of the right-most token on that line   *
-    * that covers the token at p.  When searching for the covering token,  *
-    * left-comments are ignored iff the ignore parameter is true.          *
-    ***********************************************************************/
-    { /*********************************************************************
-      * Find covering line.                                                *
-      *********************************************************************/
-      int   line = p.line - 1 ;
-      Token tok = p.toToken(spec) ;
-      boolean notDone = true ;
-      while ((line >= 0) && notDone)
-       { if (spec[line].length > 0)
-           { if (spec[line][0].type == Token.PROLOG)
-               { line = -1 ;
-                 notDone = false;
-               }
-             else
-               { int item = 0 ;
-                 if (ignore && isLeftComment(spec, new Position(line, 0)))
-                   { item = 1 ; }
-                 if (spec[line][item].column <= tok.column)
-                   { notDone = false ;} ;
-               }
-           }; // END if (spec[line].length > 0)
-         if (notDone) {line = line - 1 ;};
-       } // END while ((line >= 0) && notDone)
-
-     /**********************************************************************
-     * If no covering line, return (-1, 0).                                *
-     **********************************************************************/
-     if (line == -1) {return new Position(-1, 0);} ;
-
-     /**********************************************************************
-     *      Find covering item.                                            *
-     **********************************************************************/
-     int item = 0;
-     int nsItem = 0;
-       /********************************************************************
-       * item is the current item.                                         *
-       * nsItem is the last non-subscript item found that covers tok.      *
-       ********************************************************************/
-     boolean dashFound = false ;
-     if (spec[line][0].type == Token.DASHES)
-      { dashFound = true;} ;
-     while (    (! dashFound)
-             && (item + 1 < spec[line].length)
-             && (spec[line][item + 1].column <= tok.column))
-          { if (spec[line][item+1].type == Token.DASHES)
-             {dashFound = true;} ;
-            item = item + 1;
-            if (!spec[line][item].subscript)
-                {nsItem = item;};
-          };
-     if (dashFound) {return new Position(-1, 0);} ;
-
-     /**********************************************************************
-     * Return (line, nsItem).                                              *
-     **********************************************************************/
-     return new Position(line, nsItem);
-    } ;
-
-  private static Position blockingPosition(Token[][] spec, Position p)
-    /***********************************************************************
-    * Searches upwards from position p to find the first token at the      *
-    * same column or to the right of the token at p that is not            *
-    * a subscript token.                                                   *
-    ***********************************************************************/
-    { int line = p.line - 1 ;
-      int item = 0 ;
-      Token tok = p.toToken(spec) ;
-      boolean notDone = true ;
-      while ((line >= 0) && notDone)
-       { if (spec[line].length > 0)
-           { if (spec[line][0].type == Token.PROLOG)
-               { line = -1 ;
-                 notDone = false;
-               }
-             else
-               { item = 0 ;
-                 if (isLeftComment(spec, new Position(line, 0)))
-                   { item = 1 ; } ;
-                 while (notDone && (item < spec[line].length))
-                  { if (   (spec[line][item].column >= tok.column)
-                        && (! spec[line][item].subscript))
-                      { notDone = false ; }
-                    else 
-                      { item = item+1; };
-                  } ;
-               } ;
-           }; // END if (spec[line].length > 0)
-         if (notDone) {line = line - 1 ;} ;
-       } // END while ((line >= 0) && notDone)
-
-     /**********************************************************************
-     * If no token found, return (-1, 0).                                  *
-     **********************************************************************/
-     if (line == -1) {return new Position(-1, 0);} ;
-
-     /**********************************************************************
-     * Return (line, item).                                                *
-     **********************************************************************/
-     return new Position(line, item);
-    } ;
-
-  private static void setSubscriptField(Token[][] spec)
-    /***********************************************************************
-    * Sets the subscript field of the tokens.  (This field is true iff     *
-    * the token is part of a sub- or superscript.)  Upon encountering a    *
-    * "^" or a token that is a built-in symbol with symbolType -           *
-    * Symbol.SUBSCRIPTED token, the next token or all tokens in the        *
-    * properly parenthesized expression that follows it are                *
-    * subscripted--iff all those tokens lie on the same line.              *
-    ***********************************************************************/
-    { int line = 0 ;
-      while (line < spec.length)
-       { int item = 0 ;
-         int startSub = -1 ;
-           /****************************************************************
-           * If a subscript has begun, then this is its first item;        *
-           * otherwise, it equals -1.                                      *
-           ****************************************************************/
-         int nestingDepth = 0 ;
-           /****************************************************************
-           * The current parenthesis nesting level inside a subscript, or  *
-           * 0 if not in a subscript.                                      *
-           ****************************************************************/
-           
-
-         while (item < spec[line].length)
-          { Token tok = spec[line][item] ;
-              /*************************************************************
-              * tok is the current token.                                  *
-              *************************************************************/
-            if (startSub == -1)
-             { /************************************************************
-               * A subscript has not yet begun.                            *
-               ************************************************************/
-               if (   (tok.type == Token.BUILTIN)
-                   && (   (BuiltInSymbols.GetBuiltInSymbol(
-                                                    tok.string, true).symbolType 
-                            == Symbol.SUBSCRIPTED)
-                       || (tok.string.equals("^"))))
-                { 
-                  startSub = item + 1 ;
-                } // END then OF if ((tok.type == Token.BUILTIN) ...)
-               else 
-                { /*********************************************************
-                  * Do nothing.                                            *
-                  *********************************************************/
-                }; // END else OF if ((tok.type = Token.BUILTIN) ...)
-
-             }  // END then OF if (startSub == -1)
-            else
-             { /************************************************************
-               * A subscript has begun.                                    *
-               *                                                           *
-               * Set symType to the symbol type of the token, or           *
-               * NOT_A_SYMBOL if it isn't a symbol.                        *
-               ************************************************************/
-               int symType = Symbol.NOT_A_SYMBOL ;
-               if (tok.type == Token.BUILTIN)
-                 { symType = 
-                      BuiltInSymbols.GetBuiltInSymbol(
-                         tok.string, true).symbolType ;
-                 };
-
-               if (   (   (nestingDepth == 0) 
-                       && (symType != Symbol.LEFT_PAREN))
-                   || (   (nestingDepth == 1) 
-                       && (symType == Symbol.RIGHT_PAREN)))
-                { /*********************************************************
-                  * This ends the subscript.  Set the subscript field of   *
-                  * all tokens from startSub to item and reset startSub.   *
-                  *********************************************************/
-                  nestingDepth = 0 ;
-                  while (startSub <= item)
-                   { spec[line][startSub].subscript = true ;
-                     startSub = startSub + 1;
-                   }
-                  startSub = -1 ;
-                } // END then OF if (((nestingDepth == 0)... ))
-               else
-                { /*********************************************************
-                  * The subscript continues.                               *
-                  *********************************************************/
-                  if (symType == Symbol.LEFT_PAREN)
-                    { nestingDepth = nestingDepth + 1; }
-                  else
-                    { if (symType == Symbol.RIGHT_PAREN)
-                        { nestingDepth = nestingDepth - 1; };
-                    };
-                }; // END else OF if (((nestingDepth == 0)... ))
-
-             }; // END else OF if (startSub == -1)
-
-           item = item + 1 ;
-          } ; // END while (item < spec[line].length)
-
-
-         line = line + 1;
-       } // END while (line < spec.length)
-      
-    } ;
-    
-    
-}
-
-/* last modified on Sun  5 August 2012 at 17:07:48 PST by lamport */
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+//
+// This is the version of FindAlignments modified to be consistent
+// with the versions of TokenizeSpec, BuiltInSymbols, etc. that have
+// been modified to handle PlusCal, but with no special PlusCal alignments
+// added.  It is also being modified to add a special rule for 
+// aligning labels -- or more precisely, aligning tokens that
+// follow labels with other tokens.
+
+/***************************************************************************
+* CLASS FindAlignments                                                     *
+*                                                                          *
+* Contains the one public method                                           *
+*                                                                          *
+*    public static void FindAlignments(Token[][] spec)                     *
+*                                                                          *
+* that sets the aboveAlign, belowAlign, and isAlignmentPoint fields for    *
+* all tokens in the tokenized spec spec--except for NULL and MULTI         *
+* comment tokens, for which the aboveAlign field is set by                 *
+* CommentToken.processComments.  (This method should be called after       *
+* CommentToken.processComments.)                                           *
+*                                                                          *
+* There are six kinds of alignments, illustrated by the following          *
+* example:                                                                 *
+*                                                                          *
+*    Alignment Type         Tokens so aligned                              *
+*    --------------         -----------------                              *
+*    FirstNonLeftComment   foo, x, + , /\ , y/comment r, u/comment k       *
+*                                           ^^^^^^^^^^^                    *
+*                                I think this should be +/comment r        *
+*    LeftComment           comments j                                      *
+*    CommentInner          comments p                                      *
+*    InfixInner            ==, => , >                                      *
+*    AfterInfixInner       m, c,                                           *
+*    InfixArg              a, z/z, y/z                                     *
+*                                                                          *
+*             foo == LET x == /\ a  =>  m * n >  c                         *
+*             |   |      | |  |  |  |   |     |  |                         *
+*    (* j *)  |   |      | |  /\ a  =>  m / q >  c  (* p *)                *
+*    |        |   |      | |                        |                      *
+*    (* j *)  |   |      x ==   z                   |                      *
+*             |   |             |                   |                      *
+*             |   |           + z                   \* p                   *
+*             |   |           | |                   |                      *
+*             |   |           | |                   (* p *)                *
+*             |   |           | |                   |                      *
+*             |   |           + y                   (* p *)                *
+*             |   |           |                                            *
+*             |   |           \* r                                         *
+*             |   |                                                        *
+*             foo ==                                                       *
+*              |                                                           *
+*              (* k *)                                                     *
+*              |                                                           *
+*              u + v                                                       *
+*                                                                          *
+* For InfixInner and CommentInner alignment, a token's belowAlign          *
+* field points to the token directly below it with which it is             *
+* aligned.                                                                 *
+*                                                                          *
+* For all alignment types, if a token t1 is aligned with a token           *
+* t2 above it, then t1.aboveAlign points to:                               *
+*                                                                          *
+*   IF t2 has no aboveAlign pointer THEN t2                                *
+*                                   ELSE t2.abovealign.                    *
+*                                                                          *
+* Thus, in the example above, the aboveAlign pointer for all the p         *
+* comments point to the first p comment.                                   *
+*                                                                          *
+* To define the types of alignment, we use the following definitions:      *
+*                                                                          *
+* - A LEFT-COMMENT is a comment that is the first token on its line but    *
+*   not the last token on its line.  (The comments (* j *) in the          *
+*   example are left-comments.)  For all the types of alignment except     *
+*   CommentInner, left-comments are treated as if they didn't exist.       *
+*                                                                          *
+* - A RIGHT-COMMENT is a comment that is the last token on its line.       *
+*                                                                          *
+* Any comment other than a left-comment or a right-comment is treated      *
+* like any other non-built-in token.  We also define:                      *
+*                                                                          *
+* - The COVERING TOKEN of a token t is the right-most token ct that        *
+*   covers t on the last line (one with biggest line number) containing    *
+*   a token that covers t, where a token t1 COVERS a token t2 if t1        *
+*   lies on an earlier line than t2 and t1.column \leq t2.column.          *
+*   However, if there is a DASH between ct and t, or                       *
+*   on the same line as ct, then t has no covering token.                  *
+*   (This definition has two versions, depending on whether or not         *
+*   left-comments are ignored.)                                            *
+*                                                                          *
+* - The BLOCKING TOKEN of a token t is the left-most token s with          *
+*   s.column \geq t.column on the last line before t's line containing     *
+*   such a non-left-comment token s.                                       *
+*                                                                          *
+* Here are the descriptions of the different types of alignment.           *
+*                                                                          *
+*                                                                          *
+* CommentInner:                                                            *
+*    Token t at position pos is CommentInner above-aligned with its        *
+*    blocking token bt at position bpos iff:                               *
+*       /\ t is a right-comment                                            *
+*       /\ bt is a right-comment                                           *
+*       /\ t.column = bt.column                                            *
+*       /\ \/ bt is not above-aligned with anything                        *
+*          \/ bt is CommentInner aligned with a token above it             *
+*                                                                          *
+* FirstNonLeftComment                                                      *
+*    If t is the first token on a line that is not a left-comment, and is  *
+*    not a right-comment that is CommentInner aligned with a token above   *
+*    it, then t is FirstNonLeftComment aligned with its covering token.    *
+*                                                                          *
+* LeftComment:                                                             *
+*    If t is a left-comment token, then it is LeftComment aligned          *
+*    with its covering token (where left-comments are visible).            *
+*                                                                          *
+* InfixInner:                                                              *
+*    Token t at position pos is InfixInner aligned with its covering       *
+*    token ct at position cpos iff t is not FirstNonLeftComment aligned,   *
+*    and both t and ct are built-in symbols with the same nonzero alignment  *
+*    class.  (The name InfixInner is misleading because non-infix symbols  *
+*    like ":" get aligned by this mechanism.)                              *
+*                                                                          *
+* AfterInfixInner:                                                         *
+*    Token t is After InfixInner aligned with the token above it iff       *
+*                                                                          *
+*       LET lt  == the token to the left of t                              *
+*           alt == the token at position lt.aboveAlign                     *
+*           at  == the token to the right of alt                           *
+*       IN  /\ t is not the first token on the line                        *
+*           /\ lt is InnerAligned with token alt above it                  *
+*           /\ alt is not the last token on its line.                      *
+*           /\ at is the covering token of t                               *
+*           /\ t.column = at.column                                        *
+*           /\ at is not a comment                                         *
+*    (The name AfterInfixInner means "after InfixInner alignment".         *
+*    Remember that some non-infix symbols get InfixInner aligned.)         *
+*                                                                          *
+* InfixArg:                                                                *
+*    Token t is InfixArg aligned with its covering token ct iff:           *
+*       LET lt == the token to the left of t                               *
+*           alt == the token at position lt.aboveAlign                     *
+*       IN  /\ t is not the first token on the line                        *
+*           /\ lt is the first non-left-comment token                      *
+*           /\ lt is an infix operator                                     *
+*           /\ lt is not InfixInner aligned with any token above it.       *
+*           /\ t.column = ct.column                                        *
+*           /\ alt is the token to the left of ct                          *
+*           /\ lt is either aligned with or lies entirely to the           *
+*              right of alt.                                               *
+*                                                                          *
+*   Note: The large number of conditions are an attempt to rule            *
+*   out spurious alignments.                                               *
+* ----------------------------------------------------------------------   *
+*                                                                          *
+* PlusCal Alignment                                                        *
+* -----------------                                                        *
+*                                                                          *
+* The following rule is added for PlusCal.                                 *
+*                                                                          *
+* Define a token t to be a FIRST NON-LABEL if either                       *
+*     (a) it is the first token on the line, or                            *
+*     (b) it is the 2nd token on its line and the first token on           *
+*         the line is a label.                                             *
+*                                                                          *
+* AfterLabel:                                                              *
+*   A token t is AfterLabel aligned with a token ta iff:                   *
+*     /\ t is a first non-label that is the 2nd token on its line          *
+*     /\ t and ta are left-aligned                                         *
+*     /\ there is no line between t and ta containing any token            *
+*        to the left of t other than a label that is the line's            *
+*        first token.                                                      *
+*     /\ \/ ta is a first non-label                                        *
+*        \/ /\ ta is above t                                               *
+*           /\ \/ ta is the first token on the line                        *
+*              \/ the token to the left of ta is a label or                *
+*                 a PlusCal symbol                                         *
+*                                                                          *
+* Here's an example of AfterLabel alignment.  Token b and all the x        *
+* tokens are AfterLabel aligned with token A. Tokens A and B and all the   *
+* other x tokens are also AfterLabel aligned with the first and last x     *
+* tokens.                                                                  *
+*                                                                          *
+*            { B                                                           *
+*          l:  x                                                           *
+*          m:    z                                                         *
+*              x                                                           *
+*          l:  A                                                           *
+*                z                                                         *
+*              x                                                           *
+*        l:    x                                                           *
+*            } z                                                           *
+*                                                                          *
+* Note that B is the only token whose alignment is due to the second       *
+* disjunction of the last conjunction.  That disjunct is designed to try   *
+* to rule out bogus alignments such as the alignment of the y's with the   *
+* 0 in                                                                     *
+*                                                                          *
+*     if (x = 0) {                                                         *
+*       lab:  y := 1;                                                      *
+*       lab:  y := 2  }                                                    *
+*                                                                          *
+* while aligning the the y's in                                            *
+*                                                                          *
+*      if (x = 0) {     y := 0;                                            *
+*                  lab: y := 1;                                            *
+*                  lab: y := 2  }                                          *
+*                                                                          *           
+* and in                                                                   *
+*                                                                          *
+*      if (x = 0) { y := 0;                                                *
+*   lab:            y := 1;                                                *
+*   lab:            y := 2  }                                              *
+*                                                                          *
+* ----------------------------------------------------------------------   *
+*                                                                          *
+* Note: A possible addition is a special case to recognize alignments      *
+* after a comma, as in                                                     *
+*                                                                          *
+*        <<abcdef,  ghi + 7, jklmn>>                                       *
+*                   |        |                                             *
+*        <<a,       bc,      def>>                                         *
+*                                                                          *
+* However, that should be done only if there's a clear intention to align  *
+* them--as evidenced here by the extra spaces between the "a,", the "cb,"  *
+* and the "def".  Otherwise, there would be too many chance alignments.    *
+***************************************************************************/
+package tla2tex;
+
+public class FindAlignments
+{ public static void FindAlignments(Token[][] spec)
+    { setSubscriptField(spec) ;
+        /*******************************************************************
+        * Set the subscript fields of the tokens.                          *
+        *******************************************************************/
+      int line = 0 ;
+        
+      /*********************************************************************
+      * Skip all lines beginning with a prolog token.  This means that if  *
+      * the module begins on the same line as a prolog token, then the     *
+      * first line of the spec is ignored for alignment.  Tant pis!        *
+      *********************************************************************/
+      boolean inProlog = true ;
+      while (inProlog && (line < spec.length))
+       { if (    (spec[line].length > 0)
+              && (spec[line][0].type != Token.PROLOG))
+           { inProlog = false;}
+         else
+           { line = line + 1;}
+       };
+      
+      /*********************************************************************
+      * Set aboveAlign and belowAlign "pointers".                          *
+      *********************************************************************/
+      while (line < spec.length)
+       { int item = 0 ;
+         boolean prevInfixInner = false ;
+           /****************************************************************
+           * True iff the previous item on the line was InfixInner         *
+           * aligned.                                                      *
+           ****************************************************************/
+         while (item < spec[line].length)
+          { 
+            /***************************************************************
+            * Set token, pos to the current token and its position.        *
+            ***************************************************************/
+            Position pos = new Position(line, item) ;
+            Token    token = spec[line][item] ;
+
+            if (! token.subscript)
+             {/*************************************************************
+              * Do not align subscript tokens.                             *
+              *************************************************************/
+              if (isRightComment(spec, pos))
+              { /***********************************************************
+                * This is a RightComment.  First check if it should be     *
+                * CommentInner aligned.  If not, check if it should be     *
+                * FirstNonLeftComment aligned.                             *
+                ***********************************************************/
+
+                /***********************************************************
+                * Set btoken, bpos to the blocking token of `token' and    *
+                * its position.                                            *
+                ***********************************************************/
+                Position bpos   = blockingPosition(spec, pos);              
+                Token    btoken =  null ;
+                if (bpos.line != -1) {btoken = bpos.toToken(spec);};
+                
+                /***********************************************************
+                * Set ctok to be a CommentToken alias for token.           *
+                ***********************************************************/
+                CommentToken ctok = (CommentToken) token ;
+                if (   (ctok.subtype == CommentToken.MULTI)
+                    || (ctok.subtype == CommentToken.NULL))
+                 { /********************************************************
+                   * This is the continuation of a multi-column comment.   *
+                   ********************************************************/
+                   Debug.Assert(   (btoken != null) 
+                                && (btoken.type == Token.COMMENT),
+                      "Bad blocking token for a MULTI or NULL token");
+                   
+                   /********************************************************
+                   * Set token's aboveAlign to point to the BEGIN_MULTI    *
+                   * token beginning the commnet.                          *
+                   ********************************************************/
+                   if ( ((CommentToken) btoken).subtype 
+                            == CommentToken.BEGIN_MULTI)
+                     { token.aboveAlign = bpos ; }
+                   else
+                     { token.aboveAlign = btoken.aboveAlign ; };
+
+                   /********************************************************
+                   * Make the blocking token's belowAlign pointer point    *
+                   * to token.                                             *
+                   ********************************************************/
+                   btoken.belowAlign = pos ;
+                 } // END then OF if  if ((ctok.subtype == .. ))
+                else
+                 { /********************************************************
+                   * This is not the continuation of a multi-line          *
+                   * comment.  Try to CommentInner align it.               *
+                   ********************************************************/
+
+                   if (   (bpos.line != -1)
+                       && isRightComment(spec, bpos)
+                       && (btoken.column == token.column)
+                       && (   (   (btoken.aboveAlign.line == -1)
+                               && (bpos.item > 0))
+                           || (   (btoken.aboveAlign.line != -1)
+                               && (btoken.aboveAlign.
+                                     toToken(spec).belowAlign.line != -1))
+                                /**********************************************
+                                * Asserts that bpos is aboveAligned with a    *
+                                * token that is belowAligned with something,  *
+                                * which is possible only if bpos is           *
+                                * CommentInner aligned.                       *
+                                **********************************************/
+                          )     
+                      )
+                     { /****************************************************
+                       * CommentInner align.                               *
+                       ****************************************************/
+                       btoken.belowAlign = pos ; 
+                       if (btoken.aboveAlign.line == -1)
+                         { token.aboveAlign = bpos; }
+                       else
+                         { token.aboveAlign = btoken.aboveAlign ;};
+                     }  // END then OF if ((bpos.line != -1)...)
+                   else
+                     { /*******************************************************
+                       * FirstNonLeftComment align iff it is the first        *
+                       * non-left comment.                                    *
+                       *******************************************************/
+                       if (   (item == 0)
+                           || (   (item == 1)
+                               && (spec[line][0].type == Token.COMMENT)))
+                         { pos.toToken(spec).aboveAlign = 
+                             coveringPosition(spec, pos, true) ;
+                         } ;
+                     }; // END else OF if ((bpos.line != -1)...)
+
+
+                 } // END else OF if  if ((ctok.subtype == .. ))
+                prevInfixInner = false ;
+              } // END then OF if (isRightComment(spec, pos))                
+
+              { /***********************************************************
+                * This is not a right-comment.  Check for every kind of    *
+                * alignment except CommentInner.                           *
+                ***********************************************************/
+                if (prevInfixInner)
+                 { /********************************************************
+                   * Check for AfterInfixInner alignment.                  *
+                   *                                                       *
+                   * In the following, the positions lPos, alPos, and      *
+                   * aPos are defined by                                   *
+                   *                            alPos --- aPos             *
+                   *                              |                        *
+                   *                              |                        *
+                   *                             lPos --- pos              *
+                   ********************************************************/
+                   Debug.Assert(pos.item > 0, 
+                      "prevInfixInner true for first item on line");
+                   Position lPos  = new Position(pos.line, pos.item - 1);
+                   Debug.Assert(lPos.toToken(spec).aboveAlign.line != -1,
+                     "prevInfixInner true, but token to left not aligned");
+                   Position alPos = lPos.toToken(spec).aboveAlign ;
+                   Token alToken = alPos.toToken(spec);
+                   if (alPos.item + 1 < spec[alPos.line].length)
+                    { Position aPos   = 
+                          new Position(alPos.line, alPos.item + 1) ;
+                      Token    atoken = aPos.toToken(spec);
+                      Position cPos   = coveringPosition(spec, pos, true);
+                      if (   (cPos.line == aPos.line)
+                          && (cPos.item == aPos.item)
+                          && (token.column == atoken.column)
+                          && (atoken.type != Token.COMMENT) )
+                        { /*************************************************
+                          * AfterInfixInner aligned.                       *
+                          *************************************************/
+                           token.aboveAlign = aPos;
+                        } ;
+                    } // END if (alPos.item + 1 < spec[alPos.line].length)
+                   prevInfixInner = false;
+                 } // END then OF if (prevInfixInner)
+               else
+                 { /********************************************************
+                   * Check for every kind of alignment except              *
+                   * AfterInfixInner and CommentInner.                     *
+                   *                                                       *
+                   * First, check if FirstNonLeftComment aligned.          *
+                   ********************************************************/
+                   if (   ((item == 0) && (token.type != Token.COMMENT))
+                       || ((item == 1) && (spec[line][0].type 
+                                              == Token.COMMENT)))
+                    { /*****************************************************
+                      * FirstNonLeftCommentAligned.                        *
+                      *****************************************************/
+                      token.aboveAlign = 
+                            coveringPosition(spec, pos, true);
+                    }
+                   else
+                    { /*****************************************************
+                      * Not FirstNonLeftCommentAligned.                    *
+                      *                                                    *
+                      * Next, check if LeftComment aligned.                *
+                      *****************************************************/
+                      if (isLeftComment(spec, pos))
+                        { token.aboveAlign = 
+                            coveringPosition(spec, pos, false);
+                        }
+                      else
+                        { /*************************************************
+                          * Next, check if InfixInner aligned.             *
+                          *************************************************/
+                          Position cpos = coveringPosition(spec, pos, true) ;
+                          Token ctoken = null ;
+                            /***********************************************
+                            * cpos and ctoken are the covering position    *
+                            * and covering token of the current token.     *
+                            ***********************************************/
+                          if (cpos.line != -1)
+                            { ctoken = cpos.toToken(spec);};
+                          int alignClass = 0 ;  // The alignment classes
+                          int calignClass = 0;  //   of pos and cpos.
+                          if (token.type == Token.BUILTIN)
+                           { alignClass = 
+                                BuiltInSymbols.GetBuiltInSymbol(
+                                   token.string, true).alignmentType ; } ;
+                          if (   (ctoken != null)
+                              && (ctoken.type == Token.BUILTIN))
+                           { calignClass = 
+                                BuiltInSymbols.GetBuiltInSymbol(
+                                   ctoken.string, true).alignmentType ; } ;
+                          if (   (ctoken != null)
+                              && (token.column == ctoken.column)
+                              && (alignClass != 0)
+                              && (alignClass == calignClass))
+                           { /**********************************************
+                             * InfixInner alignment.                       *
+                             **********************************************/
+                             ctoken.belowAlign = pos ;
+                             if (ctoken.aboveAlign.line == -1)
+                              {token.aboveAlign = cpos ; }
+                             else
+                              { token.aboveAlign = ctoken.aboveAlign ; };
+                             prevInfixInner = true;
+                           }  // END then OF if ((token.column == ...))
+                          else
+                           { /**********************************************
+                             * Not InfixInner alignment.  Check last       *
+                             * possibility, which is InfixArg alignment.   *
+                             **********************************************/
+                             if (   (   (item == 1)
+                                     || (   (item == 2)
+                                         && (spec[line][0].type == 
+                                               Token.COMMENT)))
+                                 && (spec[line][item-1].type == Token.BUILTIN)
+                                 && (BuiltInSymbols.GetBuiltInSymbol(
+                                       spec[line][item-1].string, true).symbolType
+                                      == Symbol.INFIX)
+                                 /******************************************
+                                 * Correction made 7 Nov 2001.             *
+                                 * The conjunct above replaced the         *
+                                 * following.                              *
+                                 *                                         *
+                                 * && (BuiltInSymbols.GetBuiltInSymbol(    *
+                                 *       spec[line][item-1].string         *
+                                 *        ).alignmentType != 0)            *
+                                 *                                         *
+                                 * It seems reasonable that this           *
+                                 * alignment should be performed only      *
+                                 * when the token to the left is an infix  *
+                                 * operator.                               *
+                                 ******************************************/
+                                 && (ctoken != null) 
+                                 && (token.column == ctoken.column)
+                                 && (spec[line][item-1].aboveAlign.line
+                                       != -1)
+                               )
+                              { /*******************************************
+                                * Possible InfixArg alignment.             *
+                                *******************************************/
+                             // This can happen and seems harmless.
+                             //   Debug.Assert(ctoken.belowAlign.line == -1,
+                             //       "Trying to InfixArg align with token "
+                             //   + "that is not aligned with token below it");
+
+                                 Token lTok = spec[line][item-1] ;
+                                   /****************************************
+                                   * The token to the left of the current  *
+                                   * token.                                *
+                                   ****************************************/
+                                Position alPos = lTok.aboveAlign ;
+                                Token alTok = alPos.toToken(spec);
+                                  /*****************************************
+                                  * The token with which lTok is aligned   *
+                                  * above.                                 *
+                                  *****************************************/
+                                if (   (alPos.line == cpos.line)
+                                    && (alPos.item == cpos.item - 1)
+                                    )
+                                 { /****************************************
+                                   * InfixArg alignment with a token       *
+                                   * having a token to its left, as in     *
+                                   *                                       *
+                                   *   x ==   a                            *
+                                   *          |                            *
+                                   *        + b                            *
+                                   ****************************************/
+                                   token.aboveAlign = cpos; 
+                                   // following fixes the mis-alignment that occurs if
+                                   // the "x == " occupies less space than the "+".
+                                   // However, for safety, I'm only fixing it for the
+                                   // case of labels, which should be more common.
+                                   // However, I'm not doing it until I find
+                                   // some example of the bug for fear that it
+                                   // might break something else.
+                                   //
+                                   //    ctoken.belowAlign = pos ;
+                                 } 
+                               else
+                                 { if (   (cpos.item == 0)
+                                       || (   (cpos.item == 1)
+                                           && (spec[cpos.line][0].type 
+                                                        == Token.COMMENT)))
+                                    { /*****************************************
+                                      * InfixArg alignment with a token        *
+                                      * having no token to its left.  In       *
+                                      * this case, we can have a               *
+                                      * situation like                         *
+                                      *                                        *
+                                      *    x ==                                *
+                                      *     a                                  *
+                                      *   + b                                  *
+                                      *                                        *
+                                      * where a is both above-aligned          *
+                                      * with x and below-aligned with b.       *
+                                      * I hope this doesn't cause              *
+                                      * problems.                              *
+                                      *****************************************/
+                                      token.aboveAlign = cpos;           
+                                      ctoken.belowAlign = pos ;
+                                    } ;
+                                 }
+                              };
+                           };// END else OF if ((token.column == ...))
+                        }; // END else of if (isLeftComment(spec, pos))
+                    }; // END else OF if (   ((item == 0) && ... ))
+                 }; // END else OF if (prevInfixInner)
+              }; // END else OF (isRightComment(spec, pos))                   
+             }   // END then OF if (! token.subscript)
+            else
+             { prevInfixInner = false ;
+                 /**********************************************************
+                 * Need to reset prevInfixInner for the tokens following   *
+                 * a ^ or _.                                               *
+                 **********************************************************/
+             } ;  
+           item = item + 1 ;
+          } ; // END while (item < spec[line].length)
+
+         line = line + 1;
+         /******************************************************************
+         * Skip over epilog lines.                                         *
+         ******************************************************************/
+         if (    (line < spec.length) 
+              && (spec[line].length > 0)
+              && (spec[line][0].type == Token.EPILOG))
+           { line = spec.length ;} ;
+       }; // END while (line < spec.length)
+
+       // Add the AfterLabel alignments.
+       FindLabelAlignments(spec) ;
+
+       /*********************************************************************
+      * Set isAlignmentPoint flags.  For simplicity, it is set true for    *
+      * any token that is not the first on the line and is either the      *
+      * source or target of a belowAlign pointer                           *
+      *********************************************************************/
+      line = 0 ;
+      while (line < spec.length)
+       { int item = 0 ;
+         while (item < spec[line].length)
+          { Token token = spec[line][item] ;
+            if (token.aboveAlign.line != -1)
+              { if (item > 0)
+                  {token.isAlignmentPoint = true ;
+                  };
+                if (token.aboveAlign.item != 0)
+                   /********************************************************
+                   * Corrected apparent bug on 16 Jan 00: This if          *
+                   * condition was: token.aboveAlign.line != 0.            *
+                   ********************************************************/
+                  { token.aboveAlign.toToken(spec).isAlignmentPoint 
+                            = true; 
+                  };
+              } ;
+
+            if (token.belowAlign.line != -1)
+              { if (item > 0)
+                  {token.isAlignmentPoint = true ;
+                  };
+                if (token.belowAlign.line != 0)
+                  { token.belowAlign.toToken(spec).isAlignmentPoint 
+                            = true; 
+                  };
+              } ;
+
+            item = item + 1 ;
+          } ; // END while (item < spec[line].length)
+
+         line = line + 1;
+       }; // END while (line < spec.length)
+    } ;
+
+  /**
+   * Adds AfterLabel alignments to spec.  More precisely, for something like
+   * 
+   *  if (x) { stmt
+   *    label: stmt
+   *           stmt
+   *    label: stmt
+   *           stmt
+   *           stmt
+   *        }
+   * 
+   * it makes the belowAlign field of all stmts but the last one point to the
+   * stmt below it, and the aboveAlign field of all but the first stmt point to
+   * the one above it.  In this case
+   * 
+   *    while x
+   *           stmt
+   *    label: stmt
+   * 
+   * the first stmt's aboveAlign field should point to x by the FirstNonLeftComment
+   * alignment rule.  For
+   * 
+   *    while x
+   *    label: stmt
+   *    label: stmt
+   * 
+   * the method sets the first stmt's aboveAlign field
+   *  
+   * @param spec
+   */
+  public static void FindLabelAlignments(Token[][] spec) {
+      /*
+       * Do nothing if there is no PlusCal algorithm.
+       */
+      if (!TokenizeSpec.hasPcal) {
+          return ;
+      }
+      
+      /*
+       * We get the first and last line that may begin with a label or a 
+       * PlusCal statement.  Since the algorithm begins with --algorithm
+       * or --fair, we skip the algorithm's first line.
+       */
+      int pcalStartLine = TokenizeSpec.pcalStart.line + 1 ;
+      int pcalEndLine   = TokenizeSpec.pcalEnd.line ;
+      
+      /*
+       * The algorithm works by repeatedly searching for the next line beginning
+       * with a label followed by a token tok.  It then performs the following
+       * two steps
+       * 
+       * 1. Searche downward for tokens AfterLabel aligned with tok, setting
+       *    aboveAlign/belowAlign fields as it finds them. It stops when it reaches a 
+       *    line in which tok is not not aligned with the first token on the
+       *    line.  If there's a token etok on that line that is AfterLabel
+       *    aligned with tok, so it must follow a label, then the aboveAlign
+       *    field of etok is set to point to the AfterLabel aligned token above
+       *    it, whose belowAlign field points to etok.
+       *    
+       * 2. IF tok's aboveAlign field points to a token whose belowAlign field
+       *       points to a field whose belowAlign field points to tok
+       *     THEN do nothing.  I think this is possible only if those fields were set
+       *          by step 1 for a previous label.  If I'm wrong and there's some weird
+       *          situation that caused this alignment (which I think can only be
+       *          the case if tok is a comment), then not stopping will probably
+       *          do more harm than good.
+       *     ELSE search upwards to set aboveAlign/belowAlign fields of tokens 
+       *          AfterLabel aligned with tok. As indicated above, if there
+       *          are no tokens above tok that are AfterLabel aligned with it,
+       *          then tok's aboveAlign field must be set to point to its
+       *          covering token.   
+       */
+      int curLabelLine = pcalStartLine ;
+      
+      while (   (curLabelLine <= pcalEndLine)
+             && (curLabelLine < spec.length) ) {
+          /* 
+           * set curLabelLine to the first line at or below its current
+           * position that begins with a label.
+           */
+          if (   (spec[curLabelLine].length > 1)
+              && (spec[curLabelLine][0].type == Token.PCAL_LABEL) ) {
+              
+              Token tok = spec[curLabelLine][1] ;
+              int alignCol = tok.column ;
+
+              // Perform step 1 for token tok
+              int curLine = curLabelLine + 1 ;
+              
+              // spec[alignLine][alignItem] is the token to which the
+              // next token to be aligned is aligned with.
+              int alignLine = curLabelLine ;
+              int alignItem = 1 ;
+              boolean notDone = true ;
+              // Test of curLine < spec.length added 4 Aug 2015 to fix
+              // bug caused by running off the end of the spec when called by
+              // TLATeX to format a snippet of PlusCal code.  See comment from
+              // this date in TokenizeSpec.
+              while (notDone && (curLine < spec.length)) {
+                  int curItem = 0 ;
+                     // If spec[alignLine][alignItem] is to be aligned with a token
+                     // on this line, then the token is spec[curLine][nextItem] 
+                     // 
+                  boolean shouldSkip = false ;
+                  if (spec[curLine].length != 0) {
+                      if (spec[curLine][0].type == Token.PCAL_LABEL) {
+                          // line begins with label
+                          if (spec[curLine].length > 1) {
+                              curItem = 1 ;
+                          }
+                          else {
+                              // The label is the only token on the line.  Stop
+                              // iff the label is to the left of the alignment
+                              // column.
+                              notDone = (spec[curLine][0].column >= alignCol) ;
+                              shouldSkip = true ;
+                          }
+                      }
+                      else {
+                          // line doesn't begin with label
+                      }
+                      if (!shouldSkip) {
+                          if (spec[curLine][curItem].column < alignCol) {
+                              notDone = false ;
+                          }
+                          else if (spec[curLine][curItem].column == alignCol) {
+                              spec[alignLine][alignItem].belowAlign = 
+                                      new Position(curLine, curItem) ;
+                              spec[curLine][curItem].aboveAlign =
+                                      new Position(alignLine, alignItem) ;
+                              alignLine = curLine ;
+                              alignItem = curItem ;
+                          }
+                      }
+                  }
+                  curLine++ ;
+                  if (   (curLine > pcalEndLine)
+                      || (curLine >= spec.length)) {
+                      notDone = false ;
+                  }
+              }
+              
+              // Perform step 2 for token tok
+              
+              if (    (tok.aboveAlign.line != -1)
+                   && (tok.aboveAlign.toToken(spec).belowAlign.equals(new Position(curLabelLine, 1)))) {
+                  // already aligned with above tokens so do nothing
+              }
+              else {
+                  curLine = curLabelLine - 1 ;
+                  alignLine = curLabelLine ;
+                  alignItem = 1 ;
+                  notDone = true ;
+                  while (notDone) {
+                     if (   (spec[curLine].length > 0 )
+                         && (spec[curLine][0].column <= alignCol)
+                         && (   (spec[curLine][0].type != Token.PCAL_LABEL)
+                             || (   (spec[curLine].length > 1)
+                                  && (spec[curLine][1].column <= alignCol) ) )
+                            // the conjunct above causes the line to be skipped
+                            // if the only token to the left of tok on this
+                            // line is an initial label followed by a token
+                            // to the right of tok.
+                        ) {
+                         if (spec[curLine][0].column == alignCol) {
+                             //  spec[curLine][0] is to be aligned with 
+                             //  spec[alignLine][alignItem] 
+                             spec[alignLine][alignItem].aboveAlign = 
+                                     new Position(curLine, 0) ;
+                             spec[curLine][0].belowAlign =
+                                     new Position(alignLine, alignItem) ;
+                             alignLine = curLine ;
+                             alignItem = 0 ; 
+                         }
+                         else if (spec[alignLine][alignItem].aboveAlign.line == -1) {        
+                             // we need to align spec[alignLine][alignItem] with the
+                             // right-most token on line curLine with column \leq alignCol
+                             int item = 0 ;
+                             while (   (item < spec[curLine].length)
+                                    && (spec[curLine][item].column <= alignCol)) {
+                                 item++;
+                             }
+                             // item is now either off the line or pointing to a token
+                             // to the right of the alignment token ;
+                             item-- ;
+                             
+                             // We  set the aboveAlign pointer of 
+                             // to point to spec[curLine][item] if the latter token is either
+                             // the first one on its line, or else the token to its left is
+                             // either a label or a PlusCal token.  
+                             // If that's the case, we also set the belowAlign pointer of 
+                             // spec[curLine][item] if that token is in the same column
+                             // as spec[alignLine][alignItem].
+                             // 
+                             Token altok = null ;
+                             if (item > 0) {
+                                 altok = spec[curLine][item-1] ;
+                             }
+                             if (   (altok != null)
+                                 && (   (altok.type == Token.PCAL_LABEL)
+                                     || (   (altok.type == Token.BUILTIN)
+                                         && BuiltInSymbols.IsBuiltInSymbol(altok.string, true)
+                                         && ! BuiltInSymbols.IsBuiltInSymbol(altok.string, false)
+                                        )
+                                    )
+                                ) {
+                                 spec[alignLine][alignItem].aboveAlign = 
+                                            new Position(curLine, item) ;
+                                
+                                 if (spec[curLine][item].column == alignCol) {
+                                     spec[curLine][item].belowAlign =
+                                             new Position(alignLine, alignItem) ;
+                                 }
+                             }
+                             // This ends step 2
+                             notDone = false ;
+                         }
+                         else {
+                             notDone = false ;
+                         }
+                     }
+                     curLine-- ;
+                     if (curLine < pcalStartLine) {
+                         notDone = false ;
+                     }
+                  }
+              }     
+          }
+          curLabelLine++ ;
+      }       
+  }
+  /*************************************************************************
+  * The following are functions used in FindAlignments.                    *
+  *************************************************************************/
+  private static boolean isLeftComment(Token[][] spec, Position p)
+    /***********************************************************************
+    * A left-comment is a comment token that is the first token on a line  *
+    * and has another token to its right.  This method returns true iff    *
+    * the token at position p of spec is a left-comment.                   *
+    ***********************************************************************/
+    { return    (p.item == 0)
+             && (spec[p.line][p.item].type == Token.COMMENT) 
+             && (spec[p.line].length > 1) ;
+    } ;
+
+  private static boolean isRightComment(Token[][] spec, Position p)
+    /***********************************************************************
+    * A right-comment is a comment token that is the last token on its     *
+    * line.  This method returns true iff the token at position p in spec  *
+    * is a right-comment.                                                  *
+    ***********************************************************************/
+    { return    (p.item == spec[p.line].length - 1)
+             && (spec[p.line][p.item].type == Token.COMMENT) ;
+    } ;
+
+  private static Position coveringPosition( 
+                          Token[][] spec, Position p, boolean ignore)
+    /***********************************************************************
+    * A token t1 COVERS a token t2 if t1 lies on an earlier line than t2   *
+    * and t1.column \leq t2.column.  This method searches upwards to find  *
+    * the first line with a token that covers the token at position p,     *
+    * and then returns the position of the right-most token on that line   *
+    * that covers the token at p.  When searching for the covering token,  *
+    * left-comments are ignored iff the ignore parameter is true.          *
+    ***********************************************************************/
+    { /*********************************************************************
+      * Find covering line.                                                *
+      *********************************************************************/
+      int   line = p.line - 1 ;
+      Token tok = p.toToken(spec) ;
+      boolean notDone = true ;
+      while ((line >= 0) && notDone)
+       { if (spec[line].length > 0)
+           { if (spec[line][0].type == Token.PROLOG)
+               { line = -1 ;
+                 notDone = false;
+               }
+             else
+               { int item = 0 ;
+                 if (ignore && isLeftComment(spec, new Position(line, 0)))
+                   { item = 1 ; }
+                 if (spec[line][item].column <= tok.column)
+                   { notDone = false ;} ;
+               }
+           }; // END if (spec[line].length > 0)
+         if (notDone) {line = line - 1 ;};
+       } // END while ((line >= 0) && notDone)
+
+     /**********************************************************************
+     * If no covering line, return (-1, 0).                                *
+     **********************************************************************/
+     if (line == -1) {return new Position(-1, 0);} ;
+
+     /**********************************************************************
+     *      Find covering item.                                            *
+     **********************************************************************/
+     int item = 0;
+     int nsItem = 0;
+       /********************************************************************
+       * item is the current item.                                         *
+       * nsItem is the last non-subscript item found that covers tok.      *
+       ********************************************************************/
+     boolean dashFound = false ;
+     if (spec[line][0].type == Token.DASHES)
+      { dashFound = true;} ;
+     while (    (! dashFound)
+             && (item + 1 < spec[line].length)
+             && (spec[line][item + 1].column <= tok.column))
+          { if (spec[line][item+1].type == Token.DASHES)
+             {dashFound = true;} ;
+            item = item + 1;
+            if (!spec[line][item].subscript)
+                {nsItem = item;};
+          };
+     if (dashFound) {return new Position(-1, 0);} ;
+
+     /**********************************************************************
+     * Return (line, nsItem).                                              *
+     **********************************************************************/
+     return new Position(line, nsItem);
+    } ;
+
+  private static Position blockingPosition(Token[][] spec, Position p)
+    /***********************************************************************
+    * Searches upwards from position p to find the first token at the      *
+    * same column or to the right of the token at p that is not            *
+    * a subscript token.                                                   *
+    ***********************************************************************/
+    { int line = p.line - 1 ;
+      int item = 0 ;
+      Token tok = p.toToken(spec) ;
+      boolean notDone = true ;
+      while ((line >= 0) && notDone)
+       { if (spec[line].length > 0)
+           { if (spec[line][0].type == Token.PROLOG)
+               { line = -1 ;
+                 notDone = false;
+               }
+             else
+               { item = 0 ;
+                 if (isLeftComment(spec, new Position(line, 0)))
+                   { item = 1 ; } ;
+                 while (notDone && (item < spec[line].length))
+                  { if (   (spec[line][item].column >= tok.column)
+                        && (! spec[line][item].subscript))
+                      { notDone = false ; }
+                    else 
+                      { item = item+1; };
+                  } ;
+               } ;
+           }; // END if (spec[line].length > 0)
+         if (notDone) {line = line - 1 ;} ;
+       } // END while ((line >= 0) && notDone)
+
+     /**********************************************************************
+     * If no token found, return (-1, 0).                                  *
+     **********************************************************************/
+     if (line == -1) {return new Position(-1, 0);} ;
+
+     /**********************************************************************
+     * Return (line, item).                                                *
+     **********************************************************************/
+     return new Position(line, item);
+    } ;
+
+  private static void setSubscriptField(Token[][] spec)
+    /***********************************************************************
+    * Sets the subscript field of the tokens.  (This field is true iff     *
+    * the token is part of a sub- or superscript.)  Upon encountering a    *
+    * "^" or a token that is a built-in symbol with symbolType -           *
+    * Symbol.SUBSCRIPTED token, the next token or all tokens in the        *
+    * properly parenthesized expression that follows it are                *
+    * subscripted--iff all those tokens lie on the same line.              *
+    ***********************************************************************/
+    { int line = 0 ;
+      while (line < spec.length)
+       { int item = 0 ;
+         int startSub = -1 ;
+           /****************************************************************
+           * If a subscript has begun, then this is its first item;        *
+           * otherwise, it equals -1.                                      *
+           ****************************************************************/
+         int nestingDepth = 0 ;
+           /****************************************************************
+           * The current parenthesis nesting level inside a subscript, or  *
+           * 0 if not in a subscript.                                      *
+           ****************************************************************/
+           
+
+         while (item < spec[line].length)
+          { Token tok = spec[line][item] ;
+              /*************************************************************
+              * tok is the current token.                                  *
+              *************************************************************/
+            if (startSub == -1)
+             { /************************************************************
+               * A subscript has not yet begun.                            *
+               ************************************************************/
+               if (   (tok.type == Token.BUILTIN)
+                   && (   (BuiltInSymbols.GetBuiltInSymbol(
+                                                    tok.string, true).symbolType 
+                            == Symbol.SUBSCRIPTED)
+                       || (tok.string.equals("^"))))
+                { 
+                  startSub = item + 1 ;
+                } // END then OF if ((tok.type == Token.BUILTIN) ...)
+               else 
+                { /*********************************************************
+                  * Do nothing.                                            *
+                  *********************************************************/
+                }; // END else OF if ((tok.type = Token.BUILTIN) ...)
+
+             }  // END then OF if (startSub == -1)
+            else
+             { /************************************************************
+               * A subscript has begun.                                    *
+               *                                                           *
+               * Set symType to the symbol type of the token, or           *
+               * NOT_A_SYMBOL if it isn't a symbol.                        *
+               ************************************************************/
+               int symType = Symbol.NOT_A_SYMBOL ;
+               if (tok.type == Token.BUILTIN)
+                 { symType = 
+                      BuiltInSymbols.GetBuiltInSymbol(
+                         tok.string, true).symbolType ;
+                 };
+
+               if (   (   (nestingDepth == 0) 
+                       && (symType != Symbol.LEFT_PAREN))
+                   || (   (nestingDepth == 1) 
+                       && (symType == Symbol.RIGHT_PAREN)))
+                { /*********************************************************
+                  * This ends the subscript.  Set the subscript field of   *
+                  * all tokens from startSub to item and reset startSub.   *
+                  *********************************************************/
+                  nestingDepth = 0 ;
+                  while (startSub <= item)
+                   { spec[line][startSub].subscript = true ;
+                     startSub = startSub + 1;
+                   }
+                  startSub = -1 ;
+                } // END then OF if (((nestingDepth == 0)... ))
+               else
+                { /*********************************************************
+                  * The subscript continues.                               *
+                  *********************************************************/
+                  if (symType == Symbol.LEFT_PAREN)
+                    { nestingDepth = nestingDepth + 1; }
+                  else
+                    { if (symType == Symbol.RIGHT_PAREN)
+                        { nestingDepth = nestingDepth - 1; };
+                    };
+                }; // END else OF if (((nestingDepth == 0)... ))
+
+             }; // END else OF if (startSub == -1)
+
+           item = item + 1 ;
+          } ; // END while (item < spec[line].length)
+
+
+         line = line + 1;
+       } // END while (line < spec.length)
+      
+    } ;
+    
+    
+}
+
+/* last modified on Sun  5 August 2012 at 17:07:48 PST by lamport */
diff --git a/tlatools/src/tla2tex/LaTeXOutput.java b/tlatools/src/tla2tex/LaTeXOutput.java
index 051caaa6f6baebf5b49ca2d98061bc36f74d86dc..001b9aaa128ee9b3741d23611d87cc955d091637 100644
--- a/tlatools/src/tla2tex/LaTeXOutput.java
+++ b/tlatools/src/tla2tex/LaTeXOutput.java
@@ -41,6 +41,7 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.Arrays;
 import java.util.Vector;
 
 import util.ToolIO;
@@ -566,7 +567,7 @@ private static void InnerWriteAlignmentFile(Token[][] spec,
       line = line + 1;
     } ;   
 
-   PosAndCol.sort(posCols);
+   Arrays.sort(posCols);
 
    /************************************************************************
    * Compute preSpace fields for tokens in the order they appear in the    *
diff --git a/tlatools/src/tla2tex/PosAndCol.java b/tlatools/src/tla2tex/PosAndCol.java
index 828613fb0a932b67875a25e1a9470522c352113a..d33ab345896177cef279f8edbb55c5126b86682e 100644
--- a/tlatools/src/tla2tex/PosAndCol.java
+++ b/tlatools/src/tla2tex/PosAndCol.java
@@ -13,7 +13,7 @@
 package tla2tex;
 // import tlatex.Position;
 //import java.util.Comparator;
-public class PosAndCol extends Position
+public class PosAndCol extends Position implements Comparable<PosAndCol>
  { public int column ;
 
    public PosAndCol(int l, int i, int c)
@@ -22,72 +22,19 @@ public class PosAndCol extends Position
       column = c ;
     } ;
    
-   /************************************************************************
-   * The following heap-sorting method was obtained by trivially modifying *
-   * a method found on the web with the following header:                  *
-   *                                                                       *
-   *    HeapSortAlgorithm.java 1.0 95/06/23 Jason Harrison                 *
-   *    Copyright (c) 1995 University of British Columbia                  *
-   *                                                                       *
-   *    Permission to use, copy, modify, and distribute this software      *
-   *    and its documentation for NON-COMMERCIAL purposes and without      *
-   *    fee is hereby granted provided that this copyright notice          *
-   *    appears in all copies.                                             *
-   *                                                                       *
-   *    UBC MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY   *
-   *    OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT      *
-   *    LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS      *
-   *    FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UBC SHALL NOT BE    *
-   *    LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF         *
-   *    USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS              *
-   *    DERIVATIVES                                                        *
-   ************************************************************************/
-    public static void sort(PosAndCol a[]) 
-      { int N = a.length; 
-        for (int k = N/2; k > 0; k--) 
-           { downHeap(a, k, N); 
-           } 
-        do { PosAndCol T = a[0]; 
-             a[0] = a[N - 1]; 
-             a[N - 1] = T;
-             N = N - 1; 
-             downHeap(a, 1, N); 
-           } 
-        while (N > 1); 
-      }									
-    private static void downHeap(PosAndCol a[], int k, int N) 
-      { PosAndCol T = a[k - 1]; 
-        while (k <= N/2) 
-          { int j = k + k; 
-            if ((j < N) && PosAndCol.lessThan(a[j - 1],a[j]))
-              { j++; } 
-            if ( ! PosAndCol.lessThan(T, a[j - 1]) )
-              { break; } 
-           else 
-              { a[k - 1] = a[j - 1]; 
-                k = j; 
-              } 
-          }
-        a[k - 1] = T; 
-      } 
-
-   private static boolean lessThan(PosAndCol pc1, PosAndCol pc2)
-    { if (pc1.column < pc2.column)
-        { return true ;}
-      else
-        { if (pc1.column == pc2.column)
-            { return (pc1.line < pc2.line) ;
-            }
-          else
-            { return false ;
-            }
-        }
-    }
  
    public String toString()
     { return   "[line |-> " + line + ", item |-> " + item 
              + ", col |-> " + column + "]";
     }
+
+	@Override
+	public int compareTo(final PosAndCol other) {
+		if (column == other.column) {
+			return Integer.compare(line, other.line);
+		}
+		return Integer.compare(column, other.column);
+	}
   }
 
 /* Last modified on Tue 18 Sep 2007 at  6:51:12 PST0by lamport */
diff --git a/tlatools/src/tla2tex/README b/tlatools/src/tla2tex/README
index d71e841d9028a3d1bc96ec18a96f3f5c92c6ea99..dec1fad9fc82fd6ffaf5b9c65d5e9b64306ac6c4 100644
--- a/tlatools/src/tla2tex/README
+++ b/tlatools/src/tla2tex/README
@@ -1,114 +1,121 @@
-FILES
------
-These are the source files for the TLATeX program.  The main program
-is in TLA.java.  In addition to the .java and .class files, there are:
-
-tlatex.sty : A style file used by the LaTeX output.
-
-help.txt : A file with the help message.
-
-info.txt : A file with a reasonably complete description of TLATeX.
-
-words.all : 
-  A list of common English words.  It was obtained by taking the
-  lower-case words from a Linux list, and adding the following,
-  which were inexplicably missing.
-
-   a  may  I  TLA  miscellany
-
-old : a directory of old versions.
-
-POSSIBLE ENHANCEMENTS
----------------------
-1. When formatting an individual comment, TLATeX keeps track of
-   which identifiers appear in the comment as TLA tokens.  It
-   turns ambiguous instances of those identifiers into TLA tokens
-   only for identifiers that are not words.  Possibly do it
-   even if they are words--or perhaps have a sublist of common
-   words and do it only for uncommon words.
-
-2. Introduce a mechanism for turning operators into TeX macros--for
-   example, so Integral(a+b, c, d) gets typeset as 
-
-      \int^{a+b}_{c} d
-
-   This is nontrivial because it requires finding the arguments,
-   which in turn requires counting parentheses and looking for commas
-   and right-parens.  A simpler possibility is just replacing
-   certain identifiers by symbols--e.g., alpha -> \alpha.
-
-   The specification of such replacements would be in the prolog or
-   epilog, so we'd have to introduce some notation for identifying
-   pro/epilog lines as TLATeX commands.  (This notation should generalize
-   to arbitrary tools.)
-
-
-
-BUGS 
-----
-Here are the known bugs in TLATeX:
-
-1. If a ' in a comment is immediately followed by a character, as in
-
-       `token'.
-
-   then TLATeX inserts a spurious space before the ".".  Search
-   for "LEFT_SQUOTE:" in TokenizeComment.java to see how to fix this.  
-   But... a similar problem occurs with `^ ... ^' text, since the `^ 
-   and ^' effectively act as spaces for determining whether to add space
-   between adjacent tokens.  
-
-   The same problem also occurs with a `~ ... ~' term, but in that case, 
-   there may be no solution because the entire term has to be disappeared.
-
-   Some uniform approach to handling this is called for.  It seems like
-   the best approach is to try to increase the size of an immediately 
-   following token by a ` or `^, and of an immediately  preceding token 
-   by the ' or ^'.  This requires adding an explicit width field to
-   a CToken object, or something equivalent.
-
-2. The record component foo.WF_foo will be printed as
-
-      foo.WF
-            foo
-
-   Fixing this unlikely problem requires fixing the lexing algorithm
-   in TokenizeSpec.java.
-
-3. The input 
-      
-      LET x...
-      IN  x...
-
-produces
-
-      LET x...
-      IN   x...
-
-It looks like the two spaces after the "IN" produce the extra space.
-The input
-
-      LET x...
-      IN x...
-
-produces the correct output.
-
-----------
-  
-
-Revision History
-^^^^^^^^^^^^^^^^
-Version 0.95 of 11 November 2001 
- - Modified FindAlignments to do InfixArg alignment only if the
-   symbol to the left is an infix operator.
- - Modified LaTeXOutput.InnerWriteLaTeXFile to end a multiline
-   comment with a comment line that is below-aligned with a 
-   non-comment line.  (Previously, that case had been considered
-   an error.)
- - Fixed the problem of TLATeX hanging if latex or dvips produced
-   a lot of output by:
-    * Adding classes ExecuteCommand and GobbleOutput
-    * Modifying TLA.MakePSFile and LaTeXOutput.RunLatex to call 
-      ExecuteCommand.
-
-Last modified on Wed  8 February 2012 at 13:56:11 PST by lamport
+FILES
+-----
+These are the source files for the TLATeX program.  The main program
+is in TLA.java.  In addition to the .java and .class files, there are:
+
+tlatex.sty : A style file used by the LaTeX output.
+
+help.txt : A file with the help message.
+
+info.txt : A file with a reasonably complete description of TLATeX.
+
+words.all : 
+  A list of common English words.  It was obtained by taking the
+  lower-case words from a Linux list, and adding the following,
+  which were inexplicably missing.
+
+   a  may  I  TLA  miscellany
+
+old : a directory of old versions.
+
+POSSIBLE ENHANCEMENTS
+---------------------
+1. When formatting an individual comment, TLATeX keeps track of
+   which identifiers appear in the comment as TLA tokens.  It
+   turns ambiguous instances of those identifiers into TLA tokens
+   only for identifiers that are not words.  Possibly do it
+   even if they are words--or perhaps have a sublist of common
+   words and do it only for uncommon words.
+
+2. Introduce a mechanism for turning operators into TeX macros--for
+   example, so Integral(a+b, c, d) gets typeset as 
+
+      \int^{a+b}_{c} d
+
+   This is nontrivial because it requires finding the arguments,
+   which in turn requires counting parentheses and looking for commas
+   and right-parens.  A simpler possibility is just replacing
+   certain identifiers by symbols--e.g., alpha -> \alpha.
+
+   The specification of such replacements would be in the prolog or
+   epilog, so we'd have to introduce some notation for identifying
+   pro/epilog lines as TLATeX commands.  (This notation should generalize
+   to arbitrary tools.)
+
+
+
+BUGS 
+----
+Here are the known bugs in TLATeX:
+
+1. If a ' in a comment is immediately followed by a character, as in
+
+       `token'.
+
+   then TLATeX inserts a spurious space before the ".".  Search
+   for "LEFT_SQUOTE:" in TokenizeComment.java to see how to fix this.  
+   But... a similar problem occurs with `^ ... ^' text, since the `^ 
+   and ^' effectively act as spaces for determining whether to add space
+   between adjacent tokens.  
+
+   The same problem also occurs with a `~ ... ~' term, but in that case, 
+   there may be no solution because the entire term has to be disappeared.
+
+   Some uniform approach to handling this is called for.  It seems like
+   the best approach is to try to increase the size of an immediately 
+   following token by a ` or `^, and of an immediately  preceding token 
+   by the ' or ^'.  This requires adding an explicit width field to
+   a CToken object, or something equivalent.
+
+2. The record component foo.WF_foo will be printed as
+
+      foo.WF
+            foo
+
+   Fixing this unlikely problem requires fixing the lexing algorithm
+   in TokenizeSpec.java.
+
+3. The input 
+      
+      LET x...
+      IN  x...
+
+produces
+
+      LET x...
+      IN   x...
+
+It looks like the two spaces after the "IN" produce the extra space.
+The input
+
+      LET x...
+      IN x...
+
+produces the correct output.
+
+
+4. It looks like something goes wrong with the formatting of tokens 
+   that occur at the beginning of a line of a multiline comment.  For
+   example, something like Brxwee that would normally be italicized 
+   if it occured in the middle of text is not italicized if it's
+   at the beginning of the comment line (in the .tla file).
+   
+----------
+  
+
+Revision History
+^^^^^^^^^^^^^^^^
+Version 0.95 of 11 November 2001 
+ - Modified FindAlignments to do InfixArg alignment only if the
+   symbol to the left is an infix operator.
+ - Modified LaTeXOutput.InnerWriteLaTeXFile to end a multiline
+   comment with a comment line that is below-aligned with a 
+   non-comment line.  (Previously, that case had been considered
+   an error.)
+ - Fixed the problem of TLATeX hanging if latex or dvips produced
+   a lot of output by:
+    * Adding classes ExecuteCommand and GobbleOutput
+    * Modifying TLA.MakePSFile and LaTeXOutput.RunLatex to call 
+      ExecuteCommand.
+
+Last modified on Wed  8 February 2012 at 13:56:11 PST by lamport
diff --git a/tlatools/src/tla2tex/TLA.java b/tlatools/src/tla2tex/TLA.java
index 11eeace340a36009a88c7553233c344dddeb62ee..7109379a265aa1b0c0d8d0b1a5566f21ea93f6ce 100644
--- a/tlatools/src/tla2tex/TLA.java
+++ b/tlatools/src/tla2tex/TLA.java
@@ -126,6 +126,7 @@ import java.io.File;
 import java.io.IOException;
 
 import util.FileUtil;
+import util.TLAConstants;
 import util.ToolIO;
 
 public class TLA
@@ -437,7 +438,7 @@ public class TLA
                 Parameters.TLAOutFile = args[nextArg];
                 if (Parameters.TLAOutFile.indexOf(".") == -1)
                 {
-                    Parameters.TLAOutFile = Parameters.TLAOutFile + ".tla";
+                    Parameters.TLAOutFile = Parameters.TLAOutFile + TLAConstants.Files.TLA_EXTENSION;
                 }
                 if (HasPathPrefix(Parameters.TLAOutFile))
                 {
@@ -636,7 +637,7 @@ public class TLA
         ********************************************************************/
         if (args[maxArg].indexOf(".") == -1)
         {
-            Parameters.TLAInputFile = args[maxArg] + ".tla";
+            Parameters.TLAInputFile = args[maxArg] + TLAConstants.Files.TLA_EXTENSION;
         } else
         {
             Parameters.TLAInputFile = args[maxArg];
diff --git a/tlatools/src/tla2tex/TokenizeSpec.java b/tlatools/src/tla2tex/TokenizeSpec.java
index 41ce234d505d5b747704a7e4e0d9fd34c3618044..993d631d2d1373e7223fb7415e27683960e65d6a 100644
--- a/tlatools/src/tla2tex/TokenizeSpec.java
+++ b/tlatools/src/tla2tex/TokenizeSpec.java
@@ -469,9 +469,6 @@ package tla2tex;
 import java.util.Hashtable;
 import java.util.Vector;
 
-import pcal.MappingObject.EndTLAToken;
-import pcal.PCalLocation;
-
 /**
  * @author lamport
  *
@@ -1737,6 +1734,17 @@ public class TokenizeSpec
         /*
          * Prevent null pointer exception in PlusCal algorithm that was
          * not ended.
+         * 
+         * Note added 4 Aug 2015 by LL.  It appears that we will get here with hasPcal
+         * = true and pcalEnd = null TLA2TeX is called to format a snippet of PCal
+         * code.  So, I would expect setting pcalEnd to this dummy non-null value l
+         * often to cause bugs later on in the processing.  However, today is the first 
+         * time I encountered such a bug.  I seem to have fixed this with a patch
+         * to FindAlignments.FindLabelAlignments described in a comment dated today.
+         * I didn't try to understand that code to know if the bug actually was a
+         * reasonable fix or whether it prevents an exception but can produce
+         * unreasonably bad formatting.  Moreover, it seems likely that this dummy
+         * value of pcalEnd will trigger other bugs that I haven't encountered yet.
          */
         if (hasPcal) {
             if (pcalEnd == null) {
diff --git a/tlatools/src/tlc2/Generator.java b/tlatools/src/tlc2/Generator.java
deleted file mode 100644
index 096be20ef479a84b5b36a2764871fcff285a6533..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/Generator.java
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 16:11:13 PST by lamport
-//      modified on Fri Jun  1 15:26:01 PDT 2001 by yuanyu
-
-package tlc2;
-
-import tlc2.output.EC;
-import tlc2.output.MP;
-import tlc2.tool.Simulator;
-import tlc2.util.RandomGenerator;
-import util.ToolIO;
-
-public class Generator {
-  /*
-   * This TLA trace generator generates random execution traces:
-   *     srcjava tlc.Generator spec[.tla]
-   *
-   * The command line provides the following options:
-   *  o -deadlock:    do not check for deadlock.
-   *                  Defaults to checking deadlock if not specified   
-   *  o -f file:      the file storing the traces. Defaults to spec_trace
-   *  o -d num:       the max length of the trace. Defaults to 10
-   *  o -config file: the config file.  Defaults to spec.cfg
-   *  o -coverage seconds: collect coverage information on the spec,
-   *                       print out the information every seconds
-   *    Defaults to no coverage if not specified
-   *  o -n num:       the max number of traces. Defaults to 10
-   *  o -s:           the seed for random simulation
-   *                  Defaults to a random seed if not specified
-   *  o -aril num:    Adjust the seed for random simulation
-   *                  Defaults to 0 if not specified
-   */
-  public static void main(String[] args) {
-    System.out.println("TLC trace generator, " + TLCGlobals.versionOfTLC);
-
-    String mainFile = null;
-    String traceFile = null;
-    boolean deadlock = true;
-    String configFile = null;
-    int traceDepth = 10;
-    int traceNum = 10;
-    boolean noSeed = true;
-    long seed = 0;
-    long aril = 0;
-
-    int index = 0;
-    while (index < args.length) {
-      if (args[index].equals("-f")) {
-	index++;
-	if (index >= args.length) {
-	  printErrorMsg("Error: no argument given for -f option.");
-	  return;
-	}
-	traceFile = args[index++];
-      }
-      else if (args[index].equals("-deadlock")) {
-	index++;
-	deadlock = false;
-      }
-      else if (args[index].equals("-d")) {
-	index++;
-	if (index >= args.length) {
-	  printErrorMsg("Error: no argument given for -d option.");
-	  return;
-	}
-	traceDepth = Integer.parseInt(args[index]);
-	index++;
-      }
-      else if (args[index].equals("-n")) {
-	index++;
-	if (index >= args.length) {
-	  printErrorMsg("Error: no argument given for -n option.");
-	  return;
-	}
-	traceNum = Integer.parseInt(args[index]);
-	index++;
-      }
-      else if (args[index].equals("-coverage")) {
- 	index++;
- 	if (index < args.length) {
- 	  try {
- 	    TLCGlobals.coverageInterval = Integer.parseInt(args[index]) * 60 * 1000;
-	    if (TLCGlobals.coverageInterval < 0) {
-	      printErrorMsg("Error: expect a nonnegative integer for -coverage option.");
-	      return;
-	    }
- 	    index++;
- 	  }
- 	  catch (Exception e) {
- 	    printErrorMsg("Error: An integer for coverage report interval required." +
- 			  " But encountered " + args[index]);
- 	    return;
- 	  }
- 	}
- 	else {
- 	  printErrorMsg("Error: coverage report interval required.");
-	  return;
- 	}
-      }
-      else if (args[index].equals("-s")) {
-	index++;
-	if (index < args.length) {
-	  seed = Long.parseLong(args[index++]);
-	  noSeed = false;
-	}
-	else {
-	  printErrorMsg("Error: seed required.");
-	  return;
-	}
-      }
-      else if (args[index].equals("-aril")) {
-	index++;
-	if (index < args.length) {
-	  aril = Long.parseLong(args[index++]);
-	}
-	else {
-	  printErrorMsg("Error: aril required.");
-	  return;
-	}
-      }
-      else if (args[index].equals("-config")) {
-	index++;
-	if (index < args.length) {
-	  configFile = args[index];
-	  int len = configFile.length();
-	  if (configFile.startsWith(".cfg", len-4)) {
-	    configFile = configFile.substring(0, len-4);
-	  }
-	  index++;
-	}
-	else {
-	  printErrorMsg("Error: config file required.");
-	  return;
-	}
-      }
-      else {
-	if (args[index].charAt(0) == '-') {
-	  printErrorMsg("Error: unrecognized option: " + args[index]);
-	  return;
-	}
-	if (mainFile != null) {
-	  printErrorMsg("Error: more than one input files: " + mainFile
-			+ " and " + args[index]);
-	  return;
-	}
-	mainFile = args[index++];
-	int len = mainFile.length();
-	if (mainFile.startsWith(".tla", len-4)) {
-	  mainFile = mainFile.substring(0, len-4);
-	}
-      }
-    }
-    if (mainFile == null) {
-      printErrorMsg("Error: Missing input TLA+ module.");
-      return;
-    }
-    if (traceFile == null) traceFile = mainFile + "_trace";
-    if (configFile == null) configFile = mainFile;
-    
-    // Start generating traces:
-    try {
-      RandomGenerator rng = new RandomGenerator();
-      if (noSeed) {
-	seed = rng.nextLong();
-	rng.setSeed(seed);
-      }
-      else {
-	rng.setSeed(seed, aril);
-      }
-      ToolIO.out.println("Generating random traces with seed " + seed + ".");
-      Simulator simulator = new Simulator(mainFile, configFile, traceFile,
-					  deadlock, traceDepth, traceNum,
-					  rng, seed, true, null, /* no spec obj */ null);
-      simulator.simulate();
-    }
-    catch (Exception e) {
-      // Assert.printStack(e);
-        MP.printError(EC.GENERAL, "generating traces", e);  // LL changed call 7 April 2012
-    }
-    //System.exit(0); //SZ: no-op removed
-  }
-
-  // TODO replace 
-  private static void printErrorMsg(String msg) {
-      MP.printError(EC.WRONG_COMMANDLINE_PARAMS_SIMULATOR, msg);
-  }
-
-}
diff --git a/tlatools/src/tlc2/REPLSpecWriter.java b/tlatools/src/tlc2/REPLSpecWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..74af71313721d8eaca7062befd20b187a1fdffd5
--- /dev/null
+++ b/tlatools/src/tlc2/REPLSpecWriter.java
@@ -0,0 +1,84 @@
+package tlc2;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+
+import tlc2.output.AbstractSpecWriter;
+import tlc2.output.SpecWriterUtilities;
+
+class REPLSpecWriter extends AbstractSpecWriter {
+	private static final String EXPRESSION_OPEN = "\"EXPR_TLATE_BEGIN\"";
+	private static final String EXPRESSION_CLOSE = "\"EXPR_TLATE_END\"";
+	private static final String ASSUME_PREFIX = "ASSUME PrintT(" + EXPRESSION_OPEN + ") /\\ PrintT(\n";
+	private static final String ASSUME_SUFFIX = "\n) /\\ PrintT(" + EXPRESSION_CLOSE + ")\n";
+
+	REPLSpecWriter(final String specName, final List<String> expressions) {
+		super(true);
+		
+		tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(specName,
+																	   "Naturals", "Reals", "Sequences", "Bags",
+																	   "FiniteSets", "TLC"));
+		tlaBuffer.append(ASSUME_PREFIX);
+		tlaBuffer.append(String.join("\n", expressions));
+		tlaBuffer.append(ASSUME_SUFFIX);
+	}
+	
+	
+	static class REPLLogConsumerStream extends ByteArrayOutputStream {
+		private boolean inResponse;
+		private boolean haveCollected;
+		private StringBuilder currentLine;
+		
+		private String collectedContent;
+		
+		REPLLogConsumerStream() {
+			inResponse = false;
+			haveCollected = false;
+			currentLine = new StringBuilder();
+		}
+		
+		String getCollectedContent() {
+			return collectedContent;
+		}
+		
+		// Being wrapped in a Buffer invokes this method, we sub-optimally pass it off to our real digestion below
+		@Override
+		public void write(final byte b[], final int off, final int len) {
+			for (int i = off; i < (off + len); i++) {
+				write(b[i]);
+			}
+		}
+
+		@Override
+		public void write(final int b) {
+			if (haveCollected) {
+				return;
+			} else {
+				if (b == '\n') {
+					if (inResponse) {
+						if (EXPRESSION_CLOSE.equals(currentLine.toString())) {
+							haveCollected = true;
+							
+							final String allCollectedContent = toString();
+							final int length = allCollectedContent.length();
+							collectedContent = allCollectedContent.substring(0, (length - (EXPRESSION_CLOSE.length() + 1)));
+						} else {
+							super.write(b);
+						}
+					} else if (EXPRESSION_OPEN.equals(currentLine.toString())) {
+						inResponse = true;
+					}
+					
+					currentLine = new StringBuilder();
+ 				} else {
+					if (inResponse) {
+						super.write(b);
+					}
+ 					
+ 					// casting to char is probably not the best thing - suffices for the majority of the presumed cases
+ 					currentLine.append((char)b);
+ 				}
+			}
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/TLC.java b/tlatools/src/tlc2/TLC.java
index f8e2b456b4b5d122316eeffee76b7159bd808f8f..2be228550dbfe50d760faf65896e8f05b7d78ebd 100644
--- a/tlatools/src/tlc2/TLC.java
+++ b/tlatools/src/tlc2/TLC.java
@@ -5,91 +5,147 @@
 
 package tlc2;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.net.UnknownHostException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Enumeration;
+import java.util.Date;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TimeZone;
+import java.util.concurrent.Phaser;
 
 import model.InJarFilenameToStream;
 import model.ModelInJar;
-import tla2sany.modanalyzer.ParseUnit;
-import tla2sany.modanalyzer.SpecObj;
+import tlc2.input.MCOutputPipeConsumer;
+import tlc2.input.MCParser;
+import tlc2.input.MCParserResults;
 import tlc2.output.EC;
 import tlc2.output.MP;
-import tlc2.tool.AbstractChecker;
-import tlc2.tool.Cancelable;
+import tlc2.output.Messages;
+import tlc2.output.TLAMonolithCreator;
+import tlc2.output.TeeOutputStream;
 import tlc2.tool.DFIDModelChecker;
 import tlc2.tool.ModelChecker;
 import tlc2.tool.Simulator;
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.fp.FPSetFactory;
+import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.ModelConfig;
+import tlc2.tool.impl.SpecProcessor;
 import tlc2.tool.management.ModelCheckerMXWrapper;
 import tlc2.tool.management.TLCStandardMBean;
+import tlc2.util.DotStateWriter;
 import tlc2.util.FP64;
+import tlc2.util.IStateWriter;
+import tlc2.util.NoopStateWriter;
 import tlc2.util.RandomGenerator;
-import tlc2.value.Value;
+import tlc2.util.StateWriter;
+import tlc2.value.RandomEnumerableValues;
+import util.Assert.TLCRuntimeException;
 import util.DebugPrinter;
+import util.ExecutionStatisticsCollector;
 import util.FileUtil;
 import util.FilenameToStream;
 import util.MailSender;
 import util.SimpleFilenameToStream;
+import util.TLAConstants;
+import util.TLCRuntime;
 import util.ToolIO;
 import util.UniqueString;
+import util.UsageGenerator;
 
 /**
- * Main TLC starter class
+ * Main TLC starter class.
+ * 
+ * <b>Note:</b> If you are using a new instance of this class in an already existant JVM which has done processing with
+ * 			tlc2 parsers, please see the class javadocs for {@link TLCRunner}
+ * 
  * @author Yuan Yu
  * @author Leslie Lamport
  * @author Simon Zambrovski
- * @version $Id$
  */
-public class TLC
-{
-    
+public class TLC {
     private static boolean MODEL_PART_OF_JAR = false;
+    
+    private enum RunMode {
+    	MODEL_CHECK, SIMULATE;
+    }
+    
 
     // SZ Feb 20, 2009: the class has been 
     // transformed from static to dynamic
-    private boolean isSimulate; 
+
+    private RunMode runMode;
     private boolean cleanup;
     private boolean deadlock;
 
     private boolean noSeed;
     private long seed;
     private long aril;
+    
+	private long startTime;
 
     private String mainFile;
     private String configFile;
-    private String dumpFile;
+	private String metadir;
+    /**
+	 * If instantiated with a non-Noop* instance, the trace will be written to the
+	 * user provided file (-dump parameter).
+	 * <p>
+	 * Contrary to plain -dump, -dot will also write out transitions from state s to
+	 * s' if s' is already known. Thus, the resulting graph shows all successors of
+	 * s instead of just s's unexplored ones.
+	 * <p>
+	 * Off (NoopStateWriter) by default.
+	 */
+    private IStateWriter stateWriter = new NoopStateWriter();
+
     private String fromChkpt;
 
     private int fpIndex;
     /**
      * The number of traces/behaviors to generate in simulation mode
      */
-    public static long traceNum = Long.MAX_VALUE;
+    private static long traceNum = Long.MAX_VALUE;
+    private String traceFile = null;
     private int traceDepth;
     private FilenameToStream resolver;
-    private SpecObj specObj;
 
     // flag if the welcome message is already printed
     private boolean welcomePrinted;
     
-    // handle to the cancellable instance (MC or Simulator)
-    private Cancelable instance;
-
     private FPSetConfiguration fpSetConfiguration;
     
+    private File temporaryMCOutputLogFile;
+    private FileOutputStream temporaryMCOutputStream;
+    private File specTETLAFile;
+    private MCParserResults mcParserResults;
+    private MCOutputPipeConsumer mcOutputConsumer;
+    private boolean avoidMonolithSpecTECreation;
+    private final Phaser waitingOnGenerationCompletion;
+    
     /**
      * Initialization
      */
-    public TLC()
-    {
+	public TLC() {
         welcomePrinted = false;
         
-        isSimulate = false; // Default to model checking
+        runMode = RunMode.MODEL_CHECK;
         cleanup = false;
         deadlock = true;
         
@@ -99,136 +155,222 @@ public class TLC
         
         mainFile = null;
         configFile = null;
-        dumpFile = null;
         fromChkpt = null;
         resolver = null;
 
-        fpIndex = 0;
+        fpIndex = new Random().nextInt(FP64.Polys.length);
         traceDepth = 100;
-        
-        // instance is not set
-        instance = null;
 
         fpSetConfiguration = new FPSetConfiguration();
-    }
+        
+        avoidMonolithSpecTECreation = false;
+        waitingOnGenerationCompletion = new Phaser();
+        waitingOnGenerationCompletion.register();
+	}
 
     /*
      * This TLA checker (TLC) provides the following functionalities:
-     *  1. Simulation of TLA+ specs: java tlc2.TLC -simulate spec[.tla]
-     *  2. Model checking of TLA+ specs: java tlc2.TLC [-modelcheck] spec[.tla]
+     *  1. Simulation of TLA+ specs:
+     *  				java tlc2.TLC -simulate spec[.tla]
+     *  2. Model checking of TLA+ specs:
+     *  				java tlc2.TLC [-modelcheck] spec[.tla]
      *
-     * The command line also provides the following options:
+     * The command line also provides the following options observed for functionalities 1. & 2.:
      *  o -config file: provide the config file.
-     *    Defaults to spec.cfg if not provided
+     *		Defaults to spec.cfg if not provided
      *  o -deadlock: do not check for deadlock.
-     *    Defaults to checking deadlock if not specified
+     *		Defaults to checking deadlock if not specified
      *  o -depth num: specify the depth of random simulation 
-     *    Defaults to 100 if not specified
+     *		Defaults to 100 if not specified
      *  o -seed num: provide the seed for random simulation
-     *    Defaults to a random seed if not specified
+     *		Defaults to a random seed if not specified
      *  o -aril num: Adjust the seed for random simulation
-     *    Defaults to 0 if not specified
+     *		Defaults to 0 if not specified
      *  o -recover path: recover from the checkpoint at path
-     *    Defaults to scratch run if not specified
-     *  o -bound: The upper limit for sets effectively limiting the number of init states
-     *    (@see http://bugzilla.tlaplus.net/show_bug.cgi?id=264)
-     *    Defaults to 1000000 if not specified
+     *		Defaults to scratch run if not specified
      *  o -metadir path: store metadata in the directory at path
-     *    Defaults to specdir/states if not specified
+     *		Defaults to specdir/states if not specified
+	 *  o -userFile file: A full qualified/absolute path to a file to log user
+	 *					output (Print/PrintT/...) to
      *  o -workers num: the number of TLC worker threads
-     *    Defaults to 1
+     *		Defaults to 1
      *  o -dfid num: use depth-first iterative deepening with initial depth num
      *  o -cleanup: clean up the states directory
-     *  o -dump file: dump all the states into file
+     *  o -dump [dot] file: dump all the states into file. If "dot" as sub-parameter
+     *					is given, the output will be in dot notation.
      *  o -difftrace: when printing trace, show only
-     *                the differences between successive states
-     *    Defaults to printing full state descriptions if not specified
-     *    (Added by Rajeev Joshi)
+     *					the differences between successive states
+     *		Defaults to printing full state descriptions if not specified
+     *					(Added by Rajeev Joshi)
      *  o -terse: do not expand values in Print statement
-     *    Defaults to expand value if not specified
+     *		Defaults to expand value if not specified
      *  o -coverage minutes: collect coverage information on the spec,
-     *                       print out the information every minutes.
-     *    Defaults to no coverage if not specified
+     *					print out the information every minutes.
+     *		Defaults to no coverage if not specified
      *  o -continue: continue running even when invariant is violated
-     *    Defaults to stop at the first violation if not specified
+     *		Defaults to stop at the first violation if not specified
+     *  o -lncheck: Check liveness properties at different times
+     *					of model checking.
+     *		Defaults to false increasing the overall model checking time.
      *  o -nowarning: disable all the warnings
-     *    Defaults to report warnings if not specified
+     *		Defaults to report warnings if not specified
      *  o -fp num: use the num'th irreducible polynomial from the list
-     *    stored in the class FP64.
+     *					stored in the class FP64.
      *  o -view: apply VIEW (if provided) when printing out states.
      *  o -gzip: control if gzip is applied to value input/output stream.
-     *    Defaults to use gzip.
+     *		Defaults to off if not specified
      *  o -debug: debbuging information (non-production use)
      *  o -tool: tool mode (put output codes on console)
+     *  o -generateSpecTE: will generate SpecTE assets if error-states are
+     *  				encountered during model checking; this will change
+     *  				to tool mode regardless of whether '-tool' was
+     *  				explicitly specified; add on 'nomonolith' to not
+     *  				embed the dependencies in the SpecTE
      *  o -checkpoint num: interval for check pointing (in minutes)
-     *     Defaults to 30
-     *  o -fpmem num: the number of megabytes of memory used to store
-     *                the fingerprints of found states.
-     *  Defaults to 1/4 physical memory.  (Added 6 Apr 2010 by Yuan Yu.)
+     *		Defaults to 30
+     *  o -fpmem num: a value between 0 and 1, exclusive, representing the ratio
+     *  				of total system memory used to store the fingerprints of
+     *  				found states.
+     *  	Defaults to 1/4 physical memory.  (Added 6 Apr 2010 by Yuan Yu.)
      *  o -fpbits num: the number of msb used by MultiFPSet to create nested FPSets.
-     *  Defaults to 1
+     *  	Defaults to 1
      *  o -maxSetSize num: the size of the largest set TLC will enumerate.
-     *                     default: 1000000
-     *    
+     *		Defaults to 1000000
+     *   
      */
-    public static void main(String[] args) throws UnknownHostException, FileNotFoundException
+    public static void main(String[] args) throws Exception
     {
-        TLC tlc = new TLC();
+        final TLC tlc = new TLC();
 
-        // handle parameters
-        if (tlc.handleParameters(args))
-        {
-        	final MailSender ms = new MailSender(tlc.mainFile);
-        	if (MODEL_PART_OF_JAR) {
-        		tlc.setResolver(new InJarFilenameToStream(ModelInJar.PATH));
-        	} else {
-        		tlc.setResolver(new SimpleFilenameToStream());
-        	}
-            // call the actual processing method
-            tlc.process();
-
-            // Send logged output by email
-            boolean success = ms.send(tlc.getModuleFiles());
-            
-			// In case sending the mail has failed, we treat this as an error.
-			// This is needed when TLC runs on another host and email is
-			// the only means for the user to get access to the model checking
-			// results. 
-			// With distributed TLC and CloudDistributedTLCJob in particular,
-			// the cloud node is immediately turned off once the TLC process has
-			// finished. If we were to shutdown the node even when sending out 
-            // the email has failed, the result would be lost.
-			if (!success) {
-				System.exit(1);
+        // Try to parse parameters.
+        if (!tlc.handleParameters(args)) {
+            // This is a tool failure. We must exit with a non-zero exit
+            // code or else we will mislead system tools and scripts into
+            // thinking everything went smoothly.
+            //
+            // FIXME: handleParameters should return an error object (or
+            // null), where the error object contains an error message.
+            // This makes handleParameters a function we can test.
+            System.exit(1);
+        }
+        
+        if (!tlc.checkEnvironment()) {
+            System.exit(1);
+        }
+
+		final MailSender ms = new MailSender();
+		// Setup how spec files will be resolved in the filesystem.
+		if (MODEL_PART_OF_JAR) {
+			// There was not spec file given, it instead exists in the
+			// .jar file being executed. So we need to use a special file
+			// resolver to parse it.
+			tlc.setResolver(new InJarFilenameToStream(ModelInJar.PATH));
+		} else {
+			// The user passed us a spec file directly. To ensure we can
+			// recover it during semantic parsing, we must include its
+			// parent directory as a library path in the file resolver.
+			//
+			// If the spec file has no parent directory, use the "standard"
+			// library paths provided by SimpleFilenameToStream.
+			final String dir = FileUtil.parseDirname(tlc.getMainFile());
+			if (!dir.isEmpty()) {
+				tlc.setResolver(new SimpleFilenameToStream(dir));
+			} else {
+				tlc.setResolver(new SimpleFilenameToStream());
 			}
+		}
+
+		// Setup MailSender *before* calling tlc.process. The MailSender's task it to
+		// write the MC.out file. The MC.out file is e.g. used by CloudTLC to feed
+		// progress back to the Toolbox (see CloudDistributedTLCJob).
+		ms.setModelName(tlc.getModelName());
+		ms.setSpecName(tlc.getSpecName());
+
+        // Execute TLC.
+        final int errorCode = tlc.process();
+
+        // Send logged output by email.
+        //
+        // This is needed when TLC runs on another host and email is
+        // the only means for the user to get access to the model
+        // checking results.
+        boolean mailSent = ms.send(tlc.getModuleFiles());
+
+        // Treat failure to send mail as a tool failure.
+        //
+        // With distributed TLC and CloudDistributedTLCJob in particular,
+        // the cloud node is immediately turned off once the TLC process
+        // has finished. If we were to shutdown the node even when sending
+        // out the email has failed, the result would be lost.
+        if (!mailSent) {
+            System.exit(1);
         }
-        // terminate
-        System.exit(0);
+
+        // Be explicit about tool success.
+        System.exit(EC.ExitStatus.errorConstantToExitStatus(errorCode));
     }
+    
+	// false if the environment (JVM, OS, ...) makes model checking impossible.
+	// Might also result in warnings.
+	private boolean checkEnvironment() {
+		// Not a reasons to refuse startup but warn about non-ideal garbage collector.
+		// See https://twitter.com/lemmster/status/1089656514892070912 for actual
+		// performance penalty.
+		if (!TLCRuntime.getInstance().isThroughputOptimizedGC()) {
+			MP.printWarning(EC.TLC_ENVIRONMENT_JVM_GC);
+		}
+		
+		return true;
+	}
+
+	public static void setTraceNum(int aTraceNum) {
+		traceNum = aTraceNum;
+	}
 
     /**
      * This method handles parameter arguments and prepares the actual call
      * <strong>Note:</strong> This method set ups the static TLCGlobals variables
      * @return status of parsing: true iff parameter check was ok, false otherwise
+     * @throws IOException 
      */
     // SZ Feb 23, 2009: added return status to indicate the error in parsing
-    @SuppressWarnings("deprecation")
+	@SuppressWarnings("deprecation")	// we're emitting a warning to the user, but still accepting fpmem values > 1
 	public boolean handleParameters(String[] args)
     {
+		String dumpFile = null;
+		boolean asDot = false;
+	    boolean colorize = false;
+	    boolean actionLabels = false;
+		boolean snapshot = false;
+		
         // SZ Feb 20, 2009: extracted this method to separate the 
         // parameter handling from the actual processing
-               
         int index = 0;
 		while (index < args.length)
         {
             if (args[index].equals("-simulate"))
             {
-                isSimulate = true;
-                index++;
-            } else if (args[index].equals("-modelcheck"))
-            {
-                isSimulate = false;
+            	runMode = RunMode.SIMULATE;
                 index++;
+                
+				// Simulation args can be:
+				// file=/path/to/file,num=4711 or num=4711,file=/path/to/file or num=4711 or
+				// file=/path/to/file
+				// "file=..." and "num=..." are only relevant for simulation which is why they
+				// are args to "-simulate".
+				if (((index + 1) < args.length) && (args[index].contains("file=") || args[index].contains("num="))) {
+					final String[] simArgs = args[index].split(",");
+					index++; // consume simulate args
+					for (String arg : simArgs) {
+						if (arg.startsWith("num=")) {
+							traceNum = Integer.parseInt(arg.replace("num=", ""));
+						} else if (arg.startsWith("file=")) {
+							traceFile = arg.replace("file=", "");
+						}
+					}
+				}
+			} else if (args[index].equals("-modelcheck")) {
+				index++;
             } else if (args[index].equals("-difftrace"))
             {
                 index++;
@@ -248,11 +390,11 @@ public class TLC
             } else if (args[index].equals("-gzip"))
             {
                 index++;
-                TLCGlobals.useGZIP = false;
+                TLCGlobals.useGZIP = true;
             } else if (args[index].equals("-terse"))
             {
                 index++;
-                Value.expand = false;
+                TLCGlobals.expand = false;
             } else if (args[index].equals("-continue"))
             {
                 index++;
@@ -269,40 +411,152 @@ public class TLC
             {
                 index++;
                 TLCGlobals.tool = true;
-            } else if (args[index].equals("-help"))
+            } else if (args[index].equals("-generateSpecTE")) {
+                index++;
+            	
+                TLCGlobals.tool = true;
+                
+                if ((index < args.length) && args[index].equals("nomonolith")) {
+                	index++;
+                	avoidMonolithSpecTECreation = true;
+                }
+				try {
+					temporaryMCOutputLogFile = File.createTempFile("mcout_", ".out");
+					temporaryMCOutputLogFile.deleteOnExit();
+					temporaryMCOutputStream = new FileOutputStream(temporaryMCOutputLogFile);
+					final BufferedOutputStream bos = new BufferedOutputStream(temporaryMCOutputStream);
+					final PipedInputStream pis = new PipedInputStream();
+					final TeeOutputStream tos1 = new TeeOutputStream(bos, new PipedOutputStream(pis));
+					final TeeOutputStream tos2 = new TeeOutputStream(ToolIO.out, tos1);
+					ToolIO.out = new PrintStream(tos2);
+					mcOutputConsumer = new MCOutputPipeConsumer(pis, null);
+					
+					// Note, this runnable's thread will not finish consuming output until just
+					// 	before the app exits and we will use the output consumer in the TLC main
+					//	thread while it is still consuming (but at a point where the model checking
+					//	itself has finished and so the consumer is as populated as we need it to be
+					//	- but prior to the output consumer encountering the EC.TLC_FINISHED message.)
+					final Runnable r = () -> {
+						boolean haveClosedOutputStream = false;
+						try {
+							waitingOnGenerationCompletion.register();
+							mcOutputConsumer.consumeOutput(false);
+							
+							bos.flush();
+							temporaryMCOutputStream.close();
+							haveClosedOutputStream = true;
+							
+							if (mcOutputConsumer.getError() != null) {
+								final File tempTLA = File.createTempFile("temp_tlc_tla_", ".tla");
+								tempTLA.deleteOnExit();
+								FileUtil.copyFile(specTETLAFile, tempTLA);
+								
+								final FileOutputStream fos = new FileOutputStream(specTETLAFile);
+								final FileInputStream mcOutFIS = new FileInputStream(temporaryMCOutputLogFile);
+								copyStream(mcOutFIS, fos);
+								
+								final FileInputStream tempTLAFIS = new FileInputStream(tempTLA);
+								copyStream(tempTLAFIS, fos);
+								
+								fos.close();
+								mcOutFIS.close();
+								tempTLAFIS.close();
+								
+								final TLAMonolithCreator monolithCreator;
+								if (avoidMonolithSpecTECreation) {
+									monolithCreator
+										= new TLAMonolithCreator(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+																 mcOutputConsumer.getSourceDirectory(),
+																 mcParserResults.getAllExtendedModules());
+								} else {
+									final List<File> extendedModules = mcOutputConsumer.getExtendedModuleLocations();
+									monolithCreator
+										= new TLAMonolithCreator(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+																 mcOutputConsumer.getSourceDirectory(),
+																 extendedModules,
+																 mcParserResults.getAllExtendedModules(),
+																 mcParserResults.getAllInstantiatedModules());
+								}
+								
+								monolithCreator.copy();
+							}
+							
+							waitingOnGenerationCompletion.arriveAndDeregister();
+						} catch (final Exception e) {
+							MP.printMessage(EC.GENERAL,
+											"A model checking error occurred while parsing tool output; the execution "
+													+ "ended before the potential "
+													+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME
+													+ " generation stage.");
+						} finally {
+							if (!haveClosedOutputStream) {
+								try {
+									bos.flush();
+									temporaryMCOutputStream.close();
+								} catch (final Exception e) { }
+							}
+						}
+					};
+					(new Thread(r)).start();
+					
+					MP.printMessage(EC.GENERAL,
+									"Will generate a " + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME
+										+ " file pair if error states are encountered.");
+				} catch (final IOException ioe) {
+					printErrorMsg("Failed to set up piped output consumers; no potential "
+										+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " will be generated: "
+										+ ioe.getMessage());
+					mcOutputConsumer = null;
+				}
+            } else if (args[index].equals("-help") || args[index].equals("-h"))
             {
                 printUsage();
                 return false;
-            } else if (args[index].equals("-config"))
+            } else if (args[index].equals("-lncheck"))
             {
                 index++;
                 if (index < args.length)
                 {
-                    configFile = args[index];
-                    int len = configFile.length();
-                    if (configFile.startsWith(".cfg", len - 4))
-                    {
-                        configFile = configFile.substring(0, len - 4);
-                    }
+                    TLCGlobals.lnCheck = args[index].toLowerCase();
                     index++;
                 } else
                 {
-                    printErrorMsg("Error: expect a file name for -config option.");
+                    printErrorMsg("Error: expect a strategy such as final for -lncheck option.");
                     return false;
                 }
-            } else if (args[index].equals("-dump"))
+           } else if (args[index].equals("-config"))
             {
                 index++;
                 if (index < args.length)
                 {
-                    dumpFile = args[index];
-                    int len = dumpFile.length();
-                    if (!(dumpFile.startsWith(".dump", len - 5)))
-                    {
-                        dumpFile = dumpFile + ".dump";
-                    }
+                    configFile = args[index];
+					if (configFile.endsWith(TLAConstants.Files.CONFIG_EXTENSION)) {
+						configFile = configFile.substring(0,
+								(configFile.length() - TLAConstants.Files.CONFIG_EXTENSION.length()));
+					}
                     index++;
                 } else
+                {
+                    printErrorMsg("Error: expect a file name for -config option.");
+                    return false;
+                }
+            } else if (args[index].equals("-dump"))
+            {
+                index++; // consume "-dump".
+                if (((index + 1) < args.length) && args[index].startsWith("dot"))
+                {
+                	final String dotArgs = args[index].toLowerCase();
+                	index++; // consume "dot...".
+                	asDot = true;
+                	colorize = dotArgs.contains("colorize");
+                	actionLabels = dotArgs.contains("actionlabels");
+                	snapshot = dotArgs.contains("snapshot");
+					dumpFile = getDumpFile(args[index++], ".dot");
+                }
+                else if (index < args.length)
+                {
+					dumpFile = getDumpFile(args[index++], ".dump");
+                } else
                 {
                     printErrorMsg("Error: A file name for dumping states required.");
                     return false;
@@ -466,6 +720,24 @@ public class TLC
                     printErrorMsg("Error: need to specify the metadata directory.");
                     return false;
                 }
+            } else if (args[index].equals("-userFile"))
+            {
+                index++;
+                if (index < args.length)
+                {
+                    try {
+						// Most problems will only show when TLC eventually tries
+						// to write to the file.
+						tlc2.module.TLC.OUTPUT = new BufferedWriter(new FileWriter(new File(args[index++])));
+        			} catch (IOException e) {
+                        printErrorMsg("Error: Failed to create user output log file.");
+                        return false;
+        			}
+                } else
+                {
+                    printErrorMsg("Error: need to specify the full qualified file.");
+                    return false;
+                }
             } else if (args[index].equals("-workers"))
             {
                 index++;
@@ -473,7 +745,9 @@ public class TLC
                 {
                     try
                     {
-                        int num = Integer.parseInt(args[index]);
+                        int num = args[index].trim().toLowerCase().equals("auto")
+                                ? Runtime.getRuntime().availableProcessors()
+                                : Integer.parseInt(args[index]);
                         if (num < 1)
                         {
                             printErrorMsg("Error: at least one worker required.");
@@ -483,12 +757,12 @@ public class TLC
                         index++;
                     } catch (Exception e)
                     {
-                        printErrorMsg("Error: worker number required. But encountered " + args[index]);
+                        printErrorMsg("Error: worker number or 'auto' required. But encountered " + args[index]);
                         return false;
                     }
                 } else
                 {
-                    printErrorMsg("Error: expect an integer for -workers option.");
+                    printErrorMsg("Error: expect an integer or 'auto' for -workers option.");
                     return false;
                 }
             } else if (args[index].equals("-dfid"))
@@ -538,7 +812,7 @@ public class TLC
                     }
                 } else
                 {
-                    printErrorMsg("Error: expect an integer for -workers option.");
+                    printErrorMsg("Error: expect an integer for -fp option.");
                     return false;
                 }
             } else if (args[index].equals("-fpmem"))
@@ -627,16 +901,16 @@ public class TLC
                     return false;
                 }
                 mainFile = args[index++];
-                int len = mainFile.length();
-                if (mainFile.startsWith(".tla", len - 4))
+                if (mainFile.endsWith(TLAConstants.Files.TLA_EXTENSION))
                 {
-                    mainFile = mainFile.substring(0, len - 4);
+                    mainFile = mainFile.substring(0, (mainFile.length() - TLAConstants.Files.TLA_EXTENSION.length()));
                 }
             }
         }
-        
-        if (mainFile == null)
-        {
+                
+        startTime = System.currentTimeMillis();
+
+		if (mainFile == null) {
 			// command line omitted name of spec file, take this as an
 			// indicator to check the in-jar model/ folder for a spec.
 			// If a spec is found, use it instead.
@@ -645,44 +919,98 @@ public class TLC
 				ModelInJar.loadProperties();
 				TLCGlobals.tool = true; // always run in Tool mode (to parse output by Toolbox later)
 				TLCGlobals.chkptDuration = 0; // never use checkpoints with distributed TLC (highly inefficient)
-				mainFile = "MC";
+				mainFile = TLAConstants.Files.MODEL_CHECK_FILE_BASENAME;
 			} else {
 				printErrorMsg("Error: Missing input TLA+ module.");
 				return false;
 			}
-        }
-        if (configFile == null)
-        {
-            configFile = mainFile;
-        }
+		}
+
+		// The functionality to start TLC from an (absolute) path /path/to/spec/file.tla
+		// seems to have eroded over the years which is why this block of code is a
+		// clutch. It essentially massages the variable values for mainFile, specDir and
+		// the user dir to make the code below - as well as the FilenameToStream
+		// resolver -
+		// work. Original issues was https://github.com/tlaplus/tlaplus/issues/24.
+		final File f = new File(mainFile);
+		String specDir = "";
+		if (f.isAbsolute()) {
+			specDir = f.getParent() + FileUtil.separator;
+			mainFile = f.getName();
+			// Not setting user dir causes a ConfigFileException when the resolver
+			// tries to read the .cfg file later in the game.
+			ToolIO.setUserDir(specDir);
+		}
+
+		if (configFile == null) {
+			configFile = mainFile;
+		}
+
+		if (cleanup && (fromChkpt == null)) {
+			// clean up the states directory only when not recovering
+			FileUtil.deleteDir(TLCGlobals.metaRoot, true);
+		}
+
+        // Check if mainFile is an absolute or relative file system path. If it is
+		// absolute, the parent gets used as TLC's meta directory (where it stores
+		// states...). Otherwise, no meta dir is set causing states etc. to be stored in
+		// the current directory.
+    	metadir = FileUtil.makeMetaDir(new Date(startTime), specDir, fromChkpt);
+    	
+		if (dumpFile != null) {
+			if (dumpFile.startsWith("${metadir}")) {
+				// prefix dumpfile with the known value of this.metadir. There
+				// is no way to determine the actual value of this.metadir
+				// before TLC startup and thus it's impossible to make the
+				// dumpfile end up in the metadir if desired.
+				dumpFile = dumpFile.replace("${metadir}", metadir);
+			}
+			try {
+				if (asDot) {
+					this.stateWriter = new DotStateWriter(dumpFile, colorize, actionLabels, snapshot);
+				} else {
+					this.stateWriter = new StateWriter(dumpFile);
+				}
+			} catch (IOException e) {
+				printErrorMsg(String.format("Error: Given file name %s for dumping states invalid.", dumpFile));
+				return false;
+			}
+		}
         
-        if (TLCGlobals.debug) 
-        {
-            StringBuffer buffer = new StringBuffer("TLC argumens:");
-            for (int i=0; i < args.length; i++)
-            {
-                buffer.append(args[i]);
-                if (i < args.length - 1) 
-                {
-                    buffer.append(" ");
-                }
-            }
-            buffer.append("\n");
-            DebugPrinter.print(buffer.toString());
-        }
+		if (TLCGlobals.debug) {
+			final StringBuilder buffer = new StringBuilder("TLC arguments:");
+			for (int i = 0; i < args.length; i++) {
+				buffer.append(args[i]);
+				if (i < args.length - 1) {
+					buffer.append(" ");
+				}
+			}
+			buffer.append("\n");
+			DebugPrinter.print(buffer.toString());
+		}
         
         // if no errors, print welcome message
         printWelcome();
         
         return true;
-    }
-    
-    /**
+	}
+
+	/**
+	 * Require a $suffix file extension unless already given. It is not clear why
+	 * this is enforced.
+	 */
+	private static String getDumpFile(String dumpFile, String suffix) {
+		if (dumpFile.endsWith(suffix)) {
+			return dumpFile;
+		}
+		return dumpFile + suffix;
+	}
+
+	/**
      * The processing method
      */
-    public void process()
+    public int process()
     {
-        ToolIO.cleanToolObjects(TLCGlobals.ToolId);
         // UniqueString.initialize();
         
         // a JMX wrapper that exposes runtime statistics 
@@ -698,18 +1026,13 @@ public class TLC
                 // We must recover the intern var table as early as possible
                 UniqueString.internTbl.recover(fromChkpt);
             }
-            if (cleanup && fromChkpt == null)
-            {
-                // clean up the states directory only when not recovering
-                FileUtil.deleteDir(TLCGlobals.metaRoot, true);
-            }
             FP64.Init(fpIndex);
-
+    		
+    		final RandomGenerator rng = new RandomGenerator();
             // Start checking:
-            if (isSimulate)
-            {
+            final int result;
+			if (RunMode.SIMULATE.equals(runMode)) {
                 // random simulation
-                RandomGenerator rng = new RandomGenerator();
                 if (noSeed)
                 {
                     seed = rng.nextLong();
@@ -718,75 +1041,203 @@ public class TLC
                 {
                     rng.setSeed(seed, aril);
                 }
-                MP.printMessage(EC.TLC_MODE_SIMU, String.valueOf(seed));
-                Simulator simulator = new Simulator(mainFile, configFile, null, deadlock, traceDepth, 
-                        traceNum, rng, seed, true, resolver, specObj);
-// The following statement moved to Spec.processSpec by LL on 10 March 2011               
-//                MP.printMessage(EC.TLC_STARTING);
-                instance = simulator;
-                simulator.simulate();
-            } else
-            {
-                // model checking
-                MP.printMessage(EC.TLC_MODE_MC);
-                
-                AbstractChecker mc = null;
-                if (TLCGlobals.DFIDMax == -1)
+				printStartupBanner(EC.TLC_MODE_SIMU, getSimulationRuntime(seed));
+				
+				Simulator simulator = new Simulator(mainFile, configFile, traceFile, deadlock, traceDepth, 
+                        traceNum, rng, seed, resolver, TLCGlobals.getNumWorkers());
+                TLCGlobals.simulator = simulator;
+                result = simulator.simulate();
+			} else { // RunMode.MODEL_CHECK
+				if (noSeed) {
+                    seed = rng.nextLong();
+				}
+				// Replace seed with tlc2.util.FP64.Polys[fpIndex]?
+				// + No need to print seed in startup-banner for BFS and DFS
+				// - Only 131 different seeds
+				// RandomEnumerableValues.setSeed(tlc2.util.FP64.Polys[fpIndex]);
+				RandomEnumerableValues.setSeed(seed);
+            	
+				// Print startup banner before SANY writes its output.
+				printStartupBanner(isBFS() ? EC.TLC_MODE_MC : EC.TLC_MODE_MC_DFS, getModelCheckingRuntime(fpIndex, fpSetConfiguration));
+				
+            	// model checking
+                final FastTool tool = new FastTool(mainFile, configFile, resolver);
+                deadlock = deadlock && tool.getModelConfig().getCheckDeadlock();
+                if (isBFS())
                 {
-                    mc = new ModelChecker(mainFile, configFile, dumpFile, deadlock, fromChkpt, resolver, specObj, fpSetConfiguration);
-                    TLCGlobals.mainChecker = (ModelChecker) mc;
-                    modelCheckerMXWrapper = new ModelCheckerMXWrapper((ModelChecker) mc);
+					TLCGlobals.mainChecker = new ModelChecker(tool, metadir, stateWriter, deadlock, fromChkpt,
+							FPSetFactory.getFPSetInitialized(fpSetConfiguration, metadir, new File(mainFile).getName()),
+							startTime);
+					modelCheckerMXWrapper = new ModelCheckerMXWrapper((ModelChecker) TLCGlobals.mainChecker, this);
+					result = TLCGlobals.mainChecker.modelCheck();
                 } else
                 {
-                    mc = new DFIDModelChecker(mainFile, configFile, dumpFile, deadlock, fromChkpt, true, resolver, specObj);
+					TLCGlobals.mainChecker = new DFIDModelChecker(tool, metadir, stateWriter, deadlock, fromChkpt, startTime);
+					result = TLCGlobals.mainChecker.modelCheck();
+                }
+
+                if ((mcOutputConsumer != null) && (mcOutputConsumer.getError() != null)) {
+            		final SpecProcessor sp = tool.getSpecProcessor();
+            		final ModelConfig mc = tool.getModelConfig();
+            		final File sourceDirectory = mcOutputConsumer.getSourceDirectory();
+            		final String originalSpecName = mcOutputConsumer.getSpecName();
+            		
+            		MP.printMessage(EC.GENERAL,
+    								"The model check run produced error-states - we will generate the "
+    										+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " files now.");
+            		mcParserResults = MCParser.generateResultsFromProcessorAndConfig(sp, mc);
+            		final File[] files = TraceExplorer.writeSpecTEFiles(sourceDirectory, originalSpecName,
+            															mcParserResults, mcOutputConsumer.getError());
+            		specTETLAFile = files[0];
                 }
-// The following statement moved to Spec.processSpec by LL on 10 March 2011               
-//                MP.printMessage(EC.TLC_STARTING);
-                instance = mc;
-                mc.modelCheck();
-                
             }
+            return result;
         } catch (Throwable e)
         {
             if (e instanceof StackOverflowError)
             {
                 System.gc();
-                MP.printError(EC.SYSTEM_STACK_OVERFLOW, e);
+                return MP.printError(EC.SYSTEM_STACK_OVERFLOW, e);
             } else if (e instanceof OutOfMemoryError)
             {
                 System.gc();
-                MP.printError(EC.SYSTEM_OUT_OF_MEMORY, e);
+                return MP.printError(EC.SYSTEM_OUT_OF_MEMORY, e);
+            } else if (e instanceof TLCRuntimeException) {
+            	return MP.printTLCRuntimeException((TLCRuntimeException) e);
             } else if (e instanceof RuntimeException) 
             {
                 // SZ 29.07.2009 
                 // printing the stack trace of the runtime exceptions
-                MP.printError(EC.GENERAL, e);
+                return MP.printError(EC.GENERAL, e);
                 // e.printStackTrace();
             } else
             {
-                MP.printError(EC.GENERAL, e);
+                return MP.printError(EC.GENERAL, e);
             }
         } finally 
         {
-       		modelCheckerMXWrapper.unregister();
-            MP.printMessage(EC.TLC_FINISHED);
-            MP.flush();
+        	if (tlc2.module.TLC.OUTPUT != null) {
+        		try {
+        			tlc2.module.TLC.OUTPUT.flush();
+					tlc2.module.TLC.OUTPUT.close();
+				} catch (IOException e) { }
+        	}
+			modelCheckerMXWrapper.unregister();
+			// In tool mode print runtime in milliseconds, in non-tool mode print human
+			// readable runtime (days, hours, minutes, ...).
+			final long runtime = System.currentTimeMillis() - startTime;
+			MP.printMessage(EC.TLC_FINISHED,
+					TLCGlobals.tool ? Long.toString(runtime) + "ms" : convertRuntimeToHumanReadable(runtime));
+			MP.flush();
+			
+			waitingOnGenerationCompletion.arriveAndAwaitAdvance();
         }
     }
     
-    @SuppressWarnings("unchecked")
+	private static boolean isBFS() {
+		return TLCGlobals.DFIDMax == -1;
+	}
+
+	public static Map<String, String> getSimulationRuntime(final long seed) {
+		final Runtime runtime = Runtime.getRuntime();
+		final long heapMemory = runtime.maxMemory() / 1024L / 1024L;
+		
+		final TLCRuntime tlcRuntime = TLCRuntime.getInstance();
+		final long offHeapMemory = tlcRuntime.getNonHeapPhysicalMemory() / 1024L / 1024L;
+		final long pid = tlcRuntime.pid();
+		
+		final Map<String, String> result = new LinkedHashMap<>();
+		result.put("seed", String.valueOf(seed));
+		result.put("workers", String.valueOf(TLCGlobals.getNumWorkers()));
+		result.put("plural", TLCGlobals.getNumWorkers() == 1 ? "" : "s");
+		result.put("cores", Integer.toString(runtime.availableProcessors()));
+		result.put("osName", System.getProperty("os.name"));
+		result.put("osVersion", System.getProperty("os.version"));
+		result.put("osArch", System.getProperty("os.arch"));
+		result.put("jvmVendor", System.getProperty("java.vendor"));
+		result.put("jvmVersion", System.getProperty("java.version"));
+		result.put("jvmArch", tlcRuntime.getArchitecture().name());
+		result.put("jvmHeapMem", Long.toString(heapMemory));
+		result.put("jvmOffHeapMem", Long.toString(offHeapMemory));
+		result.put("jvmPid", pid == -1 ? "" : String.valueOf(pid));
+		return result;
+	}
+
+	public static Map<String, String> getModelCheckingRuntime(final int fpIndex, final FPSetConfiguration fpSetConfig) {
+		final Runtime runtime = Runtime.getRuntime();
+		final long heapMemory = runtime.maxMemory() / 1024L / 1024L;
+		
+		final TLCRuntime tlcRuntime = TLCRuntime.getInstance();
+		final long offHeapMemory = tlcRuntime.getNonHeapPhysicalMemory() / 1024L / 1024L;
+		final long pid = tlcRuntime.pid();
+		
+		// TODO Better to use Class#getSimpleName provided we would have access to the
+		// Class instance instead of just its name. However, loading the class here is
+		// overkill and might interfere if other parts of TLC pull off class-loading
+		// tricks.
+		final String fpSetClassSimpleName = fpSetConfig.getImplementation()
+				.substring(fpSetConfig.getImplementation().lastIndexOf(".") + 1);
+		
+		final String stateQueueClassSimpleName = ModelChecker.getStateQueueName();
+		
+		//  fpSetClassSimpleName and stateQueueClassSimpleName ignored in DFS mode.
+		final Map<String, String> result = new LinkedHashMap<>();
+		result.put("workers", String.valueOf(TLCGlobals.getNumWorkers()));
+		result.put("plural", TLCGlobals.getNumWorkers() == 1 ? "" : "s");
+		result.put("cores", Integer.toString(runtime.availableProcessors()));
+		result.put("osName", System.getProperty("os.name"));
+		result.put("osVersion", System.getProperty("os.version"));
+		result.put("osArch", System.getProperty("os.arch"));
+		result.put("jvmVendor", System.getProperty("java.vendor"));
+		result.put("jvmVersion", System.getProperty("java.version"));
+		result.put("jvmArch", tlcRuntime.getArchitecture().name());
+		result.put("jvmHeapMem", Long.toString(heapMemory));
+		result.put("jvmOffHeapMem", Long.toString(offHeapMemory));
+		result.put("seed", Long.toString(RandomEnumerableValues.getSeed()));
+		result.put("fpidx", Integer.toString(fpIndex));
+		result.put("jvmPid", pid == -1 ? "" : String.valueOf(pid));
+		result.put("fpset", fpSetClassSimpleName);
+		result.put("queue", stateQueueClassSimpleName);
+		return result;
+	}
+    
+    /**
+	 * @return The given milliseconds runtime converted into human readable form
+	 *         with SI unit and insignificant parts stripped (when runtime is
+	 *         days, nobody cares for minutes or seconds).
+	 */
+    public static String convertRuntimeToHumanReadable(long runtime) {
+		SimpleDateFormat df = null;
+		if (runtime > (60 * 60 * 24 * 1000L)) {
+			df = new SimpleDateFormat("D'd' HH'h'");
+			runtime -= 86400000L;
+		} else if (runtime > (60 * 60 * 24 * 1000L)) {
+			df = new SimpleDateFormat("D'd' HH'h'");
+			runtime -= 86400000L;
+		} else if (runtime > (60 * 60 * 1000L)) {
+			df = new SimpleDateFormat("HH'h' mm'min'");
+		} else if (runtime > (60 * 1000L)) {
+			df = new SimpleDateFormat("mm'min' ss's'");
+		} else {
+			df = new SimpleDateFormat("ss's'");
+		}
+		df.setTimeZone(TimeZone.getTimeZone("UTC"));
+		return df.format(runtime);
+    }
+    
 	public List<File> getModuleFiles() {
     	final List<File> result = new ArrayList<File>();
     	
-    	if (instance instanceof ModelChecker) {
-    		ModelChecker mc = (ModelChecker) instance;
-			final Enumeration<ParseUnit> parseUnitContext = mc.specObj.parseUnitContext
-					.elements();
-    		while (parseUnitContext.hasMoreElements()) {
-    			ParseUnit pu = (ParseUnit) parseUnitContext.nextElement();
-				File resolve = resolver.resolve(pu.getFileName(), false);
-				result.add(resolve);
+    	if (TLCGlobals.mainChecker instanceof ModelChecker) {
+    		final ModelChecker mc = (ModelChecker) TLCGlobals.mainChecker;
+    		result.addAll(mc.getModuleFiles(resolver));
+    		if (ModelInJar.hasCfg()) {
+    			result.add(ModelInJar.getCfg());
     		}
+			// It might be desirable to include tlc.jfr - a flight recording aka profiling
+			// at the JVM level here. This doesn't work though as the recording get created
+			// after the termination of the JVM. A recording can also be several hundred
+    		// MBs large.
     	}
         return result;
     }
@@ -802,28 +1253,6 @@ public class TLC
         ToolIO.setDefaultResolver(resolver);
     }
 
-    /**
-     * Set external specification object
-     * @param specObj spec object created external SANY run
-     */
-    public void setSpecObject(SpecObj specObj) 
-    {
-        this.specObj = specObj;
-    }
-
-    /**
-     * Delegate cancellation request to the instance
-     * @param flag
-     */
-    public void setCanceledFlag(boolean flag)
-    {
-        if (this.instance != null) 
-        {
-            this.instance.setCancelFlag(flag);
-            DebugPrinter.print("Cancel flag set to " + flag);
-        }
-    }
-    
     /**
      * Print out an error message, with usage hint
      * @param msg, message to print
@@ -843,20 +1272,237 @@ public class TLC
         if (!this.welcomePrinted) 
         {
             this.welcomePrinted = true;
-            MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC);
+            if (TLCGlobals.getRevision() == null) {
+            	MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC);
+            } else {
+            	MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC + " (rev: " + TLCGlobals.getRevision() + ")");
+            }
         }
     }
     
+	private void printStartupBanner(final int mode, final Map<String, String> parameters) {
+		MP.printMessage(mode, parameters.values().toArray(new String[parameters.size()]));
+		
+		final Map<String, String> udc = new LinkedHashMap<>();
+		// First indicate the version (to make parsing forward compatible)
+		udc.put("ver", TLCGlobals.getRevisionOrDev());
+
+		// Simulation, DFS or BFS mode.
+		udc.put("mode", mode2String(mode));
+		
+		parameters.remove("plural"); // damn hack!
+		// "pid", "seed", and "fpidx" have no relevance for us.
+		parameters.remove("jvmPid");
+		parameters.remove("fpidx");
+		parameters.remove("seed");
+		udc.putAll(parameters);
+		
+		// True if TLC is run from within the Toolbox. Derive ide name from .tool too
+		// unless set explicitly.  Eventually, we can probably remove the toolbox
+		// parameter.
+		udc.put("toolbox", Boolean.toString(TLCGlobals.tool));
+		udc.put("ide", System.getProperty(TLC.class.getName() + ".ide", TLCGlobals.tool ? "toolbox" : "cli"));
+		new ExecutionStatisticsCollector().collect(udc);
+	}
+	
+	private static String mode2String(final int mode) {
+		switch (mode) {
+		case EC.TLC_MODE_MC:
+			return "bfs";
+		case EC.TLC_MODE_MC_DFS:
+			return "dfs";
+		case EC.TLC_MODE_SIMU:
+			return "simulation";
+		default:
+			return "unknown";
+		}
+	}
+	
+	/**
+	 * This is themed on commons-io-2.6's IOUtils.copyLarge(InputStream, OutputStream, byte[]) -
+	 * 	once we move to Java9+, dump this usage in favor of InputStream.transferTo(OutputStream)
+	 * 
+	 * @param is
+	 * @param os
+	 * @return the count of bytes copied
+	 * @throws IOException
+	 */
+	private static long copyStream(final InputStream is, final OutputStream os) throws IOException {
+		final byte[] buffer = new byte[1024 * 4];
+		long byteCount = 0;
+		int n;
+		final BufferedInputStream bis = (is instanceof BufferedInputStream) ? (BufferedInputStream)is
+																			: new BufferedInputStream(is);
+		final BufferedOutputStream bos = (os instanceof BufferedOutputStream) ? (BufferedOutputStream)os
+																			  : new BufferedOutputStream(os);
+		while ((n = bis.read(buffer)) != -1) {
+			bos.write(buffer, 0, n);
+			byteCount += n;
+		}
+		
+		bos.flush();
+		
+		return byteCount;
+	}
+    
     /**
      * 
      */
     private void printUsage()
     {
-        printWelcome();
-        MP.printMessage(EC.TLC_USAGE);
+    	final List<List<UsageGenerator.Argument>> commandVariants = new ArrayList<>();
+    	final List<UsageGenerator.Argument> sharedArguments = new ArrayList<>();
+    	
+    	// N.B. alphabetical ordering is not required by the UsageGenerator, but introduced here to more easily
+    	//			find options when reading the code
+    	sharedArguments.add(new UsageGenerator.Argument("-checkpoint", "minutes",
+    													"interval between check point; defaults to 30",
+    													true));
+    	sharedArguments.add(new UsageGenerator.Argument("-cleanup", "clean up the states directory", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-config", "file",
+    													"provide the configuration file; defaults to SPEC.cfg", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-continue",
+    													"continue running even when an invariant is violated; default\n"
+    														+ "behavior is to halt on first violation", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-coverage", "minutes",
+														"interval between the collection of coverage information;\n"
+    														+ "if not specified, no coverage will be collected", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-deadlock",
+														"if specified DO NOT CHECK FOR DEADLOCK. Setting the flag is\n"
+															+ "the same as setting CHECK_DEADLOCK to FALSE in config\n"
+															+ "file. When -deadlock is specified, config entry is\n"
+															+ "ignored; default behavior is to check for deadlocks",
+														true));
+    	sharedArguments.add(new UsageGenerator.Argument("-difftrace",
+														"show only the differences between successive states when\n"
+															+ "printing trace information; defaults to printing\n"
+															+ "full state descriptions", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-debug",
+														"print various debugging information - not for production use\n",
+														true));
+    	sharedArguments.add(new UsageGenerator.Argument("-dump", "file",
+    													"dump all states into the specified file; this parameter takes\n"
+    														+ "optional parameters for dot graph generation. Specifying\n"
+    														+ "'dot' allows further options, comma delimited, of zero\n"
+    														+ "or more of 'actionlabels', 'colorize', 'snapshot' to be\n"
+    														+ "specified before the '.dot'-suffixed filename", true,
+    													"dot actionlabels,colorize,snapshot"));
+    	sharedArguments.add(new UsageGenerator.Argument("-fp", "N",
+    													"use the Nth irreducible polynomial from the list stored\n"
+    														+ "in the class FP64", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-fpbits", "num",
+														"the number of MSB used by MultiFPSet to create nested\n"
+    														+ "FPSets; defaults to 1", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-fpmem", "num",
+														"a value in (0.0,1.0) representing the ratio of total\n"
+															+ "physical memory to devote to storing the fingerprints\n"
+															+ "of found states; defaults to 0.25", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-generateSpecTE", null,
+														"if errors are encountered during model checking, generate\n"
+															+ "a SpecTE tla/cfg file pair which encapsulates Init-Next\n"
+															+ "definitions to specify the state conditions of the error\n"
+															+ "state; this enables 'tool' mode. The generated SpecTE\n"
+															+ "will include tool output as well as all non-Standard-\n"
+															+ "Modules dependencies embeded in the module. To prevent\n"
+															+ "the embedding of dependencies, add the parameter\n"
+															+ "'nomonolith' to this declaration", true,
+															"nomonolith"));
+    	sharedArguments.add(new UsageGenerator.Argument("-gzip",
+														"control if gzip is applied to value input/output streams;\n"
+															+ "defaults to 'off'", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-h", "display these help instructions", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-maxSetSize", "num",
+														"the size of the largest set which TLC will enumerate; defaults\n"
+															+ "to 1000000 (10^6)", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-metadir", "path",
+														"specify the directory in which to store metadata; defaults to\n"
+															+ "SPEC-directory/states if not specified", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-nowarning",
+														"disable all warnings; defaults to reporting warnings", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-recover", "id",
+														"recover from the checkpoint with the specified id", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-terse",
+														"do not expand values in Print statements; defaults to\n"
+															+ "expanding values", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-tool",
+														"run in 'tool' mode, surrounding output with message codes;\n"
+															+ "if '-generateSpecTE' is specified, this is enabled\n"
+															+ "automatically", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-userFile", "file",
+														"an absolute path to a file in which to log user output (for\n"
+    														+ "example, that which is produced by Print)", true));
+    	sharedArguments.add(new UsageGenerator.Argument("-workers", "num",
+														"the number of TLC worker threads; defaults to 1. Use 'auto'\n"
+    														+ "to automatically select the number of threads based on the\n"
+    														+ "number of available cores.", true));
+    	
+    	sharedArguments.add(new UsageGenerator.Argument("SPEC", null));
+    	
+    	
+    	final List<UsageGenerator.Argument> modelCheckVariant = new ArrayList<>(sharedArguments);
+    	modelCheckVariant.add(new UsageGenerator.Argument("-dfid", "num",
+														  "run the model check in depth-first iterative deepening\n"
+    														+ "starting with an initial depth of 'num'", true));
+    	modelCheckVariant.add(new UsageGenerator.Argument("-view",
+														  "apply VIEW (if provided) when printing out states", true));
+    	commandVariants.add(modelCheckVariant);
+    	
+    	
+    	final List<UsageGenerator.Argument> simulateVariant = new ArrayList<>(sharedArguments);
+    	simulateVariant.add(new UsageGenerator.Argument("-depth", "num",
+														"specifies the depth of random simulation; defaults to 100",
+														true));
+    	simulateVariant.add(new UsageGenerator.Argument("-seed", "num",
+														"provide the seed for random simulation; defaults to a\n"
+    														+ "random long pulled from a pseudo-RNG", true));
+    	simulateVariant.add(new UsageGenerator.Argument("-aril", "num",
+														"adjust the seed for random simulation; defaults to 0", true));
+    	simulateVariant.add(new UsageGenerator.Argument("-simulate", null,
+													  	"run in simulation mode; optional parameters may be specified\n"
+	    													+ "comma delimited: 'num=X' where X is the maximum number of\n"
+	    													+ "total traces to generate and/or 'file=Y' where Y is the\n"
+	    													+ "absolute-pathed prefix for trace file modules to be written\n"
+	    													+ "by the simulation workers; for example Y='/a/b/c/tr' would\n"
+	    													+ "produce, e.g, '/a/b/c/tr_1_15'", false,
+	    												"file=X,num=Y"));
+    	commandVariants.add(simulateVariant);
+
+    	
+    	final List<String> tips = new ArrayList<String>();
+    	tips.add("When using the  '-generateSpecTE' you can version the generated specification by doing:\n\t"
+    				+ "./tla2tools.jar -generateSpecTE MySpec.tla && NAME=\"SpecTE-$(date +%s)\" && sed -e \"s/MODULE"
+    				+ " SpecTE/MODULE $NAME/g\" SpecTE.tla > $NAME.tla");
+    	tips.add("If, while checking a SpecTE created via '-generateSpecTE', you get an error message concerning\n"
+    				+ "CONSTANT declaration and you've previous used 'integers' as model values, rename your\n"
+    				+ "model values to start with a non-numeral and rerun the model check to generate a new SpecTE.");
+    	tips.add("If, while checking a SpecTE created via '-generateSpecTE', you get a warning concerning\n"
+					+ "duplicate operator definitions, this is likely due to the 'monolith' specification\n"
+					+ "creation. Try re-running TLC adding the 'nomonolith' option to the '-generateSpecTE'\n"
+					+ "parameter.");
+    	
+    	UsageGenerator.displayUsage(ToolIO.out, "TLC", TLCGlobals.versionOfTLC,
+    								"provides model checking and simulation of TLA+ specifications",
+    								Messages.getString("TLCDescription"),
+    								commandVariants, tips, ' ');
     }
 
     FPSetConfiguration getFPSetConfiguration() {
     	return fpSetConfiguration;
     }
+    
+    public RunMode getRunMode() {
+    	return runMode;
+    }
+
+    public String getMainFile() {
+        return mainFile;
+    }
+
+	public String getModelName() {
+		return System.getProperty(MailSender.MODEL_NAME, this.mainFile);
+	}
+	
+	public String getSpecName() {
+		return System.getProperty(MailSender.SPEC_NAME, this.mainFile);
+	}
 }
diff --git a/tlatools/src/tlc2/TLCGlobals.java b/tlatools/src/tlc2/TLCGlobals.java
index 93f581508e6b3b8a321b2369ff87bc3ab2f14920..d88c46c664dd603e0ea539423293c045540d6ad1 100644
--- a/tlatools/src/tlc2/TLCGlobals.java
+++ b/tlatools/src/tlc2/TLCGlobals.java
@@ -2,8 +2,13 @@
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
 package tlc2;
 
-import tla2sany.semantic.FrontEnd;
-import tlc2.tool.ModelChecker;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import tlc2.tool.AbstractChecker;
+import tlc2.tool.Simulator;
 
 /**
  * Globals
@@ -15,12 +20,14 @@ import tlc2.tool.ModelChecker;
 public class TLCGlobals
 {
 
-    // The current version of TLC
-    public static String versionOfTLC = "Version 2.06 of 9 May 2015";
+	public static final int DEFAULT_CHECKPOINT_DURATION = (30 * 60 * 1000) + 42;
 
+	// The current version of TLC
+    public static String versionOfTLC = "Version 2.18 of 20 March 2023";
+    
     // The bound for set enumeration, used for pretty printing
     public static int enumBound = 2000;
-
+    
     // The bound for the cardinality of a set
     public static int setBound = 1000000;
 
@@ -33,7 +40,26 @@ public class TLCGlobals
 	 */
     public static double livenessThreshold = 0.1d;
 
-    public synchronized static void setNumWorkers(int n)
+    public static double livenessGraphSizeThreshold = 0.1d;
+
+	/**
+	 * Ratio of runtime dedicated to safety checking (80%) and liveness checking
+	 * (20%). Some aspects of liveness are also checked during state insertion
+	 * (see ILiveCheck#addNextState) and thus part of safety checking..
+	 */
+	public static double livenessRatio = 0.2d;
+	
+	public static String lnCheck = "default";
+	
+	public static boolean doLiveness() {
+		return !(lnCheck.equals("final") || lnCheck.equals("seqfinal"));
+	}
+
+	public static boolean doSequentialLiveness() {
+		return lnCheck.startsWith("seq");
+	}
+
+	public synchronized static void setNumWorkers(int n)
     {
         numWorkers = n;
     }
@@ -62,12 +88,22 @@ public class TLCGlobals
     	incNumWorkers(-1);
     }
 
-    // The main model checker object
-    public static ModelChecker mainChecker = null;
+    // The main model checker object (null if simulator non-null)
+    public static AbstractChecker mainChecker = null;
+    
+    // The main simulator object (null if mainChecker non-null)
+    public static Simulator simulator = null;
 
+    // Char to indent nested coverage information.
+	public static final char coverageIndent = '|';
+    
     // Enable collecting coverage information
     public static int coverageInterval = -1;
 
+    public static final boolean isCoverageEnabled() {
+    	return coverageInterval >= 0;
+    }
+    
     // Depth for depth-first iterative deepening
     public static int DFIDMax = -1;
 
@@ -85,7 +121,7 @@ public class TLCGlobals
 
     // The time interval to checkpoint. (in milliseconds)
 	public static long chkptDuration = Integer.getInteger(
-			TLCGlobals.class.getName() + ".chkpt", 30 * 60 * 1000);
+			TLCGlobals.class.getName() + ".chkpt", DEFAULT_CHECKPOINT_DURATION);
     
 	// MAK 08.2012: centralized checkpoint code and added disabling and
 	// externally forced checkpoints
@@ -94,7 +130,12 @@ public class TLCGlobals
     	forceChkpt = true;
     }
     private static long lastChkpt = System.currentTimeMillis();
-    
+
+	public static boolean chkptExplicitlyEnabled() {
+		// Assumption is that a user will always select a different value.
+		return chkptDuration > 0 && chkptDuration != DEFAULT_CHECKPOINT_DURATION;
+	}
+
 	/**
 	 * IMPORTANT NOTE: The method is unsynchronized. It is the caller's
 	 * responsibility to ensure that only a single thread calls this method.
@@ -130,10 +171,7 @@ public class TLCGlobals
     public static boolean useView = false;
 
     // The flag to control if gzip is applied to Value input/output stream.
-    public static boolean useGZIP = true;
-
-    // The tool id number for TLC2.
-    public static int ToolId = FrontEnd.getToolId();
+    public static boolean useGZIP = false;
 
     // debugging field
     public static boolean debug = false;
@@ -147,4 +185,29 @@ public class TLCGlobals
 		}
 		return true;
 	}
+	
+	public static String getRevision() {
+		try {
+			final Enumeration<URL> resources = TLCGlobals.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
+			while (resources.hasMoreElements()) {
+				final Manifest manifest = new Manifest(resources.nextElement().openStream());
+				final Attributes attributes = manifest.getMainAttributes();
+				if("TLA+ Tools".equals(attributes.getValue("Implementation-Title"))) {
+					if(attributes.getValue("X-Git-ShortRevision") != null) {
+						return attributes.getValue("X-Git-ShortRevision");
+					} else {
+						return null;
+					}
+				}
+			}
+		} catch (Exception ignore) {
+		}
+		return null;
+	}
+	
+	public static String getRevisionOrDev() {
+		return TLCGlobals.getRevision() == null ? "development" : TLCGlobals.getRevision();
+	}
+
+	public static boolean expand = true;
 }
diff --git a/tlatools/src/tlc2/TLCRunner.java b/tlatools/src/tlc2/TLCRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcd35035ac4e1310ae3e3f5b857a1bd1ac4b1980
--- /dev/null
+++ b/tlatools/src/tlc2/TLCRunner.java
@@ -0,0 +1,191 @@
+package tlc2;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import tlc2.output.EC;
+import tlc2.output.TeeOutputStream;
+import tlc2.tool.fp.FPSetFactory;
+
+/**
+ * Originally i was attempting to run the model check in the same JVM in which {@link TraceExplorer} was invoked (and 
+ * 	then did its parsing and generating TLA and CFG files.) The model check process would fail with a message of:
+ * 
+ * 				@!@!@STARTMSG 2233:1 @!@!@
+ * 				The initial predicate _TEInit cannot be a constant.
+ * 				@!@!@ENDMSG 2233 @!@!@
+ * 
+ * ... however, running TLC from command line freshly on the same TLA/CFG would not produce this error.  I can only
+ * 	surmise that the parsing process is setting some static value somewhere which is not being cleared and which is
+ * 	then making the model checking process fail.
+ * 
+ * In light of that, i've written this class to spin off a new JVM for the model check.
+ */
+class TLCRunner {
+	static List<String> JVM_ARGUMENTS;
+	private static final String TLC_CLASS = TLC.class.getName();
+	
+	static {
+		JVM_ARGUMENTS = new ArrayList<>();
+		JVM_ARGUMENTS.add("-XX:+UseParallelGC");
+		JVM_ARGUMENTS.add("-Dfile.encoding=UTF-8");
+		JVM_ARGUMENTS.add("-Dtlc2.tool.fp.FPSet.impl=" + FPSetFactory.getImplementationDefault());
+	}
+	
+	
+	private final File outputLogfile;
+	private final OutputStream outputOutputStream;
+	private final List<String> arguments;
+	
+	private boolean silenceStdOut;
+
+	TLCRunner(final List<String> tlcArguments, final File logfileDestination) {
+		outputLogfile = logfileDestination;
+		outputOutputStream = null;
+		arguments = tlcArguments;
+		
+		silenceStdOut = false;
+	}
+
+	TLCRunner(final List<String> tlcArguments, final OutputStream logfileOutputStream) {
+		outputLogfile = null;
+		outputOutputStream = logfileOutputStream;
+		arguments = tlcArguments;
+		
+		silenceStdOut = false;
+	}
+	
+	/**
+	 * @param flag if true, no output from the TLC process will be sent to System.out
+	 */
+	void setSilenceStdOut(final boolean flag) {
+		silenceStdOut = flag;
+	}
+	
+	/**
+	 * This will not return until the process has finished.
+	 * @return the exit value of the TLC process
+	 * @throws IOException
+	 */
+	int run() throws IOException {
+		final ProcessBuilder processBuilder = createProcess();
+		final Process p = processBuilder.start();
+		final BufferedInputStream stdOutReader = new BufferedInputStream(p.getInputStream());
+		final BufferedInputStream stdErrReader = new BufferedInputStream(p.getErrorStream());
+		final BufferedOutputStream logfileOutputStream;
+		if (outputOutputStream != null) {
+			if (outputOutputStream instanceof BufferedOutputStream) {
+				logfileOutputStream = (BufferedOutputStream)outputOutputStream;
+			} else {
+				logfileOutputStream = new BufferedOutputStream(outputOutputStream);
+			}
+		} else {
+			final FileOutputStream fos = new FileOutputStream(outputLogfile);
+			logfileOutputStream = new BufferedOutputStream(fos);
+		}
+		final OutputStream stdOutPumpOutput;
+		if (silenceStdOut) {
+			stdOutPumpOutput = logfileOutputStream;
+		} else {
+			stdOutPumpOutput = new TeeOutputStream(System.out, logfileOutputStream);
+		}
+		final StreamPump stdOutPump = new StreamPump(stdOutReader, stdOutPumpOutput);
+		final StreamPump stdErrPump = new StreamPump(stdErrReader, null);
+		
+		try {
+			(new Thread(stdOutPump)).start();
+			(new Thread(stdErrPump)).start();
+			
+			p.waitFor();
+			
+			return p.exitValue();
+		} catch (final InterruptedException ie) {
+			// TODO improve logging here
+			System.out.println("TLC process was interrupted: " + ie.getMessage());
+		} finally {
+			stdOutPump.stop();
+			stdErrPump.stop();
+			
+			try {
+				logfileOutputStream.close();
+			} catch (final Exception e) { }
+		}
+		
+		return EC.ExitStatus.ERROR_SYSTEM;
+	}
+
+	private ProcessBuilder createProcess() {
+		final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
+		final String jvm = System.getProperty("java.home")
+								+ File.separator
+								+ "bin"
+								+ File.separator
+								+ "java"
+								+ (isWindows ? ".exe" : "");
+		final List<String> command = new ArrayList<String>();
+		command.add(jvm);
+		command.addAll(JVM_ARGUMENTS);
+		command.add(TLC_CLASS);
+		command.addAll(arguments);
+
+		final ProcessBuilder processBuilder = new ProcessBuilder(command);
+		final Map<String, String> environment = processBuilder.environment();
+		environment.put("CLASSPATH", System.getProperty("java.class.path"));
+		
+		return processBuilder;
+	}
+
+	
+	private static class StreamPump implements Runnable {
+	    private static final int WAIT_SLEEP = 125;
+
+	    
+	    private final InputStream inputStream;
+	    private final OutputStream outputStream;
+
+	    private volatile boolean shouldStop;
+
+	    /**
+	     * @param is
+	     * @param os pass null to simply drain the input stream
+	     */
+	    StreamPump(final InputStream is, final OutputStream os) {
+	        this.inputStream = is;
+	        this.outputStream = os;
+	        this.shouldStop = false;
+	    }
+
+		public void run() {
+			try {
+				while (!shouldStop) {
+					while ((inputStream.available() > 0) && !shouldStop) {
+						if (outputStream != null) {
+							outputStream.write(inputStream.read());
+						} else {
+							inputStream.read();
+						}
+					}
+					if (outputStream != null) {
+						outputStream.flush();
+					}
+
+					try {
+						Thread.sleep(WAIT_SLEEP);
+					} catch (final Exception e) { }
+				}
+			} catch (final Exception e) { }
+		}
+
+		void stop() {
+	        shouldStop = true;
+	    }
+	}
+}
diff --git a/tlatools/src/tlc2/TraceExplorer.java b/tlatools/src/tlc2/TraceExplorer.java
new file mode 100644
index 0000000000000000000000000000000000000000..bed61c179df1d628aee88f2ad0e0e4573146eb7a
--- /dev/null
+++ b/tlatools/src/tlc2/TraceExplorer.java
@@ -0,0 +1,846 @@
+package tlc2;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+
+import tlc2.input.MCOutputParser;
+import tlc2.input.MCOutputPipeConsumer;
+import tlc2.input.MCParser;
+import tlc2.input.MCParserResults;
+import tlc2.model.MCError;
+import tlc2.model.MCState;
+import tlc2.output.CFGCopier;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.output.Messages;
+import tlc2.output.SpecTraceExpressionWriter;
+import tlc2.output.TLACopier;
+import tlc2.util.Vect;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueEnumeration;
+import util.TLAConstants;
+import util.ToolIO;
+import util.UsageGenerator;
+
+/**
+ * This is an application class which provides the following functionalities:
+ * 
+ * 		. given a directory of a previously run model check (containing a .tla/.cfg/.out triplet), produce a "SpecTE"
+ * 				.tla / .cfg file pair
+ * 		. given a directory of a previously run model check (containing a .out file), dump a pretty print of the
+ *				errors states to {@link System#out}
+ * 		. given a directory of a previously run model check (containing a .tla/.cfg/.out triplet) and a file of
+ * 				trace expressions, one per line, produce a "SpecTE" file pair, then run a model check
+ * 				evaluating the expressions, writing the triplet TE.tla, TE.cfg, TE.out
+ * 		. given a directory of a previously generated SpecTE file pair and a file of trace expressions, one per line,
+ * 				run a model check evaluating the expressions, writing the triplet TE.tla, TE.cfg, TE.out
+ *		. given an already executed output pipe consumer, generated a "SpecTE" .tla / .cfg pair
+ */
+public class TraceExplorer {
+	private static final String GENERATE_SPEC_FUNCTION_PARAMETER_NAME = "-generateSpecTE";
+	private static final String PRETTY_PRINT_FUNCTION_PARAMETER_NAME = "-prettyPrint";
+	private static final String QUASI_REPL_PARAMETER_NAME = "-replBis";
+	private static final String TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME = "-traceExpressions";
+
+    private static final String EXPRESSIONS_FILE_PARAMETER_NAME = "-expressionsFile";
+    private static final String MODEL_CHECK_JVM_ARGUMENTS_PARAMETER_NAME = "-jvmArguments";
+    private static final String MODEL_CHECK_TLC_ARGUMENTS_PARAMETER_NAME = "-tlcArguments";
+
+    private static final String SOURCE_DIR_PARAMETER_NAME = "-source";
+    private static final String GENERATE_SPEC_OVERWRITE_PARAMETER_NAME = "-overwrite";
+    
+    static final String SPEC_TE_INIT_ID = "_SpecTEInit";
+    static final String SPEC_TE_NEXT_ID = "_SpecTENext";
+    private static final String SPEC_TE_ACTION_CONSTRAINT_ID = "_SpecTEActionConstraint";
+    
+    // <parameter name, whether the parameter takes an argument>
+    private static final HashMap<String, Boolean> TLC_ARGUMENTS_TO_IGNORE;
+    
+    static {
+    	TLC_ARGUMENTS_TO_IGNORE = new HashMap<>();
+    	
+    	TLC_ARGUMENTS_TO_IGNORE.put("-config", Boolean.TRUE);
+    	TLC_ARGUMENTS_TO_IGNORE.put("-metadir", Boolean.TRUE);
+    	TLC_ARGUMENTS_TO_IGNORE.put("-tool", Boolean.FALSE);
+    }
+    
+    
+    /**
+	 * @param sourceDirectory
+	 * @param originalSpecName
+	 * @param results
+	 * @param error
+	 * @return an array of length two; the 0-index is the location to the
+	 *         destination TLA file, and the 1-index is that of the CFG file
+	 * @throws IOException
+	 */
+    public static File[] writeSpecTEFiles(final File sourceDirectory, final String originalSpecName,
+    									  final MCParserResults results, final MCError error) throws IOException {
+    	final StringBuilder tlaBuffer = new StringBuilder();
+    	final StringBuilder cfgBuffer = new StringBuilder();
+    	
+    	final Vect<?> configDeclaredConstants = results.getModelConfig().getConstants();
+    	final HashSet<String> constantModelValuesToDeclare = new HashSet<>();
+    	final int constantsCount = configDeclaredConstants.size();
+    	for (int i = 0; i < constantsCount; i++) {
+    		final Vect<?> constantDeclaration = (Vect<?>)configDeclaredConstants.elementAt(i);
+    		final Object value = constantDeclaration.elementAt(1);
+    		if (value instanceof SetEnumValue) {
+    			final SetEnumValue sev = (SetEnumValue)value;
+    			final ValueEnumeration ve = sev.elements();
+    			Value v = ve.nextElement();
+    			while (v != null) {
+    				constantModelValuesToDeclare.add(v.toString());
+    				v = ve.nextElement();
+    			}
+    		}
+    	}
+    	if (constantModelValuesToDeclare.size() > 0) {
+	    	cfgBuffer.append(TLAConstants.KeyWords.CONSTANTS).append(TLAConstants.CR);
+	    	for (final String modelValue : constantModelValuesToDeclare) {
+	    		cfgBuffer.append(TLAConstants.INDENT).append(modelValue).append(TLAConstants.EQ);
+	    		cfgBuffer.append(modelValue).append(TLAConstants.CR);
+	    	}
+	    	cfgBuffer.append(TLAConstants.CR);
+	    	
+	    	tlaBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.CONSTANTS).append(' ');
+	    	boolean firstDone = false;
+	    	for (final String modelValue : constantModelValuesToDeclare) {
+	    		if (firstDone) {
+	    			tlaBuffer.append(", ");
+	    		} else {
+	    			firstDone = true;
+	    		}
+	    		
+	    		tlaBuffer.append(modelValue);
+	    	}
+	    	tlaBuffer.append(TLAConstants.CR).append(TLAConstants.CR);
+    	}
+    	
+    	final List<MCState> trace = error.getStates();
+    	final StringBuilder[] tlaBuffers
+    		= SpecTraceExpressionWriter.addInitNextToBuffers(cfgBuffer, trace, null, SPEC_TE_INIT_ID, SPEC_TE_NEXT_ID,
+    														 SPEC_TE_ACTION_CONSTRAINT_ID,
+    														 results.getOriginalNextOrSpecificationName(), true);
+    	tlaBuffer.append(tlaBuffers[0].toString());
+    	SpecTraceExpressionWriter.addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, trace);
+    	tlaBuffer.append(tlaBuffers[1].toString());
+    	
+    	final List<String> extendedModules = results.getOriginalExtendedModules();
+    	final boolean specExtendsTLC = extendedModules.contains(TLAConstants.BuiltInModules.TLC);
+    	final boolean specExtendsToolbox = extendedModules.contains(TLAConstants.BuiltInModules.TRACE_EXPRESSIONS);
+		final TLACopier tlaCopier = new TLACopier(originalSpecName, TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+												  sourceDirectory, tlaBuffer.toString(), specExtendsTLC,
+												  specExtendsToolbox);
+		tlaCopier.copy();
+		MP.printMessage(EC.GENERAL,
+						"The file " + tlaCopier.getDestinationFile().getAbsolutePath() + " has been created.");
+		
+		final CFGCopier cfgCopier = new CFGCopier(originalSpecName, TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+												  sourceDirectory, cfgBuffer.toString());
+		cfgCopier.copy();
+		MP.printMessage(EC.GENERAL,
+						"The file " + cfgCopier.getDestinationFile().getAbsolutePath() + " has been created.");
+		
+		return new File[] { tlaCopier.getDestinationFile(), cfgCopier.getDestinationFile() };
+    }
+    
+    
+    private enum RunMode {
+    	GENERATE_SPEC_TE, PRETTY_PRINT, GENERATE_FROM_TLC_RUN, QUASI_REPL, TRACE_EXPLORATION;
+    }
+
+    
+    private File specGenerationSourceDirectory;
+    private String specGenerationOriginalSpecName;
+    private boolean expectedOutputFromStdIn;
+    private boolean overwriteGeneratedFiles;
+    
+    private List<String> expressions;
+    private List<String> tlcArguments;
+    
+    private String replSpecName;
+    private File temporaryREPLSpec;
+    
+    private RunMode runMode;
+
+    /**
+     * @param commandLineArguments arguments, ostensibly from the command line, with which this instance will configure
+     * 								itself.
+     */
+    public TraceExplorer(final String[] commandLineArguments) {
+    	processArguments(commandLineArguments);
+    	
+    	if (runMode == null) {
+			printUsageAndExit();
+    	}
+    }
+    
+    protected final void processArguments(final String[] args) {
+    	runMode = determineRunModeFromArguments(args);
+    	if (runMode == null) {
+    		return;
+    	}
+
+    	switch (runMode) {
+			case QUASI_REPL:
+			case TRACE_EXPLORATION:
+	    		tlcArguments = new ArrayList<>();
+				break;
+			default:
+				break;
+    	}
+    	
+		specGenerationSourceDirectory = new File(System.getProperty("user.dir"));
+		specGenerationOriginalSpecName = args[args.length - 1];
+		expectedOutputFromStdIn = (specGenerationOriginalSpecName.charAt(0) == '-');
+		if (expectedOutputFromStdIn) {
+			specGenerationOriginalSpecName = null;
+		} else if (RunMode.QUASI_REPL.equals(runMode)) {
+			printUsageAndExit();
+		}
+		overwriteGeneratedFiles = false;
+
+		String expressionsSourceFilename = null;
+		
+		boolean consumedAdditionalParameters = true;
+		final int upperIndex = expectedOutputFromStdIn ? args.length : (args.length - 1);
+    	int index = 0;
+		while (consumedAdditionalParameters) {
+			if (index < upperIndex) {
+				final String nextArg = args[index];
+				
+				if (getRunModeForArgument(nextArg) != null) {
+					index++;
+					continue;
+				}
+				
+				if (SOURCE_DIR_PARAMETER_NAME.equals(nextArg)) {
+					index++;
+					final String runDirectory = args[index++];
+            		final File f = new File(runDirectory);
+            		
+            		if (!f.exists()) {
+            			printErrorMessage("specified source directory does not exist.");
+            			return;
+            		}
+            		
+            		if (!f.isDirectory()) {
+            			printErrorMessage("specified source directory is not a directory.");
+            			return;
+            		}
+            		specGenerationSourceDirectory = f;
+
+					index++;
+				} else if (GENERATE_SPEC_OVERWRITE_PARAMETER_NAME.equals(nextArg)) {
+					overwriteGeneratedFiles = true;
+					
+					index++;
+				} else if (EXPRESSIONS_FILE_PARAMETER_NAME.equals(nextArg)) {
+					index++;
+
+					expressionsSourceFilename = args[index++];
+				} else if (MODEL_CHECK_JVM_ARGUMENTS_PARAMETER_NAME.equals(nextArg)) {
+					index++;
+
+					final String argumentList = args[index++];
+					final String[] arguments = argumentList.split(" ");
+					TLCRunner.JVM_ARGUMENTS.addAll(Arrays.asList(arguments));
+				} else if (MODEL_CHECK_TLC_ARGUMENTS_PARAMETER_NAME.equals(nextArg)) {
+					index++;
+					final String argumentList = args[index++];
+					final String[] arguments = argumentList.split(" ");
+					int argIndex = 0;
+					
+					while (argIndex < arguments.length) {
+						final String argument = arguments[argIndex];
+						final Boolean ignoreAdditionalParameter = TLC_ARGUMENTS_TO_IGNORE.get(argument);
+						
+						if (ignoreAdditionalParameter == null) {
+							tlcArguments.add(argument);
+						} else {
+							if (ignoreAdditionalParameter.booleanValue()) {
+								argIndex++;
+							}
+						}
+						
+						argIndex++;
+					}
+				} else {
+    				consumedAdditionalParameters = false;
+				}
+			} else {
+				consumedAdditionalParameters = false;
+			}
+		}
+		
+		if (RunMode.TRACE_EXPLORATION.equals(runMode) || RunMode.QUASI_REPL.equals(runMode)) {
+			if (expressionsSourceFilename == null) {
+    			printErrorMessage("no expressions file specified.");
+				runMode = null;
+				return;
+			}
+			
+			final File sourceDirFile = new File(specGenerationSourceDirectory, expressionsSourceFilename);
+			final File absoluteFile = new File(expressionsSourceFilename);
+			final File f;
+			if (sourceDirFile.exists()) {
+				f = sourceDirFile;
+			} else if (absoluteFile.exists()) {
+				f = absoluteFile;
+			} else {
+				final String errorMessageSuffix;
+				if (sourceDirFile.getAbsolutePath().equals(absoluteFile.getAbsolutePath())) {
+					errorMessageSuffix = sourceDirFile.getAbsolutePath();
+				} else {
+					errorMessageSuffix = sourceDirFile.getAbsolutePath()
+													+ " nor " + absoluteFile.getAbsolutePath();
+				}
+				printErrorMessage("an expressions file could not be found at " + errorMessageSuffix);
+				runMode = null;
+				return;
+			}
+
+			try {
+				expressions = new ArrayList<>();
+				try (final BufferedReader br = new BufferedReader(new FileReader(f))) {
+					String line;
+					while ((line = br.readLine()) != null) {
+						final String trimmed = line.trim();
+						
+						if (trimmed.length() > 0) {
+							expressions.add(trimmed);
+						}
+					}
+				}
+			} catch (final IOException e) {
+				printErrorMessage("encountered an exception reading from expressions file "
+									+ f.getAbsolutePath() + " :: " + e.getMessage());
+				runMode = null;
+				return;
+			}
+
+			if (RunMode.TRACE_EXPLORATION.equals(runMode)) {
+				tlcArguments.add("-config");
+				tlcArguments.add(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME 
+									+ TLAConstants.Files.CONFIG_EXTENSION);
+				
+				tlcArguments.add("-tool");
+			} else {
+				replSpecName = "repl"; //"REPL_" + System.currentTimeMillis();
+				temporaryREPLSpec = new File(specGenerationSourceDirectory,
+											 replSpecName + TLAConstants.Files.TLA_EXTENSION);
+				temporaryREPLSpec.deleteOnExit();
+			}
+
+			tlcArguments.add("-metadir");
+			tlcArguments.add(specGenerationSourceDirectory.getAbsolutePath());
+			
+			if (RunMode.TRACE_EXPLORATION.equals(runMode)) {
+				tlcArguments.add(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME);
+			} else {
+				tlcArguments.add(replSpecName);
+			}
+		}
+     }
+    
+    /**
+     * @return an {@link EC} defined error code representing success or failure.
+     */
+    public int execute() throws Exception {
+    	if (RunMode.QUASI_REPL.equals(runMode)) {
+    		return performREPL();
+    	} else if (expectedOutputFromStdIn) {
+    		return executeStreaming();
+    	} else {
+    		return executeNonStreaming();
+    	}
+    }
+    
+    private RunMode determineRunModeFromArguments(final String[] args) {
+    	for (int i = 0; i < args.length; i++) {
+    		final RunMode rm = getRunModeForArgument(args[i]);
+    		
+    		if (rm != null) {
+    			return rm;
+    		}
+    	}
+
+    	return null;
+    }
+    
+    private RunMode getRunModeForArgument(final String arg) {
+    	if (GENERATE_SPEC_FUNCTION_PARAMETER_NAME.equals(arg)) {
+        	return RunMode.GENERATE_SPEC_TE;
+        } else if (PRETTY_PRINT_FUNCTION_PARAMETER_NAME.equals(arg)) {
+        	return RunMode.PRETTY_PRINT;
+        } else if (TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME.equals(arg)) {
+        	return RunMode.TRACE_EXPLORATION;
+        } else if (QUASI_REPL_PARAMETER_NAME.equals(arg)) {
+        	return RunMode.QUASI_REPL;
+        }
+    	
+    	return null;
+    }
+    
+    private int executeNonStreaming() throws Exception {
+    	if (!performPreFlightFileChecks()) {
+			throw new IllegalStateException("There was an issue with the input, "
+												+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + ", or "
+												+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " file.");
+    	}
+    	
+		final boolean specifiedModuleIsSpecTE
+					= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+		final boolean needGenerateSpecTE = RunMode.GENERATE_SPEC_TE.equals(runMode) 
+											|| (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
+    	if (needGenerateSpecTE) {
+			final MCParser parser = new MCParser(specGenerationSourceDirectory, specGenerationOriginalSpecName);
+    		final MCParserResults results = parser.parse();
+    		
+    		if (results.getOutputMessages().size() == 0) {
+				MP.printMessage(EC.GENERAL, "The output file had no tool messages; was TLC not run with"
+												+ " the '-tool' option when producing it?");
+
+    			return EC.ExitStatus.ERROR;
+    		} else if (results.getError() == null) {
+    			final String msg;
+    			if (RunMode.GENERATE_SPEC_TE.equals(runMode)) {
+    				msg = "The output file contained no error-state messages, no "
+    							+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " will be produced.";
+    			} else {
+    				msg = "The output file contained no error-state messages, no "
+								+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " nor "
+								+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " will be produced, and, so, "
+								+ "no trace expressions will be evaluated.";
+    			}
+				MP.printMessage(EC.GENERAL, msg);
+
+    			return EC.NO_ERROR;
+    		} else {
+				try {
+					writeSpecTEFiles(results, results.getError());
+
+					if (RunMode.GENERATE_SPEC_TE.equals(runMode)) {
+						return EC.NO_ERROR;
+					} else if (RunMode.TRACE_EXPLORATION.equals(runMode)) { 	// currently always true
+			    		return performTraceExploration();
+			    	}
+				} catch (final Exception e) { }
+    		}
+    	} else if (RunMode.PRETTY_PRINT.equals(runMode)) {
+    		try {
+	    		MCOutputParser.prettyPrintToStream(System.out, specGenerationSourceDirectory,
+	    										   specGenerationOriginalSpecName);
+				
+				return EC.NO_ERROR;
+    		} catch (final Exception e) { }
+    	}
+    	    	
+		return EC.ExitStatus.ERROR;
+    }
+    
+    private int executeStreaming() throws Exception {
+    	final AtomicBoolean mcParserCompleted = new AtomicBoolean(false);
+    	final ReentrantLock parseLock = new ReentrantLock();
+    	final ArrayList<MCParser> parserList = new ArrayList<>(1);
+		final MCOutputPipeConsumer.ConsumerLifespanListener listener
+							= new MCOutputPipeConsumer.ConsumerLifespanListener() {
+			@Override
+			public void consumptionFoundSourceDirectoryAndSpecName(MCOutputPipeConsumer consumer) {
+				specGenerationSourceDirectory = consumer.getSourceDirectory();
+				specGenerationOriginalSpecName = consumer.getSpecName();
+				
+				final boolean specifiedModuleIsSpecTE
+							= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+				final boolean needGenerateSpecTE
+							= RunMode.GENERATE_SPEC_TE.equals(runMode)
+											|| (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
+
+				if (!performPreFlightFileChecks()) {
+					throw new IllegalStateException("There was an issue with the input, "
+														+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + ", or "
+														+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " file.");
+				}
+
+				if (needGenerateSpecTE) {
+					MP.printMessage(EC.GENERAL,
+							"Have encountered the source spec in the output logging, will begin parsing of those assets now.");
+					
+					final Runnable r = () -> {
+						final MCParser parser
+								= new MCParser(specGenerationSourceDirectory, specGenerationOriginalSpecName, true);
+						parserList.add(parser);
+						
+						parseLock.lock();
+						try {
+							parser.parse();
+						} finally {
+							mcParserCompleted.set(true);
+							parseLock.unlock();
+						}
+					};
+					(new Thread(r)).start();
+				}
+			}
+		};
+		final MCOutputPipeConsumer pipeConsumer = new MCOutputPipeConsumer(System.in, listener);
+		
+		MP.printMessage(EC.GENERAL, "TraceExplorer is expecting input on stdin...");
+
+		pipeConsumer.consumeOutput(false);
+		
+		if (pipeConsumer.outputHadNoToolMessages()) {
+			MP.printMessage(EC.GENERAL, "The output had no tool messages; was TLC not run with"
+					+ " the '-tool' option when producing it?");
+
+			return EC.ExitStatus.ERROR;
+		}
+
+		MP.printMessage(EC.GENERAL, "Have received the final output logging message - finishing TraceExplorer work.");
+
+		final boolean specifiedModuleIsSpecTE
+				= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+		final boolean needGenerateSpecTE
+				= RunMode.GENERATE_SPEC_TE.equals(runMode)
+								|| (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
+    	if (needGenerateSpecTE) {
+    		if (pipeConsumer.getError() == null) {
+    			final String msg;
+    			if (RunMode.GENERATE_SPEC_TE.equals(runMode)) {
+    				msg = "The output contained no error-state messages, no "
+    							+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " will be produced.";
+    			} else {
+    				msg = "The output contained no error-state messages, no "
+								+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " nor "
+								+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " will be produced, and, so, "
+								+ "no trace expressions will be evaluated.";
+    			}
+				MP.printMessage(EC.GENERAL, msg);
+
+    			return EC.NO_ERROR;
+    		} else {
+        		if (!mcParserCompleted.get()) {
+        			parseLock.lock();
+        		}
+        		final MCParserResults results = parserList.get(0).getParseResults();
+    			
+				try {
+					writeSpecTEFiles(results, pipeConsumer.getError());
+
+					if (RunMode.GENERATE_SPEC_TE.equals(runMode)) {
+						return EC.NO_ERROR;
+					} else if (RunMode.TRACE_EXPLORATION.equals(runMode)) { 	// currently always true
+			    		return performTraceExploration();
+			    	}
+				} catch (final Exception e) { }
+    		}
+    	} else if (RunMode.PRETTY_PRINT.equals(runMode)) {
+    		if (pipeConsumer.getError() == null) {
+    			MP.printMessage(EC.GENERAL, "The output contained no error-state messages; there is nothing to display.");
+				
+				return EC.NO_ERROR;
+    		} else {
+        		try {
+    	    		MCOutputParser.prettyPrintToStream(System.out, pipeConsumer.getError());
+    				
+    				return EC.NO_ERROR;
+        		} catch (final Exception e) { }
+    		}
+    	}
+    	
+		return EC.ExitStatus.ERROR;
+    }
+    
+    private int performREPL() throws IOException {
+    	final REPLSpecWriter writer = new REPLSpecWriter(replSpecName, expressions);
+    	final File cfgFile = new File(specGenerationSourceDirectory, replSpecName + TLAConstants.Files.CONFIG_EXTENSION);
+    	cfgFile.deleteOnExit();
+    	writer.writeFiles(temporaryREPLSpec, cfgFile);
+
+    	final REPLSpecWriter.REPLLogConsumerStream outputStream = new REPLSpecWriter.REPLLogConsumerStream();
+		final TLCRunner tlcRunner = new TLCRunner(tlcArguments, outputStream);
+		tlcRunner.setSilenceStdOut(true);
+		final int errorCode = tlcRunner.run();
+		
+		System.out.println(String.join("\n", expressions));
+		System.out.println("\t" + TLAConstants.EQ);
+		// TODO indent on multi-line
+		System.out.println("\t\t" + outputStream.getCollectedContent());
+
+    	return errorCode;
+    }
+    
+	private int performTraceExploration() throws IOException {
+		final File tlaFile = new File(specGenerationSourceDirectory,
+				TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION);
+		final TraceExpressionExplorerSpecWriter writer = new TraceExpressionExplorerSpecWriter(expressions);
+		final String configContent = writer.getConfigBuffer().toString();
+		writer.writeFiles(tlaFile, null);
+
+		final CFGCopier cfgCopier = new CFGCopier(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+				TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME, specGenerationSourceDirectory, configContent);
+		cfgCopier.copy();
+
+		final File outFile = new File(specGenerationSourceDirectory,
+				TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + TLAConstants.Files.OUTPUT_EXTENSION);
+		final TLCRunner tlcRunner = new TLCRunner(tlcArguments, outFile);
+		System.out.println("Forking TLC...");
+		final int errorCode = tlcRunner.run();
+
+		MCOutputParser.prettyPrintToStream(System.out, specGenerationSourceDirectory,
+										   TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME);
+
+		return errorCode;
+	}
+
+    private boolean performPreFlightFileChecks() {
+		final boolean specifiedModuleIsSpecTE
+				= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+		final boolean outputShouldExist = !expectedOutputFromStdIn 
+											|| (specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
+
+		String filename;
+    	
+    	if (outputShouldExist) {
+    		filename = specGenerationOriginalSpecName + TLAConstants.Files.OUTPUT_EXTENSION;
+    		final File outputFile = new File(specGenerationSourceDirectory, filename);
+    		if (!outputFile.exists()) {
+    			printErrorMessage("source directory (" + specGenerationSourceDirectory + ") does not contain "
+    					+ filename);
+    			
+    			runMode = null;
+    			return false;
+    		}
+    	}
+    	
+		if (RunMode.GENERATE_SPEC_TE.equals(runMode) || RunMode.TRACE_EXPLORATION.equals(runMode)) {
+			filename = specGenerationOriginalSpecName + TLAConstants.Files.TLA_EXTENSION;
+			final File tlaFile = new File(specGenerationSourceDirectory, filename);
+			if (!tlaFile.exists()) {
+				printErrorMessage("source directory (" + specGenerationSourceDirectory + ") does not contain "
+						+ filename);
+				
+				runMode = null;
+				return false;
+			}
+	    	
+			filename = specGenerationOriginalSpecName + TLAConstants.Files.CONFIG_EXTENSION;
+			final File configFile = new File(specGenerationSourceDirectory, filename);
+			if (!configFile.exists()) {
+				printErrorMessage("source directory (" + specGenerationSourceDirectory + ") does not contain "
+						+ filename);
+				
+				runMode = null;
+				return false;
+			}
+			
+			if (!overwriteGeneratedFiles) {
+				if (!specifiedModuleIsSpecTE) {
+					final File specTETLA = new File(specGenerationSourceDirectory,
+							(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION));
+
+					if (specTETLA.exists()) {
+						printErrorMessage("specified source directory already contains " + specTETLA.getName()
+								+ "; specify '" + GENERATE_SPEC_OVERWRITE_PARAMETER_NAME + "' to overwrite.");
+
+						runMode = null;
+						return false;
+					}
+				}
+				
+				if (RunMode.TRACE_EXPLORATION.equals(runMode)) {
+					final File teTLA = new File(specGenerationSourceDirectory,
+								(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION));
+
+					if (teTLA.exists()) {
+						printErrorMessage("specified source directory already contains " + teTLA.getName()
+								+ "; specify '" + GENERATE_SPEC_OVERWRITE_PARAMETER_NAME + "' to overwrite.");
+
+						runMode = null;
+						return false;
+					}
+				}
+			}
+		}
+		
+		return true;
+    }
+    
+    private void writeSpecTEFiles(final MCParserResults results, final MCError error) throws IOException {
+    	writeSpecTEFiles(specGenerationSourceDirectory, specGenerationOriginalSpecName, results, error);
+    }
+    
+    private void printErrorMessage(final String message) {
+    	MP.printError(EC.GENERAL, message);
+    }
+    
+    
+    private static void printUsageAndExit() {
+    	final List<List<UsageGenerator.Argument>> commandVariants = new ArrayList<>();
+    	final UsageGenerator.Argument expressionFile
+				= new UsageGenerator.Argument(EXPRESSIONS_FILE_PARAMETER_NAME, "file",
+											 "expressions specified, one per line if being used in conjunction\n"
+													+ "with " + TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME
+													+ " and just one if being used with "
+													+ QUASI_REPL_PARAMETER_NAME
+													+ "\nthis file must be in the source "
+													+ "directory or specified by\nfull path", false);
+    	final UsageGenerator.Argument jvmArguments
+				= new UsageGenerator.Argument(MODEL_CHECK_JVM_ARGUMENTS_PARAMETER_NAME,
+											  "\"-Xmx3072m -Xms512m\"",
+											  "these arguments will be passed on to the TLC JVM when performing\n"
+													+ "the model check",
+											  true);
+    	final UsageGenerator.Argument overwrite
+	    		= new UsageGenerator.Argument(GENERATE_SPEC_OVERWRITE_PARAMETER_NAME,
+	    									  "if specified, and if a SpecTE, or TE, file pair already exists,\n"
+	    											+ "they will be overwritten; if they exist, and this has not been\n"
+	    											+ "specified, the process will halt", true);
+    	final UsageGenerator.Argument sourceDirectory
+				= new UsageGenerator.Argument(SOURCE_DIR_PARAMETER_NAME, "path",
+											  "the path to the directory containing tool output from model\n"
+													+ "checking; defaults to CWD. This will be ignored if no SpecName\n"
+													+ "has been specified (and so tool output will be expected to\n"
+													+ "arrive on stdin)", true);
+    	final UsageGenerator.Argument spec
+				= new UsageGenerator.Argument("SpecName",
+											  "if no spec name is specified, tool output will be expected to arrive\n"
+													+ "via stdin and any " + SOURCE_DIR_PARAMETER_NAME
+													+ " definition will be ignored.", false);
+    	final UsageGenerator.Argument tlcArguments
+				= new UsageGenerator.Argument(MODEL_CHECK_TLC_ARGUMENTS_PARAMETER_NAME,
+											  "\"-some -other 2 -tlc arguments\"",
+											  "these arguments will be passed on to TLC when performing the\n"
+													+ "model check; -config, -tool, and -metadir will be ignored,\n"
+													+ "if specified",
+											  true);
+    	
+    	final List<UsageGenerator.Argument> traceExpressionsVariant = new ArrayList<>();
+    	traceExpressionsVariant.add(jvmArguments);
+    	traceExpressionsVariant.add(overwrite);
+    	traceExpressionsVariant.add(sourceDirectory);
+    	traceExpressionsVariant.add(tlcArguments);
+    	traceExpressionsVariant.add(spec);
+    	traceExpressionsVariant.add(expressionFile);
+    	traceExpressionsVariant.add(new UsageGenerator.Argument(
+    												TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME,
+    												"evaluate trace expressions in the context of an error state\n"
+    													+ "specified through a generated SpecTE file; if the 'SpecName'\n"
+    													+ "specified is anything other that 'SpecTE', the tool will\n"
+    													+ "generate the SpecTE file pair, prior to generating the TE\n"
+    													+ "file pair for the subsequently performed model checking",
+    													false));
+    	commandVariants.add(traceExpressionsVariant);
+
+    	
+    	final List<UsageGenerator.Argument> replBisVariant = new ArrayList<>();
+    	replBisVariant.add(jvmArguments);
+    	replBisVariant.add(tlcArguments);
+    	replBisVariant.add(new UsageGenerator.Argument(
+    												QUASI_REPL_PARAMETER_NAME,
+    												"perform a one-off evaluation of an expression", false));
+    	replBisVariant.add(expressionFile);
+    	commandVariants.add(replBisVariant);
+
+    	
+    	final List<UsageGenerator.Argument> generateSpecVariant = new ArrayList<>();
+    	generateSpecVariant.add(overwrite);
+    	generateSpecVariant.add(sourceDirectory);
+    	generateSpecVariant.add(spec);
+    	generateSpecVariant.add(new UsageGenerator.Argument(
+    												GENERATE_SPEC_FUNCTION_PARAMETER_NAME,
+    												"generate a SpecTE tla/cfg pair from a model checking tool mode\n"
+    													+ "output", false));
+    	commandVariants.add(generateSpecVariant);
+
+    	
+    	final List<UsageGenerator.Argument> prettyPrintVariant = new ArrayList<>();
+    	prettyPrintVariant.add(sourceDirectory);
+    	prettyPrintVariant.add(spec);
+    	prettyPrintVariant.add(new UsageGenerator.Argument(
+    												PRETTY_PRINT_FUNCTION_PARAMETER_NAME,
+    												"pretty print the error states of a model checking tool mode\n"
+    													+ "output", false));
+    	commandVariants.add(prettyPrintVariant);
+
+    	
+    	UsageGenerator.displayUsage(ToolIO.out, "TraceExplorer", TLCGlobals.versionOfTLC,
+    								"a tool for working with TLC tool output and exploring trace expressions",
+    								Messages.getString("TraceExplorerDescription"),
+    								commandVariants, null, ' ');
+    	
+    	System.exit(-1);
+    }
+    
+    /**
+     * Ways to run this application:
+     * 
+     *  1. Evaluation of trace expressions from afrom an existing .tla/.out/.cfg triplet in which the .out contains
+     *  	one or more MP.ERROR messages - see https://github.com/tlaplus/tlaplus/issues/393 for background:
+     *  				java tlc2.TraceExplorer -traceExpressions \
+     *  						-expressionsFile=_file_containing_expressions_one_per_line_ \
+     *  						[-tlcArguments=_directory_containing_prior_run_output_] \
+     *  						[-source=_directory_containing_prior_run_output_] \
+     *  						[-overwrite] \
+     *  						SpecName
+     *  	the source directory defaults to CWD if not defined; the expressions file must either exist in the source
+     *  	directory or be a full path; if TLC arguments are specified (all within quotes) they will be passed on to
+     *  	TLC when performing the model check; -config, -tool, and -metadir will be ignored, if specified;  if no
+     *  	SpecName is specified then we will expect the output data to arrive on stdin - anything specified via
+     *  	-source will be ignore in this case as we will derive that from the output log content.
+     *  
+     *  2. Evaluation of an expression, ala REPL-ese:
+     *  				java tlc2.TraceExplorer -replBis \
+     *  						-expressionsFile=_file_containing_expressions_to_evaluate_ \
+     *  						[-tlcArguments=_directory_containing_prior_run_output_]
+     *  	the expressions file must either exist in the source directory or be a full path; if TLC arguments are
+     *  	specified (all within quotes) they will be passed on to TLC when performing the model check; -config,
+     *  	-tool, and -metadir will be ignored, if specified
+     *  
+     *  3. Generation of a 'SpecTE.tla' from an existing .tla/.out/.cfg triplet in which the .out contains
+     *  	one or more MP.ERROR messages - see https://github.com/tlaplus/tlaplus/issues/393 for background:
+     *  				java tlc2.TraceExplorer -generateSpecTE \
+     *  						[-source=_directory_containing_prior_run_output_] \
+     *  						[-overwrite] \
+     *  						SpecName
+     *  	the source directory defaults to CWD if not defined; if overwrite is not specified and a SpecTE.tla
+     *  	already exists in the source directory, execution will halt; if no SpecName is specified then we
+     *  	will expect the output data to arrive on stdin - anything specified via -source will be ignore in this
+     *  	case as we will derive that from the output log content.
+     *  
+     *  4. Pretty print the error states from an existing .out file to {@link System#out}:
+     *  				java tlc2.TraceExplorer -prettyPrint \
+     *  						[-source=_directory_containing_prior_run_output_] \
+     *  						SpecName
+     *  	the source directory defaults to CWD if not defined; if no SpecName is specified then we
+     *  	will expect the output data to arrive on stdin - anything specified via -source will be ignore in this
+     *  	case as we will derive that from the output log content.
+     */
+    public static void main(final String[] args) {
+    	if (args.length == 0) {
+    		printUsageAndExit();
+    	}
+    	
+    	try {
+        	final TraceExplorer te = new TraceExplorer(args);
+	    	final int returnCode = te.execute();
+	    	
+	    	System.exit(returnCode);
+    	} catch (final Exception e) {
+    		System.err.println("Caught exception: " + e.getLocalizedMessage());
+    		
+    		printUsageAndExit();
+    	}
+    }
+}
diff --git a/tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java b/tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..c422a7d21ed886162846044594764c4605aa5614
--- /dev/null
+++ b/tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java
@@ -0,0 +1,152 @@
+package tlc2;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import tlc2.output.AbstractSpecWriter;
+import tlc2.output.SpecWriterUtilities;
+import util.TLAConstants;
+
+/**
+ * The class name is quite wordy - but the 'more appropriate' TraceExpressionSpecWriter class name is too close to
+ * 	the already-existing SpecTraceExpressionWriter class name.
+ * 
+ * This is the spec writer concrete class which has niceties that lend themselves to the creation of the "TE" tla/cfg
+ * 	pair.
+ */
+public class TraceExpressionExplorerSpecWriter extends AbstractSpecWriter {
+	private static final String EXPRESSION_VARIABLE_NAME_PREFIX = "_traceExpression_";
+	private static final String EXPRESSION_COMMENT_LINE_PREFIX = TLAConstants.COMMENT + " TRACE EXPRESSION: ";
+	
+    private static final String TE_INIT_ID = "_TEInit";
+    private static final String TE_INIT_ATTRIBUTE_NAME = "teBehaviorInit";
+    private static final String TE_NEXT_ID = "_TENext";
+    private static final String TE_NEXT_ATTRIBUTE_NAME = "teBehaviorNext";
+    
+    /**
+     * If the TLA file exists, it will be examined; if it is found to have been generated by this writer, a
+     * 	map of variableName -> traceExpression will be returned. Otherwise null will be returned.
+     * 
+     * @param tlaFile
+     * @return a map of variableName -> traceExpression if the file exists and was created by this writer, else null.
+     */
+    public static HashMap<String, String> getVariableExpressionMapFromTLAFile(final File tlaFile) throws IOException {
+    	if (tlaFile.exists()) {
+    		try (final BufferedReader br = new BufferedReader(new FileReader(tlaFile))) {
+    			final ArrayList<String> declarations = new ArrayList<>();
+    			boolean foundLine = true;
+    			String line;
+    			
+    			while (foundLine && ((line = br.readLine()) != null)) {
+    				if (line.startsWith(EXPRESSION_COMMENT_LINE_PREFIX)) {
+    					declarations.add(line.substring(EXPRESSION_COMMENT_LINE_PREFIX.length()));
+    				} else {
+    					foundLine = false;
+    				}
+    			}
+
+    			if (declarations.size() > 0) {
+    				final HashMap<String, String> variableExpressionMap = new HashMap<>();
+    				
+    				for (final String declaration : declarations) {
+    					final int index = declaration.indexOf(TLAConstants.EQ);
+    					if (index != -1) {
+        					final String variable = declaration.substring(0, index);
+        					final String expression = declaration.substring(index + TLAConstants.EQ.length());
+
+        					variableExpressionMap.put(variable, expression);
+    					}
+    				}
+    				
+    				return variableExpressionMap;
+    			}
+    		}
+    	}
+    	
+    	return null;
+    }
+    
+
+    private final TreeMap<String, String> variableExpressionMap;
+    
+	TraceExpressionExplorerSpecWriter(final List<String> expressions) {
+		super(true);
+		
+		variableExpressionMap = new TreeMap<>();
+		int count = 1;
+		for (final String expression : expressions) {
+			final String key = EXPRESSION_VARIABLE_NAME_PREFIX + count;
+			variableExpressionMap.put(key, expression);
+			tlaBuffer.append(EXPRESSION_COMMENT_LINE_PREFIX).append(key).append(TLAConstants.EQ);
+			tlaBuffer.append(expression).append(TLAConstants.CR);
+			count++;
+		}
+		
+		addPrimer(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME,
+				  TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+		
+		declareExpressionVariables();
+		createInitNextWithExpressions();
+		
+		tlaBuffer.append(SpecWriterUtilities.getGeneratedTimeStampCommentLine()).append(TLAConstants.CR);
+	}
+	
+	StringBuilder getConfigBuffer() {
+		return cfgBuffer;
+	}
+	
+	private void declareExpressionVariables() {
+		tlaBuffer.append("VARIABLES ");
+
+		boolean notFirst = false;
+		for (final String variable : variableExpressionMap.keySet()) {
+			if (notFirst) {
+				tlaBuffer.append(", ");
+			} else {
+				notFirst = true;
+			}
+			
+			tlaBuffer.append(variable);
+		}
+		
+		tlaBuffer.append(TLAConstants.CR).append(TLAConstants.CR);
+	}
+	
+	private void createInitNextWithExpressions() {
+		final StringBuilder initConjunction = new StringBuilder(TLAConstants.INDENTED_CONJUNCTIVE);
+		initConjunction.append(TraceExplorer.SPEC_TE_INIT_ID).append(TLAConstants.CR);
+		addExpressionsToBuffer(initConjunction, false);
+		
+		final List<String[]> initContent
+						= SpecWriterUtilities.createSourceContent(initConjunction.toString(), TE_INIT_ID, false);
+		addFormulaList(initContent, TLAConstants.KeyWords.INIT, TE_INIT_ATTRIBUTE_NAME);
+		
+		
+		final StringBuilder nextConjunction = new StringBuilder(TLAConstants.INDENTED_CONJUNCTIVE);
+		nextConjunction.append(TraceExplorer.SPEC_TE_NEXT_ID).append(TLAConstants.CR);
+		addExpressionsToBuffer(nextConjunction, true);
+
+		final List<String[]> nextContent
+						= SpecWriterUtilities.createSourceContent(nextConjunction.toString(), TE_NEXT_ID, false);
+		addFormulaList(nextContent, TLAConstants.KeyWords.NEXT, TE_NEXT_ATTRIBUTE_NAME);
+	}
+	
+	private void addExpressionsToBuffer(final StringBuilder buffer, final boolean primed) {
+		for (final Map.Entry<String, String> me : variableExpressionMap.entrySet()) {
+			buffer.append(TLAConstants.INDENTED_CONJUNCTIVE).append(me.getKey());
+			if (primed) {
+				buffer.append(TLAConstants.PRIME);
+			}
+			buffer.append(TLAConstants.EQ);
+			buffer.append(TLAConstants.L_PAREN).append(me.getValue()).append(TLAConstants.R_PAREN);
+			buffer.append(TLAConstants.CR);
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/input/AbstractMCOutputConsumer.java b/tlatools/src/tlc2/input/AbstractMCOutputConsumer.java
new file mode 100644
index 0000000000000000000000000000000000000000..439629cd36cb5c21b2c46f28e261ce03b8e6af87
--- /dev/null
+++ b/tlatools/src/tlc2/input/AbstractMCOutputConsumer.java
@@ -0,0 +1,150 @@
+package tlc2.input;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import tlc2.model.MCError;
+import tlc2.model.MCState;
+import tlc2.output.MP;
+
+abstract class AbstractMCOutputConsumer {
+	private static final int MAX_READ_RETRIES = 60;
+	private static final long SLEEP_BETWEEN_RETRIES = 500;
+	
+	// In the current world, the 'message class' (the second number run in the regex) is only a single digit, but i
+	//		specify potentially two to give room for future expansion without needing to change this code.
+	private static final String START_MESSAGE_REGEX
+				= MP.DELIM + MP.STARTMSG + "([0-9]{4})" + MP.COLON + "([0-9]{1,2})" + MP.SPACE + MP.DELIM;
+	private static final String END_MESSAGE_REGEX = MP.DELIM + MP.ENDMSG + "[0-9]{4}" + MP.SPACE + MP.DELIM;
+	
+	protected static final Pattern START_MESSAGE_PATTERN = Pattern.compile(START_MESSAGE_REGEX);
+	protected static final Pattern END_MESSAGE_PATTERN = Pattern.compile(END_MESSAGE_REGEX);
+	
+
+	private MCError error;
+	
+	public MCError getError() {
+		return error;
+	}
+
+	/**
+	 * Subclasses may override this should they wish; via this method, they will be
+	 * 	handed any line which is read and does not exist in a message block. This line
+	 * 	will not be written to the output file (if a non-null writer has been supplied to
+	 * 	the parseChunk(BufferedReader, BufferedWriter) method) until this method returns.
+	 * 
+	 * @param line
+	 */
+	protected void handleUnknownReadLine(final String line) throws IOException { }
+	
+	protected void consumeErrorMessageAndStates(final BufferedReader reader, final MCOutputMessage errorMessage)
+			throws IOException {
+		MCError currentError = null;
+		
+		// TODO unclear how - if - nested errors can occur; there is support for them in TLCError
+		//			which has [therefore] been mirrored in MCError
+		if (error == null) {
+			error = new MCError(null, errorMessage.getBody());
+			currentError = error;
+		} else {
+			currentError = new MCError((currentError != null) ? currentError : error, errorMessage.getBody());
+		}
+		
+		MCOutputMessage message = parseChunk(reader);
+		if ((message == null) || (message.getType() != MP.ERROR)) {
+			throw new IOException("Expected a useless error message like "
+									+ "'The behavior up to this point is...' but didn't find one after"
+									+ "[" + currentError.getMessage() + "]");
+		}
+		
+		boolean inStateTrace = true;
+		while (inStateTrace) {
+			message = parseChunk(reader);
+			if (message == null) {
+				throw new IOException("Unexpected end of the log during state consumption for "
+										+ "[" + currentError.getMessage() + "]");
+			}
+			
+			if (message.getType() == MP.STATE) {
+				currentError.addState(MCState.parseState(message.getBody()));
+			} else {
+				inStateTrace = false;
+				// TODO do we want to process this message?
+			}
+		}
+	}
+	
+	/**
+	 * The reader is assumed to be parked at a line containing a start message; if
+	 * not, lines will be consumed until one is found, and then the ensuing chunk
+	 * is consumed.
+	 * 
+	 * @param reader
+	 * @return a consumed message, or null if a new chunk could not be encountered
+	 * @throws IOException on a read error, or, if in an attempt to consume the next
+	 *                     chunk, we're unable to find the end of the chunk
+	 */
+	protected MCOutputMessage parseChunk(final BufferedReader reader) throws IOException {
+		MCOutputMessage message = null;
+		String startLine = null;
+		while (startLine == null) {
+			final String line = blockingReadLine(reader, true);
+			
+			if (line == null) {
+				return null;
+			}
+			
+			final Matcher m = START_MESSAGE_PATTERN.matcher(line);
+			if (m.find()) {
+				message = new MCOutputMessage(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)));
+				startLine = line;
+			} else {
+				handleUnknownReadLine(line);
+			}
+		}
+		
+		boolean chunkEndEncountered = false;
+		final StringBuilder sb = new StringBuilder();
+		while (!chunkEndEncountered) {
+			final String line = blockingReadLine(reader, false);
+			final Matcher m = END_MESSAGE_PATTERN.matcher(line);
+			if (m.find()) {
+				message.setBody(sb);
+				chunkEndEncountered = true;
+			} else {
+				if (sb.length() > 0) {
+					sb.append(MP.CR);
+				}
+				sb.append(line);
+			}
+		}
+		
+		return message;
+	}
+	
+	private String blockingReadLine(final BufferedReader reader, final boolean eofOK) throws IOException {
+		int retry = 0;
+		String line = reader.readLine();
+		while (line == null) {
+			retry++;
+			
+			if (retry == MAX_READ_RETRIES) {
+				if (eofOK) {
+					return null;
+				} else {
+					throw new IOException("We ran out of input unexpectedly.");
+				}
+			}
+			
+			try {
+				Thread.sleep(SLEEP_BETWEEN_RETRIES);
+			} catch (final Exception e) { }
+			
+			line = reader.readLine();
+		}
+		
+		return line;
+	}
+}
diff --git a/tlatools/src/tlc2/input/MCOutputMessage.java b/tlatools/src/tlc2/input/MCOutputMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..38b8db803b628fedf992f7dfa570fe6e38b59c5d
--- /dev/null
+++ b/tlatools/src/tlc2/input/MCOutputMessage.java
@@ -0,0 +1,44 @@
+package tlc2.input;
+
+public class MCOutputMessage {
+	private final int code;
+	private final int type;
+	private String body;
+	
+	MCOutputMessage(final int messageCode, final int messageType) {
+		code = messageCode;
+		type = messageType;
+	}
+	
+	void setBody(final StringBuilder sb) {
+		body = sb.toString();
+	}
+
+	/**
+	 * @return e.g EC.TLC_VERSION
+	 */
+	public int getCode() {
+		return code;
+	}
+
+	/**
+	 * Returns the type of the message - a.k.a message class
+	 * @return e.g MP.ERROR
+	 */
+	public int getType() {
+		return type;
+	}
+
+	/**
+	 * @return the body of the message - the content of the lines found between the
+	 *         start and end message delimiters
+	 */
+	public String getBody() {
+		return body;
+	}
+	
+	@Override
+	public String toString() {
+		return "[" + code + "," + type + "]\n" + body;
+	}
+}
diff --git a/tlatools/src/tlc2/input/MCOutputParser.java b/tlatools/src/tlc2/input/MCOutputParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3b0a0bbf586df23e952e1f2d7dc7ed01e8bc728
--- /dev/null
+++ b/tlatools/src/tlc2/input/MCOutputParser.java
@@ -0,0 +1,125 @@
+package tlc2.input;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import tlc2.TraceExpressionExplorerSpecWriter;
+import tlc2.model.MCError;
+import tlc2.model.MCState;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import util.TLAConstants;
+
+/**
+ * This class provides a parser to extract error & state from an MC.out file; it also provides a public static
+ * 	method which does a pretty-print dump to a provided output stream of the collected states in order.
+ */
+public class MCOutputParser extends AbstractMCOutputConsumer {
+	private final File mcOutFile;
+	
+	MCOutputParser(final File outFile) {
+		mcOutFile = outFile;
+	}
+
+	List<MCOutputMessage> parse(final boolean returnAllMessages) throws IOException {
+		final ArrayList<MCOutputMessage> encounteredMessages = returnAllMessages ? new ArrayList<>() : null;
+		
+		try (final BufferedReader br = new BufferedReader(new FileReader(mcOutFile))) {
+			MCOutputMessage message;
+			
+			while ((message = parseChunk(br)) != null) {
+				if (returnAllMessages) {
+					encounteredMessages.add(message);
+				}
+
+				if (message.getType() == MP.ERROR) {
+					consumeErrorMessageAndStates(br, message);
+				} else if (message.getCode() == EC.TLC_FINISHED) {
+					break;
+				}
+			}
+		}
+
+		return encounteredMessages;
+	}
+
+	
+	private static String getPrettyPrintForState(final MCState state) {
+		if (state.isStuttering()) {
+			return "<Stuttering>";
+		} else if (state.isBackToState()) {
+			return "<Back to state " + state.getStateNumber() + ">";
+		}
+		return state.getLabel();
+	}
+	
+	/**
+	 * @param os an output stream to send the 'pretty printed' trace to, or a
+	 *              notification if the output file contains no error-state
+	 *              messages, or if there is no output file to be found.
+	 * @param sourceDirectory
+	 * @param specName
+	 * @throws IOException
+	 */
+	public static void prettyPrintToStream(final OutputStream os, final File sourceDirectory, final String specName)
+			throws IOException {
+		final File outFile = new File(sourceDirectory, specName + TLAConstants.Files.OUTPUT_EXTENSION);
+		final MCOutputParser parser = new MCOutputParser(outFile);
+		parser.parse(false);
+		
+		final File tlaFile = new File(sourceDirectory, specName + TLAConstants.Files.TLA_EXTENSION);
+		final HashMap<String, String> variableTraceExpressionMap
+									= TraceExpressionExplorerSpecWriter.getVariableExpressionMapFromTLAFile(tlaFile);
+		prettyPrintToStream(os, parser.getError(), variableTraceExpressionMap);
+	}
+	
+	/**
+	 * @param os an output stream to send the 'pretty printed' trace to, or a
+	 *              notification if the error is null
+	 *              messages.
+	 * @param error
+	 * @throws IOException
+	 */
+	public static void prettyPrintToStream(final OutputStream os, final MCError error) {
+		prettyPrintToStream(os, error, null);
+	}
+	
+	/**
+	 * @param os an output stream to send the 'pretty printed' trace to, or a
+	 *              notification if the error is null
+	 *              messages.
+	 * @param error
+	 * @param variableTraceExpressionMap if non-null; any keys which are variable names will have the expressions
+	 * 										substituted for the display.
+	 * @throws IOException
+	 */
+	public static void prettyPrintToStream(final OutputStream os, final MCError error,
+										   final HashMap<String, String> variableTraceExpressionMap) {
+		final PrintStream ps;
+		if (os instanceof PrintStream) {
+			ps = (PrintStream)os;
+		} else {
+			ps = new PrintStream(os);
+		}
+		
+		if (error == null) {
+			ps.println("The output log contained no error-state messages; there is nothing to display.");
+		} else {
+			if (variableTraceExpressionMap != null) {
+				error.updateStatesForTraceExpression(variableTraceExpressionMap);
+			}
+			
+			for (final MCState state : error.getStates()) {
+				ps.println(getPrettyPrintForState(state));
+				ps.println(state.getConjunctiveDescription(true, "\t", true));
+			}
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/input/MCOutputPipeConsumer.java b/tlatools/src/tlc2/input/MCOutputPipeConsumer.java
new file mode 100644
index 0000000000000000000000000000000000000000..911d62febe47d6a8cd51e2c906b548ddf76cdf58
--- /dev/null
+++ b/tlatools/src/tlc2/input/MCOutputPipeConsumer.java
@@ -0,0 +1,137 @@
+package tlc2.input;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import util.TLAConstants;
+
+/**
+ * This class exists due to a request for the TraceExplorer CL application to be able to consume the output from TLC
+ * 	piped into TraceExplorer.  To that extent, this class will:
+ * 
+ * 	. read from a stream (for piping, this is System.in)
+ *	. ensure that it is reading tool messages (first line received will be a message of type EC.TLC_VERSION)
+ *			and fail appropriately if not
+ *	. look for the first TLAConstants.LoggingAtoms.PARSING_FILE line in order to derive spec name and source directory
+ *	. continue to read and wait and read and ... until an EC.TLC_FINISHED message is received - writing the content
+ *			to a temp file.
+ */
+public class MCOutputPipeConsumer extends AbstractMCOutputConsumer {
+	public interface ConsumerLifespanListener {
+		void consumptionFoundSourceDirectoryAndSpecName(final MCOutputPipeConsumer consumer);
+	}
+	
+	private String specName;
+	private File sourceDirectory;
+	private boolean currentlyEncounteringModuleInformation;
+	private final ArrayList<File> extendedModuleLocations;
+	
+	private MCOutputMessage tlcVersionMessage;
+
+	private final InputStream sourceStream;
+	private final ConsumerLifespanListener listener;
+	
+	
+	/**
+	 * @param inputStream the stream containing the tool output
+	 */
+	public MCOutputPipeConsumer(final InputStream inputStream, final ConsumerLifespanListener lifespanListener) {
+		sourceStream = inputStream;
+		listener = lifespanListener;
+		currentlyEncounteringModuleInformation = false;
+		extendedModuleLocations = new ArrayList<>();
+	}
+	
+	/**
+	 * This will not return until all output has been read and the output has correctly ended (or if there is a read
+	 * 	error, or if the output is not proper tool message output.)
+	 * 
+	 * @param returnAllMessages if true, all consumed messages will be returned
+	 * @return null or a {@link List} of {@link MCOutputMessage} instances of all messages consumed
+	 * @throws Exception
+	 */
+	public List<MCOutputMessage> consumeOutput(final boolean returnAllMessages) throws IOException {
+		final ArrayList<MCOutputMessage> encounteredMessages = returnAllMessages ? new ArrayList<>() : null;
+		
+		try (final BufferedReader br = new BufferedReader(new InputStreamReader(sourceStream))) {
+			MCOutputMessage message;
+
+			while ((message = parseChunk(br)) != null) {
+				if (returnAllMessages) {
+					encounteredMessages.add(message);
+				}
+
+				if (message.getType() == MP.ERROR) {
+					consumeErrorMessageAndStates(br, message);
+				} else if (message.getCode() == EC.TLC_VERSION) {
+					tlcVersionMessage = message;
+				} else if (message.getCode() == EC.TLC_FINISHED) {
+					break;
+				}
+			}
+		} catch (final IOException ioe) {
+			if (outputHadNoToolMessages()) {
+				// Either we threw this from handleUnknownReadLine(String), or the output was abortive.
+				return encounteredMessages;
+			}
+			
+			throw ioe;
+		}
+		
+		return encounteredMessages;
+	}
+	
+	public boolean outputHadNoToolMessages() {
+		return (tlcVersionMessage == null);
+	}
+	
+	public String getSpecName() {
+		return specName;
+	}
+	
+	public File getSourceDirectory() {
+		return sourceDirectory;
+	}
+	
+	public List<File> getExtendedModuleLocations() {
+		return extendedModuleLocations;
+	}
+	
+	@Override
+	protected void handleUnknownReadLine(final String line) throws IOException {
+		if (((specName == null) || currentlyEncounteringModuleInformation)
+							&& line.startsWith(TLAConstants.LoggingAtoms.PARSING_FILE)) {
+			if (tlcVersionMessage == null) {
+				throw new IOException("Output does not appear to be generated by TLC run with the '-tool' flag.");
+			}
+			
+			final String wholePath = line.substring(TLAConstants.LoggingAtoms.PARSING_FILE.length() + 1);
+			final File tlaFile = new File(wholePath);
+			
+			if (specName == null) {
+				sourceDirectory = tlaFile.getParentFile();
+				
+				final String tlaFilename = tlaFile.getName();
+				final int extensionDotIndex = tlaFilename.lastIndexOf('.');
+				specName = tlaFilename.substring(0, extensionDotIndex);
+				
+				if (listener != null) {
+					listener.consumptionFoundSourceDirectoryAndSpecName(this);
+				}
+				
+				currentlyEncounteringModuleInformation = true;
+			} else {
+				extendedModuleLocations.add(tlaFile);
+			}
+		} else if (currentlyEncounteringModuleInformation) {
+			currentlyEncounteringModuleInformation = false;
+		}
+	}	
+}
diff --git a/tlatools/src/tlc2/input/MCParser.java b/tlatools/src/tlc2/input/MCParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4fe53ebe4abaef18a20bbaaae2d5eae438e51be
--- /dev/null
+++ b/tlatools/src/tlc2/input/MCParser.java
@@ -0,0 +1,283 @@
+package tlc2.input;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+
+import tla2sany.semantic.InstanceNode;
+import tla2sany.semantic.ModuleNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SymbolMatcher;
+import tla2sany.semantic.SymbolNode;
+import tla2sany.st.Location;
+import tlc2.model.MCError;
+import tlc2.tool.Defns;
+import tlc2.tool.impl.ModelConfig;
+import tlc2.tool.impl.SpecProcessor;
+import tlc2.tool.impl.SymbolNodeValueLookupProvider;
+import tlc2.tool.impl.TLAClass;
+import util.FilenameToStream;
+import util.SimpleFilenameToStream;
+import util.TLAConstants;
+
+/**
+ * This class parses the triplet of .out, .cfg, and .tla files - typically thought of as MC.out, MC.cfg, MC.tla, but
+ * 	not required to be such.
+ */
+public class MCParser {
+	private static final int TOOL_ID = 0;
+	
+	/**
+	 * @param specProcessor
+	 * @param modelConfig
+	 * @return an instance of {@link MCParserResults} with neither error nor output messages set, yet
+	 */
+	public static MCParserResults generateResultsFromProcessorAndConfig(final SpecProcessor specProcessor,
+																		final ModelConfig modelConfig) {
+		final String rootModuleName;
+		final String nextOrSpecName;
+		final ArrayList<Location> initNextLocationsToDelete = new ArrayList<>();
+		final boolean isInitNext = !modelConfig.configDefinesSpecification();
+		final ModuleNode root = specProcessor.getRootModule();
+		if (isInitNext) {
+			final String initDefinitionName = modelConfig.getInit();
+			nextOrSpecName = modelConfig.getNext();
+			final Collection<SymbolNode> initNodes = root.getSymbols(new NodeNameMatcher(initDefinitionName));
+			final Collection<SymbolNode> nextNodes = root.getSymbols(new NodeNameMatcher(nextOrSpecName));
+
+			if (initNodes.size() == 1) {
+				final OpDefNode initNode = (OpDefNode) initNodes.iterator().next();
+
+				if (initNode.getOriginallyDefinedInModuleNode().equals(root)) {
+					initNextLocationsToDelete.add(initNode.getLocation());
+				}
+				// else it is defined in a module which the original spec is extending.. nothing to document
+			}
+
+			if (nextNodes.size() == 1) {
+				final OpDefNode nextNode = (OpDefNode) nextNodes.iterator().next();
+
+				if (nextNode.getOriginallyDefinedInModuleNode().equals(root)) {
+					initNextLocationsToDelete.add(nextNode.getLocation());
+				}
+				// else it is defined in a module which the original spec is extending.. nothing to document
+			}
+		} else {
+			nextOrSpecName = modelConfig.getSpec();
+		}
+		initNextLocationsToDelete.sort(new LocationComparator());
+
+		final ArrayList<String> extendees = new ArrayList<>();
+		root.getExtendedModuleSet(false).stream().forEach(moduleNode -> extendees.add(moduleNode.getName().toString()));
+
+		final HashSet<String> allExtendees = new HashSet<>();
+		root.getExtendedModuleSet(true).stream().forEach(moduleNode -> allExtendees.add(moduleNode.getName().toString()));
+		
+		rootModuleName = root.getName().toString();
+		
+		final HashSet<String> instantiatedModules = new HashSet<>();
+		collectionInstantiatedModules(root, instantiatedModules, allExtendees);
+
+		return new MCParserResults(rootModuleName, extendees, allExtendees, instantiatedModules,
+								   initNextLocationsToDelete, isInitNext, nextOrSpecName, modelConfig);
+	}
+	
+	private static void collectionInstantiatedModules(final ModuleNode node, final HashSet<String> modulesList,
+													  final HashSet<String> allExtendees) {
+		final InstanceNode[] instances = node.getInstances();
+		for (final InstanceNode instance : instances) {
+			final ModuleNode instanceModule = instance.getModule();
+			instanceModule.getExtendedModuleSet(true)
+							.stream().forEach(moduleNode -> allExtendees.add(moduleNode.getName().toString()));
+			modulesList.add(instanceModule.getName().toString());
+			collectionInstantiatedModules(instanceModule, modulesList, allExtendees);
+		}
+	}
+	
+	
+	private final ModelConfig configParser;
+
+	private MCOutputParser outputParser;
+	
+	private final TLAClass tlaClass;
+	private final Defns defns;
+	
+	private final FilenameToStream resolver;
+
+	private MCParserResults parserResults;
+	
+	private final String specBaseName;
+	
+	/**
+	 * @param sourceDirectory
+	 * @param specName
+	 * @param specialCaseOutputFile if non-null, this will be used as the source for the output parser
+	 */
+	public MCParser(final File sourceDirectory, final String specName) {
+		this(sourceDirectory, specName, null);
+		
+		File f= new File(sourceDirectory, (specBaseName + TLAConstants.Files.OUTPUT_EXTENSION));
+		if (!f.exists()) {
+			throw new IllegalArgumentException("No readable output file could be found at " + f.getAbsolutePath());
+		}
+		
+		outputParser = new MCOutputParser(f);
+		
+		f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.TLA_EXTENSION));
+		if (!f.exists()) {
+			throw new IllegalArgumentException("No readable TLA file could be found at " + f.getAbsolutePath());
+		}
+	}
+	
+	/**
+	 * @param sourceDirectory
+	 * @param specName
+	 * @param ignoreOutputParse if this is true, no output parsing will be done by this instance; the results returned
+	 * 			by {@link #parse()} will contain no output messages nor any potential {@code MCError}
+	 */
+	public MCParser(final File sourceDirectory, final String specName, final boolean ignoreOutputParse) {
+		this(sourceDirectory, specName, null);
+
+		if (!ignoreOutputParse) {
+			final File f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.OUTPUT_EXTENSION));
+			if (!f.exists()) {
+				throw new IllegalArgumentException("No readable output file could be found at " + f.getAbsolutePath());
+			}
+			
+			outputParser = new MCOutputParser(f);
+		}
+		
+		final File f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.TLA_EXTENSION));
+		if (!f.exists()) {
+			throw new IllegalArgumentException("No readable TLA file could be found at " + f.getAbsolutePath());
+		}
+	}
+	
+	private MCParser(final File sourceDirectory, final String specName, final Object signatureDifferentiator) {
+		resolver = new SimpleFilenameToStream(sourceDirectory.getAbsolutePath());
+		specBaseName = specName;
+		
+		final File f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.CONFIG_EXTENSION));
+		if (!f.exists()) {
+			throw new IllegalArgumentException("No readable config file could be found at " + f.getAbsolutePath());
+		}
+		configParser = new ModelConfig(f.getAbsolutePath(), resolver);
+		
+		tlaClass = new TLAClass("tlc2.module", resolver);
+		defns = new Defns();
+	}
+	
+	/**
+	 * @return will return null until {@link #parse()} has been run successfully
+	 */
+	public MCParserResults getParseResults() {
+		return parserResults;
+	}
+
+	/**
+	 * @return an instance of {@link MCParserResults} representing the results. If
+	 *         this instance was constructed via
+	 *         {@link #MCParser(File, String, boolean)} with the third argument
+	 *         {@code true} then the results returned will contain no output
+	 *         messages nor any potential {@code MCError}
+	 * @throws IllegalStateException if parse has already been called on this
+	 *                               instance
+	 */
+	public MCParserResults parse() {
+		if (parserResults != null) {
+			throw new IllegalStateException("Parse has already been called.");
+		}
+		
+		try {
+			final List<MCOutputMessage> encounteredMessages = (outputParser != null) ? outputParser.parse(true) : null;
+
+			configParser.parse();
+
+			// No reason to start-up SANY if we're not going to generate something because the output file is unusable
+			if ((encounteredMessages == null) || (encounteredMessages.size() > 0)) {
+				final SymbolNodeValueLookupProvider defaultLookup = new SymbolNodeValueLookupProvider() {};
+				final SpecProcessor specProcessor = new SpecProcessor(specBaseName, resolver, TOOL_ID, defns,
+																	  configParser, defaultLookup, null, tlaClass);
+				parserResults = generateResultsFromProcessorAndConfig(specProcessor, configParser);
+				
+				if (outputParser != null) {
+					parserResults.setError(outputParser.getError());
+					parserResults.setOutputMessages(encounteredMessages);
+				}
+			} else {
+				// we'll have a zero size if the output generated came from a TLC run that did not have the '-tool' flag
+				parserResults = new MCParserResults(null, ((outputParser != null) ? outputParser.getError() : null),
+													encounteredMessages, new ArrayList<>(), new HashSet<>(),
+													new HashSet<>(), new ArrayList<>(), true, null, configParser);
+			}
+			
+			return parserResults;
+		} catch (final IOException e) {
+			System.out.println("Caught exception while performing MC parsing: " + e.getMessage());
+			e.printStackTrace();
+		}
+
+		return null;
+	}
+
+	
+	public static void main(final String[] args) throws Exception {
+		final MCParser parser = new MCParser(new File(args[0]), TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, null);
+		final MCParserResults results = parser.parse();
+		
+		System.out.println("Parse encountered " + results.getOutputMessages().size() + " messages.");
+		final MCError error = results.getError();
+		if (error != null) {
+			System.out.println("Encountered error: ");
+			System.out.println(error.toSequenceOfRecords(true));
+		}
+		
+		final List<String> extendedModules = results.getOriginalExtendedModules();
+		System.out.println("Found " + extendedModules.size() + " module(s) being extended explicitly by the root spec:");
+		for (final String module : extendedModules) {
+			System.out.println("\t" + module);
+		}
+	}
+	
+	
+	private static class NodeNameMatcher implements SymbolMatcher {
+		private final String name;
+		
+		NodeNameMatcher(final String nameToMatch) {
+			name = nameToMatch;
+		}
+
+		@Override
+		public boolean matches(final SymbolNode aSymbol) {
+			if (aSymbol.getName() != null) {
+				return name.equals(aSymbol.getName().toString());
+			}
+			return false;
+		}
+	}
+	
+	
+	// orders from location first in document to that which is last in document
+	private static class LocationComparator implements Comparator<Location> {
+		@Override
+		public int compare(final Location l2, final Location l1) {
+			if (l1.beginLine() == l2.beginLine()) {
+				if (l1.beginColumn() != l2.beginColumn()) {
+					return l1.beginColumn() - l2.beginColumn();
+				}
+				
+				if (l1.endLine() != l2.endLine()) {
+					return l1.endLine() - l2.endLine();
+				}
+				
+				return l1.endColumn() - l2.endColumn();
+			}
+			
+			return l1.beginLine() - l2.beginLine();
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/input/MCParserResults.java b/tlatools/src/tlc2/input/MCParserResults.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3f57e89acfc96ddf63dbb780e7c88098551954c
--- /dev/null
+++ b/tlatools/src/tlc2/input/MCParserResults.java
@@ -0,0 +1,122 @@
+package tlc2.input;
+
+import java.util.List;
+import java.util.Set;
+
+import tla2sany.st.Location;
+import tlc2.model.MCError;
+import tlc2.tool.impl.ModelConfig;
+
+public class MCParserResults {
+	private final String moduleName;
+	
+	private MCError error;
+	private List<MCOutputMessage> outputMessages;
+
+	private final List<String> immediateExtendedModules;
+	private final Set<String> allExtendedModules;
+	private final Set<String> modulesInstantiated;
+
+	private final List<Location> initNextLocationsToDelete;
+
+	private final boolean behaviorIsInitNext;
+	
+	private final String originalNextOrSpecificationName;
+	
+	private final ModelConfig modelConfig;
+
+	MCParserResults(final String rootModuleName, final List<String> immediateExtendeds, final Set<String> allExtendeds,
+			 		final Set<String> allInstantiated, final List<Location> initNextLocations,
+			 		final boolean wasInitNext, final String nextOrSpecName, final ModelConfig config) {
+		moduleName = rootModuleName;
+		
+		immediateExtendedModules = immediateExtendeds;
+		allExtendedModules = allExtendeds;
+		
+		modulesInstantiated = allInstantiated;
+		
+		initNextLocationsToDelete = initNextLocations;
+		
+		behaviorIsInitNext = wasInitNext;
+		
+		originalNextOrSpecificationName = nextOrSpecName;
+		
+		modelConfig = config;
+	}
+	
+	MCParserResults(final String rootModuleName, final MCError mcError, final List<MCOutputMessage> messages,
+					final List<String> immediateExtendeds, final Set<String> allExtendeds,
+					final Set<String> allInstantiated, final List<Location> initNextLocations,
+					final boolean wasInitNext, final String nextOrSpecName, final ModelConfig config) {
+		this(rootModuleName, immediateExtendeds, allExtendeds, allInstantiated, initNextLocations, wasInitNext,
+			 nextOrSpecName, config);
+		
+		error = mcError;
+		outputMessages = messages;
+	}
+	
+	public ModelConfig getModelConfig() {
+		return modelConfig;
+	}
+	
+	public String getModuleName() {
+		return moduleName;
+	}
+	
+	public String getOriginalNextOrSpecificationName() {
+		return originalNextOrSpecificationName;
+	}
+
+	public MCError getError() {
+		return error;
+	}
+	
+	void setError(final MCError mcError) {
+		error = mcError;
+	}
+	
+	public List<MCOutputMessage> getOutputMessages() {
+		return outputMessages;
+	}
+	
+	void setOutputMessages(final List<MCOutputMessage> messages) {
+		outputMessages = messages;
+	}
+
+	/**
+	 * @return the {@link List} of all modules extended by the root spec explicitly - in other words, for example,
+	 * 				the X, Y, Z cited by a root spec's "EXTENDS X, Y, Z"
+	 */
+	public List<String> getOriginalExtendedModules() {
+		return immediateExtendedModules;
+	}
+	
+	/**
+	 * @return the {@link Set} of all modules extended - in other words, the modules extended by all modules extended
+	 * 				by all modules extended by ... the root spec.
+	 */
+	public Set<String> getAllExtendedModules() {
+		return allExtendedModules;
+	}
+	
+	/**
+	 * @return this returns a {@link Set} of all instantiated modules - in other words, all modules, X, found in any
+	 * 				module of the entire module tree that is instantiated in TLA+ code like {@code ... = INSTANCE X}
+	 * 				<b>Note:</b> the is the possibility of a non-null intersection between this set and the set of
+	 * 				extended modules.
+	 */
+	public Set<String> getAllInstantiatedModules() {
+		return modulesInstantiated;
+	}
+
+	/**
+	 * @return ordered from location first in document to that which is last in document
+	 */
+	public List<Location> getInitNextLocationsToDelete() {
+		return initNextLocationsToDelete;
+	}
+
+	public boolean isBehaviorInitNext() {
+		return behaviorIsInitNext;
+	}
+}
diff --git a/tlatools/src/tlc2/model/Assignment.java b/tlatools/src/tlc2/model/Assignment.java
new file mode 100644
index 0000000000000000000000000000000000000000..29e9e4a425810fa6164e44aa389732f0025d52d6
--- /dev/null
+++ b/tlatools/src/tlc2/model/Assignment.java
@@ -0,0 +1,345 @@
+package tlc2.model;
+
+/**
+ * An Assignment consists of a label, a list of parameters and the right side.
+ * e.G. <code>F(_, _, _,) <- foo</code>. <code>F</code> is the label, <code>foo</code> is the 
+ * right side, and there are three parameters. Because of the fact, that usually the user 
+ * wants to specify <code>foo</code> as a function of parameters, the parameters can be named. 
+ * So <code>F(a, b, c) <- foo(a, b) + c<code> the three parameters has names <code>a, b, c</code>.  
+ * <br><br>
+ * The label of an assignment is immutable. The parameters and the right side can be changed. 
+ * <br><br>
+ * The right side of assignments with no parameters has different meanings depending on the {@link Assignment#modelValue}.
+ * If the value of {@link Assignment#modelValue} is <code>false</code>, the right side is treated as an ordinary assignment.
+ * <br>
+ * If the value of {@link Assignment#modelValue} is <code>true</code>, the assignment is treated as a assignments of 
+ * model value(s) to a constant. If the right side is equals to the label of the assignment, it is considered, that the 
+ * assignment of type <code>F = F</code> is used. Otherwise, the right side is interpreted as a set of model values and the 
+ * assignment of type <code>F = {f_1, f_2, f_3}</code> with <code>f_1 = f_1, f_2 = f_2, f_3 = f_3</code>.
+ * <br> 
+ * Especially, the right side is parsed using {@link TypedSet#parseSet(String)}. The information whether the set of model values 
+ * is typed or not is not saved explicitly, but is based on the syntax of the set. The set <code>{p_1, p_2, p_3}</code> is typed 
+ * (type p), the set <code>{k_1, i_2, 3}</code> is untyped.
+ * <br>
+ * For assignments with at least one parameter, the value of the {@link Assignment#modelValue} is ignored.
+ * <br>
+ * <br>
+ * @author Simon Zambrovski
+ * @version $Id$
+ */
+public class Assignment extends Formula {
+    public static final String ASSIGNMENT_SIGN = " <- ";
+    public static final String IS_MV = " [ model value ] ";
+    public static final String SYMMETRICAL = " <symmetrical> ";
+
+    
+    private String label;
+    private String[] params = new String[0];
+    private boolean modelValue = false;
+    private boolean symmetry = false;
+    private TypedSet setOfModelValues = null;
+
+    /**
+     * Constructs the assignment
+     * if the right side is equals to the label, the assignment is treated as a model value assignment
+     */
+	public Assignment(String label, String[] params, String right) {
+        super(right);
+        this.label = label;
+        this.setParams(params);
+		if ((this.label != null) && (right != null) && this.label.equals(right)) {
+            // right side equals label => model value
+            setModelValue(true);
+        }
+    }
+
+    public String getFormula() {
+    	return getFormula("");
+    }
+
+    public String getFormula(String tab)
+    {
+        StringBuffer buffer = new StringBuffer(getLeft());
+        buffer.append(tab);
+        buffer.append(ASSIGNMENT_SIGN);
+        
+        if (this.modelValue)
+        {
+            buffer.append(IS_MV);
+            if (isSetOfModelValues()) 
+            {
+                if (this.isSymmetricalSet())
+                {
+                    buffer.append(SYMMETRICAL);
+                    buffer.append(getFormattedRight());
+                } else {
+                    // print out the set
+                    buffer.append(getFormattedRight());                    
+                }
+            } else 
+            {
+                // a single value, nothing to print
+            }
+        } else
+        {
+            buffer.append(getFormattedRight());
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Retrieves the left part (label with parameter list)
+     * @return
+     */
+    public String getLeft()
+    {
+        return getParametrizedLabel(this.label);
+    }
+
+    /**
+     * Appends parameters to the label
+     * @param id
+     * @return
+     */
+    public String getParametrizedLabel(String id)
+    {
+        return id + listParams();
+    }
+
+    /**
+     * @return a string representation of parameter list
+     */
+    private String listParams()
+    {
+        if (params.length == 0)
+        {
+            return "";
+        }
+        final StringBuilder buffer = new StringBuilder();
+        buffer.append('(');
+        for (int i = 0; i < params.length; i++)
+        {
+            buffer.append(params[i]);
+            buffer.append((i != params.length - 1) ? ", " : "");
+        }
+        buffer.append(')');
+        return buffer.toString();
+    }
+
+    public void setFormula(String formula)
+    {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * @return the label
+     */
+    public String getLabel()
+    {
+        return label;
+    }
+
+    /**
+     * @return "bar" for a assignment "frob!bar"
+     */
+    public String getLocalLabel() {
+		final int beginIndex = label.lastIndexOf("!") + 1;
+		return label.substring(beginIndex);
+    }
+    
+    /**
+     * Retrieve the right part
+     * @return the right side of the assignment, can be <code>null</code>
+     */
+    public String getRight()
+    {
+        return super.getFormula();
+    }
+
+    /**
+     * Retrieves a formatted version of the right side 
+     * @return right side ending with ... iff \n is contained in the right side
+     */
+    public String getFormattedRight()
+    {
+        String tempRight = getRight();
+        if (tempRight == null) 
+        {
+            return null;
+        }
+        int i = -1;
+        if ( (i = tempRight.indexOf("\n")) != -1) 
+        {
+            tempRight = tempRight.substring(0, i + 1) + " ..."; 
+        }
+        return tempRight;
+    }
+
+
+    public String[] getParams()
+    {
+        return params;
+    }
+
+    public String toString()
+    {
+        return getFormula();
+    }
+
+    /**
+     * Sets the right part
+     * @param right
+     */
+	public synchronized void setRight(String right) {
+		super.setFormula(right);
+		setOfModelValues = null;
+	}
+
+    /**
+     * Set parameters, the left part depends on
+     * @param params
+     * 
+     * Modified by LL on 11 Nov 2009 to trim spaces off the ends of 
+     * the parameters.
+     */
+    public void setParams(String[] params)
+    {
+        if (params != null)
+        {
+            this.params = params;
+            for (int i = 0; i < this.params.length; i++) {
+                this.params[i] = this.params[i].trim();
+            }
+        } else
+        {
+            this.params = new String[0];
+        }
+    }
+
+    /**
+     * @return if this assignment is to be set to the model value
+     */
+    public boolean isModelValue()
+    {
+        return modelValue;
+    }
+    
+    /**
+     * @return true iff model value but not a set of values
+     */
+    public boolean isSimpleModelValue() {
+    	return isModelValue() && !isSetOfModelValues();
+    }
+    
+    /**
+     * @return true, iff the assignment is a set of model values
+     */
+	public boolean isSetOfModelValues() {
+        return modelValue && !getLabel().equals(getRight());
+    }
+	
+	/**
+	 * @return the TypedSet for model values if {@link #isSetOfModelValues()} returns true, otherwise null
+	 */
+	public synchronized TypedSet getSetOfModelValues() {
+		if (isSetOfModelValues()) {
+			if (setOfModelValues == null) {
+				setOfModelValues = TypedSet.parseSet(getRight());
+			}
+			
+			return setOfModelValues;
+		}
+		
+		return null;
+	}
+
+    /**
+     * @return true, if the set of model values is symmetrical 
+     */
+	public boolean isSymmetricalSet() {
+		return symmetry;
+    }
+
+    /**
+     * Sets the symmetry property for a set of model values 
+     * @param isSymmetric
+     */
+    public void setSymmetric(boolean isSymmetric)
+    {
+        if (isSymmetric && !modelValue)
+        {
+            throw new IllegalArgumentException("Current assignment is not a set of model values");
+        }
+        this.symmetry = isSymmetric;
+    }
+
+    /**
+     * Set the constant assignment to be a model value
+     * @param modelValue
+     */
+    public void setModelValue(boolean modelValue)
+    {
+        if (modelValue && this.params.length != 0)
+        {
+            throw new IllegalArgumentException("Operators can not be instantiated with model values");
+        }
+        this.modelValue = modelValue;
+    }
+
+    public static String[] getArrayOfEmptyStrings(int number)
+    {
+        String[] array = new String[number];
+        String EMPTY = new String("");
+        for (int i = 0; i < number; i++)
+        {
+            array[i] = EMPTY;
+        }
+        return array;
+    }
+
+
+    /** 
+     * compares if the signature (label and the number of parameters matches)
+     */
+    public boolean equalSignature(Assignment obj)
+    {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (label == null)
+        {
+            if (obj.label != null)
+                return false;
+        } else if (!label.equals(obj.label))
+            return false;
+
+        return (params.length == obj.params.length);
+    }
+    
+    public String prettyPrint() {
+    	return prettyPrint("");
+    }
+    
+    public String prettyPrint(final String delim) {
+    	final StringBuffer buf = new StringBuffer();
+    	if (!isModelValue()) {
+    		return getFormula(delim);
+    	} else if (isSetOfModelValues()) {
+    		buf.append(getLeft());
+    		buf.append(delim);
+    		buf.append(ASSIGNMENT_SIGN);
+   			if (isSymmetricalSet()) {
+   				buf.append("s");
+   			}
+   			buf.append(getFormattedRight());
+  		} else {
+			// Ordinary model value, just skip the value (no point showing "X <-
+			// X").
+   			buf.append(getLeft());
+    	}
+    	return buf.toString();
+    }
+    
+}
diff --git a/tlatools/src/tlc2/model/Formula.java b/tlatools/src/tlc2/model/Formula.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa429c18a6269444679fa42764df77b12239b62e
--- /dev/null
+++ b/tlatools/src/tlc2/model/Formula.java
@@ -0,0 +1,86 @@
+package tlc2.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Representation of a formula.
+ * @author Simon Zambrovski
+ */
+public class Formula {
+    /**
+     * De-serialize formula list, to a list of formulas, that are selected (have a leading "1")
+     * 
+     * The first character of the formula is used to determine if the formula is enabled in the model 
+     * editor or not. This allows the user to persist formulas, which are not used in the current model 
+     */
+	public static List<Formula> deserializeFormulaList(final List<String> serializedList) {
+		final ArrayList<Formula> result = new ArrayList<>(serializedList.size());
+		for (final String entry : serializedList) {
+			if ("1".equals(entry.substring(0, 1))) {
+				result.add(new Formula(entry.substring(1)));
+			}
+		}
+		return result;
+	}
+	
+    // DOTALL to match beyond line endings.
+    private static final Pattern PATTERN = Pattern.compile("^\\s*(\\w+)\\s*==(.*)$", Pattern.DOTALL);
+    
+	
+    private String formula;
+
+    /**
+     * Constructs a formula representation
+     * @param formula
+     */
+	public Formula(String formulaString) {
+		formula = formulaString;
+	}
+
+	/**
+	 * Retrives formula
+	 * 
+	 * @return
+	 */
+	public String getFormula() {
+		return formula;
+	}
+
+	@Override
+	public String toString() {
+		return formula;
+	}
+
+	/**
+	 * @param formulaString
+	 */
+	public void setFormula(String formulaString) {
+		formula = formulaString;
+	}
+
+	// MAK 03/2019: This methods below appear redundant to the Assignment subclass
+	// but I don't have time to look into it.
+    
+    public boolean isNamed()  {
+    	return !getLeftHandSide().equals(getFormula());
+    }
+    
+    public String getLeftHandSide() {
+		final Matcher matcher = PATTERN.matcher(this.formula);
+		if (matcher.find()) {
+			return matcher.group(1).trim();
+		}
+		return getFormula();
+    }
+    
+    public String getRightHandSide() {
+		final Matcher matcher = PATTERN.matcher(this.formula);
+		if (matcher.find()) {
+			return matcher.group(2).trim();
+		}
+		return getFormula();
+    }
+}
diff --git a/tlatools/src/tlc2/model/MCError.java b/tlatools/src/tlc2/model/MCError.java
new file mode 100644
index 0000000000000000000000000000000000000000..69b60a9e894d84394628760bc6c5bc633892bdf1
--- /dev/null
+++ b/tlatools/src/tlc2/model/MCError.java
@@ -0,0 +1,79 @@
+package tlc2.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import util.TLAConstants;
+
+public class MCError {
+	private final String message;
+	private final MCError cause;
+	private final ArrayList<MCState> states;
+	
+	public MCError(final MCError errorCause, final String errorMessage) {
+		cause = errorCause;
+		message = errorMessage;
+		states = new ArrayList<>();
+	}
+	
+	public void addState(final MCState state) {
+		states.add(state);
+	}
+	
+	public List<MCState> getStates() {
+		return states;
+	}
+	
+	public String getMessage() {
+		return message;
+	}
+	
+	public MCError getCause() {
+		return cause;
+	}
+	
+	public void updateStatesForTraceExpression(final HashMap<String, String> variableExpressionMap) {
+		for (final MCState state : states) {
+			for (final MCVariable variable : state.getVariables()) {
+				final String expression = variableExpressionMap.get(variable.getName());
+
+				if (expression != null) {
+					variable.setTraceExpression(expression);
+				}
+			}
+		}
+	}
+
+	public String toSequenceOfRecords(final boolean includeHeaders) {
+		final StringBuilder buf = new StringBuilder();
+		buf.append(TLAConstants.BEGIN_TUPLE);
+		buf.append(TLAConstants.CR);
+		
+		for (int i = 0; i < states.size(); i++) {
+			final MCState tlcState = states.get(i);
+			
+			if (tlcState.isBackToState() || tlcState.isStuttering()) {
+				//TODO How to represent these two types of states?
+				continue;
+			}
+			
+			if ((tlcState.getVariables().length == 0) && !includeHeaders) {
+				// When an user has used filtering to hide all variables, the error trace here
+				// has no variables. In this case just return empty sequence <<>> by breaking
+				// from the loop.
+				break;
+			}
+			
+			if (i > 0) {
+				// Append a comma if a record is going to be added below.
+				buf.append(TLAConstants.COMMA).append(TLAConstants.CR);
+			}
+			buf.append(tlcState.asRecord(includeHeaders));
+		}
+			
+		buf.append(TLAConstants.CR);
+		buf.append(TLAConstants.END_TUPLE);
+		return buf.toString();
+	}
+}
diff --git a/tlatools/src/tlc2/model/MCState.java b/tlatools/src/tlc2/model/MCState.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa1af885eab94d9126af0437d2c2fc68812ef216
--- /dev/null
+++ b/tlatools/src/tlc2/model/MCState.java
@@ -0,0 +1,303 @@
+package tlc2.model;
+
+import java.util.ArrayList;
+
+import tla2sany.st.Location;
+import util.TLAConstants;
+
+public class MCState {
+	private static final String BACK_TO_STATE = " " + TLAConstants.BACK_TO_STATE;
+
+	public static MCState parseState(final String stateInputString) {
+		// state number
+		final int index = stateInputString.indexOf(TLAConstants.COLON);
+		// multi line
+		int index2 = stateInputString.indexOf(TLAConstants.CR, index);
+		if (index2 == -1) {
+			index2 = stateInputString.length();
+		}
+
+		final int stateNumber = Integer.parseInt(stateInputString.substring(0, index).trim());
+		final String label = stateInputString.substring((index + 1), index2);
+		final boolean isStuttering = (label.indexOf(TLAConstants.STUTTERING) == 0);
+		final boolean isBackToState = (label.indexOf(BACK_TO_STATE) == 0);
+
+		MCVariable[] vars = null;
+		final String name;
+		final Location location;
+		if (!isBackToState && !isStuttering) {
+			// string from which the variables can be parsed
+			final String variableInputString = stateInputString.substring(index2 + 1);
+			vars = parseVariables(variableInputString);
+			
+			final String sublabel = label.substring(2, (label.length() - 1));
+			final int lineIndex = sublabel.indexOf(TLAConstants.LINE);
+			if (lineIndex != -1) {
+				name = sublabel.substring(0, (lineIndex - 1));
+				location = Location.parseLocation(sublabel.substring(lineIndex));
+			} else {
+				name = sublabel;
+				location = null;
+			}
+		} else {
+			name = null;
+			location = null;
+		}
+
+		return new MCState(vars, name, label, location, isStuttering, isBackToState, stateNumber);
+	}
+
+	
+	private final MCVariable[] variables;
+	private final String name;
+	private final String label;
+	private final Location location;
+	private final boolean isStuttering;
+	private final boolean isBackToState;
+	private final int stateNumber;
+
+	/**
+	 * @param vars            variables in this state.
+	 * @param stateName       the name for this state
+	 * @param stateLabel      the display label, usually including line location and module
+	 * @param moduleLocation  the name of this module whose checking this state is from
+	 * @param stuttering      whether this is a stuttering state or not
+	 * @param backToState     whether this is a back to state or not
+	 * @param ordinal         number of the state in the trace
+	 */
+	public MCState(final MCVariable[] vars, final String stateName, final String stateLabel,
+			final Location moduleLocation, final boolean stuttering, final boolean backToState, final int ordinal) {
+		variables = vars;
+		name = stateName;
+		label = stateLabel;
+		location = moduleLocation;
+		isStuttering = stuttering;
+		isBackToState = backToState;
+		stateNumber = ordinal;
+	}
+
+	public MCVariable[] getVariables() {
+		return variables;
+	}
+	
+	public String getLabel() {
+		 return label;
+	}
+
+	public boolean isStuttering() {
+		return isStuttering;
+	}
+
+	public boolean isBackToState() {
+		return isBackToState;
+	}
+
+	public int getStateNumber() {
+		return stateNumber;
+	}
+	
+	public String asRecord(final boolean includeHeader) {
+		final StringBuilder result = new StringBuilder();
+		result.append(TLAConstants.L_SQUARE_BRACKET);
+		result.append(TLAConstants.CR);
+		
+		if (includeHeader) {
+			result.append(TLAConstants.SPACE);
+			result.append(TLAConstants.TraceExplore.ACTION);
+			result.append(TLAConstants.RECORD_ARROW);
+			
+			result.append(TLAConstants.L_SQUARE_BRACKET);
+			result.append(TLAConstants.CR);
+			result.append(TLAConstants.SPACE).append(TLAConstants.SPACE).append(TLAConstants.SPACE);
+				result.append("position");
+				result.append(TLAConstants.RECORD_ARROW);
+				result.append(getStateNumber());
+				result.append(TLAConstants.COMMA).append(TLAConstants.CR);
+			
+				result.append(TLAConstants.SPACE).append(TLAConstants.SPACE).append(TLAConstants.SPACE);
+				result.append("name");
+				result.append(TLAConstants.RECORD_ARROW);
+				result.append(TLAConstants.QUOTE);
+				result.append(name);
+				result.append(TLAConstants.QUOTE);
+				result.append(TLAConstants.COMMA).append(TLAConstants.CR);
+				
+				result.append(TLAConstants.SPACE).append(TLAConstants.SPACE).append(TLAConstants.SPACE);
+				result.append("location");
+				result.append(TLAConstants.RECORD_ARROW);
+				result.append(TLAConstants.QUOTE);
+				result.append(location);
+				result.append(TLAConstants.QUOTE);
+				
+			result.append(TLAConstants.CR);
+			result.append(TLAConstants.SPACE).append(TLAConstants.R_SQUARE_BRACKET);
+			if (variables.length != 0) {
+				// only append comma for additional records iff there are any variables to
+				// append.
+				result.append(TLAConstants.COMMA).append(TLAConstants.CR);
+			}
+		}
+		
+		for (int i = 0; i < variables.length; i++) {
+			final MCVariable variable = variables[i];
+			if (variable.isTraceExplorerExpression()) {
+				result.append(variable.getSingleLineDisplayName());
+			} else {
+				result.append(variable.getName());
+			}
+
+			result.append(TLAConstants.RECORD_ARROW);
+
+			result.append(variable.getValueAsString());
+			
+			if (i < (variables.length - 1)) {
+				result.append(TLAConstants.COMMA).append(TLAConstants.CR);
+			}
+		}
+		
+		result.append(TLAConstants.CR).append(TLAConstants.R_SQUARE_BRACKET);
+		return result.toString();
+	}
+
+	public String asSimpleRecord() {
+		final StringBuilder buf = new StringBuilder();
+		buf.append(TLAConstants.L_SQUARE_BRACKET);
+		for (int i = 0; i < variables.length; i++) {
+			final MCVariable var = variables[i];
+
+			buf.append(var.getName());
+			buf.append(TLAConstants.RECORD_ARROW);
+			buf.append(var.getValueAsString());
+
+			if (i < variables.length - 1) {
+				buf.append(TLAConstants.COMMA);
+			}
+		}
+		buf.append(TLAConstants.R_SQUARE_BRACKET);
+		return buf.toString();
+	}
+
+    /**
+     * The returns a conjunction list of variables.
+     * 
+     * For variables representing trace explorer expressions, if {@code includeTraceExpressions} is true,
+     * the returned string has:
+     * 
+     * /\ expr = value
+     * 
+     * where expr is the single line form of the trace explorer expression as shown in the Name column of
+     * the trace viewer.
+     *  
+     * For all other variables, this method attempts to display them as TLC does.
+     * 
+     * @param includeTraceExpressions whether trace expressions should be included.
+     * @param indent if non-null, this will be prepended to each line
+     * @return
+     */
+    public String getConjunctiveDescription(final boolean includeTraceExpressions, final String indent) {
+        return getConjunctiveDescription(includeTraceExpressions, indent, false);
+    }
+
+    /**
+     * The returns a conjunction list of variables.
+     * 
+     * For variables representing trace explorer expressions, if {@code includeTraceExpressions} is true,
+     * the returned string has:
+     * 
+     * /\ expr = value
+     * 
+     * where expr is the single line form of the trace explorer expression as shown in the Name column of
+     * the trace viewer.
+     *  
+     * For all other variables, this method attempts to display them as TLC does.
+     * 
+     * @param includeTraceExpressions whether trace expressions should be included.
+     * @param indent if non-null, this will be prepended to each line
+     * @param ansiMarkup if true, the String will include ANSI markup for trace expressions; this is currently ignored
+     * 							if includeTraceExpressions is false
+     * @return
+     */
+    public String getConjunctiveDescription(final boolean includeTraceExpressions, final String indent,
+    										final boolean ansiMarkup) {
+        final StringBuilder result = new StringBuilder();
+        
+		for (int i = 0; i < variables.length; i++) {
+			final MCVariable var = variables[i];
+			
+			if (var.isTraceExplorerExpression() && !includeTraceExpressions) {
+				continue;
+			}
+			
+			if (indent != null) {
+				result.append(indent);
+			}
+			
+            result.append("/\\ ");
+			if (var.isTraceExplorerExpression()) {
+				if (ansiMarkup) {
+					result.append(TLAConstants.ANSI.BOLD_CODE);
+				}
+				
+				result.append(var.getSingleLineDisplayName());
+			} else {
+				result.append(var.getName());
+			}
+
+            result.append(" = ").append(var.getValueAsString());
+
+			if (var.isTraceExplorerExpression() && ansiMarkup) {
+				result.append(TLAConstants.ANSI.RESET_CODE);
+			}
+			
+            result.append('\n');
+        }
+		
+        return result.toString();
+    }
+
+	
+	private static MCVariable[] parseVariables(final String variableInputString) {
+		String[] lines = variableInputString.split(TLAConstants.CR);
+		ArrayList<MCVariable> vars = new ArrayList<>();
+		int index;
+
+		// buffer for accumulating the state variable
+		String[] stateVarString = null;
+
+		// iterate line-wise
+		for (int j = 0; j < lines.length; j++) {
+			// find the index of the first /\ in the line
+			index = lines[j].indexOf(TLAConstants.TLA_AND);
+			// adding the current line to the previous lines
+			if (index != -1) {
+				// there was something in the buffer for the state variable
+				// found an empty line, which means that this is the end of the current state
+				if (stateVarString != null) {
+					final MCVariable var = new MCVariable(stateVarString[0], stateVarString[1]);
+					vars.add(var);
+				}
+
+				stateVarString = lines[j].substring(index + TLAConstants.TLA_AND.length() + 1).split(TLAConstants.EQ);
+			} else {
+				// no index
+
+				if (stateVarString != null) {
+					// either an empty line
+					stateVarString[1] += TLAConstants.CR;
+					stateVarString[1] += lines[j];
+				} else {
+					// the state has one variable only
+					stateVarString = lines[j].split(TLAConstants.EQ);
+				}
+			}
+		}
+
+		// write the last one
+		if (stateVarString != null) {
+			final MCVariable var = new MCVariable(stateVarString[0], stateVarString[1]);
+			vars.add(var);
+		}
+
+		return (MCVariable[]) vars.toArray(new MCVariable[vars.size()]);
+	}
+}
diff --git a/tlatools/src/tlc2/model/MCVariable.java b/tlatools/src/tlc2/model/MCVariable.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff057303a0cbd1cd270cf2935cea9d07e652382f
--- /dev/null
+++ b/tlatools/src/tlc2/model/MCVariable.java
@@ -0,0 +1,62 @@
+package tlc2.model;
+
+public class MCVariable {
+    private final String name;
+    private final String valueAsString;
+    private String traceExpression;
+
+	/**
+	 * @param varName   name of the variable
+	 * @param value     TLC string representation of the variable value
+	 */
+	public MCVariable(final String varName, final String value) {
+		name = varName;
+		valueAsString = value;
+		traceExpression = null;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+    /**
+	 * @return the name, or the trace expression if it is defined, for this variable
+	 *         in a single line String; the name could be multiple lines if this
+	 *         represents a trace explorer expression.
+	 */
+	public String getSingleLineDisplayName() {
+		final String s = isTraceExplorerExpression() ? traceExpression : name;
+		
+		return s.replaceAll("\\n", "").replaceAll("\\r", "");
+	}
+
+	public String getValueAsString() {
+		return valueAsString;
+	}
+	
+	public String getValueAsStringReIndentedAs(final String indent) {
+		final String[] split = valueAsString.split("(\\r\\n|\\r|\\n)");
+		final StringBuilder sb = new StringBuilder();
+
+		for (int i = 0; i < split.length; i++) {
+			sb.append(indent).append(split[i]);
+			if (i < (split.length - 1)) {
+				sb.append("\n");
+			}
+		}
+		
+		return sb.toString();
+	}
+	
+	public boolean isTraceExplorerExpression() {
+		return (traceExpression != null);
+	}
+	
+	public void setTraceExpression(final String expression) {
+		traceExpression = expression;
+	}
+	
+	public String getTraceExpression() {
+		return traceExpression;
+	}
+}
diff --git a/tlatools/src/tlc2/model/TraceExpressionInformationHolder.java b/tlatools/src/tlc2/model/TraceExpressionInformationHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..a771e76a13d090112b7002deaa062f914b1e42cc
--- /dev/null
+++ b/tlatools/src/tlc2/model/TraceExpressionInformationHolder.java
@@ -0,0 +1,115 @@
+package tlc2.model;
+
+import java.util.List;
+
+import tlc2.output.SpecWriterUtilities;
+import util.TLAConstants;
+
+/**
+ * A container class for the relevant information about a
+ * trace explorer expression. This class contains the expression,
+ * the identifier which is defined to be the expression, the variable
+ * that is declared for this expression, and the level of the expression.
+ * 
+ * @author Daniel Ricketts
+ *
+ */
+public class TraceExpressionInformationHolder {
+	/**
+	 * @param expressions
+	 * @param attributeName
+	 * @return expressions.size() instances of {@code TraceExpressionInformationHolder}
+	 */
+	public static TraceExpressionInformationHolder[] createHolders(final List<Formula> expressions, final String attributeName) {
+		final TraceExpressionInformationHolder[] holders = new TraceExpressionInformationHolder[expressions.size()];
+	    int position = 0;
+	    for (final Formula formula : expressions) {
+			final String expression = formula.getFormula();
+	
+			if ((expression != null) && (expression.length() > 0)) {
+	        	final String identifier
+	        			= SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.TRACE_EXPR_DEF_SCHEME);
+	        	if (formula.isNamed()) {
+	        		final String varname = formula.getLeftHandSide();
+	        		final String rightHandSide = formula.getRightHandSide();
+					holders[position] = new TraceExpressionInformationHolder(rightHandSide, identifier, varname);
+	        	} else  {
+	        		final String varname
+	        				= SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.TRACE_EXPR_VAR_SCHEME);
+	        		holders[position] = new TraceExpressionInformationHolder(expression, identifier, varname);
+	        	}
+	        }
+	
+	        position++;
+	    }
+	    
+	    return holders;
+	}
+
+	
+    /*
+     * The expression that the user wants to be evaluated at every
+     * state of the trace. 
+     */
+    private String expression;
+    /*
+     * The identifier that is defined to be the expression.
+     */
+    private String identifier;
+    /*
+     * The variable name that is declared for this expression.
+     */
+    private String variableName;
+    /*
+     * The level of the trace expression
+     */
+    private int level;
+
+    public void setExpression(String expression)
+    {
+        this.expression = expression;
+    }
+
+    public void setIdentifier(String identifier)
+    {
+        this.identifier = identifier;
+    }
+
+    public void setVariableName(String variableName)
+    {
+        this.variableName = variableName;
+    }
+
+    public void setLevel(int level)
+    {
+        this.level = level;
+    }
+
+    public TraceExpressionInformationHolder(String expression, String identifier, String variableName)
+    {
+        this.expression = expression;
+        this.identifier = identifier;
+        this.variableName = variableName;
+    }
+
+    public String getExpression()
+    {
+        return expression;
+    }
+
+    public String getIdentifier()
+    {
+        return identifier;
+    }
+
+    public String getVariableName()
+    {
+        return variableName;
+    }
+
+    public int getLevel()
+    {
+        return level;
+    }
+
+}
diff --git a/tlatools/src/tlc2/model/TypedSet.java b/tlatools/src/tlc2/model/TypedSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..e890e7e486bc9c699d998453bbd36bef4fc8edf6
--- /dev/null
+++ b/tlatools/src/tlc2/model/TypedSet.java
@@ -0,0 +1,374 @@
+package tlc2.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Representation of a typed set.
+ * <br>
+ * This class is used for typed model values. To create an instance of this class
+ * you probably want to parse it out of a string, using {@link TypedSet#parseSet(String)} method.
+ * 
+ * A TypedSet object appears to be used to represent a TLA+ set of the form
+ * 
+ *   { a1 , a2, ... , aN }
+ *   
+ * for N >= 0, where each ai is a TLA+ identifier.  If each of the ai begins with "C_"
+ * for the same character C, then it is represented by an object having type = "C_" and
+ * values[i] equal to ai with the prefix "C_" removed, for each i.  Otherwise, it is represented
+ * by an object having type = null and values[i] = ai, for each i.
+ * 
+ * @author Simon Zambrovski
+ * @version $Id$
+ */
+public class TypedSet {
+	public static final TypedSet EMPTY_SET = new TypedSet();
+	
+    private static final String SEPARATOR = "_";
+    private static final String PATTERN = "[\\s]*,[\\s]*";
+
+    
+    private String[] values = new String[0];
+    private String type = null;
+
+    /**
+     * Factory method
+     * @param set String representation of the set
+     * @return a typed set
+     */
+    public static TypedSet parseSet(String set)
+    {
+        // This code did not work properly because it could leave spaces at the
+        // beginning of values[0] or end of values[value.length-1] if there
+        // were spaces at the beginning or end of set or the curly braces were
+        // preceded or followed by spaces. This was fixed by adding the
+        // two set = set.trim(); statements and rewriting the
+        // tests for an empty or null set. Changes made by LL on 11 Nov 2009.
+        TypedSet result = new TypedSet();
+        if (set == null)
+        {
+            return result;
+        }
+        set = set.trim();
+        // if the curly braces are provided, cut them
+        if (set.length() > 0 && set.charAt(0) == '{' && set.charAt(set.length() - 1) == '}')
+        {
+            set = set.substring(1, set.length() - 1);
+            set = set.trim();
+        }
+
+        if ("".equals(set))
+        {
+            return result;
+        }
+ 
+        String[] parsedSet = null;
+
+        // split by comma surrounded by any kind of spaces/tabs/new lines
+        parsedSet = set.split(PATTERN);
+
+        if (parsedSet.length > 0)
+        {
+            // pre-fetch first element (if no type it will store -1 there)
+            int typeSeparatorPosition = parsedSet[0].indexOf("_");
+            if (typeSeparatorPosition == -1 || typeSeparatorPosition == 0)
+            {
+                // no type, provided
+                result.setValues(parsedSet);
+                return result;
+            } else
+            {
+                // type is provided
+                result.setType(parsedSet[0].substring(0, typeSeparatorPosition));
+                parsedSet[0] = parsedSet[0].substring(typeSeparatorPosition + 1);
+
+                // assume that all strings have same structure
+                // and set type violated to false
+                // this also checks that strings like e.G. "P_", "x_" are not valid
+                // because they miss the untyped part
+                boolean typePatternViolated = parsedSet[0].length() == 0;
+
+                // run through the strings and compare the position of the first "_"
+                // if it changes the type patter is violated: e.G. "p_1" "pi_2".
+                // also compare for the type: e.G. "p_1" "i_2"
+                for (int i = 1; i < parsedSet.length; i++)
+                {
+                    if (parsedSet[i].startsWith(result.getType() + "_"))
+                    {
+                        parsedSet[i] = parsedSet[i].substring(typeSeparatorPosition + 1);
+                        if (parsedSet[i].length() == 0)
+                        {
+                            typePatternViolated = true;
+                        }
+                    } else
+                    {
+                        typePatternViolated = true;
+                    }
+                    // exit if type pattern is violated
+                    if (typePatternViolated)
+                    {
+                        break;
+                    }
+                }
+
+                if (typePatternViolated)
+                {
+                    // violated type, restore the split
+                    result.setValues(set.split(PATTERN));
+                    result.setType(null);
+                } else
+                {
+                    // type recognized
+                    result.setValues(parsedSet);
+                }
+            }
+
+        } else
+        {
+            // no values in the set...
+        }
+
+        return result;
+    }
+
+    public boolean hasType()
+    {
+        return (type != null);
+    }
+
+    public String getType()
+    {
+        return type;
+    }
+
+    // If id is a "typed" identifier, then it returns
+    // the (one-character) type as a string. Else, it
+    // returns null. Note that getTypeOfId("1_xyz") = "1",
+    // and getTypteOfId("__z") = "z".
+    public static String getTypeOfId(String id)
+    {
+        if (id == null || id.length() < 2 || !id.substring(1, 2).equals("_"))
+        {
+            return null;
+        }
+        return id.substring(0, 1);
+    }
+
+    public void setType(String type)
+    {
+        this.type = type;
+    }
+    
+    public void unsetType() {
+    	setType(null);
+    }
+    
+    /**
+     * Not remotely efficient.
+     * 
+     * @param value
+     * @return true if the parameter value is one of this set's values.
+     */
+    public boolean contains(final String value) {
+    	if (value != null) {
+    		for (final String aValue : values) {
+    			if (value.equals(aValue)) {
+    				return true;
+    			}
+    		}
+    	}
+    	
+    	return false;
+    }
+
+    public String[] getValues()
+    {
+        return values;
+    }
+
+    /**
+     * Convenience interface for iteration over the values
+     * This method disconnects the actual typed set from the collection of values
+     * @return a list containing the values
+     */
+    public List<String> getValuesAsList()
+    {
+        if (!hasType())
+        {
+            return Arrays.asList(values);
+        } else
+        {
+            List<String> typedList = new ArrayList<String>(values.length);
+            // add type to the list
+            for (int i = 0; i < values.length; i++)
+            {
+                String value = type + SEPARATOR + values[i];
+                typedList.add(value);
+            }
+            return typedList;
+        }
+    }
+
+    /**
+     * retrieves the number of values in the set
+     * @return number of values in the set, or null if none
+     */
+    public int getValueCount()
+    {
+        if (values == null)
+        {
+            return 0;
+        } else
+        {
+            return values.length;
+        }
+    }
+
+    /**
+     * Retrieves a value by index
+     * @param index, index of the value, should be smaller then the value of {@link TypedSet#getValueCount()}
+     * @return value (with type, if any) or null if index out of range 
+     */
+    public String getValue(int index)
+    {
+        if (index >= getValueCount())
+        {
+            return null;
+        } else
+        {
+            return (hasType() ? getType() + SEPARATOR : "") + values[index];
+        }
+    }
+
+    public void setValues(String[] values)
+    {
+        if (values == null)
+        {
+            this.values = new String[0];
+        } else
+        {
+            this.values = values;
+        }
+    }
+
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        result = prime * result + TypedSet.hashCode(values);
+        return result;
+    }
+
+    private static int hashCode(Object[] array)
+    {
+        int prime = 31;
+        if (array == null)
+            return 0;
+        int result = 1;
+        for (int index = 0; index < array.length; index++)
+        {
+            result = prime * result + (array[index] == null ? 0 : array[index].hashCode());
+        }
+        return result;
+    }
+
+    /**
+     * Two typed sets are equals if they have
+     * the same type and the elements of the set are the same
+     * (and appear in the same order)
+     */
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TypedSet other = (TypedSet) obj;
+        if (type == null)
+        {
+            if (other.type != null)
+                return false;
+        } else if (!type.equals(other.type))
+            return false;
+        if (!Arrays.equals(values, other.values))
+            return false;
+        return true;
+    }
+
+    /**
+     * The string implementation of the typed set
+     * Is used to be set in the right side of assignment ({@link Assignment#setRight(String)})
+     * <br><b>Note:</b> {@link TypedSet#toString()} should not be used for comparison
+     * @see TypedSet#equals(Object)
+     */
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer("{");
+        for (int i = 0; i < this.values.length; i++)
+        {
+            if (this.type != null)
+            {
+                buffer.append(this.type).append(SEPARATOR);
+            }
+            buffer.append(this.values[i]);
+            if (i != this.values.length - 1)
+            {
+                buffer.append(", ");
+            }
+        }
+        buffer.append("}");
+        return buffer.toString();
+    }
+
+    /**
+     * Same as toString, but without curly braces
+     * @return
+     */
+    public String toStringWithoutBraces()
+    {
+        String set = toString();
+        return set.substring(1, set.length() - 1);
+    }
+
+    /**
+     * This test functions checks whether the type has at least one value
+     * that contain only of digits
+     * @return true if on of the values (taking the type into account) is a digit
+     * @deprecated
+     */
+    public boolean hasANumberOnlyValue()
+    {
+        if (hasType())
+        {
+            return !hasValidType();
+        } else
+        {
+            for (int i = 0; i < values.length; i++)
+            {
+                if (values[i].matches("[0-9]*"))
+                {
+                    // a digit sequence found
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public boolean hasValidType()
+    {
+        if (type != null)
+        {
+            if (!type.matches("[A-Za-z]{1}[A-Za-z0-9]*"))
+            {
+                // the type must be a valid identifier
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/tlatools/src/tlc2/module/AnySet.java b/tlatools/src/tlc2/module/AnySet.java
index a9d789f483058861a06b8c07cab01cd43cf5ddb2..90785d9d1363bf79079d950fea09ff4f307c23d4 100644
--- a/tlatools/src/tlc2/module/AnySet.java
+++ b/tlatools/src/tlc2/module/AnySet.java
@@ -7,36 +7,42 @@ package tlc2.module;
 
 import tlc2.output.EC;
 import tlc2.tool.EvalException;
-import tlc2.value.UserObj;
-import tlc2.value.UserValue;
-import tlc2.value.Value;
+import tlc2.value.Values;
+import tlc2.value.impl.UserObj;
+import tlc2.value.impl.UserValue;
+import tlc2.value.impl.Value;
 
 public class AnySet extends UserObj
 {
+	public static final long serialVersionUID = 20160822L;
 
-    private static Value AnySet = new UserValue(new AnySet());
+    private final static Value AnySet = new UserValue(new AnySet());
 
     public static Value ANY()
     {
         return AnySet;
     }
 
+    @Override
     public final int compareTo(Value val)
     {
-        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "ANY", Value.ppr(val.toString()) });
+        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "ANY", Values.ppr(val.toString()) });
     }
 
+    @Override
     public final boolean member(Value val)
     {
         return true;
     }
 
+    @Override
     public final boolean isFinite()
     {
         return false;
     }
 
-    public final StringBuffer toString(StringBuffer sb, int offset)
+    @Override
+    public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow)
     {
         return sb.append("ANY");
     }
diff --git a/tlatools/src/tlc2/module/Bags.java b/tlatools/src/tlc2/module/Bags.java
index 73973d05082404ea78e151b1f8386664827d4a71..d6651aade02556e11c05bb1058063f0c8fd2cfed 100644
--- a/tlatools/src/tlc2/module/Bags.java
+++ b/tlatools/src/tlc2/module/Bags.java
@@ -8,58 +8,68 @@ package tlc2.module;
 import tlc2.output.EC;
 import tlc2.tool.EvalControl;
 import tlc2.tool.EvalException;
-import tlc2.tool.TLARegistry;
+import tlc2.tool.impl.TLARegistry;
 import tlc2.util.Vect;
-import tlc2.value.Applicable;
-import tlc2.value.BoolValue;
-import tlc2.value.FcnRcdValue;
-import tlc2.value.IntValue;
-import tlc2.value.SetEnumValue;
-import tlc2.value.Value;
+import tlc2.value.IBoolValue;
 import tlc2.value.ValueConstants;
-import tlc2.value.ValueVec;
+import tlc2.value.Values;
+import tlc2.value.impl.Applicable;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueVec;
+import util.Assert;
 
 public class Bags implements ValueConstants
 {
+	public static final long serialVersionUID = 20160822L;
 
     static
     {
-        TLARegistry.put("BagCup", "\\oplus");
-        TLARegistry.put("BagDiff", "\\ominus");
-        TLARegistry.put("SqSubseteq", "\\sqsubseteq");
+		// The following entries in TLARegistry each defines a mapping from a TLA+ infix
+		// operator to a Java method, e.g. the TLA+ infix operator \\oplus (which is the
+		// same as (+) ) is mapped to and thus implemented by the Java method
+		// tlc2.module.Bags.BagCup(Value, Value) below.
+        Assert.check(TLARegistry.put("BagCup", "\\oplus") == null, EC.TLC_REGISTRY_INIT_ERROR, "BagCup");
+        Assert.check(TLARegistry.put("BagDiff", "\\ominus") == null, EC.TLC_REGISTRY_INIT_ERROR, "BagDiff");
+        Assert.check(TLARegistry.put("SqSubseteq", "\\sqsubseteq") == null, EC.TLC_REGISTRY_INIT_ERROR, "SqSubseteq");
     }
 
     public static Value EmptyBag()
     {
-        return EmptyFcn;
+        return FcnRcdValue.EmptyFcn;
     }
 
-    public static BoolValue IsABag(Value b)
+    public static IBoolValue IsABag(final Value b)
     {
-        FcnRcdValue fcn = FcnRcdValue.convert(b);
+        final FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd();
         if (fcn == null)
         {
-            throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "IsBag",
-                    "a function with a finite domain", Value.ppr(b.toString()) });
+        	// MAK 02/23/2018 Changed to return ValFalse instead of exception when Value is not a bag.
+            //throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "IsBag",
+            //        "a function with a finite domain", Value.ppr(b.toString()) });
+        	return BoolValue.ValFalse;
         }
-        Value[] vals = fcn.values;
+        final Value[] vals = fcn.values;
         for (int i = 0; i < vals.length; i++)
         {
-            if (!(vals[i] instanceof IntValue) || ((IntValue) vals[i]).val < 0)
+            if (!(vals[i] instanceof IntValue) || ((IntValue) vals[i]).val <= 0)
             {
-                return ValFalse;
+                return BoolValue.ValFalse;
             }
         }
-        return ValTrue;
+        return BoolValue.ValTrue;
     }
 
     public static IntValue BagCardinality(Value b)
     {
-        FcnRcdValue fcn = FcnRcdValue.convert(b);
+        FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd();
         if (fcn == null)
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagCardinality",
-                    "a function with a finite domain", Value.ppr(b.toString()) });
+                    "a function with a finite domain", Values.ppr(b.toString()) });
         }
         int num = 0;
         Value[] vals = fcn.values;
@@ -74,44 +84,42 @@ public class Bags implements ValueConstants
                 } else
                 {
                     throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagCardinality", "a bag",
-                            Value.ppr(b.toString()) });
+                            Values.ppr(b.toString()) });
                 }
             } else
             {
             	throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagCardinality", "a bag",
-                        Value.ppr(b.toString()) });
+                        Values.ppr(b.toString()) });
             }
         }
         return IntValue.gen(num);
     }
 
-    public static BoolValue BagIn(Value e, Value b)
+    public static IBoolValue BagIn(final Value e, final Value b)
     {
-        FcnRcdValue fcn = FcnRcdValue.convert(b);
-        Value[] domain = fcn.domain;
-        Value[] values = fcn.values;
-        // Value val; // SZ: variable never read locally
+        final FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd();
+        final Value[] values = fcn.values;
+        final Value[] domain = fcn.getDomainAsValues();
         for (int i = 0; i < domain.length; i++)
         {
             if (e.equals(domain[i]))
             {
                 if (values[i] instanceof IntValue)
                 {
-                    return (((IntValue) values[i]).val > 0) ? ValTrue : ValFalse;
+                    return (((IntValue) values[i]).val > 0) ? BoolValue.ValTrue : BoolValue.ValFalse;
                 }
                 throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "BagIn", "bag",
-                        Value.ppr(b.toString()) });
+                        Values.ppr(b.toString()) });
             }
         }
-        return ValFalse;
+        return BoolValue.ValFalse;
     }
 
-    public static IntValue CopiesIn(Value e, Value b)
+    public static IntValue CopiesIn(final Value e, final Value b)
     {
-        FcnRcdValue fcn = FcnRcdValue.convert(b);
-        Value[] domain = fcn.domain;
-        Value[] values = fcn.values;
-        // Value val; // SZ: variable never read locally
+        final FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd();
+        final Value[] values = fcn.values;
+        final Value[] domain = fcn.getDomainAsValues();
         for (int i = 0; i < domain.length; i++)
         {
             if (e.equals(domain[i]))
@@ -121,32 +129,32 @@ public class Bags implements ValueConstants
                     return (IntValue) values[i];
                 }
                 throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "CopiesIn", "bag",
-                        Value.ppr(b.toString()) });
+                        Values.ppr(b.toString()) });
             }
         }
-        return ValZero;
+        return IntValue.ValZero;
     }
 
     public static Value BagCup(Value b1, Value b2)
     {
-        FcnRcdValue fcn1 = FcnRcdValue.convert(b1);
-        FcnRcdValue fcn2 = FcnRcdValue.convert(b2);
-        if (!IsABag(fcn1).val)
+        FcnRcdValue fcn1 = (FcnRcdValue) b1.toFcnRcd();
+        FcnRcdValue fcn2 = (FcnRcdValue) b2.toFcnRcd();
+        if (!IsABag(fcn1).getVal())
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "(+)", "bag",
-                    Value.ppr(b1.toString()) });
+                    Values.ppr(b1.toString()) });
         }
-        if (!IsABag(fcn2).val)
+        if (!IsABag(fcn2).getVal())
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "(+)", "bag",
-                    Value.ppr(b2.toString()) });
+                    Values.ppr(b2.toString()) });
         }
-        Value[] domain1 = fcn1.domain;
+        Value[] domain1 = fcn1.getDomainAsValues();
         Value[] values1 = fcn1.values;
-        Value[] domain2 = fcn2.domain;
+        Value[] domain2 = fcn2.getDomainAsValues();
         Value[] values2 = fcn2.values;
-        Vect dVec = new Vect(domain1.length);
-        Vect vVec = new Vect(domain1.length);
+        Vect<Value> dVec = new Vect<>(domain1.length);
+        Vect<Value> vVec = new Vect<>(domain1.length);
         for (int i = 0; i < domain1.length; i++)
         {
             dVec.addElement(domain1[i]);
@@ -176,32 +184,32 @@ public class Bags implements ValueConstants
         Value[] values = new Value[dVec.size()];
         for (int i = 0; i < domain.length; i++)
         {
-            domain[i] = (Value) dVec.elementAt(i);
-            values[i] = (Value) vVec.elementAt(i);
+            domain[i] = dVec.elementAt(i);
+            values[i] = vVec.elementAt(i);
         }
         return new FcnRcdValue(domain, values, false);
     }
 
     public static Value BagDiff(Value b1, Value b2)
     {
-        FcnRcdValue fcn1 = FcnRcdValue.convert(b1);
-        FcnRcdValue fcn2 = FcnRcdValue.convert(b2);
+        FcnRcdValue fcn1 = (FcnRcdValue) b1.toFcnRcd();
+        FcnRcdValue fcn2 = (FcnRcdValue) b2.toFcnRcd();
         if (fcn1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "(-)", "bag",
-                    Value.ppr(b1.toString()) });
+                    Values.ppr(b1.toString()) });
         }
         if (fcn2 == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "(-)", "bag",
-                    Value.ppr(b2.toString()) });
+                    Values.ppr(b2.toString()) });
         }
-        Value[] domain1 = fcn1.domain;
+        Value[] domain1 = fcn1.getDomainAsValues();
         Value[] values1 = fcn1.values;
-        Value[] domain2 = fcn2.domain;
+        Value[] domain2 = fcn2.getDomainAsValues();
         Value[] values2 = fcn2.values;
-        Vect dVec = new Vect(domain1.length);
-        Vect vVec = new Vect(domain1.length);
+        Vect<Value> dVec = new Vect<>(domain1.length);
+        Vect<Value> vVec = new Vect<>(domain1.length);
         for (int i = 0; i < domain1.length; i++)
         {
             int v1 = ((IntValue) values1[i]).val;
@@ -224,34 +232,43 @@ public class Bags implements ValueConstants
         Value[] values = new Value[vVec.size()];
         for (int i = 0; i < domain.length; i++)
         {
-            domain[i] = (Value) dVec.elementAt(i);
-            values[i] = (Value) vVec.elementAt(i);
+            domain[i] = dVec.elementAt(i);
+            values[i] = vVec.elementAt(i);
         }
         return new FcnRcdValue(domain, values, fcn1.isNormalized());
     }
 
-    public static Value BagUnion(Value s)
+    public static Value BagUnion(final Value s)
     {
-        SetEnumValue s1 = SetEnumValue.convert(s);
+        final SetEnumValue s1 = (SetEnumValue) s.toSetEnum();
         if (s1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagUnion",
-                    "a finite enumerable set", Value.ppr(s.toString()) });
+                    "a finite enumerable set", Values.ppr(s.toString()) });
         }
+        // MAK 02/20/2018:
+        // Need to normalize s in cases where it is an unnormalized set of identical
+        // bags, such as h == [ i \in 1..3 |-> 1 ] and BagUnion({h, h}). In other
+        // words, let b be a bag, BagUnion({b,b}) = b and not b (+) b. This
+        // unfortunately degrades performance due to sorting s1's elements.
+        s1.normalize();
+
         ValueVec elems = s1.elems;
         int sz = elems.size();
-        if (sz == 0)
-            return EmptyFcn;
-        if (sz == 1)
-            return elems.elementAt(0);
+        if (sz == 0) {
+        	return FcnRcdValue.EmptyFcn;
+        }
+        if (sz == 1) {
+        	return elems.elementAt(0);
+        }
         ValueVec dVec = new ValueVec();
         ValueVec vVec = new ValueVec();
-        FcnRcdValue fcn = FcnRcdValue.convert(elems.elementAt(0));
+        FcnRcdValue fcn = (FcnRcdValue) elems.elementAt(0).toFcnRcd();
         if (fcn == null)
         {
-            throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Value.ppr(s.toString()));
+            throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Values.ppr(s.toString()));
         }
-        Value[] domain = fcn.domain;
+        Value[] domain = fcn.getDomainAsValues();
         Value[] values = fcn.values;
         for (int i = 0; i < domain.length; i++)
         {
@@ -260,13 +277,13 @@ public class Bags implements ValueConstants
         }
         for (int i = 1; i < sz; i++)
         {
-            fcn = FcnRcdValue.convert(elems.elementAt(i));
+            fcn = (FcnRcdValue) elems.elementAt(i).toFcnRcd();
             if (fcn == null)
             {
 
-                throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Value.ppr(s.toString()));
+                throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Values.ppr(s.toString()));
             }
-            domain = fcn.domain;
+            domain = fcn.getDomainAsValues();
             values = fcn.values;
             for (int j = 0; j < domain.length; j++)
             {
@@ -299,24 +316,24 @@ public class Bags implements ValueConstants
         return new FcnRcdValue(dom, vals, false);
     }
 
-    public static BoolValue SqSubseteq(Value b1, Value b2)
+    public static IBoolValue SqSubseteq(Value b1, Value b2)
     {
-        FcnRcdValue fcn1 = FcnRcdValue.convert(b1);
-        FcnRcdValue fcn2 = FcnRcdValue.convert(b2);
+        FcnRcdValue fcn1 = (FcnRcdValue) b1.toFcnRcd();
+        FcnRcdValue fcn2 = (FcnRcdValue) b2.toFcnRcd();
         if (fcn1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "\\sqsubseteq",
-                    "a function with a finite domain", Value.ppr(b1.toString()) });
+                    "a function with a finite domain", Values.ppr(b1.toString()) });
         }
         if (fcn2 == null)
         {
 
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "\\sqsubseteq",
-                    "a function with a finite domain", Value.ppr(b2.toString()) });
+                    "a function with a finite domain", Values.ppr(b2.toString()) });
         }
-        Value[] domain1 = fcn1.domain;
+        Value[] domain1 = fcn1.getDomainAsValues();
         Value[] values1 = fcn1.values;
-        Value[] domain2 = fcn2.domain;
+        Value[] domain2 = fcn2.getDomainAsValues();
         Value[] values2 = fcn2.values;
         for (int i = 0; i < domain1.length; i++)
         {
@@ -331,9 +348,9 @@ public class Bags implements ValueConstants
                 }
             }
             if (v1 > 0)
-                return ValFalse;
+                return BoolValue.ValFalse;
         }
-        return ValTrue;
+        return BoolValue.ValTrue;
     }
 
     public static Value BagOfAll(Value f, Value b)
@@ -341,18 +358,18 @@ public class Bags implements ValueConstants
         if (!(f instanceof Applicable))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "BagOfAll", "operator",
-                    Value.ppr(f.toString()) });
+                    Values.ppr(f.toString()) });
         }
-        FcnRcdValue fcn = FcnRcdValue.convert(b);
+        FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd();
         if (fcn == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "BagOfAll",
-                    "function with a finite domain", Value.ppr(b.toString()) });
+                    "function with a finite domain", Values.ppr(b.toString()) });
         }
         Applicable ff = (Applicable) f;
         ValueVec dVec = new ValueVec();
         ValueVec vVec = new ValueVec();
-        Value[] domain = fcn.domain;
+        Value[] domain = fcn.getDomainAsValues();
         Value[] values = fcn.values;
         Value[] args = new Value[1];
         for (int i = 0; i < domain.length; i++)
@@ -390,7 +407,7 @@ public class Bags implements ValueConstants
     /******
      // For now, we do not override SubBag. So, We are using the TLA+ definition.
     public static Value SubBag(Value b) {
-      FcnRcdValue fcn = FcnRcdValue.convert(b);
+      FcnRcdValue fcn = b.toFcnRcd();
       if (fcn == null) {
         String msg = "Applying SubBag to the following value, which is\n" +
     "not a function with a finite domain:\n" + Value.ppr(b.toString());
@@ -402,22 +419,22 @@ public class Bags implements ValueConstants
 
     public static Value BagToSet(Value b)
     {
-        FcnRcdValue fcn = FcnRcdValue.convert(b);
+        FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd();
         if (fcn == null)
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagToSet",
-                    "a function with a finite domain", Value.ppr(b.toString()) });
+                    "a function with a finite domain", Values.ppr(b.toString()) });
         }
         return fcn.getDomain();
     }
 
     public static Value SetToBag(Value b)
     {
-        SetEnumValue s1 = SetEnumValue.convert(b);
+        SetEnumValue s1 = (SetEnumValue) b.toSetEnum();
         if (s1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagToSet",
-                    "a function with a finite domain", Value.ppr(b.toString()) });
+                    "a function with a finite domain", Values.ppr(b.toString()) });
         }
         // The following `if' added by LL on 5 Mar 2012 to correct a bug found by Tom Rodeheffer,
         // in which SetToBag creates a function with multiple copies of the elements in its
@@ -431,7 +448,7 @@ public class Bags implements ValueConstants
         for (int i = 0; i < elems.size(); i++)
         {
             domain[i] = elems.elementAt(i);
-            values[i] = ValOne;
+            values[i] = IntValue.ValOne;
         }
         return new FcnRcdValue(domain, values, s1.isNormalized());
     }
diff --git a/tlatools/src/tlc2/module/BuiltInModuleHelper.java b/tlatools/src/tlc2/module/BuiltInModuleHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b62e2b08e4e23e57442eb55aed1d1ffebfdeef7
--- /dev/null
+++ b/tlatools/src/tlc2/module/BuiltInModuleHelper.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.module;
+
+import java.io.File;
+import java.lang.reflect.Field;
+
+public class BuiltInModuleHelper {
+	
+	public static final String BUNDLE_ID = "org.lamport.tlatools";
+
+	public static final String STANDARD_MODULES = "StandardModules";
+	public static final String STANDARD_MODULES_PATH = File.separator + "tla2sany" + File.separator;
+	
+	private BuiltInModuleHelper() {
+		// no instantiation
+	}
+	
+	public static boolean isBuiltInModule(Class<?> clazz) {
+        try {
+			// Compare serialVersionUID because a user is allowed to override a
+			// built-in module. Thus, the name alone does not uniquely identify
+			// a built-in class.
+			final Field field = clazz.getField("serialVersionUID");
+			if (field != null) {
+				final long value = field.getLong(null);
+				if (clazz == AnySet.class && value == AnySet.serialVersionUID) {
+					return true;
+				} else if (clazz == Bags.class && value == Bags.serialVersionUID) {
+					return true;
+				} else if (clazz == FiniteSets.class && value == FiniteSets.serialVersionUID) {
+					return true;
+				} else if (clazz == Integers.class && value == Integers.serialVersionUID) {
+					return true;
+				} else if (clazz == Naturals.class && value == Naturals.serialVersionUID) {
+					return true;
+				} else if (clazz == Sequences.class && value == Sequences.serialVersionUID) {
+					return true;
+				} else if (clazz == Strings.class && value == Strings.serialVersionUID) {
+					return true;
+				} else if (clazz == TLC.class && value == TLC.serialVersionUID) {
+					return true;
+				} else if (clazz == TransitiveClosure.class && value == TransitiveClosure.serialVersionUID) {
+					return true;
+				} else if (clazz == Randomization.class && value == Randomization.serialVersionUID) {
+					return true;
+				}
+			}
+		} catch (SecurityException e) {
+			return false;
+		} catch (NoSuchFieldException e) {
+			return false;
+		} catch (IllegalArgumentException e) {
+			return false;
+		} catch (IllegalAccessException e) {
+			return false;
+		}
+        return false;
+	}
+}
diff --git a/tlatools/src/tlc2/module/FiniteSets.java b/tlatools/src/tlc2/module/FiniteSets.java
index 751b9c0566a33f194a582c84ef427d0cac3cde03..2c4d4314093637c35696b2d5d5b0f2421ba3a00e 100644
--- a/tlatools/src/tlc2/module/FiniteSets.java
+++ b/tlatools/src/tlc2/module/FiniteSets.java
@@ -7,27 +7,30 @@ package tlc2.module;
 
 import tlc2.output.EC;
 import tlc2.tool.EvalException;
-import tlc2.value.BoolValue;
-import tlc2.value.Enumerable;
-import tlc2.value.IntValue;
-import tlc2.value.Value;
+import tlc2.value.IBoolValue;
 import tlc2.value.ValueConstants;
+import tlc2.value.Values;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.Value;
 
 public class FiniteSets implements ValueConstants
 {
+	public static final long serialVersionUID = 20160822L;
 
-    public static BoolValue IsFiniteSet(Value val)
+    public static IBoolValue IsFiniteSet(Value val)
     {
-        return val.isFinite() ? ValTrue : ValFalse;
+        return val.isFinite() ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
     public static IntValue Cardinality(Value val)
     {
         if (val instanceof Enumerable)
         {
-            return IntValue.gen(((Enumerable) val).size());
+            return IntValue.gen(val.size());
         }
-        throw new EvalException(EC.TLC_MODULE_COMPUTING_CARDINALITY, Value.ppr(val.toString()));
+        throw new EvalException(EC.TLC_MODULE_COMPUTING_CARDINALITY, Values.ppr(val.toString()));
     }
 
     // SZ 16.07.2009: commented the following code out, since it is not a part of FiniteSets
@@ -48,7 +51,7 @@ public class FiniteSets implements ValueConstants
     }
 
     public static Value listToSet(Value list) {
-      TupleValue tv = TupleValue.convert(list);
+      TupleValue tv = list.toTuple()
       if (tv == null) {
         throw new EvalException("listToSet");
       }
@@ -60,7 +63,7 @@ public class FiniteSets implements ValueConstants
     }
     
     public static Value appendSetToList(Value list, Value set) {
-      TupleValue tv = TupleValue.convert(list);
+      TupleValue tv = list.toTuple();
       if (tv == null || IsFiniteSet(set) == ValFalse) {
         throw new EvalException("appendSetToList");
       }
@@ -80,7 +83,7 @@ public class FiniteSets implements ValueConstants
     }
     
     public static Value deleteSetFromList(Value set, Value list) {
-      TupleValue tv = TupleValue.convert(list);
+      TupleValue tv = list.toTuple();
       if (tv == null) {
         throw new EvalException("deleteSetFromList");
       }
@@ -98,7 +101,7 @@ public class FiniteSets implements ValueConstants
     }
     
     public static Value keepSetFromList(Value set, Value list) {
-      TupleValue tv = TupleValue.convert(list);
+      TupleValue tv = list.toTuple()
       if (tv == null) {
         throw new EvalException("keepSetFromList");
       }
diff --git a/tlatools/src/tlc2/module/Integers.java b/tlatools/src/tlc2/module/Integers.java
index 0ecd844f8db3fc0b9e54905ce87bb08298f7d08a..bb99dc59d5b8b3f8ef7330f315afb6bbc74b2cbf 100644
--- a/tlatools/src/tlc2/module/Integers.java
+++ b/tlatools/src/tlc2/module/Integers.java
@@ -7,21 +7,29 @@ package tlc2.module;
 
 import tlc2.output.EC;
 import tlc2.tool.EvalException;
-import tlc2.tool.TLARegistry;
-import tlc2.value.BoolValue;
-import tlc2.value.IntValue;
-import tlc2.value.IntervalValue;
-import tlc2.value.ModelValue;
-import tlc2.value.UserObj;
-import tlc2.value.UserValue;
-import tlc2.value.Value;
+import tlc2.tool.impl.TLARegistry;
+import tlc2.value.IBoolValue;
 import tlc2.value.ValueConstants;
+import tlc2.value.Values;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.UserObj;
+import tlc2.value.impl.UserValue;
+import tlc2.value.impl.Value;
 
 public class Integers extends UserObj implements ValueConstants
 {
+	public static final long serialVersionUID = 20160822L;
 
     static
     {
+		// The following entries in TLARegistry each define a mapping from a TLA+ infix
+		// operator to a Java method, e.g. the TLA+ infix operator "+" is mapped to and
+		// thus implemented by the Java method tlc2.module.Integers.Plus(IntValue,
+		// IntValue) below.
+    	//TODO Why does tlc2.module.Naturals define identical mappings?
         TLARegistry.put("Plus", "+");
         TLARegistry.put("Minus", "-");
         TLARegistry.put("Times", "*");
@@ -36,7 +44,7 @@ public class Integers extends UserObj implements ValueConstants
         TLARegistry.put("Expt", "^");
     }
 
-    private static Value SetInt = new UserValue(new Integers());
+    private static final Value SetInt = new UserValue(new Integers());
 
     public static Value Int()
     {
@@ -63,36 +71,36 @@ public class Integers extends UserObj implements ValueConstants
         return Naturals.Times(x, y);
     }
 
-    public static BoolValue LT(Value x, Value y)
+    public static IBoolValue LT(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<", "integer",
-                    Value.ppr(y.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val < ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val < ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
-    public static BoolValue LE(Value x, Value y)
+    public static IBoolValue LE(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<=", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<=", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val <= ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val <= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
     public static BoolValue GT(Value x, Value y)
@@ -100,31 +108,31 @@ public class Integers extends UserObj implements ValueConstants
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val > ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val > ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
-    public static BoolValue GEQ(Value x, Value y)
+    public static IBoolValue GEQ(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">=", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">=", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val >= ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val >= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
     public static IntervalValue DotDot(IntValue x, IntValue y)
@@ -186,7 +194,7 @@ public class Integers extends UserObj implements ValueConstants
             {
                 throw new EvalException(EC.TLC_MODULE_NULL_POWER_NULL);
             }
-            return ValOne;
+            return IntValue.ValOne;
         }
         long res = x.val;
         for (int i = 1; i < y.val; i++)
@@ -201,6 +209,7 @@ public class Integers extends UserObj implements ValueConstants
         return IntValue.gen((int) res);
     }
 
+    @Override
     public final int compareTo(Value val)
     {
         if (val instanceof UserValue)
@@ -216,9 +225,10 @@ public class Integers extends UserObj implements ValueConstants
         }
         if (val instanceof ModelValue)
             return 1;
-        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Int", Value.ppr(val.toString()) });
+        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Int", Values.ppr(val.toString()) });
     }
 
+    @Override
     public final boolean member(Value val)
     {
         if (val instanceof IntValue)
@@ -227,15 +237,17 @@ public class Integers extends UserObj implements ValueConstants
         {
             return ((ModelValue) val).modelValueMember(this);
         }
-        throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(val.toString()), "Int" });
+        throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(val.toString()), "Int" });
     }
 
+    @Override
     public final boolean isFinite()
     {
         return false;
     }
 
-    public final StringBuffer toString(StringBuffer sb, int offset)
+    @Override
+    public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow)
     {
         return sb.append("Int");
     }
diff --git a/tlatools/src/tlc2/module/Naturals.java b/tlatools/src/tlc2/module/Naturals.java
index a1771f38b3a4ebc82f49c5b2520e74a9a000d132..ac1421b7f0565ed22442012ea9fbf22e590cf144 100644
--- a/tlatools/src/tlc2/module/Naturals.java
+++ b/tlatools/src/tlc2/module/Naturals.java
@@ -7,21 +7,29 @@ package tlc2.module;
 
 import tlc2.output.EC;
 import tlc2.tool.EvalException;
-import tlc2.tool.TLARegistry;
-import tlc2.value.BoolValue;
-import tlc2.value.IntValue;
-import tlc2.value.IntervalValue;
-import tlc2.value.ModelValue;
-import tlc2.value.UserObj;
-import tlc2.value.UserValue;
-import tlc2.value.Value;
+import tlc2.tool.impl.TLARegistry;
+import tlc2.value.IBoolValue;
 import tlc2.value.ValueConstants;
+import tlc2.value.Values;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.UserObj;
+import tlc2.value.impl.UserValue;
+import tlc2.value.impl.Value;
 
 public class Naturals extends UserObj implements ValueConstants
 {
-
+	public static final long serialVersionUID = 20160822L;
+	
     static
     {
+		// The following entries in TLARegistry each define a mapping from a TLA+ infix
+		// operator to a Java method, e.g. the TLA+ infix operator "+" is mapped to and
+		// thus implemented by the Java method tlc2.module.Naturals.Plus(IntValue,
+   		// IntValue) below.
+    	//TODO Why does tlc2.module.Integers define identical mappings?
         TLARegistry.put("Plus", "+");
         TLARegistry.put("Minus", "-");
         TLARegistry.put("Times", "*");
@@ -84,70 +92,70 @@ public class Naturals extends UserObj implements ValueConstants
         return IntValue.gen((int) res);
     }
 
-    public static BoolValue LT(Value x, Value y)
+    public static IBoolValue LT(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val < ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val < ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
-    public static BoolValue LE(Value x, Value y)
+    public static IBoolValue LE(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<=", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<=", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val <= ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val <= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
-    public static BoolValue GT(Value x, Value y)
+    public static IBoolValue GT(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             // On 21 May 2012 LL corrected following call, which was reporting the first argument.
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer",
-                    Value.ppr(y.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val > ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val > ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
-    public static BoolValue GEQ(Value x, Value y)
+    public static IBoolValue GEQ(Value x, Value y)
     {
         if (!(x instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">", "integer",
-                    Value.ppr(x.toString()) });
+                    Values.ppr(x.toString()) });
         }
         if (!(y instanceof IntValue))
         {
             // On 21 May 2012 LL corrected following call, which was reporting the first argument.
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer",
-                    Value.ppr(y.toString()) });
+                    Values.ppr(y.toString()) });
         }
 
-        return (((IntValue) x).val >= ((IntValue) y).val) ? ValTrue : ValFalse;
+        return (((IntValue) x).val >= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse;
     }
 
     public static IntervalValue DotDot(IntValue x, IntValue y)
@@ -197,7 +205,7 @@ public class Naturals extends UserObj implements ValueConstants
             {
                 throw new EvalException(EC.TLC_MODULE_NULL_POWER_NULL);
             }
-            return ValOne;
+            return IntValue.ValOne;
         }
         long res = n1;
         for (int i = 1; i < n2; i++)
@@ -211,6 +219,7 @@ public class Naturals extends UserObj implements ValueConstants
         return IntValue.gen((int) res);
     }
 
+    @Override
     public final int compareTo(Value val)
     {
         if (val instanceof UserValue)
@@ -226,9 +235,10 @@ public class Naturals extends UserObj implements ValueConstants
         }
         if (val instanceof ModelValue)
             return 1;
-        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Nat", Value.ppr(val.toString()) });
+        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Nat", Values.ppr(val.toString()) });
     }
 
+    @Override
     public final boolean member(Value val)
     {
         if (val instanceof IntValue)
@@ -236,15 +246,17 @@ public class Naturals extends UserObj implements ValueConstants
         if (val instanceof ModelValue)
             return ((ModelValue) val).modelValueMember(this);
 
-        throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(val.toString()), "Nat" });
+        throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(val.toString()), "Nat" });
     }
 
+    @Override
     public final boolean isFinite()
     {
         return false;
     }
 
-    public final StringBuffer toString(StringBuffer sb, int offset)
+    @Override
+    public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow)
     {
         return sb.append("Nat");
     }
diff --git a/tlatools/src/tlc2/module/Randomization.java b/tlatools/src/tlc2/module/Randomization.java
new file mode 100644
index 0000000000000000000000000000000000000000..7916353238a52cdd884dcfd2272dac16068a3f64
--- /dev/null
+++ b/tlatools/src/tlc2/module/Randomization.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.module;
+
+import tlc2.output.EC;
+import tlc2.tool.EvalException;
+import tlc2.value.ValueConstants;
+import tlc2.value.Values;
+import tlc2.value.impl.EnumerableValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.SubsetValue;
+import tlc2.value.impl.Value;
+
+public class Randomization implements ValueConstants {
+	
+	public static final long serialVersionUID = 20180618L;
+	
+    public static Value RandomSubset(final Value v1, final Value v2) {
+		if (!(v1 instanceof IntValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSubset", "nonnegative integer", Values.ppr(v1.toString()) });
+		}
+        if (!(v2 instanceof EnumerableValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSubset", "a finite set", Values.ppr(v2.toString()) });
+        }
+        return ((EnumerableValue) v2).getRandomSubset(((IntValue) v1).val);
+    }
+    
+    public static Value RandomSetOfSubsets(final Value v1, final Value v2, final Value v3) {
+		// first parameter	
+		if (!(v1 instanceof IntValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v1.toString()) });
+		}
+		final int numberOfPicks = ((IntValue) v1).val;
+		if (numberOfPicks < 0) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v1.toString()) });
+		}
+		// second parameter	
+		if (!(v2 instanceof IntValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v2.toString()) });
+		}
+		final int n = ((IntValue) v2).val;
+		if (n < 0) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v2.toString()) });
+		}
+		// third parameter	
+        if (!(v3 instanceof EnumerableValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "third", "RandomSetOfSubsets", "finite set", Values.ppr(v3.toString()) });
+        }
+        final EnumerableValue ev = (EnumerableValue) v3;
+		if (31 - Integer.numberOfLeadingZeros(numberOfPicks) + 1 > ev.size() && numberOfPicks > (1 << ev.size())) {
+			// First compare exponents before explicit calculating size of subset. The
+			// calculated value which is the subset's size then won't overflow.
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSetOfSubsets",
+							"nonnegative integer that is smaller than the subset's size of 2^" + ev.size(),
+							Integer.toString(numberOfPicks) });
+		}
+		// second parameter (now that we know third is enumerable)
+		if (ev.size() < n) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSetOfSubsets", "nonnegative integer in range 0..Cardinality(S)", Values.ppr(v2.toString()) });
+		}
+		final double probability = (1d * n) / ev.size();
+		if (probability < 0d || 1d < probability) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSetOfSubsets", "nonnegative integer in range 0..Cardinality(S)", Values.ppr(v2.toString()) });
+		}
+		return new SubsetValue(ev).getRandomSetOfSubsets(numberOfPicks, probability);
+    }
+    
+    public static Value RandomSubsetSet(final Value v1, final Value v2, final Value v3) {
+		// first parameter	
+		if (!(v1 instanceof IntValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSubsetSetProbability", "nonnegative integer", Values.ppr(v1.toString()) });
+		}
+		final int numberOfPicks = ((IntValue) v1).val;
+		if (numberOfPicks < 0) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSubsetSetProbability", "nonnegative integer", Values.ppr(v1.toString()) });
+		}
+		// second parameter	
+		if (!(v2 instanceof StringValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSubsetSetProbability", "string literal representing a probability", Values.ppr(v2.toString()) });
+			
+		}
+		double probability;
+		try {
+			probability = Double.valueOf(((StringValue) v2).getVal().toString());			
+		} catch (NumberFormatException nfe) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSubsetSetProbability", "string literal does not represent a parsable probability", Values.ppr(v2.toString()) });
+		}
+		if (probability < 0d || 1d < probability) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "second", "RandomSubsetSetProbability", "string literal does not represent a parsable probability", Values.ppr(v2.toString()) });
+		}
+		// third parameter	
+        if (!(v3 instanceof EnumerableValue)) {
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "third", "RandomSubsetSetProbability", "finite set", Values.ppr(v3.toString()) });
+        }
+        final EnumerableValue ev = (EnumerableValue) v3;
+		if (31 - Integer.numberOfLeadingZeros(numberOfPicks) + 1 > ev.size() && numberOfPicks > (1 << ev.size())) {
+			// First compare exponents before explicit calculating size of subset. The
+			// calculated value which is the subset's size then won't overflow.
+			throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR,
+					new String[] { "first", "RandomSubsetSetProbability",
+							"nonnegative integer that is smaller than the subset's size of 2^" + ev.size(),
+							Integer.toString(numberOfPicks) });
+		}
+
+		return new SubsetValue(ev).getRandomSetOfSubsets(numberOfPicks, probability);
+    }
+}
diff --git a/tlatools/src/tlc2/module/Sequences.java b/tlatools/src/tlc2/module/Sequences.java
index ecd4b58c68191cb7726b3c4fbe720d22dcfac7e4..5d4dc6c4c42e0d31c7cfe79052d0b683f7ef4d4a 100644
--- a/tlatools/src/tlc2/module/Sequences.java
+++ b/tlatools/src/tlc2/module/Sequences.java
@@ -8,25 +8,29 @@ package tlc2.module;
 import tlc2.output.EC;
 import tlc2.tool.EvalControl;
 import tlc2.tool.EvalException;
-import tlc2.tool.TLARegistry;
-import tlc2.value.Applicable;
-import tlc2.value.BoolValue;
-import tlc2.value.IntValue;
-import tlc2.value.ModelValue;
-import tlc2.value.OpLambdaValue;
-import tlc2.value.OpRcdValue;
-import tlc2.value.StringValue;
-import tlc2.value.TupleValue;
-import tlc2.value.UserObj;
-import tlc2.value.UserValue;
-import tlc2.value.Value;
+import tlc2.tool.impl.TLARegistry;
+import tlc2.value.IBoolValue;
 import tlc2.value.ValueConstants;
-import tlc2.value.ValueVec;
+import tlc2.value.Values;
+import tlc2.value.impl.Applicable;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.OpLambdaValue;
+import tlc2.value.impl.OpRcdValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.UserObj;
+import tlc2.value.impl.UserValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueVec;
 import util.Assert;
 import util.UniqueString;
 
 public class Sequences extends UserObj implements ValueConstants
 {
+	public static final long serialVersionUID = 20160822L;
+	
     private Value range;
     private int size;
 
@@ -36,9 +40,11 @@ public class Sequences extends UserObj implements ValueConstants
         this.size = size;
     }
 
-    static
+	static
     {
-        // SZ Jul 13, 2009: added message for initialization assertion
+		// This entry in TLARegistry defines a mapping from TLA+' infix
+		// operator \o to the Java method tlc2.module.Sequences.Concat(Value, Value)
+		// below.
         Assert.check(TLARegistry.put("Concat", "\\o") == null, EC.TLC_REGISTRY_INIT_ERROR, "Concat");
     }
 
@@ -56,18 +62,27 @@ public class Sequences extends UserObj implements ValueConstants
             return IntValue.gen(((StringValue) s).length());
         }
 
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq != null)
         {
             return IntValue.gen(seq.size());
         }
-        throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b", "Len", "sequence",
-                Value.ppr(s.toString()) });
+        throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "Len", "sequence",
+                Values.ppr(s.toString()) });
     }
 
     public static Value Head(Value s)
     {
-        TupleValue seq = TupleValue.convert(s);
+    	// Implementation of Head(string) by MAK on 4 Dec 2019
+    	if (s instanceof StringValue) {
+    		String str = ((StringValue) s).val.toString();
+    		if (str.equals("")) {
+    			throw new EvalException(EC.TLC_MODULE_APPLY_EMPTY_SEQ, "Head");
+    		}
+    		return new StringValue(str.substring(0,1));
+    	}
+    	
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq != null)
         {
             if (seq.size() == 0)
@@ -76,8 +91,8 @@ public class Sequences extends UserObj implements ValueConstants
             }
             return seq.elems[0];
         }
-        throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b", "Head", "sequence",
-                Value.ppr(s.toString()) });
+        throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "Head", "sequence",
+                Values.ppr(s.toString()) });
     }
 
     public static Value Tail(Value s)
@@ -91,7 +106,7 @@ public class Sequences extends UserObj implements ValueConstants
     		return new StringValue(str.substring(1));
     	}
     	
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq != null)
         {
             if (seq.size() == 0)
@@ -103,17 +118,17 @@ public class Sequences extends UserObj implements ValueConstants
             System.arraycopy(seq.elems, 1, vals, 0, vals.length);
             return new TupleValue(vals);
         }
-        throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b", "Tail", "sequence",
-                Value.ppr(s.toString()) });
+        throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "Tail", "sequence",
+                Values.ppr(s.toString()) });
     }
 
     public static Value Cons(Value v, Value s)
     {
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "Cons(v, s)", "sequence",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
         }
         int len = seq.size();
         Value[] values = new Value[len + 1];
@@ -124,11 +139,24 @@ public class Sequences extends UserObj implements ValueConstants
 
     public static Value Append(Value s, Value v)
     {
-        TupleValue seq = TupleValue.convert(s);
+    	// Implementation of Append(string, string) by MAK on 4 Dec 2019
+        if (s instanceof StringValue)
+        {
+            if (!(v instanceof StringValue))
+            {
+                throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "t \\o s", "string",
+                        Values.ppr(v.toString()) });
+            }
+            UniqueString u1 = ((StringValue) s).val;
+            UniqueString u2 = ((StringValue) v).val;
+            return new StringValue(u1.concat(u2));
+        }
+        
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "Append(v, s)", "sequence",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
         }
         int len = seq.size();
         Value[] values = new Value[len + 1];
@@ -144,24 +172,24 @@ public class Sequences extends UserObj implements ValueConstants
             if (!(s2 instanceof StringValue))
             {
                 throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "t \\o s", "string",
-                        Value.ppr(s2.toString()) });
+                        Values.ppr(s2.toString()) });
             }
             UniqueString u1 = ((StringValue) s1).val;
             UniqueString u2 = ((StringValue) s2).val;
             return new StringValue(u1.concat(u2));
         }
 
-        TupleValue seq1 = TupleValue.convert(s1);
+        TupleValue seq1 = (TupleValue) s1.toTuple();
         if (seq1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "s \\o t", "sequence",
-                    Value.ppr(s1.toString()) });
+                    Values.ppr(s1.toString()) });
         }
-        TupleValue seq2 = TupleValue.convert(s2);
+        TupleValue seq2 = (TupleValue) s2.toTuple();
         if (seq2 == null)
         {
             throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "t \\o s", "sequence",
-                    Value.ppr(s2.toString()) });
+                    Values.ppr(s2.toString()) });
         }
         int len1 = seq1.size();
         int len2 = seq2.size();
@@ -187,16 +215,16 @@ public class Sequences extends UserObj implements ValueConstants
      */
     public static Value SelectInSeq(Value s, Value test)
     {
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SelectInSeq", "sequence",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
         }
         if (!(test instanceof Applicable))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectInSeq", "function",
-                    Value.ppr(test.toString()) });
+                    Values.ppr(test.toString()) });
         }
         int len = seq.size();
         Applicable ftest = (Applicable) test;
@@ -205,20 +233,20 @@ public class Sequences extends UserObj implements ValueConstants
         {
             args[0] = seq.elems[i];
             Value val = ftest.apply(args, EvalControl.Clear);
-            if (!(val instanceof BoolValue))
+            if (!(val instanceof IBoolValue))
             {
                 throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectInSeq",
-                        "boolean-valued function", Value.ppr(test.toString()) });
+                        "boolean-valued function", Values.ppr(test.toString()) });
             }
             if (((BoolValue) val).val)
                 return IntValue.gen(i + 1);
         }
-        return ValZero;
+        return IntValue.ValZero;
     }
 
     /**  Not in the standard interface.
     public static Value Remove(Value s, Value index) {
-      TupleValue seq = TupleValue.convert(s);
+      TupleValue seq = s.toTuple()
       if (seq != null) {
         if (index instanceof IntValue) {
     int ridx = ((IntValue)index).val;
@@ -264,23 +292,23 @@ public class Sequences extends UserObj implements ValueConstants
     	}
     	
     	if (! isString) {
-          seq = TupleValue.convert(s);
+          seq = (TupleValue) s.toTuple();
           if (seq == null)
           {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SubSeq", "sequence",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
           }
     	}
     	
         if (!(m instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SubSeq", "natural number",
-                    Value.ppr(m.toString()) });
+                    Values.ppr(m.toString()) });
         }
         if (!(n instanceof IntValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "third", "SubSeq", "natural number",
-                    Value.ppr(n.toString()) });
+                    Values.ppr(n.toString()) });
         }
         int beg = ((IntValue) m).val;
         int end = ((IntValue) n).val;
@@ -289,7 +317,7 @@ public class Sequences extends UserObj implements ValueConstants
         		return new StringValue("") ;
         	} 
         	else {
-              return EmptyTuple;
+              return TupleValue.EmptyTuple;
         	}
         }
         
@@ -298,13 +326,13 @@ public class Sequences extends UserObj implements ValueConstants
         if (beg < 1 || beg > len)
         {
 
-            throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "second", "SubSeq",
-                    Value.ppr(s.toString()), Value.ppr(m.toString()) });
+            throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "second", "SubSeq", "first",
+                    Values.ppr(s.toString()), Values.ppr(m.toString()) });
         }
         if (end < 1 || end > len)
         {
-            throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "third", "SubSeq",
-                    Value.ppr(s.toString()), Value.ppr(n.toString()) });
+            throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "third", "SubSeq", "first",
+                    Values.ppr(s.toString()), Values.ppr(n.toString()) });
         }
         
         if (isString) {
@@ -320,19 +348,19 @@ public class Sequences extends UserObj implements ValueConstants
 
     public static Value SelectSeq(Value s, Value test)
     {
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SelectSeq", "sequence",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
         }
         int len = seq.size();
         if (len == 0)
-            return EmptyTuple;
+            return TupleValue.EmptyTuple;
         if (!(test instanceof OpLambdaValue) && !(test instanceof OpRcdValue))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectSeq", "operator",
-                    Value.ppr(test.toString()) });
+                    Values.ppr(test.toString()) });
         }
         ValueVec vals = new ValueVec();
         Applicable ftest = (Applicable) test;
@@ -341,14 +369,14 @@ public class Sequences extends UserObj implements ValueConstants
         {
             args[0] = seq.elems[i];
             Value val = ftest.apply(args, EvalControl.Clear);
-            if (val instanceof BoolValue)
+            if (val instanceof IBoolValue)
             {
                 if (((BoolValue) val).val)
                     vals.addElement(args[0]);
             } else
             {
                 throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectSeq",
-                        "boolean-valued operator", Value.ppr(test.toString()) });
+                        "boolean-valued operator", Values.ppr(test.toString()) });
             }
         }
         Value[] elems = new Value[vals.size()];
@@ -359,6 +387,7 @@ public class Sequences extends UserObj implements ValueConstants
         return new TupleValue(elems);
     }
 
+    @Override
     public final int compareTo(Value s)
     {
         if ((s instanceof UserValue) && (((UserValue) s).userObj instanceof Sequences))
@@ -377,19 +406,20 @@ public class Sequences extends UserObj implements ValueConstants
         }
         // SZ Jul 14, 2009:
         // replaced the message with a standard one, thrown by mismatch of compared elements
-        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { Value.ppr(this.toString()),
-                Value.ppr(s.toString()) });
+        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { Values.ppr(this.toString()),
+                Values.ppr(s.toString()) });
     }
 
+    @Override
     public final boolean member(Value s)
     {
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             if (s instanceof ModelValue)
                 return ((ModelValue) s).modelValueMember(this);
-            throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(s.toString()),
-                    Value.ppr(this.toString()) });
+            throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(s.toString()),
+                    Values.ppr(this.toString()) });
         }
         int len = seq.size();
         if (len > this.size)
@@ -402,22 +432,24 @@ public class Sequences extends UserObj implements ValueConstants
         return true;
     }
 
+    @Override
     public final boolean isFinite()
     {
         return this.size != Integer.MAX_VALUE;
     }
 
-    public final StringBuffer toString(StringBuffer sb, int offset)
+    @Override
+    public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow)
     {
         if (this.size == Integer.MAX_VALUE)
         {
             sb = sb.append("Seq(");
-            sb = this.range.toString(sb, offset);
+            sb = this.range.toString(sb, offset, swallow);
             sb = sb.append(")");
         } else
         {
             sb = sb.append("BSeq(");
-            sb = this.range.toString(sb, offset);
+            sb = this.range.toString(sb, offset, swallow);
             sb = sb.append(", ");
             sb = sb.append(this.size);
             sb = sb.append(")");
@@ -427,16 +459,16 @@ public class Sequences extends UserObj implements ValueConstants
 
     public static Value Insert(Value s, Value v, Value test)
     {
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "Insert", "sequence",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
         }
         if (!(test instanceof Applicable))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SubSeq", "function",
-                    Value.ppr(test.toString()) });
+                    Values.ppr(test.toString()) });
         }
         int len = seq.size();
         Applicable ftest = (Applicable) test;
@@ -448,10 +480,10 @@ public class Sequences extends UserObj implements ValueConstants
         {
             args[1] = seq.elems[idx - 1];
             Value val = ftest.apply(args, EvalControl.Clear);
-            if (!(val instanceof BoolValue))
+            if (!(val instanceof IBoolValue))
             {
                 throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "third", "Insert",
-                        "boolean-valued operator", Value.ppr(test.toString()) });
+                        "boolean-valued operator", Values.ppr(test.toString()) });
             }
             if (((BoolValue) val).val && v.compareTo(args[1]) < 0)
             {
diff --git a/tlatools/src/tlc2/module/Strings.java b/tlatools/src/tlc2/module/Strings.java
index f9fb934198201afa670c3d3aca861ce65d1a0617..2be9f901b8539d66fcb40832c7459a27c8569cfe 100644
--- a/tlatools/src/tlc2/module/Strings.java
+++ b/tlatools/src/tlc2/module/Strings.java
@@ -7,14 +7,16 @@ package tlc2.module;
 
 import tlc2.output.EC;
 import tlc2.tool.EvalException;
-import tlc2.value.ModelValue;
-import tlc2.value.StringValue;
-import tlc2.value.UserObj;
-import tlc2.value.UserValue;
-import tlc2.value.Value;
+import tlc2.value.Values;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.UserObj;
+import tlc2.value.impl.UserValue;
+import tlc2.value.impl.Value;
 
 public class Strings extends UserObj
 {
+	public static final long serialVersionUID = 20160822L;
 
     private static Value SetString = new UserValue(new Strings());
 
@@ -23,6 +25,7 @@ public class Strings extends UserObj
         return SetString;
     }
 
+    @Override
     public final int compareTo(Value val)
     {
         if ((val instanceof UserValue) && (((UserValue) val).userObj instanceof Strings))
@@ -31,24 +34,27 @@ public class Strings extends UserObj
         }
         if (val instanceof ModelValue)
             return 1;
-        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "STRING", Value.ppr(val.toString()) });
+        throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "STRING", Values.ppr(val.toString()) });
     }
 
+    @Override
     public final boolean member(Value val)
     {
         if (val instanceof StringValue)
             return true;
         if (val instanceof ModelValue)
             return ((ModelValue) val).modelValueMember(this);
-        throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(val.toString()), "STRING" });
+        throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(val.toString()), "STRING" });
     }
 
+    @Override
     public final boolean isFinite()
     {
         return false;
     }
 
-    public final StringBuffer toString(StringBuffer sb, int offset)
+    @Override
+    public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow)
     {
         return sb.append("STRING");
     }
diff --git a/tlatools/src/tlc2/module/TLC.java b/tlatools/src/tlc2/module/TLC.java
index dd0969ba415ff9c6b6e031a78d40690470b5dd38..b1a5277f1f056ec7be82f9ff2887ce7ddb87e2d2 100644
--- a/tlatools/src/tlc2/module/TLC.java
+++ b/tlatools/src/tlc2/module/TLC.java
@@ -5,41 +5,63 @@
 
 package tlc2.module;
 
+import java.io.BufferedWriter;
+import java.io.IOException;
+
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
+import tlc2.output.MP;
 import tlc2.tool.EvalControl;
 import tlc2.tool.EvalException;
-import tlc2.tool.TLARegistry;
-import tlc2.tool.Worker;
-import tlc2.util.RandomGenerator;
-import tlc2.value.Applicable;
-import tlc2.value.BoolValue;
-import tlc2.value.FcnRcdValue;
-import tlc2.value.IntValue;
-import tlc2.value.IntervalValue;
-import tlc2.value.RecordValue;
-import tlc2.value.SetEnumValue;
-import tlc2.value.SetOfFcnsValue;
-import tlc2.value.SetOfRcdsValue;
-import tlc2.value.SetOfTuplesValue;
-import tlc2.value.StringValue;
-import tlc2.value.TupleValue;
-import tlc2.value.Value;
+import tlc2.tool.ModelChecker;
+import tlc2.tool.TLCState;
+import tlc2.tool.impl.TLARegistry;
+import tlc2.util.IdThread;
+import tlc2.value.IBoolValue;
 import tlc2.value.ValueConstants;
-import tlc2.value.ValueVec;
+import tlc2.value.Values;
+import tlc2.value.impl.Applicable;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.SetOfFcnsValue;
+import tlc2.value.impl.SetOfRcdsValue;
+import tlc2.value.impl.SetOfTuplesValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueVec;
 import util.Assert;
 import util.ToolIO;
+import util.UniqueString;
 
 public class TLC implements ValueConstants
 {
+	private static final UniqueString LEVEL = UniqueString.uniqueStringOf("level");
+	private static final UniqueString DURATION = UniqueString.uniqueStringOf("duration");
+	private static final UniqueString QUEUE = UniqueString.uniqueStringOf("queue");
+	private static final UniqueString DISTINCT = UniqueString.uniqueStringOf("distinct");
+	private static final UniqueString DIAMETER = UniqueString.uniqueStringOf("diameter");
+	private static final UniqueString EXIT = UniqueString.uniqueStringOf("exit");
+	private static final UniqueString PAUSE = UniqueString.uniqueStringOf("pause");
+
+	public static final long serialVersionUID = 20160822L;
+
+	private static final long startTime = System.currentTimeMillis();
 
-    private static RandomGenerator rng;
+	public static BufferedWriter OUTPUT;
 
     static
     {
+		// The following two entries in TLARegistry define a mapping from a TLA+ infix
+		// operator to a Java method, e.g. the TLA+ infix operator "@@" is mapped to and
+		// thus implemented by the Java method tlc2.module.TLC.CombineFcn(Value, Value)
+		// below.
         Assert.check(TLARegistry.put("MakeFcn", ":>") == null, EC.TLC_REGISTRY_INIT_ERROR, "MakeFcn");
         Assert.check(TLARegistry.put("CombineFcn", "@@") == null, EC.TLC_REGISTRY_INIT_ERROR, "CombineFcn");
-        rng = new RandomGenerator();
     }
 
     /**
@@ -53,11 +75,19 @@ public class TLC implements ValueConstants
      */
     public static Value Print(Value v1, Value v2)
     {
-        Value v1c = v1.deepCopy();
-        Value v2c = v2.deepCopy();
+        Value v1c = (Value) v1.deepCopy();
+        Value v2c = (Value) v2.deepCopy();
         v1c.deepNormalize();
         v2c.deepNormalize();
-        ToolIO.out.println(Value.ppr(v1c.toString()) + "  " + Value.ppr(v2c.toString()));
+        if (OUTPUT == null) {
+        	ToolIO.out.println(Values.ppr(v1c.toStringUnchecked()) + "  " + Values.ppr(v2c.toStringUnchecked()));
+        } else {
+        	try {
+        		OUTPUT.write(Values.ppr(v1c.toStringUnchecked()) + "  " + Values.ppr(v2c.toStringUnchecked()) + "\n");
+        	} catch (IOException e) {
+        		MP.printError(EC.GENERAL, e);
+        	}
+        }
         return v2;
     }
 
@@ -66,18 +96,27 @@ public class TLC implements ValueConstants
      * 
      * Modified on 22 June 2011 by LL.  See comment on the Print method
      */
-    public static Value PrintT(Value v1)
+    public static Value  PrintT(Value v1)
     {
-        Value v1c = v1.deepCopy();
+        Value v1c = (Value) v1.deepCopy();
         v1c.deepNormalize();   
-        ToolIO.out.println(Value.ppr(v1c.toString()));
-        return ValTrue;
+        if (OUTPUT == null) {
+        	String ppr = Values.ppr(v1c.toStringUnchecked());
+        	ToolIO.out.println(ppr);
+        } else {
+        	try {
+        		OUTPUT.write(Values.ppr(v1c.toStringUnchecked("\n")));
+        	} catch (IOException e) {
+        		MP.printError(EC.GENERAL, e);
+        	}
+        }
+        return BoolValue.ValTrue;
     }
 
     /* Returns the string value of the string representation of v. */
     public static Value ToString(Value v)
     {
-        return new StringValue(v.toString());
+        return new StringValue(v.toStringUnchecked());
     }
 
     /**
@@ -86,11 +125,11 @@ public class TLC implements ValueConstants
      */
     public static Value Assert(Value v1, Value v2)
     {
-        if ((v1 instanceof BoolValue) && ((BoolValue) v1).val)
+        if ((v1 instanceof IBoolValue) && ((BoolValue) v1).val)
         {
             return v1;
         }
-        throw new EvalException(EC.TLC_VALUE_ASSERT_FAILED, Value.ppr(v2.toString()));
+        throw new EvalException(EC.TLC_VALUE_ASSERT_FAILED, Values.ppr(v2.toString()));
     }
 
     /**
@@ -112,12 +151,15 @@ public class TLC implements ValueConstants
             {
                 Thread th = Thread.currentThread();
                 Value res = null;
-                if (th instanceof Worker)
+                if (th instanceof IdThread)
                 {
-                    res = ((Worker) th).getLocalValue(idx);
-                } else
+                    res = (Value) ((IdThread) th).getLocalValue(idx);
+                } else if (TLCGlobals.mainChecker != null)
                 {
-                    res = tlc2.TLCGlobals.mainChecker.getValue(0, idx);
+                    res = (Value) tlc2.TLCGlobals.mainChecker.getValue(0, idx);
+                } else 
+                {	
+                    res = (Value) tlc2.TLCGlobals.simulator.getLocalValue(idx);
                 }
                 if (res == null)
                 {
@@ -125,12 +167,76 @@ public class TLC implements ValueConstants
                 }
                 return res;
             }
+        } else if (vidx instanceof StringValue) {
+			return TLCGetStringValue(vidx);
         }
-        throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b" /* delete the space*/, "TLCGet",
-                "nonnegative integer", Value.ppr(vidx.toString()) });
+        throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "TLCGet",
+                "nonnegative integer", Values.ppr(vidx.toString()) });
     }
 
-    public static Value TLCSet(Value vidx, Value val)
+	private static final Value TLCGetStringValue(final Value vidx) {
+		final StringValue sv = (StringValue) vidx;
+		if (DIAMETER == sv.val) {
+			try {
+				return IntValue.gen(TLCGlobals.mainChecker.getProgress());
+			} catch (ArithmeticException e) {
+				throw new EvalException(EC.TLC_MODULE_OVERFLOW,
+						Long.toString(TLCGlobals.mainChecker.getProgress()));
+			} catch (NullPointerException npe) {
+				// TLCGlobals.mainChecker is null while the spec is parsed. A constant
+				// expression referencing one of the named values here would thus result in an
+				// NPE.
+				throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
+			}
+		} else if (DISTINCT == sv.val) {
+			try {
+				return IntValue.gen(Math.toIntExact(TLCGlobals.mainChecker.getDistinctStatesGenerated()));
+			} catch (ArithmeticException e) {
+				throw new EvalException(EC.TLC_MODULE_OVERFLOW,
+						Long.toString(TLCGlobals.mainChecker.getDistinctStatesGenerated()));
+			} catch (NullPointerException npe) {
+				throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
+			}
+		} else if (QUEUE == sv.val) {
+			try {
+				return IntValue.gen(Math.toIntExact(TLCGlobals.mainChecker.getStateQueueSize()));
+			} catch (ArithmeticException e) {
+				throw new EvalException(EC.TLC_MODULE_OVERFLOW,
+						Long.toString(TLCGlobals.mainChecker.getStateQueueSize()));
+			} catch (NullPointerException npe) {
+				throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
+			}
+		} else if (DURATION == sv.val) {
+			try {
+				final int duration = (int) ((System.currentTimeMillis() - startTime) / 1000L);
+				return IntValue.gen(Math.toIntExact(duration));
+			} catch (ArithmeticException e) {
+				throw new EvalException(EC.TLC_MODULE_OVERFLOW,
+						Long.toString(((System.currentTimeMillis() - startTime) / 1000L)));
+			}
+		} else if (LEVEL == sv.val) {
+			// Contrary to "diameter", "level" is not monotonically increasing. "diameter" is
+			// because it calls tlc2.tool.TLCTrace.getLevelForReporting(). "level" is the height
+			// stores as part of the state that is currently explored.
+			final TLCState currentState = IdThread.getCurrentState();
+			if (currentState != null) {
+				return IntValue.gen(currentState.getLevel());
+			} else {
+				if (TLCGlobals.mainChecker == null && TLCGlobals.simulator == null) {
+					// Be consistent with TLCGet("diameter") when TLCGet("level") appears as
+					// constant substitution.
+					throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
+				}
+				// Not an IdThread (hence currentState is null) implies that TLCGet("level") is
+				// evaluated as part of the initial predicate where the level - by definition -
+				// is 0 (see TLCState#level).
+				return IntValue.gen(TLCState.INIT_LEVEL - 1);
+			}
+		}
+		throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
+	}
+
+    public static Value  TLCSet(Value vidx, Value val)
     {
         if (vidx instanceof IntValue)
         {
@@ -138,50 +244,83 @@ public class TLC implements ValueConstants
             if (idx >= 0)
             {
                 Thread th = Thread.currentThread();
-                if (th instanceof Worker)
+                if (th instanceof IdThread)
                 {
-                    ((Worker) th).setLocalValue(idx, val);
-                } else
+                    ((IdThread) th).setLocalValue(idx, val);
+                } else if (TLCGlobals.mainChecker != null)
                 {
                     TLCGlobals.mainChecker.setAllValues(idx, val);
+                } else 
+                {	
+                    tlc2.TLCGlobals.simulator.setAllValues(idx, val);
                 }
-                return ValTrue;
+                return BoolValue.ValTrue;
             }
+        } else if (vidx instanceof StringValue) {
+        	final StringValue sv = (StringValue) vidx;
+        	if (EXIT == sv.val) {
+        		if (val == BoolValue.ValTrue) {
+        			TLCGlobals.mainChecker.stop();
+        		}
+        		return BoolValue.ValTrue;
+        	} else if (PAUSE == sv.val) {
+				// Provisional TLCSet("pause", TRUE) implementation that suspends BFS model
+				// checking until enter is pressed on system.in.  Either use in spec as:
+        		//   TLCSet("pause", guard)
+        		// but it might be better guarded by IfThenElse for performance reasons:
+        		//   IF guard THEN TLCSet("pause", TRUE) ELSE TRUE
+        		if (val == BoolValue.ValTrue && TLCGlobals.mainChecker instanceof ModelChecker) {
+        			final ModelChecker mc = (ModelChecker) TLCGlobals.mainChecker;
+        			synchronized (mc.theStateQueue) {
+        				ToolIO.out.println("Press enter to resume model checking.");
+        				ToolIO.out.flush();
+						try {
+							System.in.read();
+						} catch (IOException e) {
+							throw new EvalException(EC.GENERAL, e.getMessage());
+						}
+        			}
+        		}
+        		return BoolValue.ValTrue;
+        	}
         }
-
         throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "TLCSet", "nonnegative integer",
-                Value.ppr(vidx.toString()) });
+                Values.ppr(vidx.toString()) });
     }
 
     public static Value MakeFcn(Value d, Value e)
     {
-        Value[] dom = new Value[1];
-        Value[] vals = new Value[1];
+    	Value[] dom = new Value[1];
+    	Value[] vals = new Value[1];
         dom[0] = d;
         vals[0] = e;
         return new FcnRcdValue(dom, vals, true);
     }
 
+    /**
+     * f @@ g == [x \in (DOMAIN f) \cup (DOMAIN g) |->
+     *            IF x \in DOMAIN f THEN f[x] ELSE g[x]]
+     */
     public static Value CombineFcn(Value f1, Value f2)
     {
-        FcnRcdValue fcn1 = FcnRcdValue.convert(f1);
-        FcnRcdValue fcn2 = FcnRcdValue.convert(f2);
+        FcnRcdValue fcn1 = (FcnRcdValue) f1.toFcnRcd();
+        FcnRcdValue fcn2 = (FcnRcdValue) f2.toFcnRcd();
         if (fcn1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "@@", "function",
-                    Value.ppr(f1.toString()) });
+                    Values.ppr(f1.toString()) });
         }
         if (fcn2 == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "@@", "function",
-                    Value.ppr(f2.toString()) });
+                    Values.ppr(f2.toString()) });
         }
         ValueVec dom = new ValueVec();
         ValueVec vals = new ValueVec();
-        Value[] vals1 = fcn1.values;
-        Value[] vals2 = fcn2.values;
+        Value [] vals1 = fcn1.values;
+        Value [] vals2 = fcn2.values;
 
-        Value[] dom1 = fcn1.domain;
+        Value [] dom1 = fcn1.domain;
         if (dom1 == null)
         {
             IntervalValue intv1 = fcn1.intv;
@@ -200,13 +339,13 @@ public class TLC implements ValueConstants
         }
 
         int len1 = dom.size();
-        Value[] dom2 = fcn2.domain;
+        Value [] dom2 = fcn2.domain;
         if (dom2 == null)
         {
             IntervalValue intv2 = fcn2.intv;
             for (int i = intv2.low; i <= intv2.high; i++)
             {
-                Value val = IntValue.gen(i);
+            	Value val = IntValue.gen(i);
                 boolean found = false;
                 for (int j = 0; j < len1; j++)
                 {
@@ -219,14 +358,14 @@ public class TLC implements ValueConstants
                 if (!found)
                 {
                     dom.addElement(val);
-                    vals.addElement(vals2[i]);
+                    vals.addElement(vals2[i - intv2.low]);
                 }
             }
         } else
         {
             for (int i = 0; i < dom2.length; i++)
             {
-                Value val = dom2[i];
+            	Value  val = dom2[i];
                 boolean found = false;
                 for (int j = 0; j < len1; j++)
                 {
@@ -244,8 +383,8 @@ public class TLC implements ValueConstants
             }
         }
 
-        Value[] domain = new Value[dom.size()];
-        Value[] values = new Value[dom.size()];
+        Value [] domain = new Value[dom.size()];
+        Value [] values = new Value[dom.size()];
         for (int i = 0; i < domain.length; i++)
         {
             domain[i] = dom.elementAt(i);
@@ -256,24 +395,24 @@ public class TLC implements ValueConstants
 
     public static Value SortSeq(Value s, Value cmp)
     {
-        TupleValue seq = TupleValue.convert(s);
+        TupleValue seq = (TupleValue) s.toTuple();
         if (seq == null)
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SortSeq", "natural number",
-                    Value.ppr(s.toString()) });
+                    Values.ppr(s.toString()) });
         }
         if (!(cmp instanceof Applicable))
         {
             throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SortSeq", "function",
-                    Value.ppr(cmp.toString()) });
+                    Values.ppr(cmp.toString()) });
         }
         Applicable fcmp = (Applicable) cmp;
-        Value[] elems = seq.elems;
+        Value [] elems = seq.elems;
         int len = elems.length;
         if (len == 0)
             return seq;
-        Value[] args = new Value[2];
-        Value[] newElems = new Value[len];
+        Value [] args = new Value[2];
+        Value [] newElems = new Value[len];
         newElems[0] = elems[0];
         for (int i = 1; i < len; i++)
         {
@@ -293,51 +432,51 @@ public class TLC implements ValueConstants
         return new TupleValue(newElems);
     }
 
-    private static boolean compare(Applicable fcmp, Value[] args)
+    private static boolean compare(Applicable fcmp, Value [] args)
     {
-        Value res = fcmp.apply(args, EvalControl.Clear);
-        if (res instanceof BoolValue)
+        Value  res = fcmp.apply(args, EvalControl.Clear);
+        if (res instanceof IBoolValue)
         {
             return ((BoolValue) res).val;
         }
-        throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SortSeq", "noolean function",
-                Value.ppr(res.toString()) });
+        throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SortSeq", "boolean function",
+                Values.ppr(res.toString()) });
     }
 
+    // Returns a set of size n! where n = |s|.
     public static Value Permutations(Value s)
     {
-        SetEnumValue s1 = SetEnumValue.convert(s);
+        SetEnumValue s1 = (SetEnumValue) s.toSetEnum();
         if (s1 == null)
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "Permutations",
-                    "a finite set", Value.ppr(s.toString()) });
+                    "a finite set", Values.ppr(s.toString()) });
         }
         s1.normalize();
         ValueVec elems = s1.elems;
         int len = elems.size();
         if (len == 0)
         {
-            Value[] elems1 = { EmptyFcn };
+        	Value[] elems1 = { FcnRcdValue.EmptyFcn };
             return new SetEnumValue(elems1, true);
         }
 
-        Value[] domain = new Value[len];
-        for (int i = 0; i < len; i++)
-        {
-            domain[i] = elems.elementAt(i);
-        }
+        int factorial = 1;
+        Value [] domain = new Value[len];
         int[] idxArray = new int[len];
         boolean[] inUse = new boolean[len];
         for (int i = 0; i < len; i++)
         {
+            domain[i] = elems.elementAt(i);
             idxArray[i] = i;
             inUse[i] = true;
+            factorial = factorial * (i + 1);
         }
 
-        ValueVec fcns = new ValueVec();
+        ValueVec fcns = new ValueVec(factorial);
         _done: while (true)
         {
-            Value[] vals = new Value[len];
+        	Value [] vals = new Value[len];
             for (int i = 0; i < len; i++)
             {
                 vals[i] = domain[idxArray[i]];
@@ -358,10 +497,12 @@ public class TLC implements ValueConstants
                         break;
                     }
                 }
-                if (found)
+                if (found) {
                     break;
-                if (i == 0)
+                }
+                if (i == 0) {
                     break _done;
+                }
                 inUse[idxArray[i]] = false;
             }
             for (int j = i + 1; j < len; j++)
@@ -380,22 +521,22 @@ public class TLC implements ValueConstants
         return new SetEnumValue(fcns, false);
     }
 
-    public static Value RandomElement(Value val)
+    public static Value RandomElement(Value  val)
     {
         switch (val.getKind()) {
         case SETOFFCNSVALUE: {
             SetOfFcnsValue sfv = (SetOfFcnsValue) val;
             sfv.normalize();
-            SetEnumValue domSet = SetEnumValue.convert(sfv.domain);
+            SetEnumValue domSet = (SetEnumValue) sfv.domain.toSetEnum();
             if (domSet == null)
             {
                 throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "RandomElement",
-                        "a finite set", Value.ppr(val.toString()) });
+                        "a finite set", Values.ppr(val.toString()) });
             }
             domSet.normalize();
             ValueVec elems = domSet.elems;
-            Value[] dom = new Value[elems.size()];
-            Value[] vals = new Value[elems.size()];
+            Value [] dom = new Value[elems.size()];
+            Value [] vals = new Value[elems.size()];
             for (int i = 0; i < dom.length; i++)
             {
                 dom[i] = elems.elementAt(i);
@@ -406,7 +547,7 @@ public class TLC implements ValueConstants
         case SETOFRCDSVALUE: {
             SetOfRcdsValue srv = (SetOfRcdsValue) val;
             srv.normalize();
-            Value[] vals = new Value[srv.names.length];
+            Value [] vals = new Value[srv.names.length];
             for (int i = 0; i < vals.length; i++)
             {
                 vals[i] = RandomElement(srv.values[i]);
@@ -416,7 +557,7 @@ public class TLC implements ValueConstants
         case SETOFTUPLESVALUE: {
             SetOfTuplesValue stv = (SetOfTuplesValue) val;
             stv.normalize();
-            Value[] vals = new Value[stv.sets.length];
+            Value [] vals = new Value[stv.sets.length];
             for (int i = 0; i < vals.length; i++)
             {
                 vals[i] = RandomElement(stv.sets[i]);
@@ -424,15 +565,13 @@ public class TLC implements ValueConstants
             return new TupleValue(vals);
         }
         default: {
-            SetEnumValue enumVal = SetEnumValue.convert(val);
+            SetEnumValue enumVal = (SetEnumValue) val.toSetEnum();
             if (enumVal == null)
             {
                 throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "RandomElement",
-                        "a finite set", Value.ppr(val.toString()) });
+                        "a finite set", Values.ppr(val.toString()) });
             }
-            int sz = enumVal.size();
-            int index = (int) Math.floor(rng.nextDouble() * sz);
-            return enumVal.elems.elementAt(index);
+            return enumVal.randomElement();
         }
         }
     }
@@ -450,12 +589,12 @@ public class TLC implements ValueConstants
      * @param val
      * @return
      */
-    public static Value TLCEval(Value val) {
-        Value evalVal = SetEnumValue.convert(val);
+    public static Value  TLCEval(Value val) {
+        Value  evalVal = val.toSetEnum();
         if (evalVal != null) {
             return evalVal;
         }
-        evalVal = FcnRcdValue.convert(val);
+        evalVal = val.toFcnRcd();
         if (evalVal != null) {
             return evalVal;
         }
@@ -464,7 +603,7 @@ public class TLC implements ValueConstants
     }
     /*
     public static Value FApply(Value f, Value op, Value base) {
-      FcnRcdValue fcn = FcnRcdValue.convert(f);
+      FcnRcdValue fcn = f.toFcnRcd();
       if (fcn == null) {
         String msg = "The first argument of FApply must be a " +
     "function with finite domain, but instead it is\n" +
@@ -487,7 +626,7 @@ public class TLC implements ValueConstants
     }
     
     public static Value FSum(Value f) {
-      FcnRcdValue fcn = FcnRcdValue.convert(f);
+      FcnRcdValue fcn = f.toFcnRcd();
       if (fcn == null) {
         String msg = "The argument of FSum should be a function; " +
     "but instead it is:\n" + Value.ppr(f.toString());
diff --git a/tlatools/src/tlc2/module/TransitiveClosure.java b/tlatools/src/tlc2/module/TransitiveClosure.java
index cd10f0cf0f99a36b6022931ff39b7f3d91cb0749..49e53efa03be6c388127e369e5c27dffb8490dac 100644
--- a/tlatools/src/tlc2/module/TransitiveClosure.java
+++ b/tlatools/src/tlc2/module/TransitiveClosure.java
@@ -10,16 +10,18 @@ import java.util.Hashtable;
 import tlc2.output.EC;
 import tlc2.tool.EvalException;
 import tlc2.util.Vect;
-import tlc2.value.Enumerable;
-import tlc2.value.SetEnumValue;
-import tlc2.value.TupleValue;
-import tlc2.value.Value;
 import tlc2.value.ValueConstants;
-import tlc2.value.ValueEnumeration;
-import tlc2.value.ValueVec;
+import tlc2.value.Values;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueEnumeration;
+import tlc2.value.impl.ValueVec;
 
 public class TransitiveClosure implements ValueConstants
 {
+	public static final long serialVersionUID = 20160822L;
 
     /* Implement the Warshall algorithm for transitive closure. */
     public static Value Warshall(Value rel)
@@ -27,26 +29,26 @@ public class TransitiveClosure implements ValueConstants
         if (!(rel instanceof Enumerable))
         {
             throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "TransitiveClosure",
-                    "an enumerable set", Value.ppr(rel.toString()) });
+                    "an enumerable set", Values.ppr(rel.toString()) });
         }
         int maxLen = 2 * rel.size();
         boolean[][] matrix = new boolean[maxLen][maxLen];
         ValueEnumeration elems = ((Enumerable) rel).elements();
-        Vect elemList = new Vect();
-        Hashtable fps = new Hashtable();
+        Vect<Value> elemList = new Vect<>();
+        Hashtable<Value, Integer> fps = new Hashtable<>();
         int cnt = 0;
         Value elem = null;
         while ((elem = elems.nextElement()) != null)
         {
-            TupleValue tv = TupleValue.convert(elem);
+            TupleValue tv = (TupleValue) elem.toTuple();
             if (tv == null || tv.size() != 2)
             {
-                throw new EvalException(EC.TLC_MODULE_TRANSITIVE_CLOSURE, Value.ppr(elem.toString()));
+                throw new EvalException(EC.TLC_MODULE_TRANSITIVE_CLOSURE, Values.ppr(elem.toString()));
             }
             Value elem1 = tv.elems[0];
             Value elem2 = tv.elems[1];
             int num1 = cnt;
-            Integer num = (Integer) fps.get(elem1);
+            Integer num = fps.get(elem1);
             if (num == null)
             {
                 fps.put(elem1, new Integer(cnt));
@@ -57,7 +59,7 @@ public class TransitiveClosure implements ValueConstants
                 num1 = num.intValue();
             }
             int num2 = cnt;
-            num = (Integer) fps.get(elem2);
+            num = fps.get(elem2);
             if (num == null)
             {
                 fps.put(elem2, new Integer(cnt));
@@ -92,9 +94,9 @@ public class TransitiveClosure implements ValueConstants
             {
                 if (matrix[i][j])
                 {
-                    Value elem1 = (Value) elemList.elementAt(i);
-                    Value elem2 = (Value) elemList.elementAt(j);
-                    Value newElem = new TupleValue(elem1, elem2);
+                	Value elem1 = elemList.elementAt(i);
+                	Value elem2 = elemList.elementAt(j);
+                	Value newElem = new TupleValue(elem1, elem2);
                     newElems.addElement(newElem);
                 }
             }
diff --git a/tlatools/src/tlc2/output/AbstractCopier.java b/tlatools/src/tlc2/output/AbstractCopier.java
new file mode 100644
index 0000000000000000000000000000000000000000..2289cb54b1dc1c9c6858f86b99442716e7623a16
--- /dev/null
+++ b/tlatools/src/tlc2/output/AbstractCopier.java
@@ -0,0 +1,93 @@
+package tlc2.output;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * This is the abstract copying class for the generation of SpecTE file assets.
+ */
+abstract class AbstractCopier {
+	protected final String originalModuleName;
+	protected final String newModuleName;
+
+	protected final File sourceDirectory;
+	
+	protected File sourceFile;
+	protected File destinationFile;
+	
+	AbstractCopier(final String originalName, final String newName, final File sourceLocation) {
+		originalModuleName = originalName;
+		newModuleName = newName;
+		
+		sourceDirectory = sourceLocation;
+	}
+	
+	/**
+	 * @return the extension including the prefixed '.'
+	 */
+	protected abstract String getFileExtension();
+
+	/**
+	 * @param writer
+	 * @param originalLine
+	 * @param lineNumber this is 1-based (e.g the first line of the file read will be lineNumber == 1)
+	 * @throws IOException
+	 */
+	protected abstract void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber)
+			throws IOException;
+	
+	/**
+	 * Overriders will receive this notification after the final line of input has
+	 * been consumed and {@link #copyLine(BufferedWriter, String, int)} has been
+	 * invoked on that line.
+	 */
+	protected void allInputHasBeenConsumed(final BufferedWriter writer) throws IOException { }
+	
+	/**
+	 * Overriders will receive this notification after the reader and writer
+	 * instances employed in the copy haven been closed but before
+	 * {@link #copy(Reader, Writer)} exits.
+	 */
+	protected void copyHasFinished() throws IOException { }
+
+	/**
+	 * @return null until {@link #copy()} has been invoked, thereafter the location of the destination file.
+	 */
+	public File getDestinationFile() {
+		return destinationFile;
+	}
+	
+	public final void copy() throws IOException {
+		final String extension = getFileExtension();
+		
+		sourceFile = new File(sourceDirectory, (originalModuleName + extension));
+		destinationFile = new File(sourceDirectory, (newModuleName + extension));
+		
+		copy(new FileReader(sourceFile), new FileWriter(destinationFile));
+	}
+	
+	// This extra level of abstraction is done for unit tests
+	protected void copy(final Reader reader, final Writer writer) throws IOException {
+		try (final BufferedReader br = new BufferedReader(reader)) {
+			try (final BufferedWriter bw = new BufferedWriter(writer)) {
+				String line;
+				int lineCount = 1;	// staying 1-based since Location is as well and our subclasses use Location instances
+				while ((line = br.readLine()) != null) {
+					copyLine(bw, line, lineCount);
+
+					lineCount++;
+				}
+				
+				allInputHasBeenConsumed(bw);
+			}
+		}
+		
+		copyHasFinished();
+	}
+}
diff --git a/tlatools/src/tlc2/output/AbstractSpecWriter.java b/tlatools/src/tlc2/output/AbstractSpecWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb6e81936a8fe2470934743f536ddcb55b3d24af
--- /dev/null
+++ b/tlatools/src/tlc2/output/AbstractSpecWriter.java
@@ -0,0 +1,499 @@
+package tlc2.output;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+
+import tlc2.model.Assignment;
+import tlc2.model.TypedSet;
+import util.TLAConstants;
+
+/**
+ * This is the abstract class of spec writers; there's no reason this need be abstract, it is more a semantic
+ * 	denoting that within our code base, it acts as only a superclass to other employed classes.
+ */
+public abstract class AbstractSpecWriter {
+    /**
+     * Assigns a right side to a label using an id generated from given schema
+	 * @param tlaBuffer the buffer into which the TLA code will be placed
+	 * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed
+     * @param constant, constant containing the values
+     * @param schema schema to generate the Id
+     * @return generated id
+     */
+	public static String addArrowAssignmentToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final Assignment constant, final String schema) {
+		// constant instantiation
+		// to .cfg : foo <- <id>
+		// to _MC.tla : <id>(a, b, c)==
+		// <expression>
+		final String id = SpecWriterUtilities.getValidIdentifier(schema);
+		tlaBuffer.append(constant.getParametrizedLabel(id)).append(TLAConstants.DEFINES).append(TLAConstants.CR);
+		tlaBuffer.append(constant.getRight()).append(TLAConstants.CR);
+		
+		if (cfgBuffer != null) {
+			cfgBuffer.append(TLAConstants.KeyWords.CONSTANT).append(TLAConstants.CR);
+			cfgBuffer.append(constant.getLabel()).append(TLAConstants.ARROW).append(id).append(TLAConstants.CR);
+		}
+		
+		return id;
+	}
+
+	
+	/**
+	 * Subclasses may implement this interface in order to invoke {@link #writeFiles(ContentWriter)} for cases
+	 * in which the subclass is has file handles which are not suitable to being converted and used in
+	 * {@link #writeStreamToFile(InputStream, boolean)}
+	 */
+    protected interface ContentWriter {
+    	void writeStreamToFile(final InputStream inputStream, final boolean forTLAFile) throws IOException;
+    }
+    
+
+    protected static final String CLOSING_SEP = TLAConstants.CR + TLAConstants.SEP + TLAConstants.CR;
+	
+	
+    protected final StringBuilder tlaBuffer;
+    protected final StringBuilder cfgBuffer;
+    
+    /**
+	 * @param generateConfigurationContent if true, configuration file (.cfg)
+	 *                                     accompanying the TLA module content will
+	 *                                     be generated
+	 */
+    protected AbstractSpecWriter(final boolean generateConfigurationContent) {
+    	tlaBuffer = new StringBuilder();
+    	cfgBuffer = generateConfigurationContent ? new StringBuilder() : null;
+    }
+
+    /**
+     * Subclasses may override this to set their own spin on a closing tag.
+     * 
+     * @return the String content of a module closing tag.
+     */
+    protected String getTLAModuleClosingTag() {
+    	final StringBuilder sb = SpecWriterUtilities.getModuleClosingTag(77, false);
+    	
+    	return sb.toString();
+    }
+    
+    /**
+     * Provided for test code
+     * @param tla if non-null the content will be appended to the tlaBuffer
+     * @param cfg if non-null the content will be appended to the cfgBuffer
+     */
+    void appendContentToBuffers(final String tla, final String cfg) {
+    	if (tla != null) {
+    		tlaBuffer.append(tla);
+    	}
+    	
+    	if (cfg != null) {
+    		cfgBuffer.append(cfg);
+    	}
+    }
+    
+    /**
+     * Write the buffers to files.
+     * 
+     * @param tlaFile if null, nothing is written
+     * @param cfgFile if null, nothing is written
+     * @throws IOException
+     */
+	public void writeFiles(final File tlaFile, final File cfgFile) throws IOException {
+		final ContentWriter cw = (inputStream, forTLAFile) -> {
+			final File f = (forTLAFile ? tlaFile: cfgFile);
+			
+			if (f != null) {
+		        Files.copy(inputStream, f.toPath(), StandardCopyOption.REPLACE_EXISTING);
+			}
+		};
+		
+		writeFiles(cw);
+	}
+	
+	/**
+	 * Subclasses may use to this to provide a content writer which deals with output other than using {@code java.io}
+	 * directly.
+	 * 
+	 * @param contentWriter
+	 * @throws IOException
+	 */
+	protected void writeFiles(final ContentWriter contentWriter) throws IOException {
+        tlaBuffer.append(getTLAModuleClosingTag());
+        final ByteArrayInputStream tlaBAIS = new ByteArrayInputStream(tlaBuffer.toString().getBytes());
+        contentWriter.writeStreamToFile(tlaBAIS, true);
+        
+		if (cfgBuffer != null) {
+			cfgBuffer.append(SpecWriterUtilities.getGeneratedTimeStampCommentLine());
+	        final ByteArrayInputStream configurationBAIS = new ByteArrayInputStream(cfgBuffer.toString().getBytes());
+	        contentWriter.writeStreamToFile(configurationBAIS, false);
+		}
+	}
+
+	/**
+	 * Add file header, which consists of the module-beginning ----- MODULE ... ----
+	 * line and the EXTENDS statement.
+	 * 
+	 * @param moduleFilename
+	 * @param extendedModuleName
+	 */
+	public void addPrimer(final String moduleFilename, final String extendedModuleName) {
+		tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(moduleFilename,
+																	   new String[] { extendedModuleName, "TLC" }));
+	}
+
+	/**
+	 * Add spec definition
+	 * 
+	 * @param specDefinition
+	 * @param attributeName
+	 */
+	public void addSpecDefinition(final String[] specDefinition, final String attributeName) {
+		if (cfgBuffer != null) {
+			cfgBuffer.append(TLAConstants.KeyWords.SPECIFICATION).append(TLAConstants.SPACE);
+			cfgBuffer.append(specDefinition[0]).append(TLAConstants.CR);
+		}
+		
+		tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.SPECIFICATION).append(TLAConstants.SPACE);
+		tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR);
+		tlaBuffer.append(specDefinition[1]).append(CLOSING_SEP);
+	}
+	
+	/**
+	 * Add an init-next pair of definitions.
+	 * 
+	 * @param initDefinition the 0th element is the init definition name, the 1rst is the entire definition
+	 * @param nextDefinition the 0th element is the next definition name, the 1rst is the entire definition
+	 * @param initAttributeName
+	 * @param nextAttributeName
+	 */
+	public void addInitNextDefinitions(final String[] initDefinition, final String[] nextDefinition,
+									   final String initAttributeName, final String nextAttributeName) {
+		if (cfgBuffer != null) {
+			cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INIT).append(" definition");
+			cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.INIT).append(TLAConstants.CR);
+			cfgBuffer.append(initDefinition[0]).append(TLAConstants.CR);
+		}
+
+		tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INIT).append(" definition ");
+		tlaBuffer.append(TLAConstants.ATTRIBUTE).append(initAttributeName).append(TLAConstants.CR);
+		tlaBuffer.append(initDefinition[1]).append(TLAConstants.CR);
+
+		
+		if (cfgBuffer != null) {
+			cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.NEXT).append(" definition");
+			cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.NEXT).append(TLAConstants.CR);
+			cfgBuffer.append(nextDefinition[0]).append(TLAConstants.CR);
+		}
+
+		tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.NEXT).append(" definition ");
+		tlaBuffer.append(TLAConstants.ATTRIBUTE).append(nextAttributeName).append(TLAConstants.CR);
+		tlaBuffer.append(nextDefinition[1]).append(TLAConstants.CR);
+	}
+
+    /**
+     * Documentation by SZ: Add constants declarations. 
+     * 
+     * On 17 March 2012, LL split the original addConstants method
+     * into the current one plus the addConstantsBis method.  As explained in Bugzilla Bug 280,
+     * this was to allow the user definitions added on the Advanced Model page to appear between
+     * the CONSTANT declarations for model values and the definitions of the expressions that 
+     * instantiate CONSTANT parameters.  (This allows symbols defined in those user definitions to
+     * appear in the expressions instantiated for CONSTANT parameters.)
+     * 
+     * See the use of these two methods in TLCModelLaunchDelegate.buildForLaunch for a description
+     * of what these methods do.
+     * 
+     * @param constants
+     * @param modelValues
+     * @param attributeConstants
+     * @param attributeMVs
+     */
+	public void addConstants(final List<Assignment> constants, final TypedSet modelValues, final String attributeConstants,
+			final String attributeMVs) {
+        // add declarations for model values introduced on Advanced Model page.
+        addMVTypedSet(modelValues, "MV CONSTANT declarations ", attributeMVs);
+
+        final ArrayList<String> symmetrySets = new ArrayList<>();
+
+        Assignment constant;
+        // first run for all the declarations
+		for (int i = 0; i < constants.size(); i++) {
+			constant = constants.get(i);
+			if (constant.isModelValue()) {
+				if (constant.isSetOfModelValues()) {
+					// set model values
+					addMVTypedSet(constant.getSetOfModelValues(), "MV CONSTANT declarations", attributeConstants);
+				}
+			}
+		}
+
+        // now all the definitions
+		for (int i = 0; i < constants.size(); i++) {
+			constant = constants.get(i);
+			if (constant.isModelValue()) {
+				if (constant.isSetOfModelValues()) {
+                    // set model values
+					if (cfgBuffer != null) {
+						cfgBuffer.append(TLAConstants.COMMENT).append("MV CONSTANT definitions").append(TLAConstants.CR);
+					}
+                    tlaBuffer.append(TLAConstants.COMMENT).append("MV CONSTANT definitions " + constant.getLeft());
+                    tlaBuffer.append(TLAConstants.CR);
+
+                    final String id = addArrowAssignment(constant, TLAConstants.Schemes.CONSTANT_SCHEME);
+					if (constant.isSymmetricalSet()) {
+						symmetrySets.add(id);
+					}
+                    tlaBuffer.append(TLAConstants.SEP).append(TLAConstants.CR).append(TLAConstants.CR);
+				} else if (cfgBuffer != null) {
+					cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.CONSTANT)
+							 .append(" declarations").append(TLAConstants.CR);
+					// model value assignment
+					// to .cfg : foo = foo
+					// to _MC.tla : <nothing>, since the constant is already defined in one of the
+					// spec modules
+					cfgBuffer.append(TLAConstants.KeyWords.CONSTANT).append(TLAConstants.SPACE).append(constant.getLabel());
+					cfgBuffer.append(TLAConstants.EQ).append(constant.getRight()).append(TLAConstants.CR);
+				}
+			} else {
+//                // simple constant value assignment
+//                cfgBuffer.append(COMMENT).append("CONSTANT definitions").append(CR);
+//
+//                tlaBuffer.append(COMMENT).append("CONSTANT definitions ").append(ATTRIBUTE).append(attributeConstants)
+//                        .append(INDEX).append(i).append(constant.getLeft()).append(CR);
+//                addArrowAssignment(constant, CONSTANT_SCHEME);
+//                tlaBuffer.append(SEP).append(CR).append(CR);
+            }
+        }
+
+        // symmetry
+		if (!symmetrySets.isEmpty()) {
+			final String label = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.SYMMETRY_SCHEME);
+
+			tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.SYMMETRY)
+					 .append(" definition").append(TLAConstants.CR);
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.SYMMETRY)
+						 .append(" definition").append(TLAConstants.CR);
+			}
+
+			tlaBuffer.append(label).append(TLAConstants.DEFINES).append(TLAConstants.CR);
+			// symmetric model value sets added
+			for (int i = 0; i < symmetrySets.size(); i++) {
+				tlaBuffer.append(TLAConstants.BuiltInOperators.PERMUTATIONS).append("(");
+				tlaBuffer.append(symmetrySets.get(i)).append(")");
+				if (i != symmetrySets.size() - 1) {
+					tlaBuffer.append(' ').append(TLAConstants.KeyWords.UNION).append(' ');
+				}
+			}
+
+			tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.KeyWords.SYMMETRY).append(TLAConstants.SPACE)
+						 .append(label).append(TLAConstants.CR);
+			}
+		}
+    }
+
+	public void addConstantsBis(final List<Assignment> constants, final String attributeConstants) {
+		for (int i = 0; i < constants.size(); i++) {
+			final Assignment constant = constants.get(i);
+			if (! constant.isModelValue()) {
+				if (cfgBuffer != null) {
+					// simple constant value assignment
+					cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.CONSTANT).append(" definitions");
+					cfgBuffer.append(TLAConstants.CR);
+				}
+
+                tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.CONSTANT).append(" definitions ");
+                tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeConstants).append(TLAConstants.COLON);
+                tlaBuffer.append(i).append(constant.getLeft()).append(TLAConstants.CR);
+                
+                addArrowAssignment(constant, TLAConstants.Schemes.CONSTANT_SCHEME);
+                
+                tlaBuffer.append(TLAConstants.SEP).append(TLAConstants.CR).append(TLAConstants.CR);
+            }
+        }
+    }
+
+    /**
+     * Adds the ASSUME PrintT statement and identifier for the constant expression
+     * evaluation. The MC.tla file will contain:
+     * 
+     * const_expr_1232141234123 ==
+     * expression
+     * -----
+     * ASSUME PrintT(<<"$!@$!@$!@$!@$!", const_expr_1232141234123>>)
+     * 
+     * See the comments in the method for an explanation of defining
+     * an identifier.
+     * 
+     * @param expression
+     * @param attributeName
+     */
+	public void addConstantExpressionEvaluation(final String expression, final String attributeName) {
+		if (expression.trim().length() != 0) {
+			/*
+			 * Identifier definition We define an identifier for more sensible error
+			 * messages For example, if the user enters "1+" into the constant expression
+			 * field and "1+" is placed as the second element of the tuple that is the
+			 * argument for PrintT(), then the parse error would be something like
+			 * "Encountered >>" which would be mysterious to the user. With an identifier
+			 * defined, the message says "Encountered ----" which is the separator after
+			 * each section in TLA buffer. This error message is equally mysterious, but at
+			 * least it is the same message that would appear were the same error present in
+			 * another section in the model editor. We can potentially replace such messages
+			 * with something more sensible in the future in the appendError() method in
+			 * TLCErrorView.
+			 */
+			final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.CONSTANTEXPR_SCHEME);
+			tlaBuffer.append(TLAConstants.COMMENT).append("Constant expression definition ");
+			tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR);
+			tlaBuffer.append(id).append(TLAConstants.DEFINES).append(TLAConstants.CR).append(expression);
+			tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+
+			// ASSUME PrintT(<<"$!@$!@$!@$!@$!", const_expr_23423232>>) statement
+			// The "$!@$!@$!@$!@$!" allows the toolbox to identify the
+			// value of the constant expression in the TLC output
+			tlaBuffer.append(TLAConstants.COMMENT).append("Constant expression ASSUME statement ");
+			tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR);
+			tlaBuffer.append("ASSUME PrintT(").append(TLAConstants.BEGIN_TUPLE);
+			tlaBuffer.append(TLAConstants.CONSTANT_EXPRESSION_EVAL_IDENTIFIER).append(TLAConstants.COMMA).append(id);
+			tlaBuffer.append(TLAConstants.END_TUPLE).append(")").append(CLOSING_SEP).append(TLAConstants.CR);
+		}
+	}
+
+    /**
+     * New definitions are added to the TLA buffer only
+     * @param defnitions
+     * @param attributeName
+     */
+	public void addNewDefinitions(final String definitions, final String attributeName) {
+		if (definitions.trim().length() == 0) {
+			return;
+		}
+		tlaBuffer.append(TLAConstants.COMMENT).append("New definitions ");
+		tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR);
+		tlaBuffer.append(definitions).append(CLOSING_SEP);
+	}
+
+	/**
+	 * Convenience method to call {@link #addFormulaList(List, String, String)} wrapping the {@code element} parameter.
+	 * 
+	 * @param element
+	 * @param keyword
+	 * @param attributeName
+	 */
+    public void addFormulaList(final String element, final String keyword, final String attributeName) {
+    	final List<String[]> elements = new ArrayList<>(1);
+    	elements.add(new String[] {element, TLAConstants.EMPTY_STRING});
+    	addFormulaList(elements, keyword, attributeName);
+    }
+    
+    /**
+	 * Puts (String[])element[0] to configuration buffer; if this instance was
+	 * appropriately constructed, and element[1] to the TLA buffer; if element[2]
+	 * present, uses it as index.
+	 * 
+	 * @param elements      a list of String[] elements
+	 * @param keyword       the keyword to be used in the CFG buffer
+	 * @param attributeName the name of the attribute in the TLA buffer
+	 */
+	public void addFormulaList(final List<String[]> elements, final String keyword, final String attributeName) {
+		if (elements.isEmpty()) {
+			return;
+		}
+		
+		if (cfgBuffer != null) {
+			cfgBuffer.append(TLAConstants.COMMENT).append(keyword + " definition").append(TLAConstants.CR);
+			cfgBuffer.append(keyword).append(TLAConstants.CR);
+		}
+
+		for (int i = 0; i < elements.size(); i++) {
+			final String[] element = elements.get(i);
+			
+			if (cfgBuffer != null) {
+				cfgBuffer.append(element[0]).append(TLAConstants.CR);
+			}
+			
+			// when a definition in the root module is overridden as a model value
+			// there is nothing to add to the MC.tla file so, we do not do the following
+			if (!element[1].equals(TLAConstants.EMPTY_STRING)) {
+				tlaBuffer.append(TLAConstants.COMMENT).append(keyword + " definition ").append(TLAConstants.ATTRIBUTE);
+				tlaBuffer.append(attributeName).append(TLAConstants.COLON).append(element.length > 2 ? element[2] : i);
+				tlaBuffer.append(TLAConstants.CR).append(element[1]).append(CLOSING_SEP);
+			}
+		}
+	}
+
+    /**
+     * Add the view definition
+     * @param viewString the string that the user enters into the view field
+     * @param attributeName the attribute name of the view field
+     */
+	public void addView(final String viewString, final String attributeName) {
+		if (viewString.trim().length() != 0) {
+			final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.VIEW_SCHEME);
+			
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.COMMENT).append("VIEW definition").append(TLAConstants.CR);
+				cfgBuffer.append("VIEW").append(TLAConstants.CR).append(id).append(TLAConstants.CR);
+			}
+
+			tlaBuffer.append(TLAConstants.COMMENT).append("VIEW definition ").append(TLAConstants.ATTRIBUTE);
+			tlaBuffer.append(attributeName).append(TLAConstants.CR);
+			tlaBuffer.append(id).append(TLAConstants.DEFINES).append(TLAConstants.CR).append(viewString);
+			tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+		}
+	}
+	
+    /**
+     * Assigns a right side to a label using an id generated from given schema
+     * @param constant, constant containing the values
+     * @param schema schema to generate the Id
+     * @return generated id
+     */
+	public String addArrowAssignment(final Assignment constant, final String schema) {
+		return addArrowAssignmentToBuffers(tlaBuffer, cfgBuffer, constant, schema);
+	}
+
+    /**
+     * Creates a serial version of an MV set in both files
+     * @param mvSet typed set containing the model values
+     * @param comment a comment to put before the declarations, null and empty strings are OK
+     */
+	public void addMVTypedSet(final TypedSet mvSet, final String comment, final String attributeName) {
+		if (mvSet.getValueCount() != 0) {
+            // create a declaration line
+            // CONSTANTS
+            // a, b, c
+			if ((comment != null) && (comment.length() != 0)) {
+                tlaBuffer.append(TLAConstants.COMMENT).append(comment).append(TLAConstants.ATTRIBUTE);
+                tlaBuffer.append(attributeName).append(TLAConstants.CR);
+            }
+            tlaBuffer.append(TLAConstants.KeyWords.CONSTANTS).append(TLAConstants.CR).append(mvSet.toStringWithoutBraces());
+            tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+
+			if (cfgBuffer != null) {
+				// create MV assignments
+				// a = a
+				// b = b
+				// c = c
+				if ((comment != null) && (comment.length() != 0)) {
+					cfgBuffer.append(TLAConstants.COMMENT).append(comment).append(TLAConstants.CR);
+				}
+				cfgBuffer.append(TLAConstants.KeyWords.CONSTANTS).append(TLAConstants.CR);
+				for (int i = 0; i < mvSet.getValueCount(); i++) {
+					final String mv = mvSet.getValue(i);
+					cfgBuffer.append(mv).append(TLAConstants.EQ).append(mv).append(TLAConstants.CR);
+				}
+			}
+        }
+    }
+}
diff --git a/tlatools/src/tlc2/output/AbstractTLACopier.java b/tlatools/src/tlc2/output/AbstractTLACopier.java
new file mode 100644
index 0000000000000000000000000000000000000000..38c3a918fb7b896443584fa695032f209f089a59
--- /dev/null
+++ b/tlatools/src/tlc2/output/AbstractTLACopier.java
@@ -0,0 +1,35 @@
+package tlc2.output;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+import util.TLAConstants;
+
+/**
+ * The abstract superclass for classes which copy a TLA file.
+ */
+abstract class AbstractTLACopier extends AbstractCopier {
+	protected static final Pattern CLOSING_BODY_PATTERN = Pattern.compile(TLAConstants.MODULE_CLOSING_REGEX);
+	
+
+	protected final Pattern modulePattern;
+	
+	protected boolean inBody;
+	
+	AbstractTLACopier(final String originalName, final String newName, final File sourceLocation) {
+		super(originalName, newName, sourceLocation);
+
+		final String regex = TLAConstants.MODULE_OPENING_PREFIX_REGEX + originalModuleName;
+		modulePattern = Pattern.compile(regex);
+		
+		inBody = false;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected String getFileExtension() {
+		return TLAConstants.Files.TLA_EXTENSION;
+	}
+}
diff --git a/tlatools/src/tlc2/output/CFGCopier.java b/tlatools/src/tlc2/output/CFGCopier.java
new file mode 100644
index 0000000000000000000000000000000000000000..89f80c77dedbff5b22d8cd65a3e16df5eda3eb35
--- /dev/null
+++ b/tlatools/src/tlc2/output/CFGCopier.java
@@ -0,0 +1,100 @@
+package tlc2.output;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import util.TLAConstants;
+
+/**
+ * This class which copies a CFG file, mutating it appropriately to become a SpecTE CFG file.  This is becoming
+ * 	pretty brittle; we would probably be better served at this point to work on the {@link ModelConfig} class
+ *  and make it able to write itself to a stream (and from that point forward, just modify an instance of that
+ *  directly.) (TODO)
+ */
+public class CFGCopier extends AbstractCopier {
+	private static final String SPECIFICATION_COMMENT_REGEX = "^\\\\\\* " + TLAConstants.KeyWords.SPECIFICATION + ".*$?";
+	private static final Pattern SPECIFICATION_COMMENT_PATTERN = Pattern.compile(SPECIFICATION_COMMENT_REGEX);
+	private static final String INIT_COMMENT_REGEX = "^\\\\\\* " + TLAConstants.KeyWords.INIT + ".*$?";
+	private static final Pattern INIT_COMMENT_PATTERN = Pattern.compile(INIT_COMMENT_REGEX);
+	private static final String NEXT_COMMENT_REGEX = "^\\\\\\* " + TLAConstants.KeyWords.NEXT + ".*$?";
+	private static final Pattern NEXT_COMMENT_PATTERN = Pattern.compile(NEXT_COMMENT_REGEX);
+
+	
+	private boolean skipNextLine;
+	
+	private final String initNextConfiguration;
+	
+	public CFGCopier(final String originalName, final String newName, final File sourceLocation, final String initNextCFG) {
+		super(originalName, newName, sourceLocation);
+
+		skipNextLine = false;
+		
+		initNextConfiguration = initNextCFG;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected String getFileExtension() {
+		return TLAConstants.Files.CONFIG_EXTENSION;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber)
+			throws IOException {
+		if (skipNextLine) {
+			skipNextLine = false;
+			return;
+		}
+		
+		final Matcher comment = SPECIFICATION_COMMENT_PATTERN.matcher(originalLine);
+		if (!comment.matches()) {
+			final Matcher init = INIT_COMMENT_PATTERN.matcher(originalLine);
+			
+			if (!init.matches()) {
+				final Matcher next = NEXT_COMMENT_PATTERN.matcher(originalLine);
+				
+				if (!next.matches()) {
+					final String trimmed = originalLine.trim();
+
+					if (TLAConstants.KeyWords.SPECIFICATION.equals(trimmed)
+							|| TLAConstants.KeyWords.INIT.equals(trimmed)
+							|| TLAConstants.KeyWords.NEXT.equals(trimmed)) {
+						skipNextLine = true;
+					} else if (trimmed.startsWith(TLAConstants.KeyWords.SPECIFICATION)
+								|| trimmed.startsWith(TLAConstants.KeyWords.INIT)
+								|| trimmed.startsWith(TLAConstants.KeyWords.NEXT)) {
+						// NO-OP - don't write since it starts with the keyword, but trimmed doesn't match
+						//	the naming of the keyword-denoted-attribute is on this same line, e.g:
+						//			SPECIFICATION MySpec
+					} else if (!trimmed.startsWith(TLAConstants.GENERATION_TIMESTAMP_PREFIX)) {
+						writer.write(originalLine + '\n');
+					}
+				}
+			}
+		}
+	}
+	
+	@Override
+	protected void allInputHasBeenConsumed(final BufferedWriter writer) throws IOException {
+		writer.write(initNextConfiguration + '\n');
+		writer.write(SpecWriterUtilities.getGeneratedTimeStampCommentLine().toString() + '\n');
+	}
+	
+	
+	public static void main(final String[] args) throws Exception {
+		final String initNext = "INIT\ninit_abc_ldq\n\nNEXT\nnext_abc_ldq";
+		final CFGCopier copier = new CFGCopier(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME,
+											   TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+											   new File(args[0]),
+											   initNext);
+		copier.copy();
+	}
+}
diff --git a/tlatools/src/tlc2/output/EC.java b/tlatools/src/tlc2/output/EC.java
index 379b4e356fefa51467ac1b8f6422335c85fc367b..48203f947eb0b674ff3c768382525bffb4140380 100644
--- a/tlatools/src/tlc2/output/EC.java
+++ b/tlatools/src/tlc2/output/EC.java
@@ -1,251 +1,418 @@
-package tlc2.output;
-
-
-/**
- * Interface containing the error code constants 
- * @author Simon Zambrovski
- * @version $Id$
- */
-public interface EC
-{
-    // Check and CheckImpl
-    // check if the TLC option is the same for params
-    public static final int CHECK_FAILED_TO_CHECK = 3000;
-    public static final int CHECK_COULD_NOT_READ_TRACE = 3001;
-
-    public static final int CHECK_PARAM_EXPECT_CONFIG_FILENAME = 3100;
-    public static final int CHECK_PARAM_USAGE = 3101;
-    public static final int CHECK_PARAM_MISSING_TLA_MODULE = 3102;
-    public static final int CHECK_PARAM_NEED_TO_SPECIFY_CONFIG_DIR = 3103;
-    public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED = 3104;
-    public static final int CHECK_PARAM_WORKER_NUMBER_TOO_SMALL = 3105;
-    public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED2 = 3106;
-    public static final int CHECK_PARAM_DEPTH_REQUIRED = 3107;
-    public static final int CHECK_PARAM_DEPTH_REQUIRED2 = 3108;
-    public static final int CHECK_PARAM_TRACE_REQUIRED = 3109;
-    public static final int CHECK_PARAM_COVREAGE_REQUIRED = 3110;
-    public static final int CHECK_PARAM_COVREAGE_REQUIRED2 = 3111;
-    public static final int CHECK_PARAM_COVREAGE_TOO_SMALL = 3112;
-    public static final int CHECK_PARAM_UNRECOGNIZED = 3113;
-    public static final int CHECK_PARAM_TOO_MANY_INPUT_FILES = 3114;
-
-    
-    public final static int SANY_PARSER_CHECK_1 = 4000;
-    public final static int SANY_PARSER_CHECK_2 = 4001;
-    public final static int SANY_PARSER_CHECK_3 = 4002;
-
-    public static final int UNKNOWN = -1;  // TODO remove all these
-    public final static int UNIT_TEST = -123456;
-
-    public static final int GENERAL = 1000;
-    public static final int SYSTEM_OUT_OF_MEMORY = 1001;
-    public static final int SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT = 1002;
-    public static final int SYSTEM_STACK_OVERFLOW = 1005;
-
-    public static final int WRONG_COMMANDLINE_PARAMS_SIMULATOR = 1101;
-    public static final int WRONG_COMMANDLINE_PARAMS_TLC = 1102;
-
-    public static final int TLC_PP_PARSING_VALUE = 2000;
-    public static final int TLC_PP_FORMATING_VALUE = 2001;
-
-    public static final int TLC_METADIR_EXISTS = 2100;
-    public static final int TLC_METADIR_CAN_NOT_BE_CREATED = 2101;
-    public static final int TLC_INITIAL_STATE = 2102;
-    public static final int TLC_NESTED_EXPRESSION = 2103;
-    public static final int TLC_ASSUMPTION_FALSE = 2104;
-    public static final int TLC_ASSUMPTION_EVALUATION_ERROR = 2105;
-    public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL = 2106;
-
-    public static final int TLC_INVARIANT_VIOLATED_INITIAL = 2107;
-    public static final int TLC_PROPERTY_VIOLATED_INITIAL = 2108;
-    public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT = 2109;
-    public static final int TLC_INVARIANT_VIOLATED_BEHAVIOR = 2110;
-    public static final int TLC_INVARIANT_EVALUATION_FAILED = 2111;
-    public static final int TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR = 2112;
-    public static final int TLC_ACTION_PROPERTY_EVALUATION_FAILED = 2113;
-    public static final int TLC_DEADLOCK_REACHED = 2114;
-
-    public static final int TLC_TEMPORAL_PROPERTY_VIOLATED = 2116;
-    public static final int TLC_FAILED_TO_RECOVER_NEXT = 2117;
-    public static final int TLC_NO_STATES_SATISFYING_INIT = 2118;
-    public static final int TLC_STRING_MODULE_NOT_FOUND = 2119;
-
-    public static final int TLC_ERROR_STATE = 2120;
-    public static final int TLC_BEHAVIOR_UP_TO_THIS_POINT = 2121;
-    public static final int TLC_BACK_TO_STATE = 2122;  // The "back to state" message from a liveness-error trace.
-    public static final int TLC_FAILED_TO_RECOVER_INIT = 2123;
-    public static final int TLC_REPORTER_DIED = 2124;
-
-    public static final int SYSTEM_ERROR_READING_POOL = 2125;
-    public static final int SYSTEM_CHECKPOINT_RECOVERY_CORRUPT = 2126;
-    public static final int SYSTEM_ERROR_WRITING_POOL = 2127;
-    public static final int SYSTEM_INDEX_ERROR = 2134;
-    public static final int SYSTEM_STREAM_EMPTY = 2135;
-    public static final int SYSTEM_FILE_NULL = 2137;
-    public static final int SYSTEM_INTERRUPTED = 2138;
-    public static final int SYSTEM_UNABLE_NOT_RENAME_FILE = 2160;
-    public static final int SYSTEM_DISK_IO_ERROR_FOR_FILE = 2161;
-    public static final int SYSTEM_METADIR_EXISTS = 2162;
-    public static final int SYSTEM_METADIR_CREATION_ERROR = 2163;
-    public static final int SYSTEM_UNABLE_TO_OPEN_FILE = 2167;
-    public static final int TLC_BUG = 2128; // TODO Bad description
-
-    public static final int SYSTEM_DISKGRAPH_ACCESS = 2129; // TODO refactor  
-
-    public static final int TLC_AAAAAAA = 2130;
-    public static final int TLC_REGISTRY_INIT_ERROR = 2131;
-    public static final int TLC_CHOOSE_ARGUMENTS_WRONG = 2164;
-    public static final int TLC_CHOOSE_UPPER_BOUND = 2165;
-
-    public static final int TLC_VALUE_ASSERT_FAILED = 2132;
-    public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE = 2154;
-
-    public static final int TLC_FP_NOT_IN_SET = 2133;
-    public static final int TLC_FP_VALUE_ALREADY_ON_DISK = 2166;
-
-    public static final int TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT = 2159;
-    public static final int TLC_PARAMETER_MUST_BE_POSTFIX = 2136; 
-    public static final int TLC_COULD_NOT_DETERMINE_SUBSCRIPT = 2139;
-    public static final int TLC_SUBSCRIPT_CONTAIN_NO_STATE_VAR = 2140;
-    public static final int TLC_WRONG_TUPLE_FIELD_NAME = 2141;
-    public static final int TLC_WRONG_RECORD_FIELD_NAME = 2142;
-    public static final int TLC_UNCHANGED_VARIABLE_CHANGED = 2143;
-    public static final int TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD = 2144;
-
-    public static final int TLC_MODULE_TLCGET_UNDEFINED = 2145;
-    /** Attempted to compare %1% with the value\n%2% */
-    public static final int TLC_MODULE_COMPARE_VALUE = 2155;
-    public static final int TLC_MODULE_CHECK_MEMBER_OF = 2158;
-    public static final int TLC_MODULE_TRANSITIVE_CLOSURE = 2157;
-    /** The %1% argument of %2% should be a %3%, but instead it is:<br>%4% */
-    public static final int TLC_MODULE_ARGUMENT_ERROR = 2169;
-    /** Simon used an argument like "\bn apple" to TLC_MODULE_ARGUMENT_ERROR to turn
-     * an "a" into an "an".  This doesn't work on the Toolbox's console.  Hence, LL added
-     * the following message type on 21 May 2012. */
-    public static final int TLC_MODULE_ARGUMENT_ERROR_AN = 2266;
-    public static final int TLC_ARGUMENT_MISMATCH = 2170;
-    public static final int TLC_PARSING_FAILED2 = 2171;
-    public static final int TLC_PARSING_FAILED = 3002;
-    public static final int TLC_TOO_MNY_POSSIBLE_STATES = 2172;
-    public static final int TLC_ERROR_REPLACING_MODULES = 2173;
-    public static final int SYSTEM_ERROR_READING_STATES = 2174;
-    public static final int SYSTEM_ERROR_WRITING_STATES = 2175;
-    public static final int TLC_MODULE_APPLYING_TO_WRONG_VALUE = 2176;
-    public static final int TLC_MODULE_BAG_UNION1 = 2177;
-    public static final int TLC_MODULE_OVERFLOW = 2178;
-    public static final int TLC_MODULE_DIVISION_BY_ZERO = 2179;
-    public static final int TLC_MODULE_NULL_POWER_NULL = 2180;
-    public static final int TLC_MODULE_COMPUTING_CARDINALITY = 2181;
-    public static final int TLC_MODULE_EVALUATING = 2182;
-    /** The %1% argument of %2% must be in the domain of its first argument:<br>%3%<br>, but instead it is<br>%4% */
-    public static final int TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN = 2183;
-    public static final int TLC_MODULE_APPLY_EMPTY_SEQ = 2184;
-    
-    public static final int TLC_STARTING = 2185;
-    public static final int TLC_FINISHED = 2186;
-    
-    // distributed TLC
-    
-    public static final int TLC_DISTRIBUTED_SERVER_RUNNING = 7000;
-    public static final int TLC_DISTRIBUTED_WORKER_REGISTERED = TLC_DISTRIBUTED_SERVER_RUNNING + 1;
-    public static final int TLC_DISTRIBUTED_WORKER_DEREGISTERED = TLC_DISTRIBUTED_WORKER_REGISTERED + 1;
-    public static final int TLC_DISTRIBUTED_WORKER_STATS = TLC_DISTRIBUTED_WORKER_DEREGISTERED + 1;
-    public static final int TLC_DISTRIBUTED_SERVER_NOT_RUNNING = TLC_DISTRIBUTED_WORKER_STATS + 1;
-    public static final int TLC_DISTRIBUTED_VM_VERSION = TLC_DISTRIBUTED_SERVER_NOT_RUNNING + 1;
-    public static final int TLC_DISTRIBUTED_WORKER_LOST = TLC_DISTRIBUTED_VM_VERSION + 1;
-    public static final int TLC_DISTRIBUTED_EXCEED_BLOCKSIZE = TLC_DISTRIBUTED_WORKER_LOST + 1;
-    public static final int TLC_DISTRIBUTED_SERVER_FPSET_WAITING = TLC_DISTRIBUTED_EXCEED_BLOCKSIZE + 1;
-    public static final int TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED = TLC_DISTRIBUTED_SERVER_FPSET_WAITING + 1;
-    public static final int TLC_DISTRIBUTED_SERVER_FINISHED = TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED + 1;
-    
-    // errors during parsing of the model configuration
-    
-    public static final int CFG_ERROR_READING_FILE = 5001;
-    public static final int CFG_GENERAL = 5002;
-    public static final int CFG_MISSING_ID = 5003;
-    public static final int CFG_TWICE_KEYWORD = 5004;
-    public static final int CFG_EXPECT_ID = 5005;
-    public static final int CFG_EXPECTED_SYMBOL = 5006;
-    public static final int TLC_MODE_MC = 2187;
-    public static final int TLC_MODE_SIMU = 2188;
-    public static final int TLC_COMPUTING_INIT = 2189;
-    public static final int TLC_INIT_GENERATED1 = 2190;
-    public static final int TLC_INIT_GENERATED2 = 2191;
-    public static final int TLC_INIT_GENERATED3 = 2207;
-    public static final int TLC_INIT_GENERATED4 = 2208;
-    public static final int TLC_CHECKING_TEMPORAL_PROPS = 2192;
-    public static final int TLC_SUCCESS = 2193;
-    public static final int TLC_SEARCH_DEPTH = 2194;
-    public static final int TLC_CHECKPOINT_START = 2195;
-    public static final int TLC_CHECKPOINT_END = 2196;
-    public static final int TLC_CHECKPOINT_RECOVER_START = 2197;
-    public static final int TLC_CHECKPOINT_RECOVER_END = 2198;
-    public static final int TLC_STATS = 2199;
-    public static final int TLC_STATS_DFID = 2204;
-    public static final int TLC_STATS_SIMU = 2210;
-    public static final int TLC_PROGRESS_STATS = 2200;
-    public static final int TLC_COVERAGE_START = 2201;
-    public static final int TLC_COVERAGE_END = 2202;
-    public static final int TLC_CHECKPOINT_RECOVER_END_DFID = 2203;
-    public static final int TLC_PROGRESS_START_STATS_DFID = 2205;
-    public static final int TLC_PROGRESS_STATS_DFID = 2206;
-    public static final int TLC_PROGRESS_SIMU = 2209;
-    public static final int TLC_FP_COMPLETED = 2211;
-    
-    public static final int TLC_LIVE_IMPLIED = 2212;
-    public static final int TLC_LIVE_CANNOT_HANDLE_FORMULA = 2213;
-    public static final int TLC_LIVE_WRONG_FORMULA_FORMAT = 2214;
-    public static final int TLC_LIVE_ENCOUNTERED_ACTIONS = 2249;
-    public static final int TLC_LIVE_STATE_PREDICATE_NON_BOOL = 2250;
-    public static final int TLC_LIVE_CANNOT_EVAL_FORMULA = 2251;
-    public static final int TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE = 2252;
-
-    
-    public static final int TLC_EXPECTED_VALUE = 2215;
-    public static final int TLC_EXPECTED_EXPRESSION = 2246;
-    public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING = 2247;
-    public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248;
-    
-    
-    // state printing
-    public static final int TLC_STATE_PRINT1 = 2216;
-    public static final int TLC_STATE_PRINT2 = 2217;
-    public static final int TLC_STATE_PRINT3 = 2218;  // This seems to be a "Stuttering" message from a liveness-error trace 
-    public static final int TLC_SANY_END = 2219;
-    public static final int TLC_SANY_START = 2220;
-    public static final int TLC_COVERAGE_VALUE = 2221;
-    
-    // config file errors
-    public static final int TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM = 2222;
-    public static final int TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID = 2223;
-    public static final int TLC_CONFIG_WRONG_SUBSTITUTION = 2224;
-    public static final int TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS = 2225;
-    public static final int TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC = 2226;
-    public static final int TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT = 2227;
-    public static final int TLC_CONFIG_ID_REQUIRES_NO_ARG = 2228;
-    public static final int TLC_CONFIG_SPECIFIED_NOT_DEFINED = 2229;
-    public static final int TLC_CONFIG_ID_HAS_VALUE = 2230;
-    public static final int TLC_CONFIG_MISSING_INIT = 2231;
-    public static final int TLC_CONFIG_MISSING_NEXT = 2232;
-    public static final int TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT = 2233;
-    public static final int TLC_CONFIG_OP_NO_ARGS = 2234;
-    public static final int TLC_CONFIG_OP_NOT_IN_SPEC = 2235;
-    public static final int TLC_CONFIG_OP_IS_EQUAL = 2236;
-    public static final int TLC_CONFIG_SPEC_IS_TRIVIAL = 2237;
-    public static final int TLC_CANT_HANDLE_SUBSCRIPT = 2238;
-    public static final int TLC_CANT_HANDLE_CONJUNCT = 2239;
-    public static final int TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS = 2240;
-    public static final int TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED = 2241;
-    public static final int TLC_CONFIG_OP_ARITY_INCONSISTENT = 2242;
-    public static final int TLC_CONFIG_NO_STATE_TYPE = 2243;
-    public static final int TLC_CANT_HANDLE_REAL_NUMBERS = 2244;
-    public static final int TLC_NO_MODULES = 2245;
-    
-    public static final int TLC_ENABLED_WRONG_FORMULA = 2260;
-    public static final int TLC_ENCOUNTERED_FORMULA_IN_PREDICATE = 2261;
-    public static final int TLC_VERSION = 2262;
-    public static final int TLC_USAGE = 2263;
-    public static final int TLC_COUNTER_EXAMPLE = 2264;
-    
-    public static final int TLC_INTEGER_TOO_BIG = 2265;
-}
+package tlc2.output;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Interface containing the error code constants 
+ * @author Simon Zambrovski
+ * @version $Id$
+ */
+public interface EC
+{
+    // This is reserved so that an optional error code can be safely represented in a single int
+    public static final int NO_ERROR = 0;
+    
+    // Check and CheckImpl
+    // check if the TLC option is the same for params
+    public static final int CHECK_FAILED_TO_CHECK = 3000;
+    public static final int CHECK_COULD_NOT_READ_TRACE = 3001;
+
+    public static final int CHECK_PARAM_EXPECT_CONFIG_FILENAME = 3100;
+    public static final int CHECK_PARAM_USAGE = 3101;
+    public static final int CHECK_PARAM_MISSING_TLA_MODULE = 3102;
+    public static final int CHECK_PARAM_NEED_TO_SPECIFY_CONFIG_DIR = 3103;
+    public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED = 3104;
+    public static final int CHECK_PARAM_WORKER_NUMBER_TOO_SMALL = 3105;
+    public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED2 = 3106;
+    public static final int CHECK_PARAM_DEPTH_REQUIRED = 3107;
+    public static final int CHECK_PARAM_DEPTH_REQUIRED2 = 3108;
+    public static final int CHECK_PARAM_TRACE_REQUIRED = 3109;
+    public static final int CHECK_PARAM_COVREAGE_REQUIRED = 3110;
+    public static final int CHECK_PARAM_COVREAGE_REQUIRED2 = 3111;
+    public static final int CHECK_PARAM_COVREAGE_TOO_SMALL = 3112;
+    public static final int CHECK_PARAM_UNRECOGNIZED = 3113;
+    public static final int CHECK_PARAM_TOO_MANY_INPUT_FILES = 3114;
+
+    
+    public final static int SANY_PARSER_CHECK_1 = 4000;
+    public final static int SANY_PARSER_CHECK_2 = 4001;
+    public final static int SANY_PARSER_CHECK_3 = 4002;
+
+    public static final int UNKNOWN = -1;  // TODO remove all these
+    public final static int UNIT_TEST = -123456;
+	/**
+	 * A feature of TLA+/TLC is not supported by TLC's current mode. E.g.
+	 * TLCGet/TLCSet operator cannot be used in distributed TLC.
+	 */
+	public static final int TLC_FEATURE_UNSUPPORTED = 2156;
+	public static final int TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY = 2279;
+	public static final int TLC_FEATURE_LIVENESS_CONSTRAINTS = 2284;
+
+    public static final int GENERAL = 1000;
+    public static final int SYSTEM_OUT_OF_MEMORY = 1001;
+    public static final int SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT = 1002;
+    public static final int SYSTEM_OUT_OF_MEMORY_LIVENESS = 1003;
+    public static final int SYSTEM_STACK_OVERFLOW = 1005;
+
+    public static final int WRONG_COMMANDLINE_PARAMS_SIMULATOR = 1101;
+    public static final int WRONG_COMMANDLINE_PARAMS_TLC = 1102;
+
+    public static final int TLC_PP_PARSING_VALUE = 2000;
+    public static final int TLC_PP_FORMATING_VALUE = 2001;
+
+    public static final int TLC_METADIR_EXISTS = 2100;
+    public static final int TLC_METADIR_CAN_NOT_BE_CREATED = 2101;
+    public static final int TLC_INITIAL_STATE = 2102;
+    public static final int TLC_NESTED_EXPRESSION = 2103;
+    public static final int TLC_ASSUMPTION_FALSE = 2104;
+    public static final int TLC_ASSUMPTION_EVALUATION_ERROR = 2105;
+    public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL = 2106;
+
+    public static final int TLC_INVARIANT_VIOLATED_INITIAL = 2107;
+    public static final int TLC_PROPERTY_VIOLATED_INITIAL = 2108;
+    public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT = 2109;
+    public static final int TLC_INVARIANT_VIOLATED_BEHAVIOR = 2110;
+    public static final int TLC_INVARIANT_EVALUATION_FAILED = 2111;
+    public static final int TLC_INVARIANT_VIOLATED_LEVEL = 2146;
+    public static final int TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR = 2112;
+    public static final int TLC_ACTION_PROPERTY_EVALUATION_FAILED = 2113;
+    public static final int TLC_DEADLOCK_REACHED = 2114;
+
+    public static final int TLC_STATES_AND_NO_NEXT_ACTION = 2115;
+    public static final int TLC_TEMPORAL_PROPERTY_VIOLATED = 2116;
+    public static final int TLC_FAILED_TO_RECOVER_NEXT = 2117;
+    public static final int TLC_NO_STATES_SATISFYING_INIT = 2118;
+    public static final int TLC_STRING_MODULE_NOT_FOUND = 2119;
+
+    public static final int TLC_ERROR_STATE = 2120;
+    public static final int TLC_BEHAVIOR_UP_TO_THIS_POINT = 2121;
+    public static final int TLC_BACK_TO_STATE = 2122;  // The "back to state" message from a liveness-error trace.
+    public static final int TLC_FAILED_TO_RECOVER_INIT = 2123;
+    public static final int TLC_REPORTER_DIED = 2124;
+
+    public static final int SYSTEM_ERROR_READING_POOL = 2125;
+    public static final int SYSTEM_CHECKPOINT_RECOVERY_CORRUPT = 2126;
+    public static final int SYSTEM_ERROR_WRITING_POOL = 2127;
+    public static final int SYSTEM_ERROR_CLEANING_POOL = 2270;
+    public static final int SYSTEM_INDEX_ERROR = 2134;
+    public static final int SYSTEM_STREAM_EMPTY = 2135;
+    public static final int SYSTEM_FILE_NULL = 2137;
+    public static final int SYSTEM_INTERRUPTED = 2138;
+    public static final int SYSTEM_UNABLE_NOT_RENAME_FILE = 2160;
+    public static final int SYSTEM_DISK_IO_ERROR_FOR_FILE = 2161;
+    public static final int SYSTEM_METADIR_EXISTS = 2162;
+    public static final int SYSTEM_METADIR_CREATION_ERROR = 2163;
+    public static final int SYSTEM_UNABLE_TO_OPEN_FILE = 2167;
+    public static final int TLC_BUG = 2128; // TODO Bad description
+    public static final int TLC_FINGERPRINT_EXCEPTION = 2147;
+
+    public static final int SYSTEM_DISKGRAPH_ACCESS = 2129; // TODO refactor  
+
+    public static final int TLC_AAAAAAA = 2130;
+    public static final int TLC_REGISTRY_INIT_ERROR = 2131;
+    public static final int TLC_CHOOSE_ARGUMENTS_WRONG = 2164;
+    public static final int TLC_CHOOSE_UPPER_BOUND = 2165;
+
+    public static final int TLC_VALUE_ASSERT_FAILED = 2132;
+	public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE = 2154;
+    public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED = 2168;
+    public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH = 2400;
+    public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH = 2402;
+    public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH = 2403;
+    // User-provided module overrides print to stdout.
+    public static final int TLC_MODULE_OVERRIDE_STDOUT = 20000;
+
+    public static final int TLC_FP_NOT_IN_SET = 2133;
+    public static final int TLC_FP_VALUE_ALREADY_ON_DISK = 2166;
+
+    public static final int TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT = 2159;
+    public static final int TLC_PARAMETER_MUST_BE_POSTFIX = 2136; 
+    public static final int TLC_COULD_NOT_DETERMINE_SUBSCRIPT = 2139;
+    public static final int TLC_SUBSCRIPT_CONTAIN_NO_STATE_VAR = 2140;
+    public static final int TLC_WRONG_TUPLE_FIELD_NAME = 2141;
+    public static final int TLC_WRONG_RECORD_FIELD_NAME = 2142;
+    public static final int TLC_UNCHANGED_VARIABLE_CHANGED = 2143;
+    public static final int TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD = 2144;
+
+    public static final int TLC_MODULE_TLCGET_UNDEFINED = 2145;
+    /** Attempted to compare %1% with the value\n%2% */
+    public static final int TLC_MODULE_COMPARE_VALUE = 2155;
+    public static final int TLC_MODULE_CHECK_MEMBER_OF = 2158;
+    public static final int TLC_MODULE_TRANSITIVE_CLOSURE = 2157;
+    /** The %1% argument of %2% should be a %3%, but instead it is:<br>%4% */
+    public static final int TLC_MODULE_ARGUMENT_ERROR = 2169;
+    /** Simon used an argument like "\bn apple" to TLC_MODULE_ARGUMENT_ERROR to turn
+     * an "a" into an "an".  This doesn't work on the Toolbox's console.  Hence, LL added
+     * the following message type on 21 May 2012. */
+    public static final int TLC_MODULE_ARGUMENT_ERROR_AN = 2266;
+    public static final int TLC_MODULE_ONE_ARGUMENT_ERROR = 2283;
+    public static final int TLC_ARGUMENT_MISMATCH = 2170;
+    public static final int TLC_PARSING_FAILED2 = 2171;
+    public static final int TLC_PARSING_FAILED = 3002;
+    public static final int TLC_TOO_MNY_POSSIBLE_STATES = 2172;
+    public static final int TLC_ERROR_REPLACING_MODULES = 2173;
+    public static final int SYSTEM_ERROR_READING_STATES = 2174;
+    public static final int SYSTEM_ERROR_WRITING_STATES = 2175;
+    public static final int TLC_MODULE_APPLYING_TO_WRONG_VALUE = 2176;
+    public static final int TLC_MODULE_BAG_UNION1 = 2177;
+    public static final int TLC_MODULE_OVERFLOW = 2178;
+    public static final int TLC_MODULE_DIVISION_BY_ZERO = 2179;
+    public static final int TLC_MODULE_NULL_POWER_NULL = 2180;
+    public static final int TLC_MODULE_COMPUTING_CARDINALITY = 2181;
+    public static final int TLC_MODULE_EVALUATING = 2182;
+    /** The %1% argument of %2% must be in the domain of its first argument:<br>%3%<br>, but instead it is<br>%4% */
+    public static final int TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN = 2183;
+    public static final int TLC_MODULE_APPLY_EMPTY_SEQ = 2184;
+    
+    public static final int TLC_SYMMETRY_SET_TOO_SMALL = 2300;
+    public static final int TLC_SPECIFICATION_FEATURES_TEMPORAL_QUANTIFIER = 2301;
+    
+    public static final int TLC_STARTING = 2185;
+    public static final int TLC_FINISHED = 2186;
+    
+    // distributed TLC
+    
+    public static final int TLC_DISTRIBUTED_SERVER_RUNNING = 7000;
+    public static final int TLC_DISTRIBUTED_WORKER_REGISTERED = TLC_DISTRIBUTED_SERVER_RUNNING + 1;
+    public static final int TLC_DISTRIBUTED_WORKER_DEREGISTERED = TLC_DISTRIBUTED_WORKER_REGISTERED + 1;
+    public static final int TLC_DISTRIBUTED_WORKER_STATS = TLC_DISTRIBUTED_WORKER_DEREGISTERED + 1;
+    public static final int TLC_DISTRIBUTED_SERVER_NOT_RUNNING = TLC_DISTRIBUTED_WORKER_STATS + 1;
+    public static final int TLC_DISTRIBUTED_VM_VERSION = TLC_DISTRIBUTED_SERVER_NOT_RUNNING + 1;
+    public static final int TLC_DISTRIBUTED_WORKER_LOST = TLC_DISTRIBUTED_VM_VERSION + 1;
+    public static final int TLC_DISTRIBUTED_EXCEED_BLOCKSIZE = TLC_DISTRIBUTED_WORKER_LOST + 1;
+    public static final int TLC_DISTRIBUTED_SERVER_FPSET_WAITING = TLC_DISTRIBUTED_EXCEED_BLOCKSIZE + 1;
+    public static final int TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED = TLC_DISTRIBUTED_SERVER_FPSET_WAITING + 1;
+    public static final int TLC_DISTRIBUTED_SERVER_FINISHED = TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED + 1;
+    
+    // errors during parsing of the model configuration
+    
+    public static final int CFG_ERROR_READING_FILE = 5001;
+    public static final int CFG_GENERAL = 5002;
+    public static final int CFG_MISSING_ID = 5003;
+    public static final int CFG_TWICE_KEYWORD = 5004;
+    public static final int CFG_EXPECT_ID = 5005;
+    public static final int CFG_EXPECTED_SYMBOL = 5006;
+    public static final int TLC_MODE_MC = 2187;
+    public static final int TLC_MODE_MC_DFS = 2271;
+    public static final int TLC_MODE_SIMU = 2188;
+    public static final int TLC_COMPUTING_INIT = 2189;
+	public static final int TLC_COMPUTING_INIT_PROGRESS = 2269;
+    public static final int TLC_INIT_GENERATED1 = 2190;
+    public static final int TLC_INIT_GENERATED2 = 2191;
+    public static final int TLC_INIT_GENERATED3 = 2207;
+    public static final int TLC_INIT_GENERATED4 = 2208;
+    public static final int TLC_CHECKING_TEMPORAL_PROPS = 2192;
+    public static final int TLC_CHECKING_TEMPORAL_PROPS_END = 2267;
+    public static final int TLC_SUCCESS = 2193;
+    public static final int TLC_SEARCH_DEPTH = 2194;
+    public static final int TLC_STATE_GRAPH_OUTDEGREE = 2268;
+    public static final int TLC_CHECKPOINT_START = 2195;
+    public static final int TLC_CHECKPOINT_END = 2196;
+    public static final int TLC_CHECKPOINT_RECOVER_START = 2197;
+    public static final int TLC_CHECKPOINT_RECOVER_END = 2198;
+    public static final int TLC_STATS = 2199;
+    public static final int TLC_STATS_DFID = 2204;
+    public static final int TLC_STATS_SIMU = 2210;
+    public static final int TLC_PROGRESS_STATS = 2200;
+    public static final int TLC_COVERAGE_START = 2201;
+    public static final int TLC_COVERAGE_END = 2202;
+    public static final int TLC_CHECKPOINT_RECOVER_END_DFID = 2203;
+    public static final int TLC_PROGRESS_START_STATS_DFID = 2205;
+    public static final int TLC_PROGRESS_STATS_DFID = 2206;
+    public static final int TLC_PROGRESS_SIMU = 2209;
+    public static final int TLC_FP_COMPLETED = 2211;
+    
+    public static final int TLC_LIVE_IMPLIED = 2212;
+    public static final int TLC_LIVE_CANNOT_HANDLE_FORMULA = 2213;
+    public static final int TLC_LIVE_WRONG_FORMULA_FORMAT = 2214;
+    public static final int TLC_LIVE_ENCOUNTERED_ACTIONS = 2249;
+    public static final int TLC_LIVE_STATE_PREDICATE_NON_BOOL = 2250;
+    public static final int TLC_LIVE_CANNOT_EVAL_FORMULA = 2251;
+    public static final int TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE = 2252;
+    public static final int TLC_LIVE_FORMULA_TAUTOLOGY = 2253;
+
+    
+    public static final int TLC_EXPECTED_VALUE = 2215;
+    public static final int TLC_EXPECTED_EXPRESSION = 2246;
+    public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING = 2247;
+    public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248;
+    
+    
+    // state printing
+    public static final int TLC_STATE_PRINT1 = 2216;
+    public static final int TLC_STATE_PRINT2 = 2217;
+    public static final int TLC_STATE_PRINT3 = 2218;  // This seems to be a "Stuttering" message from a liveness-error trace 
+    public static final int TLC_SANY_END = 2219;
+    public static final int TLC_SANY_START = 2220;
+    public static final int TLC_COVERAGE_MISMATCH = 2776;
+    public static final int TLC_COVERAGE_VALUE = 2221;
+    public static final int TLC_COVERAGE_VALUE_COST = 2775;
+    public static final int TLC_COVERAGE_NEXT = 2772;
+    public static final int TLC_COVERAGE_INIT = 2773;
+    public static final int TLC_COVERAGE_PROPERTY = 2774;
+    public static final int TLC_COVERAGE_END_OVERHEAD = 2777;
+    
+    // config file errors
+    public static final int TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM = 2222;
+    public static final int TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID = 2223;
+    public static final int TLC_CONFIG_WRONG_SUBSTITUTION = 2224;
+    public static final int TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS = 2225;
+    public static final int TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR = 2280;
+    public static final int TLC_CONFIG_SUBSTITUTION_NON_CONSTANT = 2281;
+    public static final int TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC = 2226;
+    public static final int TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT = 2227;
+    public static final int TLC_CONFIG_ID_REQUIRES_NO_ARG = 2228;
+    public static final int TLC_CONFIG_SPECIFIED_NOT_DEFINED = 2229;
+    public static final int TLC_CONFIG_ID_HAS_VALUE = 2230;
+    public static final int TLC_CONFIG_MISSING_INIT = 2231;
+    public static final int TLC_CONFIG_MISSING_NEXT = 2232;
+    public static final int TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT = 2233;
+    public static final int TLC_CONFIG_OP_NO_ARGS = 2234;
+    public static final int TLC_CONFIG_OP_NOT_IN_SPEC = 2235;
+    public static final int TLC_CONFIG_OP_IS_EQUAL = 2236;
+    public static final int TLC_CONFIG_SPEC_IS_TRIVIAL = 2237;
+    public static final int TLC_CANT_HANDLE_SUBSCRIPT = 2238;
+    public static final int TLC_CANT_HANDLE_CONJUNCT = 2239;
+    public static final int TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS = 2240;
+    public static final int TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED = 2241;
+    public static final int TLC_CONFIG_OP_ARITY_INCONSISTENT = 2242;
+    public static final int TLC_CONFIG_NO_STATE_TYPE = 2243;
+    public static final int TLC_CANT_HANDLE_REAL_NUMBERS = 2244;
+    public static final int TLC_NO_MODULES = 2245;
+    
+    public static final int TLC_ENABLED_WRONG_FORMULA = 2260;
+    public static final int TLC_ENCOUNTERED_FORMULA_IN_PREDICATE = 2261;
+    public static final int TLC_VERSION = 2262;
+    public static final int TLC_COUNTER_EXAMPLE = 2264;
+    
+    public static final int TLC_INTEGER_TOO_BIG = 2265;
+    public static final int TLC_TRACE_TOO_LONG = 2282;
+    
+    public static final int TLC_ENVIRONMENT_JVM_GC = 2401;
+
+    
+    //**************************************************************//
+    // Mapping error constants above to process exit/return values. //
+    // Because Linux and macOS only support 8-bit exit values, this //
+    // mapping is necessary.                                        //
+    //**************************************************************//
+
+    public static class ExitStatus {
+	    	
+    	public static final int ERROR = 255;
+	    public static final int SUCCESS = 0;
+	
+	    // (Safety/Liveness) Violations
+	    public static final int VIOLATION_ASSUMPTION = 10; 
+	    public static final int VIOLATION_DEADLOCK = VIOLATION_ASSUMPTION + 1; 
+	    public static final int VIOLATION_SAFETY = VIOLATION_DEADLOCK + 1;
+	    public static final int VIOLATION_LIVENESS = VIOLATION_SAFETY + 1;
+		public static final int VIOLATION_ASSERT = VIOLATION_LIVENESS + 1;
+	
+	    // Evaluation failures
+	    public static final int FAILURE_SPEC_EVAL = 75;
+	    public static final int FAILURE_SAFETY_EVAL = FAILURE_SPEC_EVAL + 1;
+	    public static final int FAILURE_LIVENESS_EVAL = FAILURE_SAFETY_EVAL + 1;
+	    
+	    // Errors
+	    public static final int ERROR_SPEC_PARSE = 150;
+	    public static final int ERROR_CONFIG_PARSE = ERROR_SPEC_PARSE + 1;
+	    public static final int ERROR_STATESPACE_TOO_LARGE = ERROR_CONFIG_PARSE + 1;
+	    public static final int ERROR_SYSTEM = ERROR_STATESPACE_TOO_LARGE + 1;
+	
+	    /**
+	     * Returns an exit status for an error code.
+	     */
+	    public static int errorConstantToExitStatus(final int ec) {
+	        // TODO Allocate a range of exit status to indicate classes of errors.
+	        // For a great example of potential classes see: https://github.com/tlaplus/tlaplus/pull/308#discussion_r285840112
+	    	switch (ec) {
+	    	case NO_ERROR:
+	    		return SUCCESS;
+	    	
+	    	// failures
+	    		
+	    	case TLC_LIVE_FORMULA_TAUTOLOGY:
+	    		return FAILURE_LIVENESS_EVAL;
+	    		
+	    	case TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT:
+	    	case TLC_STATES_AND_NO_NEXT_ACTION:
+	    	case TLC_NESTED_EXPRESSION:
+	    	case TLC_FINGERPRINT_EXCEPTION:
+	    		return FAILURE_SPEC_EVAL;
+	    		
+	    	case TLC_INVARIANT_EVALUATION_FAILED:
+	    	case TLC_INVARIANT_VIOLATED_LEVEL:
+	    		return FAILURE_SAFETY_EVAL;
+	    		
+	    	// violations
+	    		
+	        case TLC_INVARIANT_VIOLATED_INITIAL:
+	        case TLC_INVARIANT_VIOLATED_BEHAVIOR:
+				return VIOLATION_SAFETY;
+				
+	        case TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR:
+	        case TLC_ACTION_PROPERTY_EVALUATION_FAILED:
+	        case TLC_TEMPORAL_PROPERTY_VIOLATED:
+	        case TLC_PROPERTY_VIOLATED_INITIAL:
+				return VIOLATION_LIVENESS;
+				
+	        case TLC_DEADLOCK_REACHED:
+	        	return VIOLATION_DEADLOCK;
+	        	
+	        case TLC_ASSUMPTION_FALSE:
+	        case TLC_ASSUMPTION_EVALUATION_ERROR:
+	            return VIOLATION_ASSUMPTION;
+	        
+	        case TLC_VALUE_ASSERT_FAILED:
+	        	return VIOLATION_ASSERT;
+	        	
+	        // errors
+	        case TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM:
+	        case TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID:
+	        case TLC_CONFIG_WRONG_SUBSTITUTION:
+	        case TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS:
+	        case TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR:
+	        case TLC_CONFIG_SUBSTITUTION_NON_CONSTANT:
+	        case TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC:
+	        case TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT:
+	        case TLC_CONFIG_ID_REQUIRES_NO_ARG:
+	        case TLC_CONFIG_SPECIFIED_NOT_DEFINED:
+	        case TLC_CONFIG_ID_HAS_VALUE:
+	        case TLC_CONFIG_MISSING_INIT:
+	        case TLC_CONFIG_MISSING_NEXT:
+	        case TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT:
+	        case TLC_CONFIG_OP_NO_ARGS:
+	        case TLC_CONFIG_OP_NOT_IN_SPEC:
+	        case TLC_CONFIG_OP_IS_EQUAL:
+	        case TLC_CONFIG_SPEC_IS_TRIVIAL:
+	        case TLC_CANT_HANDLE_SUBSCRIPT:
+	        case TLC_CANT_HANDLE_CONJUNCT:
+	        case TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS:
+	        case TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED:
+	        case TLC_CONFIG_OP_ARITY_INCONSISTENT:
+	        case TLC_CONFIG_NO_STATE_TYPE:
+	        case TLC_CANT_HANDLE_REAL_NUMBERS: // might also be in the spec
+	        case TLC_NO_MODULES:
+	        	return ERROR_CONFIG_PARSE;
+	        	
+	        case TLC_PARSING_FAILED2:
+	        case TLC_PARSING_FAILED:
+	        	return ERROR_SPEC_PARSE;
+
+			default:
+				return 255;
+			}
+	    }
+
+		private static final Set<Integer> knownExitValues = Stream.of(SUCCESS, FAILURE_LIVENESS_EVAL, FAILURE_SPEC_EVAL,
+				FAILURE_SAFETY_EVAL, VIOLATION_SAFETY, VIOLATION_LIVENESS, VIOLATION_DEADLOCK, VIOLATION_ASSUMPTION,
+				VIOLATION_ASSERT, ERROR_CONFIG_PARSE, ERROR_SPEC_PARSE).collect(Collectors.toSet());
+		
+		public static boolean exitStatusToCrash(final int exitStatus) {
+			return !knownExitValues.contains(exitStatus);
+		}
+    }
+}
diff --git a/tlatools/src/tlc2/output/MP.java b/tlatools/src/tlc2/output/MP.java
index 593529e5ecb2981b7f27b3574abc67be9449dd1d..0d1299c019fa4c9b037f3192f7b52bf5aff227f2 100644
--- a/tlatools/src/tlc2/output/MP.java
+++ b/tlatools/src/tlc2/output/MP.java
@@ -12,8 +12,12 @@ import tlc2.tool.TLCState;
 import tlc2.tool.TLCStateInfo;
 import tlc2.tool.liveness.LiveWorker;
 import tlc2.util.statistics.IBucketStatistics;
+import util.Assert;
+import util.Assert.TLCRuntimeException;
 import util.DebugPrinter;
 import util.Set;
+import util.TLAConstants;
+import util.TLAFlightRecorder;
 import util.ToolIO;
 
 /**
@@ -76,6 +80,82 @@ import util.ToolIO;
  */
 public class MP
 {
+	// https://stackoverflow.com/a/45444716/6291195
+	public static class Colors {
+		// Reset
+		public static final String RESET = "\033[0m"; // Text Reset
+
+		// Regular Colors
+		public static final String BLACK = "\033[0;30m"; // BLACK
+		public static final String RED = "\033[0;31m"; // RED
+		public static final String GREEN = "\033[0;32m"; // GREEN
+		public static final String YELLOW = "\033[0;33m"; // YELLOW
+		public static final String BLUE = "\033[0;34m"; // BLUE
+		public static final String PURPLE = "\033[0;35m"; // PURPLE
+		public static final String CYAN = "\033[0;36m"; // CYAN
+		public static final String WHITE = "\033[0;37m"; // WHITE
+
+		// Bold
+		public static final String BLACK_BOLD = "\033[1;30m"; // BLACK
+		public static final String RED_BOLD = "\033[1;31m"; // RED
+		public static final String GREEN_BOLD = "\033[1;32m"; // GREEN
+		public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW
+		public static final String BLUE_BOLD = "\033[1;34m"; // BLUE
+		public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE
+		public static final String CYAN_BOLD = "\033[1;36m"; // CYAN
+		public static final String WHITE_BOLD = "\033[1;37m"; // WHITE
+
+		// Underline
+		public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK
+		public static final String RED_UNDERLINED = "\033[4;31m"; // RED
+		public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN
+		public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW
+		public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE
+		public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE
+		public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN
+		public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE
+
+		// Background
+		public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK
+		public static final String RED_BACKGROUND = "\033[41m"; // RED
+		public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN
+		public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW
+		public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE
+		public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE
+		public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN
+		public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE
+
+		// High Intensity
+		public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK
+		public static final String RED_BRIGHT = "\033[0;91m"; // RED
+		public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN
+		public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW
+		public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE
+		public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE
+		public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN
+		public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE
+
+		// Bold High Intensity
+		public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK
+		public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED
+		public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN
+		public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW
+		public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE
+		public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE
+		public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN
+		public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE
+
+		// High Intensity backgrounds
+		public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK
+		public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED
+		public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN
+		public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW
+		public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE
+		public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE
+		public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN
+		public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE
+	}
+
     /**
      * 
      */
@@ -83,15 +163,15 @@ public class MP
     /**
      * 
      */
-    private static final String CR = "\n";
+    public static final String CR = TLAConstants.CR;
     /**
      * 
      */
-    private static final String SPACE = " ";
+    public static final String SPACE = TLAConstants.SPACE;
     /**
      * 
      */
-    public static final String COLON = ":";
+    public static final String COLON = TLAConstants.COLON;
     public static final String DELIM = "@!@!@"; //$NON-NLS-1$
     public static final String STARTMSG = "STARTMSG "; //$NON-NLS-1$
 
@@ -128,7 +208,7 @@ public class MP
 
     private static MP instance = null;
 	private static MPRecorder recorder = new MPRecorder();
-    private Set warningHistory;
+    private final Set warningHistory;
     private static final String CONFIG_FILE_ERROR = "TLC found an error in the configuration file at line %1%\n";
     private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ 
 	private static final DecimalFormat df = new DecimalFormat("###,###.###");
@@ -164,15 +244,13 @@ public class MP
      * @param parameters string of parameters to be replaced in the message
      * @return the formatted message
      */
-    private synchronized static String getMessage(int messageClass, int messageCode, String[] parameters)
+    public synchronized static String getMessage(int messageClass, int messageCode, String[] parameters)
     {
 
         if (parameters == null)
         {
             parameters = EMPTY_PARAMS;
         }
-        OutputCollector.saveMessage(messageClass, messageCode, parameters);
-        
         DebugPrinter.print("entering MP.getMessage() with error code " + messageCode + " and " + parameters.length //$NON-NLS-1$
                 + " parameters"); //$NON-NLS-1$
 
@@ -210,7 +288,42 @@ public class MP
                 break;
             }
         }
-        // fill with different message depending on the error code
+        
+        b.append(getMessage0(messageClass, messageCode, parameters));
+
+        if (TLCGlobals.tool)
+        {
+            // for the tool we always print the message code
+            b.append(CR).append(DELIM).append(ENDMSG).append(messageCode).append(SPACE).append(DELIM);
+        } else
+        {
+
+            // post processing
+            switch (messageClass) {
+            case WARNING:
+            	if (instance.warningHistory.isEmpty()) {
+            		b.append("\n(Use the -nowarning option to disable this warning.)");
+            	}
+                break;
+            case ERROR:
+                if (TLCGlobals.tool)
+                {
+                    b.append("\n--End Error.");
+                }
+                break;
+            case TLCBUG:
+            case NONE:
+            default:
+                break;
+            }
+        }
+        DebugPrinter.print("Leaving getMessage()"); //$NON-NLS-1$
+        return b.toString();
+    }
+
+	private static String getMessage0(int messageClass, int messageCode, String[] parameters) {
+        final StringBuffer b = new StringBuffer();
+		// fill with different message depending on the error code
         switch (messageCode) {
         case EC.UNIT_TEST:
             b.append("[%1%][%2%]");
@@ -226,6 +339,11 @@ public class MP
                     + "pool (heap) may fix this.  But it won't help if some state has an enormous\n"
                     + "number of successor states, or if TLC must compute the value of a huge set.");
             break;
+        case EC.SYSTEM_OUT_OF_MEMORY_LIVENESS:
+            b.append("Java ran out of memory during liveness checking.  Running Java with a larger memory\n"
+                    + "allocation pool (heap) may fix this.  But it won't help if paths in the liveness graph\n"
+                    + "have an enormous number of states.");
+            break;
         case EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT:
             b.append("Out Of Memory. There are probably too many initial states.");
             break;
@@ -247,6 +365,13 @@ public class MP
         case EC.SYSTEM_ERROR_WRITING_POOL:
             b.append("when writing the disk (StatePoolWriter.run):\n%1%");
             break;
+		case EC.SYSTEM_ERROR_CLEANING_POOL:
+			if (messageClass == ERROR) {
+				b.append("Exception cleaning up an obsolete disk file.\n%1%");
+			} else if (messageClass == WARNING) {
+				b.append("Failed to clean up an obsolete disk file. Please manually delete %1% if free disk space is low.");
+			}
+            break;
 
         case EC.SYSTEM_DISKGRAPH_ACCESS:
             b.append("DiskGraph.toString()");
@@ -294,9 +419,6 @@ public class MP
             b.append("%1%\nUsage: java tlc2.Simulator [-option] inputfile");
             break;
         /* ----------------------------------------------------------------- */
-        case EC.TLC_USAGE:
-            b.append(Messages.getString("HelpMessage"));// $NON-NLS-1$
-            break;
         case EC.TLC_VERSION:
             b.append("TLC2 %1%");
             break;
@@ -341,7 +463,7 @@ public class MP
             b.append("Evaluating assumption %1% failed.\n%2%");
             break;
         case EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL:
-            b.append("State is not completely specified by the " + "initial predicate:\n%2%");
+            b.append("State is not completely specified by the initial predicate:\n%1%");
             break;
         case EC.TLC_INVARIANT_VIOLATED_INITIAL:
             b.append("Invariant %1% is violated by the initial state:\n%2%");
@@ -351,14 +473,35 @@ public class MP
             break;
 
         case EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT:
-            b.append("Successor state is not completely specified by the" + " next-state action.\n");
+			if (parameters.length == 3) {
+				b.append(
+						"Successor state is not completely specified by action %1% of the next-state relation. The following variable%2% not assigned: %3%.\n");
+			} else if (parameters.length == 2) {
+				b.append(
+						"Successor state is not completely specified by the next-state action. The following variable%1% not assigned: %2%.\n");
+			} else {
+				b.append("Successor state is not completely specified by the next-state action.\n");
+			}
             break;
         case EC.TLC_INVARIANT_VIOLATED_BEHAVIOR:
             b.append("Invariant %1% is violated.");
             break;
+        case EC.TLC_INVARIANT_VIOLATED_LEVEL:
+        	b.append("The invariant %1% is not a state predicate (one with no primes or temporal operators).");
+        	if (parameters.length > 1) {
+        		b.append("\nNote that a bug can cause TLC to incorrectly report this error.\n"
+        				+ "If you believe your TLA+ or PlusCal specification to be correct,\n"
+        				+ "please check if this bug described in LevelNode.java starting at line 590ff affects you.");
+        	}
+            break;
 
         case EC.TLC_INVARIANT_EVALUATION_FAILED:
-            b.append("Evaluating invariant %1% failed:\n%2%");
+            if (parameters.length == 1) {
+                b.append("Evaluating invariant %1% failed.");
+            }
+            else if (parameters.length == 2) {
+                b.append("Evaluating invariant %1% failed.\n%2%");
+            }
             break;
 
         case EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR:
@@ -366,7 +509,12 @@ public class MP
             break;
 
         case EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED:
-            b.append("Evaluating action property %1% failed.");
+            if (parameters.length == 1) {
+                b.append("Evaluating action property %1% failed.");
+            }
+            else if (parameters.length == 2) {
+                b.append("Evaluating action property %1% failed.\n%2%");
+            }
             break;
 
         case EC.TLC_DEADLOCK_REACHED:
@@ -380,6 +528,9 @@ public class MP
             break;
 
         // this is a TLC bug
+        case EC.TLC_STATES_AND_NO_NEXT_ACTION:
+            b.append("No next state actions defined to generate successor states from.");
+            break;
         case EC.TLC_FAILED_TO_RECOVER_NEXT:
             b.append("Failed to recover the next state from its fingerprint.");
             break;
@@ -392,6 +543,16 @@ public class MP
             b.append("This is probably a TLC bug(%1%).");
             break;
 
+        case EC.TLC_FINGERPRINT_EXCEPTION:
+            b.append("TLC was unable to fingerprint."
+                + "\n"
+                + "\nFingerprint Stack Trace:"
+                + "\n%1%"
+                + "\nReason:"
+                + "\n%2%"); // reason must come last, with nothing following, because Tool cannot parse the embedded error otherwise
+
+            break;
+
         case EC.TLC_NO_STATES_SATISFYING_INIT:
             b.append("There is no state satisfying the initial state predicate.");
             break;
@@ -404,9 +565,19 @@ public class MP
             if (TLCGlobals.tool) {
                 // format same as state printing for easier
                 // parsing by toolbox
-                b.append("%1%: Back to state.\n");
+                if (parameters.length == 1) {
+                    b.append("%1%: ").append(TLAConstants.BACK_TO_STATE).append("\n");
+                }
+                else if (parameters.length == 2) {
+                    b.append("%1%: ").append(TLAConstants.BACK_TO_STATE).append(": %2%\n");
+                }
             } else {
-                b.append("Back to state %1%.\n");
+                if (parameters.length == 1) {
+                    b.append(TLAConstants.BACK_TO_STATE).append(" %1%\n");
+                }
+                else if (parameters.length == 2) {
+                    b.append(TLAConstants.BACK_TO_STATE).append(" %1%: %2%\n");
+                }
             }
             break;
 
@@ -487,7 +658,12 @@ public class MP
             break;
 
         case EC.CHECK_PARAM_UNRECOGNIZED:
-            b.append("Unrecognized option: %1%");
+            if (parameters.length == 1) {
+                b.append("Unrecognized option: %1%");
+            }
+            else if (parameters.length == 2) {
+                b.append("Unrecognized option in file %2%: %1%");
+            }
             break;
         case EC.CHECK_PARAM_TOO_MANY_INPUT_FILES:
             b.append("More than one input files: %1% and %2%");
@@ -545,6 +721,14 @@ public class MP
         case EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD:
             b.append("The EXCEPT was applied to non-existing fields of the value at\n%1%");
             break;
+            
+        case EC.TLC_SYMMETRY_SET_TOO_SMALL:
+        	b.append("The set%1% %2% %3% been defined to be a symmetry set but contain%4% less than two elements.");
+        	break;
+            
+        case EC.TLC_SPECIFICATION_FEATURES_TEMPORAL_QUANTIFIER:
+        	b.append("TLC does not support temporal existential, nor universal, quantification over state variables.");
+        	break;
         /* ************************************************************************ */
         case EC.TLC_MODULE_TLCGET_UNDEFINED:
             b.append("TLCGet(%1%) was undefined.");
@@ -554,6 +738,10 @@ public class MP
             b.append("Overflow when computing %1%");
             break;
 
+        case EC.TLC_MODULE_ONE_ARGUMENT_ERROR:
+            b.append("The argument of %1% should be a %2%, but instead it is:\n%3%");
+            break;
+
         case EC.TLC_MODULE_ARGUMENT_ERROR:
             b.append("The %1% argument of %2% should be a %3%, but instead it is:\n%4%");
             break;
@@ -592,15 +780,15 @@ public class MP
             break;
 
         case EC.TLC_MODULE_BAG_UNION1:
-            b.append("Attemtped to apply BagUnion to the following set, whose\nelement is not a bag:\n%1%");
+            b.append("Attempted to apply BagUnion to the following set, whose\nelement is not a bag:\n%1%");
             break;
 
         case EC.TLC_MODULE_TRANSITIVE_CLOSURE:
-            b.append("Attemtped to apply TransitiveClosure to a set containing\nthe following value:\n%1%");
+            b.append("Attempted to apply TransitiveClosure to a set containing\nthe following value:\n%1%");
             break;
 
         case EC.TLC_MODULE_COMPUTING_CARDINALITY:
-            b.append("Attemtped to compute cardinality of the value\n%1%");
+            b.append("Attempted to compute cardinality of the value\n%1%");
             break;
         case EC.TLC_MODULE_EVALUATING:
             b.append("Evaluating an expression of the form %1% when s is not a %2%:\n%3%");
@@ -610,6 +798,37 @@ public class MP
             b.append("Attempted to apply the operator overridden by the Java method"
                     + "\n%1%,\nbut it produced the following error:\n%2%");
             break;
+        case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED:
+            b.append("Loading %1% operator override from %2% with signature: %3%.");
+            break;
+        case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH:
+            b.append("Failed to match %1% operator override from %2% with signature: %3%.");
+            break;
+        case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH:
+            b.append("Failed to match %1% operator override from %2% with signature: %3% (no such operator).");
+            break;
+        case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH:
+            b.append("Failed to match %1% operator override from %2% with signature: %3% (no such module).");
+            break;
+        case EC.TLC_MODULE_OVERRIDE_STDOUT:
+            b.append("%1%");
+            break;
+       case EC.TLC_FEATURE_UNSUPPORTED:
+            b.append("%1%");
+            break;
+        case EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY:
+            b.append("Declaring symmetry during liveness checking is dangerous. "
+            		+ "It might cause TLC to miss violations of the stated liveness properties. "
+            		+ "Please check liveness without symmetry defined.");
+            break;
+        case EC.TLC_FEATURE_LIVENESS_CONSTRAINTS:
+        	// Specifying Systems Section 14.3.5 page 247.
+        	// https://lamport.azurewebsites.net/tla/book.html
+			b.append("Declaring state or action constraints during liveness checking is dangerous: "
+					+ "Please read section 14.3.5 on page 247 of Specifying Systems "
+					+ "(https://lamport.azurewebsites.net/tla/book.html) and optionally the "
+					+ "discussion at https://discuss.tlapl.us/msg00994.html for more details.");
+            break;
 
         /* Liveness errors */
         case EC.TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT:
@@ -619,7 +838,11 @@ public class MP
             b.append("Implied-temporal checking--satisfiability problem has %1% branches.");
             break;
         case EC.TLC_LIVE_CANNOT_HANDLE_FORMULA:
-            b.append("TLC cannot handle the temporal formula %1%");
+        	if (parameters.length > 1) {
+        		b.append("TLC cannot handle the temporal formula %1%:\n%2%");
+        	} else {
+        		b.append("TLC cannot handle the temporal formula %1%");
+        	}
             break;
         case EC.TLC_LIVE_WRONG_FORMULA_FORMAT:
             b.append("Temporal formulas containing actions must be of forms <>[]A or []<>A.");
@@ -636,6 +859,9 @@ public class MP
         case EC.TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE:
             b.append("Encountered an action predicate that's not a boolean.");
             break;
+        case EC.TLC_LIVE_FORMULA_TAUTOLOGY:
+            b.append("Temporal formula is a tautology (its negation is unsatisfiable).");
+            break;
 
         case EC.TLC_EXPECTED_VALUE:
             b.append("TLC expected a %1% value, but did not find one. %2%");
@@ -688,90 +914,139 @@ public class MP
         /*------------------------------------------- */
         // TLC distributed 
         case EC.TLC_DISTRIBUTED_SERVER_RUNNING:
-            b.append("TLC server at %1% is ready (").append(SDF.format(new Date())).append(")");
+            b.append("TLC server at %1% is ready (").append(now()).append(")");
             break;
         case EC.TLC_DISTRIBUTED_WORKER_REGISTERED:
-            b.append("Registration for worker at %1% completed (").append(SDF.format(new Date())).append(")");
+            b.append("Registration for worker at %1% completed (").append(now()).append(")");
             break;
         case EC.TLC_DISTRIBUTED_WORKER_DEREGISTERED:
-            b.append("TLC worker %1% disconnected (").append(SDF.format(new Date())).append(")");
+            b.append("TLC worker %1% disconnected (").append(now()).append(")");
             break;
         case EC.TLC_DISTRIBUTED_WORKER_STATS:
         	//new Date() + " Worker: " + name + " Sent: " + sentStates + " Rcvd: " + receivedStates
-			b.append("Worker: %1% Sent: %2% Rcvd: %3% CacheRatio: %4% (").append(SDF.format(new Date())).append(")");
+			b.append("Worker: %1% Sent: %2% Rcvd: %3% CacheRatio: %4% (").append(now()).append(")");
             break;
         case EC.TLC_DISTRIBUTED_SERVER_NOT_RUNNING:
-            b.append("TLCServer is gone due to %1%, exiting worker... (").append(SDF.format(new Date())).append(")");
+            b.append("TLCServer is gone due to %1%, exiting worker... (").append(now()).append(")");
             break;
         case EC.TLC_DISTRIBUTED_SERVER_FINISHED:
-            b.append("TLCServer has finished, exiting worker... (").append(SDF.format(new Date())).append(")");
+            b.append("TLCServer has finished, exiting worker... (").append(now()).append(")");
             break;
 		case EC.TLC_DISTRIBUTED_VM_VERSION:
 			b.append(
 					"VM does not allow to get the UnicastRef port.\nWorker will be identified with port 0 in output (")
-					.append(SDF.format(new Date())).append(")");
+					.append(now()).append(")");
 			break;
 		case EC.TLC_DISTRIBUTED_WORKER_LOST:
-			b.append("TLC worker connection lost %1% (").append(SDF.format(new Date())).append(")");
+			b.append("TLC worker connection lost %1% (").append(now()).append(")");
 			break;
 		case EC.TLC_DISTRIBUTED_EXCEED_BLOCKSIZE:
-			b.append("Trying to limit max block size (to recover from transport failure): %1% (").append(SDF.format(new Date())).append(")");
+			b.append("Trying to limit max block size (to recover from transport failure): %1% (").append(now()).append(")");
 			break;
 		case EC.TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED:
-			b.append("%1% out of %2% FPSet server(s) registered (").append(SDF.format(new Date())).append(")");
+			b.append("%1% out of %2% FPSet server(s) registered (").append(now()).append(")");
 			break;
 		case EC.TLC_DISTRIBUTED_SERVER_FPSET_WAITING:
-			b.append("Waiting for %1% FPSet server(s) to register (").append(SDF.format(new Date())).append(")");
+			b.append("Waiting for %1% FPSet server(s) to register (").append(now()).append(")");
 			break;
             
         /*------------------------------------------- */
         case EC.TLC_STARTING:
-            b.append("Starting... (").append(SDF.format(new Date())).append(")");
+            b.append("Starting... (").append(now()).append(")");
             break;
         case EC.TLC_FINISHED:
-            b.append("Finished. (").append(SDF.format(new Date())).append(")");
-            break;
+            b.append("Finished in %1% at (").append(now()).append(")");
+            break;
+		/*
+		 * The two startup banners below are parsed by the Toolbox in
+		 * org.lamport.tla.toolbox.tool.tlc.output.data.TLCModelLaunchDataProvider.
+		 * startupMessagePattern.  Remember to update when the banners below 
+		 * get changed 
+		 * (see org.lamport.tla.toolbox.tool.tlc.output.data.TLCModelLaunchDataProviderTest)!!!
+		 */
         case EC.TLC_MODE_MC:
-            b.append("Running in Model-Checking mode.");
+            b.append("Running breadth-first search Model-Checking with fp %13% and seed %12% with %1% worker%2% on %3% cores with %10%MB heap and %11%MB offheap memory");
+            if (!"".equals(parameters[13])) {
+            	b.append(" [pid: %14%]");
+            } else {
+				// Make sure subsequent parameters, %15%... are processed below in the
+				// replaceString method. replaceString terminates if a string is not present,
+				// thus we replace %14% with the zero-length string to not change the output but
+				// to make replaceString happy.
+            	b.append("%14%");
+            }
+            b.append(" (%4% %5% %6%, %7% %8% %9%, %15%, %16%).");
+            break;
+        case EC.TLC_MODE_MC_DFS:
+            b.append("Running depth-first search Model-Checking with fp %13% and seed %12% with %1% worker%2% on %3% cores with %10%MB heap and %11%MB offheap memory");
+            if (!"".equals(parameters[13])) {
+            	b.append(" [pid: %14%]");
+            } else {
+            	// see TLC_MODE_MC above.
+            	b.append("%14%");
+            }
+     		b.append(" (%4% %5% %6%, %7% %8% %9%).");
             break;
         case EC.TLC_MODE_SIMU:
-            b.append("Running Random Simulation with seed %1%.");
+            b.append("Running Random Simulation with seed %1% with %2% worker%3% on %4% cores with %11%MB heap and %12%MB offheap memory");
+            if (!"".equals(parameters[12])) {
+            	b.append(" [pid: %13%]");
+            } else {
+            	// see TLC_MODE_MC above.
+            	b.append("%13%");
+            }
+            b.append(" (%5% %6% %7%, %8% %9% %10%).");
             break;
         case EC.TLC_COMPUTING_INIT:
             b.append("Computing initial states...");
             break;
+        case EC.TLC_COMPUTING_INIT_PROGRESS:
+            b.append("Computed %1% initial states...");
+            break;
         case EC.TLC_INIT_GENERATED1:
-            b.append("Finished computing initial states: %1% distinct state%2% generated.");
+			b.append("Finished computing initial states: %1% distinct state%2% generated at ")
+					.append(now()).append(".");
             break;
         case EC.TLC_INIT_GENERATED2:
-            b.append("Finished computing initial states: %1% states generated, with %2% of them distinct.");
+			b.append("Finished computing initial states: %1% state%2% generated, with %3% of them distinct at ")
+					.append(now()).append(".");
             break;
         case EC.TLC_INIT_GENERATED3:
-            b.append("Finished computing initial states: %1% states generated.\n"
-                    + "Because TLC recovers from a previous checkpoint, only %2% of them require further exploration.");
+			b.append("Finished computing initial states: %1% states generated.\n"
+					+ "Because TLC recovers from a previous checkpoint, only %2% of them require further exploration at ")
+					.append(now()).append(".");
             break;
         case EC.TLC_INIT_GENERATED4:
             b.append("Finished computing initial states: %1% states generated, with %2% of them distinct.");
             break;
         case EC.TLC_CHECKING_TEMPORAL_PROPS:
-			b.append("Checking temporal properties for the %1% state space with %2% distinct states at (")
-					.append(SDF.format(new Date())).append(")");
+			b.append("Checking %3%temporal properties for the %1% state space with %2% total distinct states at (")
+					.append(now()).append(")");
             break;
-
+		case EC.TLC_CHECKING_TEMPORAL_PROPS_END:
+			b.append("Finished checking temporal properties in %1% at " + now());
+	        break;
         case EC.TLC_SUCCESS:
             b.append("Model checking completed. No error has been found.\n"
-                    + "  Estimates of the probability that TLC did not check all reachable states\n"
-                    + "  because two distinct states had the same fingerprint:\n" + "  calculated (optimistic):  %1%\n"
-                    + "  based on the actual fingerprints:  %2%");
+                    + "  Estimates of the probability that TLC did not check all reachable states\n");
+            if (parameters.length == 1) {
+            	b.append("  because two distinct states had the same fingerprint:\n" + "  calculated (optimistic):  %1%");
+            } else {
+            	b.append("  because two distinct states had the same fingerprint:\n"
+            			+ "  calculated (optimistic):  %1%\n" + "  based on the actual fingerprints:  %2%");
+            }
             break;
         case EC.TLC_SEARCH_DEPTH:
-            b.append("The depth of the complete state graph search is %1%.");
+			b.append("The depth of the complete state graph search is %1%.");
+            break;
+        case EC.TLC_STATE_GRAPH_OUTDEGREE:
+			b.append("The average outdegree of the complete state graph is %2% (minimum is %1%, the maximum %4% and the 95th percentile is %3%).");
             break;
-        case EC.TLC_CHECKPOINT_START:
+       case EC.TLC_CHECKPOINT_START:
             b.append("Checkpointing of run %1%");
             break;
         case EC.TLC_CHECKPOINT_END:
-            b.append("Checkpointing completed at (").append(SDF.format(new Date())).append(")");
+            b.append("Checkpointing completed at (").append(now()).append(")");
             break;
         case EC.TLC_CHECKPOINT_RECOVER_START:
             b.append("Starting recovery from checkpoint %1%");
@@ -793,9 +1068,13 @@ public class MP
             b.append("The number of states generated: %1%\nSimulation using seed %2% and aril %3%");
             break;
         case EC.TLC_PROGRESS_STATS:
-			b.append("Progress(%1%) at " + SDF.format(new Date()) + ": %2% states generated ("
-					+ df.format(Long.valueOf(parameters[4])) + " s/min), %3% distinct states found ("
-					+ df.format(Long.valueOf(parameters[5])) + " ds/min), %4% states left on queue.");
+        	if (parameters.length == 4) {
+				b.append("Progress(%1%) at " + now() + ": %2% states generated, "
+						+ "%3% distinct states found, " + "%4% states left on queue.");
+        	} else if (parameters.length == 6) {
+        		b.append("Progress(%1%) at " + now() + ": %2% states generated ("
+        				+ "%5% s/min), %3% distinct states found (%6% ds/min), %4% states left on queue.");
+        	}
             break;
         case EC.TLC_PROGRESS_START_STATS_DFID:
             b.append("Starting level %1%: %2% states generated, %3% distinct states found.");
@@ -804,7 +1083,7 @@ public class MP
             if (TLCGlobals.tool)
             {
                 // same format as model checking progress reporting for easier parsing by the toolbox
-                b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + SDF.format(new Date())
+                b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + now()
                         + ": %1% states generated, %2% distinct states found, " + NOT_APPLICABLE_VAL
                         + " states left on queue.");
             } else
@@ -816,7 +1095,7 @@ public class MP
             if (TLCGlobals.tool)
             {
                 // same format as model checking progress reporting for easier parsing by the toolbox
-                b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + SDF.format(new Date())
+                b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + now()
                         + ": %1% states generated, " + NOT_APPLICABLE_VAL + " distinct states found, "
                         + NOT_APPLICABLE_VAL + " states left on queue.");
             } else
@@ -826,15 +1105,34 @@ public class MP
             break;
 
         case EC.TLC_COVERAGE_START:
-            b.append("The coverage statistics at " + SDF.format(new Date()));
+            b.append("The coverage statistics at " + now());
             break;
         case EC.TLC_COVERAGE_VALUE:
             b.append("  %1%: %2%");
             break;
-
+        case EC.TLC_COVERAGE_VALUE_COST:
+            b.append("  %1%: %2%:%3%");
+            break;
+        case EC.TLC_COVERAGE_INIT:
+       		b.append("%1%: %2%:%3%");
+            break;
+        case EC.TLC_COVERAGE_NEXT:
+       		b.append("%1%: %2%:%3%");
+            break;
+        case EC.TLC_COVERAGE_PROPERTY:
+       		b.append("%1%");
+            break;
+        case EC.TLC_COVERAGE_MISMATCH:
+			b.append(
+					"CostModel lookup failed for expression <%1%>. Reporting costs into <%2%> instead (Safety and Liveness checking is unaffected. Please report a bug.)");
+        	break;
         case EC.TLC_COVERAGE_END:
             b.append("End of statistics.");
             break;
+		case EC.TLC_COVERAGE_END_OVERHEAD:
+			b.append("End of statistics (please note that for performance reasons large models\n"
+					+ "are best checked with coverage and cost statistics disabled).");
+            break;
 
         /* ************************************************************************ */
         // errors evaluating the config file and the MC file
@@ -848,6 +1146,12 @@ public class MP
         case EC.TLC_CONFIG_WRONG_SUBSTITUTION:
             b.append("The configuration file substitutes for %1% with the undefined identifier %2%.");
             break;
+        case EC.TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR:
+            b.append("In evaluation, the identifier %1% is either undefined or not an operator.\n%2%");
+            break;
+        case EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT:
+            b.append("The configuration file substitutes constant %1% with non-constant %2%%3%");
+            break;
         case EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS:
             b.append("The configuration file substitutes for %1% with %2% of different number of arguments.");
             break;
@@ -858,7 +1162,12 @@ public class MP
             b.append("The configuration file cannot specify both INIT/NEXT and SPECIFICATION fields.");
             break;
         case EC.TLC_CONFIG_ID_REQUIRES_NO_ARG:
-            b.append("TLC requires %1% not to take any argument.");
+            if (parameters.length == 1) {
+                b.append("TLC requires %1% not to take any argument.");
+            }
+            else if (parameters.length == 2) {
+                b.append("TLC requires %1% not to take any argument, but one was given: %2%");
+            }
             break;
         case EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED:
             b.append("The %1% %2% specified in the configuration file" + "\nis not defined in the specification.");
@@ -868,7 +1177,13 @@ public class MP
             break;
         case EC.TLC_CONFIG_MISSING_INIT:
             b.append("The configuration file did not specify the initial state predicate." +
-                     // Following part of error message added by LL on 15 Nov 2012
+                     // The below part of the error message was added by LL on 15 Nov 2012
+            		 //
+            		 //	ldq, 13 Feb 2020: I don't think this is semantically correct; I receive
+                     //			no errors when defining a specification that references
+            		 //			a formula which is a parameterized INSTANCE. I *do* receive
+                     //			such an error when that formula is being constrained via
+            		 //			the temporal existential qualifier.
                      "\nCan also be caused by trying to run TLC on a specification from" +
                      "\na module imported with a parameterized INSTANCE statement.");
             break;
@@ -900,6 +1215,11 @@ public class MP
             b.append("The specification contains more than one conjunct of the form [][Next]_v,"
                     + "\nbut TLC can handle only specifications with one next-state relation.");
             break;
+        case EC.TLC_TRACE_TOO_LONG:
+            b.append("The specification contains one or more behaviors with 65536 or more states,"
+                    + "\nbut TLC can only handle behaviors of length up to 65535 states. The last\n"
+                    + "state in the behavior is:\n%1%");
+        	break;
         case EC.TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED:
             b.append("The property %1% is not correctly defined.");
             break;
@@ -925,6 +1245,10 @@ public class MP
         case EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE:
             b.append("TLC encountered a temporal formula (%1%) when evaluating" + " a predicate or action.\n%2%");
             break;
+        case EC.TLC_ENVIRONMENT_JVM_GC:
+			b.append(
+					"Please run the Java VM which executes TLC with a throughput optimized garbage collector by passing the \"-XX:+UseParallelGC\" property.");
+            break;
 
         /* ************************************************************************ */
         // state printing
@@ -932,10 +1256,14 @@ public class MP
             b.append("%1%:\n%2%");
             break;
         case EC.TLC_STATE_PRINT2:
-            b.append("%1%: %2%\n%3%");
+        	if (DO_DEBUG) {
+        		b.append("%1%: %2%\n%3%fp: %4%\n");
+        	} else {
+        		b.append("%1%: %2%\n%3%");
+        	}
             break;
         case EC.TLC_STATE_PRINT3:
-            b.append("%1%: Stuttering");
+            b.append("%1%:").append(TLAConstants.STUTTERING);
             break;
 
         /* ************************************************************************ */
@@ -985,34 +1313,8 @@ public class MP
         }
 
         replaceString(b, parameters);
-
-        if (TLCGlobals.tool)
-        {
-            // for the tool we always print the message code
-            b.append(CR).append(DELIM).append(ENDMSG).append(messageCode).append(SPACE).append(DELIM);
-        } else
-        {
-
-            // post processing
-            switch (messageClass) {
-            case WARNING:
-                b.append("\n(Use the -nowarning option to disable this warning.)");
-                break;
-            case ERROR:
-                if (TLCGlobals.tool)
-                {
-                    b.append("\n--End Error.");
-                }
-                break;
-            case TLCBUG:
-            case NONE:
-            default:
-                break;
-            }
-        }
-        DebugPrinter.print("Leaving getMessage()"); //$NON-NLS-1$
         return b.toString();
-    }
+	}
 
     /**
      * Returns the error  
@@ -1041,6 +1343,7 @@ public class MP
      */
     public static String getError(int errorCode, String[] parameters)
     {
+    	recorder.record(errorCode, (Object[]) parameters);
         return getMessage(ERROR, errorCode, parameters);
     }
 
@@ -1071,6 +1374,7 @@ public class MP
      */
     public static String getMessage(int errorCode, String[] parameters)
     {
+    	recorder.record(errorCode, (Object[]) parameters);
         return getMessage(NONE, errorCode, parameters);
     }
 
@@ -1088,9 +1392,9 @@ public class MP
      * Prints the error for a given error code
      * @param errorCode
      */
-    public static void printError(int errorCode)
+    public static int printError(int errorCode)
     {
-        printError(errorCode, EMPTY_PARAMS);
+        return printError(errorCode, EMPTY_PARAMS);
     }
 
     /**
@@ -1098,9 +1402,9 @@ public class MP
      * @param errorCode
      * @param parameter
      */
-    public static void printError(int errorCode, String parameter)
+    public static int printError(int errorCode, String parameter)
     {
-        printError(errorCode, new String[] { parameter });
+        return printError(errorCode, new String[] { parameter });
     }
 
     /**
@@ -1109,13 +1413,14 @@ public class MP
      * @param parameters a list of string parameters to be inserted into the message, by replacing 
      * %i% with the i-th parameter in the array
      */
-    public static void printError(int errorCode, String[] parameters)
+    public static int printError(int errorCode, String[] parameters)
     {
     	recorder.record(errorCode, (Object[]) parameters);
         // write the output
         DebugPrinter.print("entering printError(int, String[]) with errorCode " + errorCode); //$NON-NLS-1$
         ToolIO.out.println(getMessage(ERROR, errorCode, parameters));
         DebugPrinter.print("leaving printError(int, String[])"); //$NON-NLS-1$
+        return errorCode;
     }
 
     /**
@@ -1197,7 +1502,14 @@ public class MP
         } else {
             msg = msg + "\nThe error occurred when TLC was " + cause + ".";
         }
-        msg = msg + "\nThe exception was a " + throwable.getClass().getName();
+        if (throwable instanceof Assert.TLCRuntimeException) {
+			// MK 07/25/2017: Disguise TLCRuntimeException with its parent class
+			// RuntimeException to not change the externally visible TLC output 
+        	// when an exception gets reports.
+        	msg = msg + "\nThe exception was a " +  RuntimeException.class.getName() + "\n";
+        } else {
+        	msg = msg + "\nThe exception was a " +  throwable.getClass().getName() + "\n";
+        }
         if (throwable.getMessage() != null) {
             msg = msg + ": " + throwable.getMessage();
             
@@ -1233,13 +1545,14 @@ public class MP
      * @param errorCode
      * @param cause
      */
-    public static void printError(int errorCode, Throwable cause)
+    public static int printError(int errorCode, Throwable cause)
     {
         if (errorCode == EC.GENERAL) {
             printError(errorCode, "", cause);
         } else {
             printError(errorCode, cause.getMessage(), cause, true);
         }
+        return errorCode;
     }
 
     /**
@@ -1267,15 +1580,31 @@ public class MP
      * @param parameters a list of string parameters to be inserted into the message, by replacing 
      * %i% with the i-th parameter in the array
      */
-    public static void printMessage(int errorCode, String[] parameters)
+    public static void printMessage(int errorCode, String... parameters)
     {
     	recorder.record(errorCode, (Object[]) parameters);
         DebugPrinter.print("entering printMessage(int, String[]) with errorCode " + errorCode); //$NON-NLS-1$
         // write the output
-        ToolIO.out.println(getMessage(NONE, errorCode, parameters));
+		ToolIO.out.println(getMessage(NONE, errorCode, parameters));
+		// Don't log the start and end markers when in -tool mode.
+		TLAFlightRecorder.message(getMessage0(NONE, errorCode, parameters));
         DebugPrinter.print("leaving printError(int, String[]) with errorCode "); //$NON-NLS-1$
     }
 
+    public static int printTLCRuntimeException(final TLCRuntimeException tre) {
+    	if (tre.parameters != null) {
+    		recorder.record(tre.errorCode, (Object[]) new Object[] {tre});
+    		DebugPrinter.print("entering printTLCRuntimeException(TLCRuntimeException) with errorCode " + tre.errorCode); //$NON-NLS-1$
+    		// write the output
+    		ToolIO.out.println(getMessage(ERROR, tre.errorCode, tre.parameters));
+    		DebugPrinter.print("leaving printTLCRuntimeException(TLCRuntimeException) with errorCode "); //$NON-NLS-1$
+    	} else {
+    		// Legacy code path except actual errorCode instead of EC.General.
+    		printError(tre.errorCode, tre);
+    	}
+    	return tre.errorCode;
+    }
+    
     /** 
      * Prints the state
      * @param parameters
@@ -1311,6 +1640,13 @@ public class MP
         DebugPrinter.print("leaving printTLCBug(int, String[])"); //$NON-NLS-1$
     }
 
+    /**
+     * @see MP#printWarning(int)
+     */
+	public static void printWarning(final int errorCode) {
+		printWarning(errorCode, new String[0]);
+	}
+
     /**
      * @see MP#printWarning(int, String[])
      */
@@ -1323,7 +1659,7 @@ public class MP
      * @param errorCode
      * @param parameters
      */
-    public static void printWarning(int errorCode, String[] parameters)
+    public static void printWarning(int errorCode, String... parameters)
     {
     	recorder.record(errorCode, (Object[]) parameters);
         DebugPrinter.print("entering printWarning(int, String[]) with errorCode " + errorCode); //$NON-NLS-1$
@@ -1424,4 +1760,18 @@ public class MP
 	public static void setRecorder(MPRecorder aRecorder) {
 		recorder = aRecorder;
 	}
+
+    private static String now() {
+    	if (Boolean.getBoolean(MP.class.getName() + ".noTimestamps")) {
+			// Return NOW if requested by setting -Dtlc2.output.MP.noTimestamps=true on the
+			// command-line. Can be useful if one wants to compare TLC's output from
+			// multiple executions.
+    		return "NOW";
+    	}
+		return SDF.format(new Date());
+	}
+
+	public static String format(final long l) {
+		return df.format(l);
+	}
 }
diff --git a/tlatools/src/tlc2/output/Message.java b/tlatools/src/tlc2/output/Message.java
deleted file mode 100644
index 83bbb86372defcbe5d1d1d4d755fe785090d94ca..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/output/Message.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package tlc2.output;
-
-import java.util.Date;
-
-public class Message {
-	
-	private int messageClass;
-	private int errorCode;
-	private String[] parameters;
-	private Date date;
-	
-	public Message(int errorCode, String[] parameters, Date date){
-		this.errorCode = errorCode;
-		this.parameters = parameters;
-		this.date = date;
-	}
-
-	public Message(int messageClass, int errorCode, String[] parameters,
-			Date date) {
-		this.messageClass = messageClass;
-		this.errorCode = errorCode;
-		this.parameters = parameters;
-		this.date = date;
-	}
-
-	public int getMessageClass(){
-		return messageClass;
-	}
-	
-	public int getMessageCode() {
-		return errorCode;
-	}
-
-	public String[] getParameters() {
-		return parameters;
-	}
-
-	public Date getDate(){
-		return date;
-	}
-	
-}
diff --git a/tlatools/src/tlc2/output/OutputCollector.java b/tlatools/src/tlc2/output/OutputCollector.java
deleted file mode 100644
index 6a7db4b5d56329dcd53f597be182766e6bd9983c..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/output/OutputCollector.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package tlc2.output;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Hashtable;
-
-import tla2sany.semantic.ExprNode;
-import tla2sany.semantic.ModuleNode;
-import tla2sany.st.Location;
-import tlc2.tool.TLCState;
-import tlc2.tool.TLCStateInfo;
-
-public class OutputCollector {
-
-	private static TLCState initialState = null;
-	private static ArrayList<TLCStateInfo> trace = null;
-	private static ArrayList<Message> allMessages = new ArrayList<Message>();
-	private static Hashtable<Location, Long> lineCount = new Hashtable<Location, Long>();
-	private static ModuleNode moduleNode = null;
-	private static ArrayList<ExprNode> violatedAssumptions = new ArrayList<>();
-
-	public static ArrayList<TLCStateInfo> getTrace() {
-		return trace;
-	}
-
-	public static void setTrace(ArrayList<TLCStateInfo> trace) {
-		OutputCollector.trace = trace;
-	}
-
-	public static void addStateToTrace(TLCStateInfo tlcStateInfo) {
-		if (trace == null) {
-			trace = new ArrayList<TLCStateInfo>();
-		}
-		trace.add(tlcStateInfo);
-	}
-
-	public static void setInitialState(TLCState initialState) {
-		OutputCollector.initialState = initialState;
-	}
-
-	public static TLCState getInitialState() {
-		return OutputCollector.initialState;
-	}
-
-	public static ArrayList<Message> getAllMessages() {
-		return allMessages;
-	}
-
-	public static void addViolatedAssumption(ExprNode assumption) {
-			violatedAssumptions.add(assumption);
-	}
-
-	public static ArrayList<ExprNode> getViolatedAssumptions() {
-		return violatedAssumptions;
-	}
-
-	public synchronized static void saveMessage(int messageClass,
-			int messageCode, String[] parameters) {
-
-		Message m = new Message(messageClass, messageCode, parameters,
-				new Date());
-		allMessages.add(m);
-	}
-
-	public static ModuleNode getModuleNode() {
-		return moduleNode;
-	}
-
-	public static void setModuleNode(ModuleNode moduleNode) {
-		OutputCollector.moduleNode = moduleNode;
-	}
-
-	public static Hashtable<Location, Long> getLineCountTable() {
-		return new Hashtable<Location, Long>(lineCount);
-	}
-
-	public static void putLineCount(Location location, long val) {
-		lineCount.put(location, val);
-	}
-}
diff --git a/tlatools/src/tlc2/output/SpecTraceExpressionWriter.java b/tlatools/src/tlc2/output/SpecTraceExpressionWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..38d4cc4e6968030a9c7fa253ca8c8f2a2b1c14f9
--- /dev/null
+++ b/tlatools/src/tlc2/output/SpecTraceExpressionWriter.java
@@ -0,0 +1,847 @@
+package tlc2.output;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import tlc2.model.Assignment;
+import tlc2.model.Formula;
+import tlc2.model.MCState;
+import tlc2.model.MCVariable;
+import tlc2.model.TraceExpressionInformationHolder;
+import util.TLAConstants;
+
+/**
+ * This is a reified class of spec writer which can produce specs capable of containing trace expressions; it is also
+ * 	the parent class for a more specialized version used by the toolbox, {@code TraceExpressionModelWriter}.
+ */
+public class SpecTraceExpressionWriter extends AbstractSpecWriter {
+	private static final String TRACE_EXPRESSION_VARIABLE = "TraceExp";
+	private static final String TRI_INDENT = TLAConstants.INDENT + TLAConstants.INDENT + TLAConstants.INDENT;
+	
+	/**
+	 * This will generate three identifiers equal to the initial and next state
+	 * predicate for the trace, and the action constraint.
+	 * 
+	 * @param tlaBuffer the buffer into which the TLA code will be placed
+	 * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed
+	 * @param trace
+	 * @param expressionData data on trace explorer expressions, can be null
+	 * @return String[], first element is the identifier for the initial state predicate,
+	 * second element is the identifier for the next-state action, the third element is the identifier for
+	 * the action contraint
+	 * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String)
+	 */
+	public static String[] addInitNextToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData) {
+	    final String initId = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.INIT_SCHEME);
+	    final String nextId = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.NEXT_SCHEME);
+	    final String actionConstraintId = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.ACTIONCONSTRAINT_SCHEME);
+	
+	    addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId);
+	
+	    return new String[] { initId, nextId, actionConstraintId };
+	}
+	
+	/**
+	 * This calls:
+	 * 	{@code addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId, TLAConstants.Schemes.NEXT_SCHEME, false);}
+	 * 
+	 * @param tlaBuffer the buffer into which the TLA code will be placed
+	 * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed
+	 * @param trace
+	 * @param expressionData data on trace explorer expressions, can be null
+	 * @param initId the identifier to be used for the initial state predicate, cannot be null
+	 * @param nextId the identifier to be used for the next-state action, cannot be null
+	 * @param actionConstraintId the indentified used for the action constraint
+	 * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String, String)
+	 */
+	public static void addInitNextToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData, final String initId,
+			final String nextId, final String actionConstraintId) {
+		addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId,
+							 TLAConstants.Schemes.NEXT_SCHEME, false);
+	}
+
+	/**
+	 * This calls:
+	 * 	{@code addInitNextToBuffers(cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId, nextSubActionBasename, leaveStubsForTraceExpression);}
+	 * and then concatenates the returned {@code StringBuilder} instances in order to tlaBuffer.
+	 * 
+	 * @param tlaBuffer the buffer into which the TLA code will be placed
+	 * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed
+	 * @param trace
+	 * @param expressionData data on trace explorer expressions, can be null
+	 * @param initId the identifier to be used for the initial state predicate, cannot be null
+	 * @param nextId the identifier to be used for the next-state action, cannot be null
+	 * @param actionConstraintId the indentified used for the action constraint
+	 * @param nextSubActionBasename the base string to be used as the prefix to unique names for next sub-actions
+	 * @param leaveStubsForTraceExpression if true, then a variable will be defined {@link TRACE_EXPRESSION_VARIABLE},
+	 * 						yet commented out, and similarly conjoined, but commented out in the SpecTE Init and Next
+	 * 						declarations
+	 */
+	public static void addInitNextToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData, final String initId,
+			final String nextId, final String actionConstraintId, final String nextSubActionBasename,
+			final boolean leaveStubsForTraceExpression) {
+		final StringBuilder[] tlaBuffers = addInitNextToBuffers(cfgBuffer, trace, expressionData, initId, nextId,
+				actionConstraintId, nextSubActionBasename, leaveStubsForTraceExpression);
+		
+		tlaBuffer.append(tlaBuffers[0].toString());
+		tlaBuffer.append(tlaBuffers[1].toString());
+	}
+
+	/**
+	 * This will set initId equal to the initial state predicate, nextId equal to the next state
+	 * action for the trace, and actionConstraintId equal to the action constraint for the trace.
+	 * If expressionData is not null, it should contain information about trace explorer expressions. This
+	 * information is used to appropriately put the variables representing trace explorer expressions
+	 * in the trace. In the following example, trace explorer expressions are used, but if expressionData
+	 * is null, those variables will not appear in the init and next definitions, but everything else will be the same.
+	 * 
+	 * Note: In the following example, the expressions expr1,...,expr6, texpr1, texpr2 can take up multiple
+	 * lines.
+	 * 
+	 * Consider the following trace:
+	 * 
+	 * <Initial predicate> <State num 1>
+	 * var1=expr1
+	 * var2=expr2
+	 * 
+	 * <Action...> <State num 2>
+	 * var1=expr3
+	 * var2=expr4
+	 * 
+	 * <Action...> <State num 3>
+	 * var1=expr5
+	 * var2=expr6
+	 * 
+	 * The user has defined two expressions in the trace explorer:
+	 * 
+	 * texpr1 (level 2 represented by var3)
+	 * texpr2 (level 1 represented by var4)
+	 * 
+	 * This method defines the following identifiers:
+	 * 
+	 * init_4123123123 ==
+	 * var1=(
+	 * expr1
+	 * )/\
+	 * var2=(
+	 * expr2
+	 * )/\
+	 * var3=(
+	 * "--"
+	 * )/\
+	 * var4=(
+	 * texpr2
+	 * )
+	 * 
+	 * next_12312312312 ==
+	 * (var1=(
+	 * expr1
+	 * )/\
+	 * var2=(
+	 * expr2
+	 * )/\
+	 * var1'=(
+	 * expr3
+	 * )/\
+	 * var2'=(
+	 * expr4
+	 * )/\
+	 * var3'=(
+	 * texpr1
+	 * )/\
+	 * var4'=(
+	 * texpr2
+	 * )')
+	 * \/
+	 * (var1=(
+	 * expr3
+	 * )/\
+	 * var2=(
+	 * expr4
+	 * )/\
+	 * var1'=(
+	 * expr5
+	 * )/\
+	 * var2'=(
+	 * expr6
+	 * )/\
+	 * var3'=(
+	 * texpr1
+	 * )/\
+	 * var4'=(
+	 * texpr2
+	 * )')
+	 * 
+	 * If the last state is back to state i, then this method treats
+	 * the trace as if it has the state labeled "Back to state i" removed and
+	 * replaced with a copy of state i.
+	 * 
+	 * If the last state is stuttering, then this method treats the trace as if it
+	 * has the state labeled "Stuttering" removed and replaced with a copy
+	 * of the state before the state labeled "Stuttering".
+	 * 
+	 * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed
+	 * @param trace
+	 * @param expressionData data on trace explorer expressions, can be null
+	 * @param initId the identifier to be used for the initial state predicate, cannot be null
+	 * @param nextId the identifier to be used for the next-state action, cannot be null
+	 * @param actionConstraintId the indentified used for the action constraint
+	 * @param nextSubActionBasename the base string to be used as the prefix to unique names for next sub-actions
+	 * @param leaveStubsForTraceExpression if true, then a variable will be defined {@link TRACE_EXPRESSION_VARIABLE},
+	 * 						yet commented out, and similarly conjoined, but commented out in the SpecTE Init and Next
+	 * 						declarations
+	 * @return an array of length 2, the first element is a buffer containing all trace expression subaction
+	 * 				declarations followed by the action constraint definition; the second element is a buffer
+	 * 				containing a potential VARIABLE stub for the trace expression variable, followed by the
+	 * 				definitions for Init and finally Next. This will return null if {@code trace.size() == 0}
+	 */
+	public static StringBuilder[] addInitNextToBuffers(final StringBuilder cfgBuffer,
+													   final List<MCState> trace,
+													   final TraceExpressionInformationHolder[] expressionData,
+													   final String initId, final String nextId,
+													   final String actionConstraintId,
+													   final String nextSubActionBasename,
+													   final boolean leaveStubsForTraceExpression) {
+		if (trace.size() > 0) {
+	        final Iterator<MCState> it = trace.iterator();
+	        MCState currentState = it.next();
+	        final StringBuilder subActionsAndConstraint = new StringBuilder();
+	        final StringBuilder initAndNext = new StringBuilder();
+	
+	        /*******************************************************
+	         * Add the init definition.                            *
+	         *******************************************************/
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INIT).append(" definition");
+				cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.INIT).append(TLAConstants.CR);
+				cfgBuffer.append(initId).append(TLAConstants.CR);
+			}
+			
+			if (leaveStubsForTraceExpression) {
+				initAndNext.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.VARIABLE).append(' ');
+				initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.CR).append(TLAConstants.CR);
+			}
+	
+			initAndNext.append(TLAConstants.COMMENT).append("TRACE INIT definition ");
+			initAndNext.append(TLAConstants.TraceExplore.TRACE_EXPLORE_INIT).append(TLAConstants.CR);
+			initAndNext.append(initId).append(TLAConstants.DEFINES_CR);
+	        final MCVariable[] vars = currentState.getVariables();
+	
+	        // variables from spec
+			for (int i = 0; i < vars.length; i++) {
+	            final MCVariable var = vars[i];
+	            /*
+	             *    /\ var = (
+	             *            expr
+	             *          )
+	             */
+	            initAndNext.append(TLAConstants.INDENTED_CONJUNCTIVE);
+	            initAndNext.append(var.getName()).append(TLAConstants.EQ).append(TLAConstants.L_PAREN);
+	            initAndNext.append(TLAConstants.CR);
+	            
+	            initAndNext.append(var.getValueAsStringReIndentedAs(TRI_INDENT)).append(TLAConstants.CR);
+	            
+	            initAndNext.append(TLAConstants.INDENT).append(TLAConstants.INDENT);
+	            initAndNext.append(TLAConstants.R_PAREN).append(TLAConstants.CR);
+	        }
+	
+	        // variables representing trace explorer expressions
+			if (expressionData != null) {
+				for (int i = 0; i < expressionData.length; i++) {
+	                final TraceExpressionInformationHolder expressionInfo = expressionData[i];
+	                initAndNext.append(TLAConstants.INDENTED_CONJUNCTIVE);
+	                initAndNext.append(expressionInfo.getVariableName()).append(TLAConstants.EQ);
+	                initAndNext.append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	
+	                initAndNext.append(TRI_INDENT);
+					if (expressionInfo.getLevel() == 2) {
+	                    // add "--" if the expression is temporal level
+						initAndNext.append(TLAConstants.TRACE_NA);
+					} else {
+	                    // add the actual expression if it is not temporal level
+						initAndNext.append(expressionInfo.getIdentifier());
+	                }
+	
+					initAndNext.append(TLAConstants.CR).append(TLAConstants.INDENT).append(TLAConstants.INDENT);
+		            initAndNext.append(TLAConstants.R_PAREN).append(TLAConstants.CR);
+	            }
+	        }
+			
+			if (leaveStubsForTraceExpression) {
+				initAndNext.append(TLAConstants.COMMENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+				initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.EQ);
+				initAndNext.append(TLAConstants.KeyWords.TRUE).append(TLAConstants.CR);
+			}
+	
+			initAndNext.append(CLOSING_SEP).append(TLAConstants.CR);
+	
+	        /**********************************************************
+	         *  Now add the next state actions definition             *
+	         **********************************************************/
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.NEXT).append(" definition");
+				cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.NEXT).append(TLAConstants.CR);
+				cfgBuffer.append(nextId).append(TLAConstants.CR);
+			}
+	
+	        MCState nextState;
+	        final boolean isSingleState;
+			if (it.hasNext()) {
+				nextState = it.next();
+				isSingleState = false;
+			} else {
+	            nextState = currentState;
+	            isSingleState = true;
+	        }
+	
+	        /*
+	         * MAK 09/25/2019: Previously, TE.tla was a next-state relation consisting of
+	         * disjuncts of (unnamed) sub-actions:
+	         * 
+	         * Next_123 == (x=1 /\ x'=2) \/ (x=2 /\ x'=3) \/ ... \/ (x=42 /\ x'=42)
+	         * 
+	         * At runtime, TLC created an Action for each sub-action of the next-state
+	         * relation (42 for the example above). For each state generated during
+	         * breadth-first search, all Actions were evaluated, but the assumption was
+	         * that only the one corresponding to the level of the current state would
+	         * generate a valid successor state. However, this is not true if a trace expression This poses two problems:
+	         * 1)  Actions may 
+	         * 
+	         * However, for some next-state relations
+	         * 
+	         * Non-determinism in trace expression
+	         */
+	        final StringBuilder nextDisjunctBuffer = new StringBuilder();
+	        nextDisjunctBuffer.append(nextId).append(TLAConstants.DEFINES_CR);
+	        final String firstIndent;
+			if (leaveStubsForTraceExpression) {
+				nextDisjunctBuffer.append(TLAConstants.TLA_AND).append(' ');
+				firstIndent = " ";
+			} else {
+				firstIndent = TLAConstants.INDENT;
+			}
+	        
+	        final StringBuilder actionConstraintBuffer = new StringBuilder();
+	        actionConstraintBuffer.append(actionConstraintId).append(TLAConstants.DEFINES_CR);
+	        actionConstraintBuffer.append(TLAConstants.BEGIN_TUPLE).append(TLAConstants.CR);
+	
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.COMMENT).append("Action Constraint definition").append(TLAConstants.CR);
+				cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.ACTION_CONSTRAINT).append(TLAConstants.CR);
+				cfgBuffer.append(TLAConstants.COMMENT).append(actionConstraintId).append(TLAConstants.CR);
+			}
+
+	        int subActionIndex = 0;
+			while (nextState != null) {
+				final String nextDisjunct = String.format("%s_sa_%d", nextSubActionBasename, subActionIndex);
+				nextDisjunctBuffer.append((subActionIndex == 0) ? firstIndent : TLAConstants.INDENT);
+				nextDisjunctBuffer.append(TLAConstants.TLA_OR).append(' ').append(nextDisjunct).append(TLAConstants.CR);
+		        actionConstraintBuffer.append(nextDisjunct);
+		        	        	
+		        subActionsAndConstraint.append(TLAConstants.COMMENT).append("TRACE Sub-Action definition ");
+		        subActionsAndConstraint.append(subActionIndex++).append(TLAConstants.CR);
+		        subActionsAndConstraint.append(nextDisjunct).append(TLAConstants.DEFINES_CR);
+	            /*
+	             * Handle Back to state and stuttering.
+	             * 
+	             * nextState is assigned to the state which the "Back to state"
+	             * or "Stuttering" state represents. If nextState is "Back to state i",
+	             * then it is assigned to state i. If nextState is "Stuttering", then
+	             * it is assigned to the current state.
+	             */
+				if (nextState.isBackToState()) {
+					nextState = trace.get(nextState.getStateNumber() - 1);
+				} else if (nextState.isStuttering()) {
+					nextState = currentState;
+				}
+	
+	            /*
+	             * Write the action:
+	             * 
+	             * (/\ var1=(
+	             * expr1
+	             * )
+	             * /\ var2=(
+	             * expr2
+	             * )
+	             * /\ var1'=(
+	             * expr3
+	             * )
+	             * /\ var2'=(
+	             * expr4
+	             * ))
+	             */
+				subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	
+	            final MCVariable[] currentStateVars = currentState.getVariables();
+	            final MCVariable[] nextStateVars = nextState.getVariables();
+	
+	            /*
+	             * Iterate through current state variables. This adds:
+	             * 
+	             * /\ var1=(
+	             * expr1
+	             * )
+	             * /\ var2=(
+	             * expr2
+	             * )
+	             * 
+	             */
+				for (int i = 0; i < currentStateVars.length; i++) {
+					final MCVariable currentStateVar = currentStateVars[i];
+					subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+					subActionsAndConstraint.append(currentStateVar.getName()).append(TLAConstants.EQ);
+					subActionsAndConstraint.append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+					subActionsAndConstraint.append(currentStateVar.getValueAsStringReIndentedAs(TRI_INDENT + TLAConstants.INDENT));
+					subActionsAndConstraint.append(TLAConstants.CR);
+					subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN).append(TLAConstants.CR);
+	            }
+	
+	            /*
+	             * If the trace is a single state, make the next state
+	             * action never enabled. The model will deadlock in the initial state.
+	             * This adds:
+	             * 
+	             * /\ FALSE
+	             */
+				if (isSingleState) {
+					subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+					subActionsAndConstraint.append("FALSE").append(TLAConstants.CR);
+	            }
+	
+	            /*
+	             * Iterate through next state variables. This adds:
+	             * 
+	             * /\ var1'=(
+	             * expr3
+	             * )
+	             * /\ var2'=(
+	             * expr4
+	             * )
+	             */
+				for (int i = 0; i < currentStateVars.length; i++) {
+	                final MCVariable nextStateVar = nextStateVars[i];
+	                subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+	                subActionsAndConstraint.append(nextStateVar.getName()).append(TLAConstants.PRIME);
+	                subActionsAndConstraint.append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+					subActionsAndConstraint.append(nextStateVar.getValueAsStringReIndentedAs(TRI_INDENT + TLAConstants.INDENT));
+					subActionsAndConstraint.append(TLAConstants.CR);
+					subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN).append(TLAConstants.CR);
+	            }
+	
+	            /*
+	             * Iterate through the trace explorer expressions if there are any. This adds:
+	             * 
+	             * /\ var3'=(
+	             * texpr1
+	             * )
+	             * /\ var4'=(
+	             * texpr2
+	             * )'
+	             * 
+	             */
+				if (expressionData != null) {
+					for (int i = 0; i < expressionData.length; i++) {
+	                    final TraceExpressionInformationHolder expressionInfo = expressionData[i];
+		                subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+	                    subActionsAndConstraint.append(expressionInfo.getVariableName()).append(TLAConstants.PRIME);
+	                    subActionsAndConstraint.append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	                    subActionsAndConstraint.append(TRI_INDENT);
+	                    subActionsAndConstraint.append(expressionInfo.getIdentifier()).append(TLAConstants.CR);
+	                    subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN);
+	
+						if (expressionInfo.getLevel() < 2) {
+							subActionsAndConstraint.append(TLAConstants.PRIME);
+	                    }
+						subActionsAndConstraint.append(TLAConstants.CR);
+	                }
+	            }
+	
+				subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.R_PAREN);
+				subActionsAndConstraint.append(TLAConstants.CR).append(TLAConstants.CR);
+	
+				if (it.hasNext()) {
+	                actionConstraintBuffer.append(TLAConstants.COMMA);
+	            }
+				actionConstraintBuffer.append(TLAConstants.CR);
+	
+	            currentState = nextState;
+	
+				if (it.hasNext()) {
+					nextState = it.next();
+				} else {
+					nextState = null;
+				}
+	        }
+	
+	        initAndNext.append(TLAConstants.COMMENT).append("TRACE NEXT definition ");
+	        initAndNext.append(TLAConstants.TraceExplore.TRACE_EXPLORE_NEXT).append(TLAConstants.CR);
+	        initAndNext.append(nextDisjunctBuffer.toString());
+			
+			if (leaveStubsForTraceExpression) {
+				initAndNext.append(TLAConstants.COMMENT).append(TLAConstants.TLA_AND).append(' ');
+				initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.PRIME).append(TLAConstants.EQ);
+				initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.CR);
+			}
+	        
+			initAndNext.append(TLAConstants.CR).append(TLAConstants.CR);
+	        
+			
+			subActionsAndConstraint.append(TLAConstants.COMMENT).append("TRACE Action Constraint definition ");
+			subActionsAndConstraint.append(TLAConstants.TraceExplore.TRACE_EXPLORE_ACTION_CONSTRAINT);
+			subActionsAndConstraint.append(TLAConstants.CR).append(actionConstraintBuffer.toString());
+			subActionsAndConstraint.append(TLAConstants.END_TUPLE).append("[TLCGet(\"level\")]");
+
+			subActionsAndConstraint.append(CLOSING_SEP).append(TLAConstants.CR);
+
+			
+	        return new StringBuilder[] { subActionsAndConstraint, initAndNext };
+	    }
+		
+		return null;
+	}
+
+	public static void addTraceFunctionToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final List<MCState> input) {
+		// Filter stuttering or back2state instances from trace.
+		final List<MCState> trace = input.stream()
+				.filter(state -> !state.isBackToState() && !state.isStuttering())
+				.collect(Collectors.toList());
+		
+		if (trace.isEmpty()) {
+			return;
+	    }
+		
+		// Trace
+		final StringBuilder traceFunctionDef = new StringBuilder();
+		traceFunctionDef.append(TLAConstants.BEGIN_TUPLE).append(TLAConstants.CR);
+		for (int j = 0; j < trace.size(); j++) {
+			final MCState state = trace.get(j);
+
+			traceFunctionDef.append(TLAConstants.L_PAREN).append(state.asSimpleRecord()).append(TLAConstants.R_PAREN);
+
+			if (j < trace.size() - 1) {
+				traceFunctionDef.append(TLAConstants.COMMA).append(TLAConstants.CR);
+			}
+		}
+		traceFunctionDef.append(TLAConstants.CR).append(TLAConstants.END_TUPLE);
+		traceFunctionDef.append(CLOSING_SEP).append(TLAConstants.CR);
+		
+		addArrowAssignmentToBuffers(tlaBuffer, cfgBuffer,
+				new Assignment(TLAConstants.TraceExplore.TRACE, new String[0], traceFunctionDef.toString()),
+				TLAConstants.Schemes.DEFOV_SCHEME);
+	}
+	
+	
+	public SpecTraceExpressionWriter() {
+		super(true);
+	}
+	
+	/**
+	 * This only changes the tla file. This method generates and adds a variable declaration
+	 * for each expression in the list. It also creates an identifier for each
+	 * expression and defines the identifier to be that expression.
+	 * It returns an array of {@link TraceExpressionInformationHolder} where each element
+	 * contains the expression, the identifier, and the variable name.
+	 * 
+	 * If the expressions are x' + y and x > 3, The tla file will contain something like
+	 * 
+	 *\* comment line
+	 * VARIABLES __trace_var_21034978347834, __trace_var_90234782309
+	 * 
+	 * \* comment line
+	 * trace_def_3214234234234 ==
+	 * x' + y
+	 * ----
+	 * 
+	 * \* comment line
+	 * trace_def_2342342342342 ==
+	 * x > 3
+	 * ----
+	 * 
+	 * @param expressions a list of formulas, each one an expression the user wants to have evaluated
+	 * at each state of the trace
+	 * @return array of {@link TraceExpressionInformationHolder} where each element
+	 * contains the expression, the identifier, and the variable name
+	 */
+	public TraceExpressionInformationHolder[] createAndAddVariablesAndDefinitions(final List<Formula> expressions,
+			final String attributeName) {
+		final TraceExpressionInformationHolder[] expressionData
+								= TraceExpressionInformationHolder.createHolders(expressions, attributeName);
+	
+	    addVariablesAndDefinitions(expressionData, attributeName, true);
+	
+	    return expressionData;
+	}
+	
+	@Override
+	public void addPrimer(final String moduleFilename, final String extendedModuleName) {
+		addPrimer(moduleFilename, extendedModuleName, new HashSet<>());
+	}
+	
+	public void addPrimer(final String moduleFilename, final String extendedModuleName, final Set<String> extraExtendedModules) {
+		if (extendedModuleName != null) {
+			extraExtendedModules.add(extendedModuleName);
+		}
+		
+		// Not sure why this is required by TE.tla.
+		extraExtendedModules.add("TLC");
+		
+		// A TE spec has to extend Toolbox to have access to _TETrace and _TEPosition
+		// operators.
+		extraExtendedModules.add("Toolbox");
+		
+		tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(moduleFilename,
+				extraExtendedModules.toArray(new String[extraExtendedModules.size()])));
+	}
+
+	/**
+	 * This only changes the tla file. This method adds a variable declaration
+	 * for each element of traceExpressionData and, if the flag addDefinitions is true,
+	 * defines the identifier of each element to be the expression for that element.
+	 * 
+	 * If the expressions are x' + y and x > 3, The tla file will contain something like
+	 * 
+	 *\* comment line
+	 * VARIABLES __trace_var_21034978347834, __trace_var_90234782309
+	 * 
+	 * \* comment line
+	 * trace_def_3214234234234 ==
+	 * x' + y
+	 * ----
+	 * 
+	 * \* comment line
+	 * trace_def_2342342342342 ==
+	 * x > 3
+	 * ----
+	 * 
+	 * @param traceExpressionData information about the trace expressions
+	 * @param attributeName
+	 * @param addDefinitions whether or not to define each identifier as the expression
+	 */
+	public void addVariablesAndDefinitions(final TraceExpressionInformationHolder[] traceExpressionData, final String attributeName,
+			final boolean addDefinitions) {
+		if (traceExpressionData.length == 0) {
+	        return;
+	    }
+	
+	    final StringBuilder variableDecls = new StringBuilder();
+	    final StringBuilder definitions = new StringBuilder();
+		for (int i = 0; i < traceExpressionData.length; i++) {
+	        final TraceExpressionInformationHolder expressionInfo = traceExpressionData[i];
+	
+	        variableDecls.append(expressionInfo.getVariableName());
+	        // we add a comma after every variable except for the last
+	        if (i != traceExpressionData.length - 1)
+	        {
+	            variableDecls.append(TLAConstants.COMMA);
+	        }
+	
+	        if (addDefinitions)
+	        {
+	            // define the identifier corresponding to this expression - looks like:
+	            // \* comment line
+	            // trace_def_213123123123 ==
+	            // expression
+	            // ----
+	            definitions.append(TLAConstants.COMMENT).append("TRACE EXPLORER identifier definition ");
+	            definitions.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.COLON);
+	            definitions.append(i).append(TLAConstants.CR);
+	            definitions.append(expressionInfo.getIdentifier()).append(TLAConstants.DEFINES_CR);
+	            definitions.append(expressionInfo.getExpression()).append(CLOSING_SEP).append(TLAConstants.CR);
+	        }
+	    }
+	
+	    // variable declaration
+	    tlaBuffer.append(TLAConstants.COMMENT).append("TRACE EXPLORER variable declaration ");
+	    tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR);
+	    tlaBuffer.append("VARIABLES ").append(variableDecls.toString()).append(CLOSING_SEP).append(TLAConstants.CR);
+	
+		if (addDefinitions) {
+	        // append the expression definitions
+	        tlaBuffer.append(definitions.toString());
+	    }
+	}
+
+	/**
+	 * Adds the invariant ~(P) where P is the formula describing finalState. The format
+	 * in the tla file is as follows:
+	 * 
+	 * inv_12312321321 ==
+	 * ~(
+	 * P
+	 * )
+	 * ----
+	 * 
+	 * @param finalState
+	 */
+	public void addInvariant(final MCState finalState) {
+	    final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.INVARIANT_SCHEME);
+	    cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INVARIANT).append(" definition");
+	    cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.INVARIANT).append(TLAConstants.CR);
+	    cfgBuffer.append(id).append(TLAConstants.CR);
+	
+	    tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INVARIANT).append(" definition");
+	    tlaBuffer.append(TLAConstants.CR).append(id).append(TLAConstants.DEFINES_CR);
+	    tlaBuffer.append(TLAConstants.TLA_NOT).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	    tlaBuffer.append(getStateConjunction(finalState)).append(TLAConstants.CR).append(TLAConstants.R_PAREN);
+	
+	    tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+	}
+
+	/**
+	 * Adds the temporal property ~<>[](P) where P is the formula describing finalState.
+	 * The format in the tla file is as follows:
+	 * 
+	 * prop_23112321 ==
+	 * ~<>[](
+	 * P
+	 * )
+	 * ----
+	 * 
+	 * @param finalState
+	 */
+	public void addStutteringProperty(final MCState finalState) {
+	    String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.PROP_SCHEME);
+	    cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition");
+	    cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.PROPERTY).append(TLAConstants.CR);
+	    cfgBuffer.append(id).append(TLAConstants.CR);
+	
+	    tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition");
+	    tlaBuffer.append(TLAConstants.CR).append(id).append(TLAConstants.DEFINES_CR);
+	    tlaBuffer.append(TLAConstants.TLA_NOT).append(TLAConstants.TLA_EVENTUALLY_ALWAYS);
+	    tlaBuffer.append(TLAConstants.L_PAREN).append(TLAConstants.CR).append(getStateConjunction(finalState));
+	    tlaBuffer.append(TLAConstants.CR).append(TLAConstants.R_PAREN).append(CLOSING_SEP).append(TLAConstants.CR);
+	}
+
+	/**
+	 * Adds the temporal property ~([]<>P /\ []<>Q), where P is the formula describing finalState and 
+	 * Q the formula describing backToState. The formating in the tla file is as follows:
+	 * 
+	 * prop_21321312 ==
+	 * ~(([]<>(
+	 * P
+	 * ))/\([]<>(
+	 * Q
+	 * )))
+	 * ----
+	 * 
+	 * @param finalState
+	 * @param backToState
+	 */
+	public void addBackToStateProperty(final MCState finalState, final MCState backToState) {
+	    final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.PROP_SCHEME);
+	    cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition");
+	    cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.PROPERTY).append(TLAConstants.CR);
+	    cfgBuffer.append(id).append(TLAConstants.CR);
+	
+	    tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition");
+	    tlaBuffer.append(TLAConstants.CR).append(id).append(TLAConstants.DEFINES_CR);
+	    tlaBuffer.append(TLAConstants.TLA_NOT).append(TLAConstants.L_PAREN).append(TLAConstants.L_PAREN);
+	    tlaBuffer.append(TLAConstants.TLA_INF_OFTEN).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	    tlaBuffer.append(getStateConjunction(finalState)).append(TLAConstants.CR).append(TLAConstants.R_PAREN);
+	    tlaBuffer.append(TLAConstants.R_PAREN).append(TLAConstants.TLA_AND).append(TLAConstants.L_PAREN);
+	    tlaBuffer.append(TLAConstants.TLA_INF_OFTEN).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	    tlaBuffer.append(getStateConjunction(backToState)).append(TLAConstants.CR).append(TLAConstants.R_PAREN);
+	    tlaBuffer.append(TLAConstants.R_PAREN).append(TLAConstants.R_PAREN).append(CLOSING_SEP).append(TLAConstants.CR);
+	}
+
+	/**
+	 * Writes comments that will be used for associating variable names with expressions
+	 * and will give the level of each expression. In particular, for each expression "expr"
+	 * with level x and variable name ___trace_var_3242348934343 this
+	 * will append the following comment to the tla file:
+	 * 
+	 * \* :x:___trace_var_3242348934343:expr"$!@$!@$!@$!@$!"
+	 * 
+	 * @param traceExpressionData
+	 */
+	public void addInfoComments(final TraceExpressionInformationHolder[] traceExpressionData) {
+		for (final TraceExpressionInformationHolder expressionData : traceExpressionData) {
+	        tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.COLON).append(expressionData.getLevel());
+	        tlaBuffer.append(TLAConstants.COLON).append(expressionData.getVariableName()).append(TLAConstants.COLON);
+	        tlaBuffer.append(expressionData.getExpression()).append(TLAConstants.CONSTANT_EXPRESSION_EVAL_IDENTIFIER);
+	        tlaBuffer.append(TLAConstants.CR);
+	    }
+	}
+
+	/**
+	 * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[])
+	 */
+	public String[] addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData) {
+		return addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData);
+	}
+
+	/**
+	 * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String)
+	 */
+	public void addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData,
+							final String initId, String nextId, final String actionConstraintId) {
+		addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId);
+	}
+
+	/**
+	 * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String, String, boolean)
+	 */
+	public void addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData,
+							final String initId, String nextId, final String actionConstraintId,
+							final String nextSubActionBasename) {
+		addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId,
+							 nextSubActionBasename, true);
+	}
+
+	public void addTraceFunction(final List<MCState> input) {
+		addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, input);
+	}
+	
+    /**
+     * Returns a string representing the formula describing the state.
+     * If the state has var1=expr1, var2 = expr2, and var3=expr3, then this returns:
+     * 
+     * var1=(
+     * expr1
+     * )/\
+     * var2=(
+     * expr2
+     * )/\
+     * var3=(
+     * expr3
+     * )
+     * 
+     * 
+     * The expressions expr1, expr2, and expr3 can take up multiple lines.
+     * 
+     * This will return null if the state is stuttering or back to state.
+     * 
+     * @param state
+     * @return
+     */
+	private static String getStateConjunction(final MCState state) {
+		if (state.isBackToState()) {
+			return null;
+		} else if (state.isStuttering()) {
+			return null;
+		} else {
+            final StringBuilder formula = new StringBuilder();
+            final MCVariable[] vars = state.getVariables();
+			for (int i = 0; i < vars.length; i++) {
+				final MCVariable var = vars[i];
+				formula.append(var.getName()).append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+				formula.append(var.getValueAsString()).append(TLAConstants.CR).append(TLAConstants.R_PAREN);
+
+				// append /\ except for the last variable
+				if (i != (vars.length - 1)) {
+                    formula.append(TLAConstants.TLA_AND).append(TLAConstants.CR);
+                }
+            }
+
+            return formula.toString();
+        }
+    }
+}
diff --git a/tlatools/src/tlc2/output/SpecWriterUtilities.java b/tlatools/src/tlc2/output/SpecWriterUtilities.java
new file mode 100644
index 0000000000000000000000000000000000000000..32229aa9f49405a21bdf10f094480ab9631f0328
--- /dev/null
+++ b/tlatools/src/tlc2/output/SpecWriterUtilities.java
@@ -0,0 +1,349 @@
+package tlc2.output;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
+
+import tla2sany.modanalyzer.SpecObj;
+import tla2sany.semantic.OpDefNode;
+import tlc2.model.Assignment;
+import tlc2.model.Formula;
+import util.StringHelper;
+import util.TLAConstants;
+
+/**
+ * There were a lot of utility methods in the toolbox's ModelWriter class that made what statefulness of that class which
+ * existed, obscured. I broke out the utility methods into their own class on migration and clean-up.
+ */
+public final class SpecWriterUtilities {
+	/*
+     * Constants used for displaying modification history.
+     * It has the syntax:
+     *   modificationHistory +
+     *   (lastModified + date + modifiedBy +
+     *      username + newline)*
+     *      
+     * Note: The StringHelper.newline string wasn't being found on my 64-bit
+     * Windows 7 machine.  So on 8 Dec 2010 I removed it from here, and added
+     * a "\n" before it when writing a new file.
+     */
+    public static String MODIFICATION_HISTORY = /* StringHelper.PLATFORM_NEWLINE + */ "\\* Modification History";
+    public static String LAST_MODIFIED = StringHelper.PLATFORM_NEWLINE + "\\* Last modified ";
+    public static String MODIFIED_BY = " by ";
+
+    /**
+     * @param rightMarginWidth the margin width of the output
+     * @param addModificationHistory whether to add the modification history
+     * @return the content for the end of the module
+     */
+	public static StringBuilder getModuleClosingTag(final int rightMarginWidth, final boolean addModificationHistory) {
+        final StringBuilder buffer = new StringBuilder();
+        buffer.append(StringHelper.copyString("=", rightMarginWidth)).append(StringHelper.PLATFORM_NEWLINE);
+
+        if (addModificationHistory) {
+            buffer.append(MODIFICATION_HISTORY).append(StringHelper.PLATFORM_NEWLINE)
+                .append("\\* Created ").append(new Date()).append(MODIFIED_BY)
+                .append(System.getProperty("user.name")).append(StringHelper.PLATFORM_NEWLINE);
+        }
+        return buffer;
+    }
+
+	/**
+	 * @return the content for the end of the module
+	 */
+	public static StringBuilder getGeneratedTimeStampCommentLine() {
+		final StringBuilder buffer = new StringBuilder();
+		buffer.append(TLAConstants.GENERATION_TIMESTAMP_PREFIX).append(new Date());
+		return buffer;
+	}
+
+    /**
+     * A pattern to match IDs generated by the {@link AbstractSpecWriter#getValidIdentifier(String)} method
+     */
+    public static final Pattern ID_MATCHER
+    	= Pattern.compile("(" + TLAConstants.Schemes.SPEC_SCHEME + "|"
+    						  + TLAConstants.Schemes.INIT_SCHEME + "|"
+    						  + TLAConstants.Schemes.NEXT_SCHEME + "|"
+    						  + TLAConstants.Schemes.CONSTANT_SCHEME + "|"
+    						  + TLAConstants.Schemes.SYMMETRY_SCHEME + "|"
+    						  + TLAConstants.Schemes.DEFOV_SCHEME + "|"
+    						  + TLAConstants.Schemes.CONSTRAINT_SCHEME + "|"
+    						  + TLAConstants.Schemes.ACTIONCONSTRAINT_SCHEME + "|"
+    						  + TLAConstants.Schemes.INVARIANT_SCHEME + "|"
+    						  + TLAConstants.Schemes.PROP_SCHEME + ")_[0-9]{17,}");
+
+    /**
+     * Creates a new valid unqiue identifier from given scheme
+     * @param scheme a naming scheme, one of the {@link TLAConstants.Schemes} constants
+     * @return a valid identifier
+     */
+	public static String getValidIdentifier(final String scheme) {
+		return String.format("%s_%s%s", scheme, System.currentTimeMillis(), 1000 * COUNTER.incrementAndGet());
+	}
+
+    /**
+     * Converts formula list to a string representation
+     * @param formulaList list of assignments
+	 * @param labelingScheme one of the {@link TLAConstants.Schemes} constants
+     * @return
+     */
+	public static List<String[]> createListContent(final List<Formula> formulaList, final String labelingScheme) {
+		ArrayList<String[]> resultContent = new ArrayList<>(formulaList.size());
+		String[] content;
+		String label;
+		for (int i = 0; i < formulaList.size(); i++) {
+			label = getValidIdentifier(labelingScheme);
+			// formulas
+			// to .cfg : <id>
+			// to _MC.tla : <id> == <expression>
+			content = new String[] { label, label + TLAConstants.DEFINES_CR + formulaList.get(i).getFormula(), String.valueOf(i) };
+			resultContent.add(content);
+		}
+		return resultContent;
+	}
+
+    /**
+     * Retrieves the name of the module (filename without extension)
+     * 
+     * @param moduleFilename
+     *            filename of a module
+     * @param checkExistence
+     *            if true, the method returns module name, iff the specified file exists or null, if the specified file
+     *            does not exist, if false - only string manipulations are executed
+     * @return module name
+     */
+	public static String getModuleNameChecked(final String moduleFilename, final boolean checkExistence) {
+		final File f = new File(moduleFilename);
+		final int index = f.getName().lastIndexOf('.');
+		if (checkExistence) {
+			return (f.exists())
+						? (index != -1) ? f.getName().substring(0, index) : f.getName()
+						: null;
+		}
+		return (index != -1) ? f.getName().substring(0, index) : f.getName();
+	}
+
+    /**
+     * Creates a simple content for a new TLA+ module
+     *  
+     * @param moduleFileName, name of the file 
+     * @return the stream with content
+     */
+	public static StringBuilder getExtendingModuleContent(final String moduleFilename, final String... extendedModuleName) {
+		final StringBuilder buffer = new StringBuilder();
+		buffer.append(TLAConstants.SEP).append(' ').append(TLAConstants.KeyWords.MODULE).append(' ');
+		buffer.append(getModuleNameChecked(moduleFilename, false)).append(' ').append(TLAConstants.SEP).append('\n');
+		buffer.append(TLAConstants.KeyWords.EXTENDS).append(' ');
+		buffer.append(String.join(", ", extendedModuleName));
+		buffer.append("\n\n");
+		return buffer;
+	}
+
+	public static List<String[]> createFalseInit(final String var) {
+		final List<String[]> list = new ArrayList<>();
+		final String identifier = getValidIdentifier(TLAConstants.Schemes.INIT_SCHEME);
+		list.add(new String[] { identifier,
+							    (identifier + TLAConstants.DEFINES_CR + "FALSE/\\" + var + TLAConstants.EQ + "0") });
+		return list;
+	}
+
+	public static List<String[]> createFalseNext(final String var) {
+		final List<String[]> list = new ArrayList<>();
+		final String identifier = getValidIdentifier(TLAConstants.Schemes.NEXT_SCHEME);
+		list.add(new String[] { identifier,
+								(identifier + TLAConstants.DEFINES_CR + "FALSE/\\" + var + TLAConstants.PRIME
+											+ TLAConstants.EQ + var) });
+		return list;
+	}
+
+    /**
+     * Converts formula list to a string representation
+     * @param serializedFormulaList, list of strings representing formulas (with enablement flag)
+	 * @param labelingScheme one of the {@link TLAConstants.Schemes} constants
+     * @return
+     */
+	public static List<String[]> createFormulaListContent(final List<String> serializedFormulaList,
+			final String labelingScheme) {
+		List<Formula> formulaList = Formula.deserializeFormulaList(serializedFormulaList);
+		return createFormulaListContentFormula(formulaList, labelingScheme);
+	}
+
+	public static List<String[]> createFormulaListContentFormula(final List<Formula> serializedFormulaList,
+			final String labelingScheme) {
+		return SpecWriterUtilities.createListContent(serializedFormulaList, labelingScheme);
+	}
+	
+	/**
+	 * @param value if null or empty, an empty list will be returned
+	 * @param labelingScheme one of the {@link TLAConstants.Schemes} constants
+	 * @return the content for a single source element
+	 */
+	public static List<String[]> createSourceContent(final String value, final String labelingScheme) {
+		return createSourceContent(value, labelingScheme, true);
+	}
+	
+	/**
+	 * @param value if null or empty, an empty list will be returned
+	 * @param identifierOrLabelingScheme an identifier or one of the
+	 *                                   {@link TLAConstants.Schemes} constants
+	 * @param isScheme                   true if {@code identifierOrLabelingScheme}
+	 *                                   is one of the scheme constants (in which
+	 *                                   case an identifier will be generated based
+	 *                                   off of it.)
+	 * @return the content for a single source element
+	 */
+	public static List<String[]> createSourceContent(final String value, final String identifierOrLabelingScheme,
+			final boolean isScheme) {
+		final ArrayList<String[]> result = new ArrayList<>();
+		if ((value == null) || (value.trim().length() == 0)) {
+			return result;
+		}
+		
+        final String identifier = isScheme ? SpecWriterUtilities.getValidIdentifier(identifierOrLabelingScheme)
+        								   : identifierOrLabelingScheme;
+        final StringBuilder buffer = new StringBuilder();
+
+        buffer.append(identifier).append(TLAConstants.DEFINES_CR);
+        buffer.append(value);
+
+        result.add(new String[] { identifier, buffer.toString() });
+        return result;
+	}
+
+    /**
+     * Create a list of overrides. If the override is not in the spec's root module, then
+     * the config file will have     A <- [M] id . This means that A is defined in module M,
+     * and its definition is being overriden in the spec root module which is dependent upon M.
+     * The following is an example from Leslie Lamport that explains what occurred before changing
+     * the code and what occurs now.
+     * Consider the root module
+
+    ----------------- MODULE TestA --------------------
+    M(a,b) == INSTANCE TestB WITH CB <- a, CD <- b
+    ==================================================
+
+    which imports the module
+
+    ----------------- MODULE TestB --------------------
+    CONSTANTS CB, CD
+
+    Foo(x) == <<x, CB, CD>>
+    ==================================================
+
+    If you go to definition overrides, you'll find the option of
+    overriding M!Foo.  Selecting it, the toolbox asks you to define an operator
+    M!Foo of 3 arguments.  If you do it and run TLC, you get the error
+
+    The configuration file substitutes for Foo with
+    def_ov_12533499062845000 of different number of arguments.
+
+    Here's what's going on.  The INSTANCE statement imports the operator
+    M!Foo into module TestA.  As you may recall, you use that operator
+    in an expression by writing something like
+
+    M(42, "a")!F(-13)
+
+    but in the semantic tree, it looks just as if M!F were any other
+    operator with three arguments.  When TLC sees the override instruction
+
+    Foo <- [TestB]def_ov_12533495599614000
+
+    in the .cfg file, it tries to substitute an operator def_ov_...  with
+    3 arguments for the operator Foo of module TestB that has only a
+    single argument.  Hence, the error.
+
+    ------
+
+    Here's the fix.  Instead of giving the user the option of overriding
+    M!Foo, in the menu, he should simply see Foo and, if he clicks once
+    it, he should see that it's in module TestB. If he chooses to override
+    Foo, he should be asked to define an operator of one argument.
+    
+     * @param overrides
+	 * @param labelingScheme one of the {@link TLAConstants.Schemes} constants
+	 * @param specObj
+     * @return
+     * 
+     * Was throwing null-pointer exception when called with spec unparsed.
+     * Hacked a fix to handle this case.  LL 20 Sep 2009
+     */
+	public static List<String[]> createOverridesContent(final List<Assignment> overrides, final String labelingScheme,
+			final SpecObj specObj) {
+		final ArrayList<String[]> resultContent = new ArrayList<>(overrides.size());
+		String[] content;
+		String id;
+		Assignment formula;
+
+        // getting the opdefnodes is necessary for retrieving the correct label
+        // to appear in the cfg file as explained in the documentation for this method
+		if (specObj == null) {
+			return resultContent;
+		}
+		final OpDefNode[] opDefNodes = specObj.getExternalModuleTable().getRootModule().getOpDefs();
+		final HashMap<String, OpDefNode> nodeTable = new HashMap<>(opDefNodes.length);
+
+		for (int j = 0; j < opDefNodes.length; j++) {
+			final String key = opDefNodes[j].getName().toString();
+			nodeTable.put(key, opDefNodes[j]);
+		}
+
+		for (int i = 0; i < overrides.size(); i++) {
+            id = getValidIdentifier(labelingScheme);
+            // formulas
+            // to .cfg : <id>
+            // to _MC.tla : <id> == <expression>
+            formula = overrides.get(i);
+
+            final OpDefNode defNode = nodeTable.get(formula.getLabel());
+			if (defNode == null) {
+				// should raise an error
+				content = null;
+			} else {
+				final OpDefNode source = defNode.getSource();
+				if (source == defNode) {
+                    // user is overriding a definition in the root module
+					if (formula.isModelValue() && !formula.isSetOfModelValues()) {
+                        // model value
+                        content = new String[] { formula.getLabel() + TLAConstants.EQ + formula.getLabel(), TLAConstants.EMPTY_STRING };
+					} else {
+                        // not a model value
+                        content = new String[] { formula.getLabel() + TLAConstants.ARROW + id,
+                                formula.getParametrizedLabel(id) + TLAConstants.DEFINES_CR + formula.getRight() };
+                    }
+				} else if (source.getSource() == source) {
+					// user is overriding a definition that is not in the root module
+					if (formula.isModelValue() && !formula.isSetOfModelValues()) {
+                        // model value
+                        content = new String[] {
+                                source.getName().toString() + TLAConstants.ARROW + "["
+                                        + source.getOriginallyDefinedInModuleNode().getName().toString() + "]" + id
+                                        + " " + id + TLAConstants.EQ + source.getName().toString(), "CONSTANT " + id };
+					} else {
+                        // not a model value
+                        content = new String[] {
+                                source.getName().toString() + TLAConstants.ARROW + "["
+                                        + source.getOriginallyDefinedInModuleNode().getName().toString() + "]" + id,
+                                formula.getParametrizedLabel(id) + TLAConstants.DEFINES_CR + formula.getRight() };
+                    }
+				} else {
+                    // should raise an error window
+                    content = null;
+                }
+            }
+
+            resultContent.add(content);
+        }
+		
+        return resultContent;
+    }
+
+    /**
+     * Counter to be able to generate unique identifiers
+     */
+    private static final AtomicLong COUNTER = new AtomicLong(1L);
+
+}
diff --git a/tlatools/src/tlc2/output/StatePrinter.java b/tlatools/src/tlc2/output/StatePrinter.java
index 295712ed3aadcc0e6547c898dc2e6e2ca41bcd51..aa1078a1603cdacb8fdf5e7b4ac96a6313c9b10d 100644
--- a/tlatools/src/tlc2/output/StatePrinter.java
+++ b/tlatools/src/tlc2/output/StatePrinter.java
@@ -45,6 +45,15 @@ public class StatePrinter
         MP.printState(EC.TLC_STATE_PRINT1, new String[] { "", currentState.toString() }, currentState, -1);
     }
 
+    public static void printState(TLCStateInfo currentStateInfo) {
+    	if (currentStateInfo.predecessorState == null) {
+    		// It's an initial state
+			printState(currentStateInfo, null, (int) currentStateInfo.stateNumber);
+    	} else {
+			printState(currentStateInfo, currentStateInfo.predecessorState.state, (int) currentStateInfo.stateNumber);
+    	}
+    }
+    
     /**
      * Prints the state information
      * if the TLC runs in print-diff-only mode and the last state is set, it will print the diff only 
@@ -61,9 +70,14 @@ public class StatePrinter
         {
             stateString = currentStateInfo.state.toString();
         }
-        MP.printState(EC.TLC_STATE_PRINT2, new String[] { String.valueOf(num), currentStateInfo.info.toString(),
-        stateString }, currentStateInfo, num);
-        OutputCollector.addStateToTrace(currentStateInfo);
+        if (currentStateInfo.state.allAssigned()) {
+        	MP.printState(EC.TLC_STATE_PRINT2, new String[] { String.valueOf(num), currentStateInfo.info.toString(),
+        			stateString, String.valueOf(currentStateInfo.fingerPrint()) }, currentStateInfo, num);
+        } else {
+        	// fingerprint can't be calculated when state is incomplete, just return a random value.
+        	MP.printState(EC.TLC_STATE_PRINT2, new String[] { String.valueOf(num), currentStateInfo.info.toString(),
+        			stateString, "-1" }, currentStateInfo, num);
+        }
     }
 
     /**
@@ -71,7 +85,21 @@ public class StatePrinter
      */
     public static void printStutteringState(int num)
     {
-        MP.printState(EC.TLC_STATE_PRINT3, new String[] { String.valueOf(num) }, (TLCState) null, num);
+        MP.printState(EC.TLC_STATE_PRINT3, new String[] { String.valueOf(num + 1) }, (TLCState) null, num + 1);
     }
 
+	/**
+	 * Prints a marker (EC.TLC_BACK_TO_STATE) looping back to the state with the
+	 * given stateNum.
+	 * @param currentStateInfo 
+	 * 
+	 * @param stateNum
+	 */
+	public static void printBackToState(final TLCStateInfo currentStateInfo, final long stateNum) {
+		if (TLCGlobals.tool) {
+			MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + stateNum, currentStateInfo.info.toString() }, (TLCState) null, -1);
+		} else {
+			MP.printMessage(EC.TLC_BACK_TO_STATE, new String[] {"" + stateNum, currentStateInfo.info.toString()});
+		}
+	}
 }
diff --git a/tlatools/src/tlc2/output/TLACopier.java b/tlatools/src/tlc2/output/TLACopier.java
new file mode 100644
index 0000000000000000000000000000000000000000..13682602103235961ac6f21b52a93ded74ad5318
--- /dev/null
+++ b/tlatools/src/tlc2/output/TLACopier.java
@@ -0,0 +1,69 @@
+package tlc2.output;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.util.regex.Matcher;
+
+import util.TLAConstants;
+
+/**
+ * This class which copies a TLA file, mutating it appropriately to become a SpecTE TLA file.
+ */
+public class TLACopier extends AbstractTLACopier {
+	private final String initNextDefinitions;
+	
+	private final boolean needExtendTLC;
+	private final boolean needExtendToolbox;
+	
+	public TLACopier(final String originalName, final String newName, final File sourceLocation,
+					 final String initNextDefinitionTLA, final boolean originalExtendsTLC,
+					 final boolean originalExtendsToolbox) {
+		super(originalName, newName, sourceLocation);
+		
+		initNextDefinitions = initNextDefinitionTLA;
+		
+		needExtendTLC = !originalExtendsTLC;
+		needExtendToolbox = !originalExtendsToolbox;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber)
+			throws IOException {
+		if (!inBody) {
+			final Matcher m = modulePattern.matcher(originalLine);
+			final String lineToWrite;
+
+			if (m.find()) {
+				lineToWrite = m.group(1) + ' ' + newModuleName + ' ' + TLAConstants.SEP;
+				inBody = true;
+			} else {
+				lineToWrite = originalLine;
+			}
+
+			writer.write(lineToWrite + '\n');
+		} else {
+			if (originalLine.trim().startsWith(TLAConstants.KeyWords.EXTENDS)) {
+				String line = originalLine;
+				if (needExtendTLC) {
+					line += ", " + TLAConstants.BuiltInModules.TLC;
+				}
+				if (needExtendToolbox) {
+					line += ", " + TLAConstants.BuiltInModules.TRACE_EXPRESSIONS;
+				}
+				writer.write(line + '\n');
+			} else {
+				final Matcher m = CLOSING_BODY_PATTERN.matcher(originalLine);
+
+				if (m.matches()) {
+					writer.write(initNextDefinitions + '\n');
+				}
+
+				writer.write(originalLine + '\n');
+			}
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/output/TLAMonolithCreator.java b/tlatools/src/tlc2/output/TLAMonolithCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ce15e66acc9a54bc29b3a438d224643f1f11981
--- /dev/null
+++ b/tlatools/src/tlc2/output/TLAMonolithCreator.java
@@ -0,0 +1,288 @@
+package tlc2.output;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import tla2sany.semantic.StandardModules;
+import tlc2.input.MCParser;
+import tlc2.input.MCParserResults;
+import util.TLAConstants;
+
+public class TLAMonolithCreator extends AbstractTLACopier {
+	private static final String NESTED_MODULE_INDENT = "    ";
+	private static final String LOCAL_INSTANCE_REGEX = "^LOCAL INSTANCE ([^\\s]+)\\s*$";
+	private static final Pattern LOCAL_INSTANCE_PATTERN = Pattern.compile(LOCAL_INSTANCE_REGEX);
+	
+	
+	// these are modules which SANY logs that it has parsed, scrubbed of Standard Modules and in reverse order
+	private final List<File> modulesToEmbed;
+	private final Set<String> moduleNamesBeingEmbedded;
+	// these are the modules which the root ModuleNode or one of its sub-ModuleNodes (or one or their sub-ModuleNodes
+	//		and so on, turtles all the way down) has defined as EXTENDS-ing in their spec
+	private final Set<String> modulesToSpecifyInExtends;
+	// TODO this is insufficient for nestings beyond one level
+	private final List<File> modulesToNest;
+	
+	/**
+	 * This is the constructor for the version which embeds no dependent modules.
+	 * 
+	 * @param entireExtendsList the modules which the root ModuleNode or one of its sub-ModuleNodes (or one or their
+	 * 								sub-ModuleNodes and so on, turtles all the way down) has defined as EXTENDS-ing
+	 * 								in their spec.
+	 */
+	public TLAMonolithCreator(final String rootSpecName, final File sourceLocation, final Set<String> entireExtendsList) {
+		this(rootSpecName, sourceLocation, null, entireExtendsList, null);
+	}
+	
+	/**
+	 * @param rootSpecName
+	 * @param sourceLocation
+	 * @param extendeds these are modules which SANY logs that it has parsed; we expect to receive this in the order
+	 * 								which SANY emits it in logging; if that order changes in the future, the monolith
+	 * 								spec will potentially break due to dependent functions being declared in the
+	 * 								wrong order
+	 * @param entireExtendsList the modules which the root ModuleNode or one of its sub-ModuleNodes (or one or their
+	 * 								sub-ModuleNodes and so on, turtles all the way down) has defined as EXTENDS-ing
+	 * 								in their spec; this will get the non Standard Modules filtered out of it prior to
+	 * 								usage in this class since those will get embedded as a dependent module.
+	 * @param allInstantiatedModules
+	 */
+	public TLAMonolithCreator(final String rootSpecName, final File sourceLocation, final List<File> extendeds,
+							  final Set<String> entireExtendsList, final Set<String> allInstantiatedModules) {
+		super(rootSpecName, ("tmp_" + System.currentTimeMillis() + "_monolith"), sourceLocation);
+		
+		final boolean willEmbedDependents = (extendeds != null);
+		
+		moduleNamesBeingEmbedded = new HashSet<>();
+		modulesToNest = new ArrayList<>();
+		modulesToEmbed = new ArrayList<>();
+		if (willEmbedDependents) {
+			final HashSet<String> instantiatedModules = new HashSet<>(allInstantiatedModules);
+			final Stack<File> embedStack = new Stack<>();
+			for (final File f : extendeds) {
+				final String name = f.getName();
+				final int index = name.toLowerCase().indexOf(TLAConstants.Files.TLA_EXTENSION);
+				final boolean keep;
+				final String moduleName;
+				if (index == -1) {
+					// this should never be the case
+					keep = true;
+					moduleName = name;
+				} else {
+					moduleName = name.substring(0, index);
+	
+					keep = !StandardModules.isDefinedInStandardModule(moduleName);
+				}
+				
+				if (keep) {
+					embedStack.push(f);
+					instantiatedModules.remove(moduleName);
+					moduleNamesBeingEmbedded.add(moduleName);
+				}
+			}
+			
+			while (!embedStack.isEmpty()) {
+				modulesToEmbed.add(embedStack.pop());
+			}
+			
+			for (final String module : instantiatedModules) {
+				if (!StandardModules.isDefinedInStandardModule(module)) {
+					modulesToNest.add(new File(sourceLocation, (module + TLAConstants.Files.TLA_EXTENSION)));
+				}
+			}
+		}
+		
+		modulesToSpecifyInExtends = new HashSet<>(entireExtendsList);
+		if (willEmbedDependents) {
+			StandardModules.filterNonStandardModulesFromSet(modulesToSpecifyInExtends);
+		}
+		// for TLC things
+		modulesToSpecifyInExtends.add(TLAConstants.BuiltInModules.TLC);
+		// for _TE things
+		modulesToSpecifyInExtends.add(TLAConstants.BuiltInModules.TRACE_EXPRESSIONS);
+	}
+	
+	@Override
+	protected void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber)
+			throws IOException {
+		if (!inBody) {
+			final Matcher m = modulePattern.matcher(originalLine);
+
+			inBody = m.find();
+
+			if (!vetoLocalInstanceLine(originalLine)) {
+				writer.write(originalLine + '\n');
+			}
+		} else {
+			if (originalLine.trim().startsWith(TLAConstants.KeyWords.EXTENDS)) {
+				writer.write(TLAConstants.KeyWords.EXTENDS + " " + String.join(", ", modulesToSpecifyInExtends) + "\n");
+
+				for (final File f : modulesToNest) {
+					insertModuleIntoMonolith(f, writer, true);
+				}
+				
+				for (final File f : modulesToEmbed) {
+					insertModuleIntoMonolith(f, writer, false);
+				}
+				
+				final StringBuilder commentLine = new StringBuilder(TLAConstants.CR);
+				commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR);
+				commentLine.append(TLAConstants.COMMENT).append(' ');
+				commentLine.append(originalModuleName);
+				commentLine.append(" follows\n");
+				commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR).append(TLAConstants.CR);
+				writer.write(commentLine.toString());
+			} else {
+				writer.write(originalLine + '\n');
+			}
+		}
+	}
+	
+	@Override
+	protected void copyHasFinished() throws IOException {
+		final Path originalPath = sourceFile.toPath();
+		Files.delete(originalPath);
+		
+		final Path monolithPath = destinationFile.toPath();
+		Files.move(monolithPath, originalPath, new CopyOption[0]);
+	}	
+	
+	private void insertModuleIntoMonolith(final File module, final BufferedWriter monolithWriter,
+										  final boolean nestedModule)
+			throws IOException {
+		final StringBuilder commentLine = new StringBuilder(TLAConstants.CR);
+		final String moduleFilename = module.getName();
+		final int index = moduleFilename.indexOf(TLAConstants.Files.TLA_EXTENSION);
+		final String moduleName;
+		if (index != -1) {
+			moduleName = moduleFilename.substring(0, index);
+		} else {
+			moduleName = moduleFilename;
+		}
+		commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR);
+		commentLine.append(TLAConstants.COMMENT).append(' ').append(moduleName).append(" follows\n");
+		commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR).append(TLAConstants.CR);
+		
+		monolithWriter.write(commentLine.toString());
+		
+		final String regex = TLAConstants.MODULE_OPENING_PREFIX_REGEX + moduleName;
+		final Pattern insertingModuleMatcher = Pattern.compile(regex);
+		
+		try (final BufferedReader br = new BufferedReader(new FileReader(module))) {
+			String line;
+			boolean inModuleBody = false;
+			
+			while ((line = br.readLine()) != null) {
+				if (!inModuleBody) {
+					final Matcher m = insertingModuleMatcher.matcher(line);
+
+					inModuleBody = m.find();
+					if (inModuleBody && nestedModule) {
+						monolithWriter.write(NESTED_MODULE_INDENT + line + '\n');
+					}
+				} else {
+					if (!line.trim().startsWith(TLAConstants.KeyWords.EXTENDS)) {
+						final Matcher m = CLOSING_BODY_PATTERN.matcher(line);
+
+						if (m.matches()) {
+							if (nestedModule) {
+								monolithWriter.write(NESTED_MODULE_INDENT + line + '\n');
+							}
+							break;
+						}
+
+						if (!vetoLocalInstanceLine(line)) {
+							if (nestedModule) {
+								monolithWriter.write(NESTED_MODULE_INDENT);
+							}
+							monolithWriter.write(line + '\n');
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	private boolean vetoLocalInstanceLine(final String line) {
+		final Matcher m = LOCAL_INSTANCE_PATTERN.matcher(line);
+		if (m.matches()) {
+			return moduleNamesBeingEmbedded.contains(m.group(1));
+		}
+		return false;
+	}
+	
+	
+	public static void main(final String[] args) {
+		if (args.length == 0) {
+			System.out.println("java tlc2.output.TLAMonolithCreator \\\n"
+									+ "\t[-sourceDir=_directory_containing_spec_tla_] \\\n"
+									+ "\t_specName_");
+			
+			System.exit(-2);
+		}
+		
+		final File sourceDirectory;
+		final String specName;
+		if (args.length == 1) {
+			sourceDirectory = new File(System.getProperty("user.dir"));
+			specName = args[0];
+		} else {
+			sourceDirectory = new File(args[0]);
+			specName = args[1];
+		}
+		final File originalTLA = new File(sourceDirectory, (specName + TLAConstants.Files.TLA_EXTENSION));
+		if (!originalTLA.exists()) {
+			System.out.println("Excepted to find the TLA file but could not: " + originalTLA.getAbsolutePath());
+			
+			System.exit(-3);
+		}
+		
+		final MCParser parser = new MCParser(sourceDirectory, specName, true);
+		final MCParserResults results = parser.parse();
+		
+		final File backupTLA = new File(sourceDirectory, (specName + "_orig" + TLAConstants.Files.TLA_EXTENSION));
+		
+		try {
+			Files.copy(originalTLA.toPath(), backupTLA.toPath(), StandardCopyOption.COPY_ATTRIBUTES);
+		} catch (final IOException ioe) {
+			System.out.println("Exception encountered while making a backup of the original TLA file: "
+									+ ioe.getMessage());
+			
+			System.exit(-1);
+		}
+		
+		try {
+			final ArrayList<File> extendeds = new ArrayList<>();
+			for (final String extended : results.getOriginalExtendedModules()) {
+				extendeds.add(new File(sourceDirectory, (extended + TLAConstants.Files.TLA_EXTENSION)));
+			}
+			
+			final TLAMonolithCreator creator = new TLAMonolithCreator(specName, sourceDirectory, extendeds,
+																	  results.getAllExtendedModules(),
+																	  results.getAllInstantiatedModules());
+			creator.copy();
+			
+			System.out.println("The monolith file is now present at: " + originalTLA.getAbsolutePath());
+		} catch (final IOException ioe) {
+			System.out.println("Exception encountered while making creating the monolith: " + ioe.getMessage());
+			
+			System.exit(-4);
+		} finally {
+			System.out.println("The original TLA file was backed up to: " + backupTLA.getAbsolutePath());
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/output/TeeOutputStream.java b/tlatools/src/tlc2/output/TeeOutputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..84247e67c25971b58f84c42532a6a4acad379887
--- /dev/null
+++ b/tlatools/src/tlc2/output/TeeOutputStream.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package tlc2.output;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Classic splitter of OutputStream. Named after the unix 'tee' command. It
+ * allows a stream to be branched off so there are now two streams.
+ *
+ * This code is copy-and-pasted from commons-io 2.6
+ */
+public class TeeOutputStream extends FilterOutputStream {
+    /** the second OutputStream to write to */
+    protected OutputStream branch; //TODO consider making this private
+
+    /**
+     * Constructs a TeeOutputStream.
+     * @param out the main OutputStream
+     * @param branch the second OutputStream
+     */
+    public TeeOutputStream(final OutputStream out, final OutputStream branch) {
+        super(out);
+        this.branch = branch;
+    }
+
+    /**
+     * Write the bytes to both streams.
+     * @param b the bytes to write
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized void write(final byte[] b) throws IOException {
+        out.write(b);
+        this.branch.write(b);
+    }
+
+    /**
+     * Write the specified bytes to both streams.
+     * @param b the bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized void write(final byte[] b, final int off, final int len) throws IOException {
+    	out.write(b, off, len);
+        this.branch.write(b, off, len);
+    }
+
+    /**
+     * Write a byte to both streams.
+     * @param b the byte to write
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized void write(final int b) throws IOException {
+    	out.write(b);
+        this.branch.write(b);
+    }
+
+    /**
+     * Flushes both streams.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public void flush() throws IOException {
+    	out.flush();
+        this.branch.flush();
+    }
+
+    /**
+     * Closes both output streams.
+     *
+     * If closing the main output stream throws an exception, attempt to close the branch output stream.
+     *
+     * If closing the main and branch output streams both throw exceptions, which exceptions is thrown by this method is
+     * currently unspecified and subject to change.
+     *
+     * @throws IOException
+     *             if an I/O error occurs
+     */
+    @Override
+    public void close() throws IOException {
+        try {
+        	out.close();
+        } finally {
+            this.branch.close();
+        }
+    }
+}
diff --git a/tlatools/src/tlc2/output/messages.properties b/tlatools/src/tlc2/output/messages.properties
index 6e118859bce31c2ce34f9bc422bb4802b3d7d981..e26b31fc964efb984e748d1ef92c66384398d4cc 100644
--- a/tlatools/src/tlc2/output/messages.properties
+++ b/tlatools/src/tlc2/output/messages.properties
@@ -1,53 +1,22 @@
-VersionTag=Version 2.02 of 3 August 2009
-HelpMessage=The model checker (TLC) provides the functionalities of model\n\
-checking or simulation of TLA+ specifications. The syntax for this command is:\n\n\
-java tlc2.TLC [GENERAL-SWITCHES] [MODE-SWITCHES] SPEC\n\n\
-where SPEC is the name of the specification's root module\n\
-and the optional GENERAL-SWITCHES are:\n\
- -checkpoint num: interval between check point (in minutes)\n\
- \tDefaults 30 if not provided.\n\
- -cleanup: clean up the states directory\n\
- -config file: provide the config file.\n\
- \tDefaults to SPEC.cfg if not provided\n\
- -continue: continue running even when invariant is violated\n\
- \tDefaults to stop at the first violation if not specified\n\
- -coverage minutes: interval between collection of coverage information \n\
- \ton the spec in minutes. Defaults to no coverage if not specified\n\
- -deadlock: do not check for deadlock.\n\
- \tDefaults to check deadlock if not specified\n\
- -difftrace: show only the differences between successive states,\n\
- \twhen printing trace. Defaults to print full state descriptions\n\
- -debug: debugging information (not for production use)\n\
- -dump file: dump all the states into file\n\
- -fp num: use the num'th irreducible polynomial from the list \n\
- \tstored in the class FP64.\n\
- -gzip: control if gzip is applied to value input/output stream.\n\
- \tDefaults to use gzip.\n\
- -metadir path: store metadata in the directory at path\n\
- \tDefaults to SPEC-directory/states if not specified\n\
- -recover id: recover from the checkpoint with id\n\
- \tDefaults to scratch run if not specified\n\
- -terse: do not expand values in Print statement\n\
- \tDefaults to expand value if not specified\n\
- -tool: tool mode (put message codes on console)\n\
-  -nowarning: disable all the warnings.\n\
- \tDefaults to report warnings if not specified\n\
- -workers num: the number of TLC worker threads. Defaults to 1.\n\n\
+TLCDescription=\
+The model checker (TLC) provides the functionalities of model checking\n\
+or simulation of TLA+ specifications. It may be invoked from the command\n\
+line, or via the model checking functionality of the Toolbox.\n\n\
 By default, TLC starts in the model checking mode using breadth-first\n\
-approach for the state space exploration. This can be changed using\n\
-the MODE-SWITCHES. In contrast to the GENERAL-SWITCHES these can be used \n\
-if applied in certain combinations only:\n\n\
-{[-dfid num][ -view]|-simulate[ -depth num][ -aril num][ -seed num]}\n\n\
- -modelcheck: run in model checking mode using breadth-first approach for the\n\
- \tstate space exploration (default)\n\
- -dfid num: run in model checking mode and use depth-first iterative deepening\n\
- \twith initial depth num\n\
- -view: apply VIEW (if provided) when printing out states.\n\
- -simulate: run in simulation mode\n\
- -depth num: specify the depth of random simulation\n\
- \tDefaults to 100 if not specified\n\
- -seed num: provide the seed for random simulation\n\
- \tDefaults to a random seed if not specified)\n\
- -aril num: Adjust the seed for random simulation\n\
- \tDefaults to 0 if not specified\n\
- 
\ No newline at end of file
+approach for the state space exploration.
+TraceExplorerDescription=\
+TraceExplorer performs the parsing / consuming of the TLC model checking\n\
+'tool mode' output. With this parsed output, it allows the user to do\n\
+three actions, at this time:\n\
+\to pretty print the error states in a fashion similar to what would\n\
+\t    be seen in the Toolbox\n\
+\to generate a SpecTE tla/cfg file pair which allows a user to then\n\
+\t    work with their model so that it is at the error state\n\
+\to evalute trace expressions at each error state; this action also\n\
+\t    also generates the SpecTE tla/cfg pair if it hasn't already\n\
+\t    been done\n\n\
+TraceExplorer also allows the user to evaluate any single expression\n\
+via the model checker.\n\n\
+If a SpecName is not specified, it is expected that tool output will\n\
+be arriving on stdin; in this case, any specification of -source will\n\
+be ignored.
diff --git a/tlatools/src/tlc2/overrides/Evaluation.java b/tlatools/src/tlc2/overrides/Evaluation.java
new file mode 100644
index 0000000000000000000000000000000000000000..2422f496837c47d71a92c0ff080cc7cb24e193ea
--- /dev/null
+++ b/tlatools/src/tlc2/overrides/Evaluation.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.overrides;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @see EvaluatingValue.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface Evaluation {
+
+	/**
+	 * @return The identifier of a TLA+ state or action predicate. 
+	 */
+	String definition();
+
+	/**
+	 * @return The name of the TLA+ module declaring the definition above.
+	 */
+	String module();
+
+	/**
+	 * @return The minimum level that will be assigned to the OpDefNode that
+	 *         represents the EvaluatingValue in the semantic graph. Unless
+	 *         the actual level checking in Spec.getLevelBound assigns a
+	 *         greater value, the OpDefNode is a constant-level expression if
+	 *         0 causing it to be eagerly evaluated in 
+	 *         SpecProcessor.processConstantDefns.
+	 * @see tla2sany.semantic.LevelNode.getLevel()
+	 * @see tlc2.tool.impl.Spec.getLevelBound(SemanticNode, Context)
+	 * @see tlc2.value.impl.EvaluatingValue
+	 * @see tlc2.tool.impl.SpecProcessor.processConstantDefns()
+	 */
+	int minLevel() default 0;
+	
+	/**
+	 * @return true if a warning should be printed when a EV cannot be mapped to the
+	 *         given TLA+ definition in module.
+	 * @see Evaluation#definition()
+	 * @see Evaluation#module()
+	 */
+	boolean warn() default true;
+}
diff --git a/tlatools/src/tlc2/overrides/ITLCOverrides.java b/tlatools/src/tlc2/overrides/ITLCOverrides.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ac6fb6f0870c8a98ffa204edf71918ea30efa6e
--- /dev/null
+++ b/tlatools/src/tlc2/overrides/ITLCOverrides.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.overrides;
+
+// This is provisional and might change or disappear in future version.
+public interface ITLCOverrides {
+
+	Class[] get();
+
+}
diff --git a/tlatools/src/tlc2/overrides/TLAPlusCallable.java b/tlatools/src/tlc2/overrides/TLAPlusCallable.java
new file mode 100644
index 0000000000000000000000000000000000000000..529176f51606a060304de1be249b674af9d6994c
--- /dev/null
+++ b/tlatools/src/tlc2/overrides/TLAPlusCallable.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.overrides;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @see MethodValue.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface TLAPlusCallable {
+
+	/**
+	 * @return The identifier of a TLA+ state or action predicate. 
+	 */
+	String definition();
+
+	/**
+	 * @return The name of the TLA+ module declaring the definition above.
+	 */
+	String module();
+
+	/**
+	 * @return The minimum level that will be assigned to the OpDefNode that
+	 *         represents the EvaluatingValue in the semantic graph. Unless
+	 *         the actual level checking in Spec.getLevelBound assigns a
+	 *         greater value, the OpDefNode is a constant-level expression if
+	 *         0 causing it to be eagerly evaluated in 
+	 *         SpecProcessor.processConstantDefns.
+	 * @see tla2sany.semantic.LevelNode.getLevel()
+	 * @see tlc2.tool.impl.Spec.getLevelBound(SemanticNode, Context)
+	 * @see tlc2.value.impl.EvaluatingValue
+	 * @see tlc2.tool.impl.SpecProcessor.processConstantDefns()
+	 */
+	int minLevel() default 0;
+	
+	/**
+	 * @return true if a warning should be printed when a EV cannot be mapped to the
+	 *         given TLA+ definition in module.
+	 * @see Evaluation#definition()
+	 * @see Evaluation#module()
+	 */
+	boolean warn() default true;
+}
diff --git a/tlatools/src/tlc2/overrides/TLAPlusOperator.java b/tlatools/src/tlc2/overrides/TLAPlusOperator.java
new file mode 100644
index 0000000000000000000000000000000000000000..780c0f8dfa50f1d9b6a4094415a2db02ff8a6565
--- /dev/null
+++ b/tlatools/src/tlc2/overrides/TLAPlusOperator.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.overrides;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @see MethodValue.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface TLAPlusOperator {
+
+	/**
+	 * @return The identifier of a TLA+ operator. 
+	 */
+	String identifier();
+
+	/**
+	 * @return The name of the TLA+ module declaring the definition above.
+	 */
+	String module();
+
+	/**
+	 * @see tlc2.overrides.Evaluation.minLevel()
+	 */
+	int minLevel() default 0;
+	
+	/**
+	 * @return tlc2.overrides.Evaluation.warn()
+	 */
+	boolean warn() default true;
+}
diff --git a/tlatools/src/tlc2/tool/AbstractChecker.java b/tlatools/src/tlc2/tool/AbstractChecker.java
index 7ba4caea0207d961d797c682c1ec44380db506f1..7c2c6722d224b2004f4f5abd4b8ea7f9f92f25dc 100644
--- a/tlatools/src/tlc2/tool/AbstractChecker.java
+++ b/tlatools/src/tlc2/tool/AbstractChecker.java
@@ -1,58 +1,64 @@
 package tlc2.tool;
 
 import java.io.IOException;
-import java.util.Hashtable;
-import java.util.concurrent.atomic.AtomicLong;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.Timer;
+import java.util.TimerTask;
 
-import tla2sany.modanalyzer.SpecObj;
-import tla2sany.semantic.SemanticNode;
-import tla2sany.st.Location;
+import tlc2.TLC;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
-import tlc2.output.OutputCollector;
+import tlc2.tool.coverage.CostModelCreator;
+import tlc2.tool.impl.FastTool;
+import tlc2.tool.liveness.AddAndCheckLiveCheck;
 import tlc2.tool.liveness.ILiveCheck;
 import tlc2.tool.liveness.LiveCheck;
 import tlc2.tool.liveness.Liveness;
 import tlc2.tool.liveness.NoOpLiveCheck;
+import tlc2.util.IStateWriter;
 import tlc2.util.IdThread;
-import tlc2.util.ObjLongTable;
-import tlc2.util.StateWriter;
-import tlc2.util.statistics.BucketStatistics;
+import tlc2.util.statistics.ConcurrentBucketStatistics;
 import tlc2.util.statistics.DummyBucketStatistics;
 import tlc2.util.statistics.IBucketStatistics;
+import tlc2.value.IValue;
 import util.DebugPrinter;
-import util.FileUtil;
-import util.FilenameToStream;
 
 /**
  * The abstract checker
  * @author Simon Zambrovski
- * @version $Id$
  */
-public abstract class AbstractChecker implements Cancelable
+public abstract class AbstractChecker
 {
+	/**
+	 * True when unit tests explicitly request to use
+	 * {@link AddAndCheckLiveCheck} to run liveness checking after each
+	 * insertion into the behavior graph. This should only be true if you
+	 * exactly know what you are doing. If you don't and this is true, make sure
+	 * it's false.
+	 */
+	public static boolean LIVENESS_TESTING_IMPLEMENTATION = Boolean.getBoolean(ILiveCheck.class.getName() + ".testing");
+	
 	protected static final boolean LIVENESS_STATS = Boolean.getBoolean(Liveness.class.getPackage().getName() + ".statistics");
 	
-    // SZ Mar 9, 2009: static modifier removed
-    protected AtomicLong numOfGenStates;
     protected TLCState predErrState;
     protected TLCState errState;
+    protected int errorCode;
     protected boolean done;
     protected boolean keepCallStack;
-    protected boolean checkDeadlock;
-    protected boolean checkLiveness;
-    protected String fromChkpt;
-    public String metadir;
-    public Tool tool;
-    public final SpecObj specObj;
-    public Action[] invariants;
-    public Action[] impliedActions;
-    public Action[] impliedInits;
-    public Action[] actions;
-    protected StateWriter allStateWriter;
-    protected boolean cancellationFlag;
+    protected final boolean checkDeadlock;
+    protected final boolean checkLiveness;
+    protected final String fromChkpt;
+    public final String metadir;
+    public final ITool tool;
+    protected final IStateWriter allStateWriter;
+    protected IWorker[] workers;
 	protected final ILiveCheck liveCheck;
+    /**
+     * Timestamp of when model checking started.
+     */
+	protected final long startTime;
 
     /**
      * Constructor of the abstract model checker
@@ -65,59 +71,63 @@ public abstract class AbstractChecker implements Cancelable
      * @param resolver
      * @param spec - pre-built specification object (e.G. from calling SANY from the tool previously)
      */
-    public AbstractChecker(String specFile, String configFile, String dumpFile, boolean deadlock, String fromChkpt,
-            boolean preprocess, FilenameToStream resolver, SpecObj spec) throws EvalException, IOException
-    {
-        this.cancellationFlag = false;
-
-        this.checkDeadlock = deadlock;
-
-        int lastSep = specFile.lastIndexOf(FileUtil.separatorChar);
-        String specDir = (lastSep == -1) ? "" : specFile.substring(0, lastSep + 1);
-        specFile = specFile.substring(lastSep + 1);
-
-        this.tool = new Tool(specDir, specFile, configFile, resolver);
-        
-        this.specObj = this.tool.init(preprocess, spec);
+	public AbstractChecker(ITool tool, String metadir, final IStateWriter stateWriter,
+			boolean deadlock, String fromChkpt, final long startTime) throws EvalException, IOException {
+        this.tool = tool;
+		
+		this.checkDeadlock = deadlock;
         this.checkLiveness = !this.tool.livenessIsTrue();
 
-        OutputCollector.setModuleNode(this.tool.rootModule);
-        
         // moved to file utilities
-        this.metadir = FileUtil.makeMetaDir(specDir, fromChkpt);
+        this.metadir = metadir;
         
-        this.numOfGenStates = new AtomicLong(0);
         this.errState = null;
         this.predErrState = null;
         this.done = false;
+        this.errorCode = EC.NO_ERROR;
         this.keepCallStack = false;
 
         this.fromChkpt = fromChkpt;
+        
+        this.allStateWriter = stateWriter;
+        
+        this.startTime = startTime;
 
-        // Initialize dumpFile:
-        if (dumpFile != null)
-        {
-            this.allStateWriter = new StateWriter(dumpFile);
+        if (TLCGlobals.isCoverageEnabled()) {
+        	CostModelCreator.create(this.tool);
         }
-
-        this.impliedInits = this.tool.getImpliedInits(); // implied-inits to be checked
-        this.invariants = this.tool.getInvariants(); // invariants to be checked
-        this.impliedActions = this.tool.getImpliedActions(); // implied-actions to be checked
-        this.actions = this.tool.getActions(); // the sub-actions
-
+        
         if (this.checkLiveness) {
+        	if (tool.hasSymmetry()) {
+        		// raise warning...
+				MP.printWarning(EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY);
+        	}
+        	if (tool.hasStateOrActionConstraints()) {
+				MP.printWarning(EC.TLC_FEATURE_LIVENESS_CONSTRAINTS);
+        	}
             // Initialization for liveness checking:
             report("initializing liveness checking");
 			IBucketStatistics stats = new DummyBucketStatistics();
 			if (LIVENESS_STATS) {
-				stats = new BucketStatistics("Histogram vertex out-degree", LiveCheck.class.getPackage().getName(),
+				stats = new ConcurrentBucketStatistics("Histogram vertex out-degree", LiveCheck.class.getPackage().getName(),
 						"DiskGraphsOutDegree");
 			}
-			this.liveCheck = new LiveCheck(this.tool, this.actions, this.metadir, stats);
+			if (LIVENESS_TESTING_IMPLEMENTATION) {
+				this.liveCheck = new AddAndCheckLiveCheck(this.tool, this.metadir, stats);
+			} else {
+				this.liveCheck = new LiveCheck(this.tool, this.metadir, stats, stateWriter);
+			}
             report("liveness checking initialized");
         } else {
         	this.liveCheck = new NoOpLiveCheck(this.tool, this.metadir);
         }
+        
+        scheduleTermination(new TimerTask() {
+			@Override
+			public void run() {
+				AbstractChecker.this.stop();
+			}
+		});
     }
 
     public final void setDone()
@@ -125,21 +135,19 @@ public abstract class AbstractChecker implements Cancelable
         this.done = true;
     }
 
-    protected final void incNumOfGenStates(int n)
-    {
-        this.numOfGenStates.getAndAdd(n);
-    }
-
     /**
      * Set the error state. 
      * <strong>Note:</note> this method must be protected by lock 
      */
-    public boolean setErrState(TLCState curState, TLCState succState, boolean keep)
+    public boolean setErrState(TLCState curState, TLCState succState, boolean keep, int errorCode)
     {
-        if (!TLCGlobals.continuation && this.done)
+       assert Thread.holdsLock(this) : "Caller thread has to hold monitor!";
+       if (!TLCGlobals.continuation && this.done)
             return false;
+        IdThread.resetCurrentState();
         this.predErrState = curState;
         this.errState = (succState == null) ? curState : succState;
+        this.errorCode = errorCode;
         this.done = true;
         this.keepCallStack = keep;
         return true;
@@ -151,46 +159,221 @@ public abstract class AbstractChecker implements Cancelable
      */
     protected void reportCoverage(IWorker[] workers)
     {
-        if (TLCGlobals.coverageInterval >= 0)
-        {
-            MP.printMessage(EC.TLC_COVERAGE_START);
-            // First collecting all counts from all workers:
-            ObjLongTable counts = this.tool.getPrimedLocs();
-            OutputCollector.setModuleNode(this.tool.rootModule);
-            Hashtable<String, Location> locationTable = new Hashtable<String, Location>();
-            for (int i = 0; i < workers.length; i++)
-            {
-                ObjLongTable counts1 = workers[i].getCounts();
-                ObjLongTable.Enumerator keys = counts1.keys();
-                Object key;
-                while ((key = keys.nextElement()) != null)
-                {
-                    String loc = ((SemanticNode) key).getLocation().toString();
-                    counts.add(loc, counts1.get(key));
-                    locationTable.put(loc, ((SemanticNode) key).getLocation());
+		// Without actions (empty spec) there won't be any statistics anyway.
+		if (TLCGlobals.isCoverageEnabled() && this.tool.getActions().length > 0)
+		{
+            CostModelCreator.report(this.tool, this.startTime);
+        }
+    }
+    
+    public static final double calculateOptimisticProbability(final long numOfDistinctStates, final long numOfGenStates) {
+        return numOfDistinctStates * ((numOfGenStates - numOfDistinctStates) / Math.pow(2, 64));
+    }
+    
+	public static final void reportSuccess(final long numOfDistinctStates, final long numOfGenStates)
+			throws IOException {
+		final double optimisticProb = calculateOptimisticProbability(numOfDistinctStates, numOfGenStates);
+		MP.printMessage(EC.TLC_SUCCESS, new String[] { "val = " + ProbabilityToString(optimisticProb, 2) });
+	}
+   
+	public static final void reportSuccess(final long numOfDistinctStates, final long actualDistance,
+			final long numOfGenStates) throws IOException {
+		// Prevent div-by-zero when calculating collision probabilities when no states
+		// are generated.
+		if (numOfDistinctStates == numOfGenStates && numOfGenStates == 0) {
+			// When the number of states is zero, printing a collision probability is
+			// useless anyway. But the Toolbox will probably crash if omitted.
+			MP.printMessage(EC.TLC_SUCCESS, new String[] { "val = 0.0", "val = 0.0" });
+			return;
+		}
+		// shown as 'calculated' in Toolbox
+		final String optimisticProbStr = "val = "
+				+ ProbabilityToString(calculateOptimisticProbability(numOfDistinctStates, numOfGenStates), 2);
+
+		// shown as 'observed' in Toolbox
+		final BigDecimal actualProb = BigDecimal.valueOf(1d).divide(BigDecimal.valueOf(actualDistance),
+				new MathContext(2));
+		final String actualProbStr = "val = " + ProbabilityToString(actualProb.doubleValue(), 2);
+		MP.printMessage(EC.TLC_SUCCESS, new String[] { optimisticProbStr, actualProbStr });
+	}
+    
+    /**
+     * This method added by LL on 17 April 2012 to replace the use of the PrintfFormat
+     * method in reportSuccess.
+     * 
+     * Returns a string representing the decimal representation of a probability to
+     * a given number of significant digits.  If the input is not a probability, or if
+     * some error is found, then it returns the result of applying Double.toString(long)
+     * to the value.
+     * 
+     * Warning: the code makes the following assumption:
+     *  - Double.toString(v) returns a decimal representation of v of the
+     *    form  [d]* ["." [d]+ ["E" [+ | -] [d]+]  where d is a decimal digit and
+     *      [x]   = 0 or 1 instance of x
+     *      [x]*  = any number of instances of x
+     *      [x]+  = any non-zero number of instances of x
+     *      x | y = an x or a y
+     * 
+     * @param val                - the probability represented as a long; must satisfy 0 <= val <= 1.
+     * @param significantDigits  - the number of significant digits to include; must be > 0.
+     * @return
+     */
+    private static final String ProbabilityToString(double val, int significantDigits) {
+        /*
+         * If val = 0 (which shouldn't happen), return "0.0"
+         */
+        if (val == 0) {
+            return "0.0";
+        }
+                
+        String valString = Double.toString(val) ;
+        int valStringLen = valString.length();
+        
+        String result = "";
+        int next = 0; // pointer to the next character in valString to examine.
+        int significantDigitsFound = 0;
+        
+        /*
+         * Skip past leading zeros.
+         */
+        while ((next < valStringLen)  && (valString.charAt(next) == '0')) {
+            next++ ;
+        }
+        
+        /*
+         * Append all the following digits to result, incrementing
+         * significantDigits for each one.  
+         */
+        while ( (next < valStringLen)  && 
+                Character.isDigit(valString.charAt(next))) {
+            result = result + valString.charAt(next);
+            significantDigitsFound++;
+            next++ ;
+         }
+        
+        /*
+         * IF next character is not "." 
+         *   THEN IF at end THEN return result
+         *                  ELSE return valString.
+         */
+        if (next == valStringLen) {
+            return result;
+        } else if (valString.charAt(next) != '.') {
+            return valString;
+        }
+        
+        
+        /*
+         * IF significantDigitsFound >= significantDigits, 
+         *    THEN skip over "." and the following digits.
+         *         (this should not happen)
+         *    ELSE append "." to result ;
+         *         IF significantDigitsFound = 0  
+         *           THEN copy each of the following "0"s of valString to result;
+         *         copy up to significantDigits - significantDigitsFound
+         *            following digits of valString to result;
+         *         IF next char of valString a digit >= "5"
+         *           THEN propagate a carry backwards over the digits of result
+         *                 -- e.g., changing ".019" to ".020";
+         *         Skip over remaining digits of valString;
+         */
+        if (significantDigitsFound >= significantDigits) {
+            next++ ;
+            while ( (next < valStringLen)  && 
+                    Character.isDigit(valString.charAt(next))) {
+                 next++ ;
+             }
+        } else {
+            next++;
+            result = result + ".";
+            if (significantDigitsFound == 0) {
+                while ((next < valStringLen)  && (valString.charAt(next) == '0')) {
+                    next++ ;
+                    result = result + "0";
                 }
             }
-            // Reporting:
-            Object[] skeys = counts.sortStringKeys();
-            for (int i = 0; i < skeys.length; i++)
-            {
-                long val = counts.get(skeys[i]);
-                //MP.printMessage(EC.TLC_COVERAGE_VALUE, new String[] { skeys[i].toString(), String.valueOf(val) });
-                Location location = locationTable.get(skeys[i]);
-                if(location != null){
-                    OutputCollector.putLineCount(location, val);
+            while ((next < valStringLen)  && 
+                  Character.isDigit(valString.charAt(next)) &&
+                  significantDigitsFound < significantDigits ) {
+                      result = result + valString.charAt(next);
+                      next++;
+                      significantDigitsFound++;
+             }
+            if ((next < valStringLen)  &&  
+                 Character.isDigit(valString.charAt(next)) &&
+                 Character.digit(valString.charAt(next), 10) >= 5) {
+                int prev = result.length()-1; // the next digit of result to increment
+                boolean done = false;
+                while (!done) {
+                    if (prev < 0) {
+                        result = "1" + result;
+                        done = true;
+                    } else {
+                        char prevChar = result.charAt(prev);
+                        String front = result.substring(0, prev);
+                        String back = result.substring(prev+1);
+                        if (Character.isDigit(prevChar)) {
+                            if (prevChar == '9') {
+                                result = front + '0' + back;
+                            } else {
+                                result = front + Character.forDigit(Character.digit(prevChar, 10)+1, 10) + back;
+                                done = true;
+                            }
+                            
+                        } else {
+                            // prevChar must be '.', so just continue
+                        }
+                    }
+                    prev--;
                 }
             }
-            MP.printMessage(EC.TLC_COVERAGE_END);
+            while ((next < valStringLen)  &&  
+                    Character.isDigit(valString.charAt(next))) {
+                next++;
+            }
+        }
+        
+        /*
+         * IF next at end of valString or at "E"
+         *   THEN copy remaining chars of valString to result;
+         *        return result
+         *   ELSE return valString
+         */
+        if (next >= valStringLen) {
+            return result;
+        }
+        if (valString.charAt(next)=='E') {
+            next++;
+            result = result + "E";
+            while (next < valStringLen) {
+                result = result + valString.charAt(next);
+                next++;
+            }
+            return result;
         }
+        return valString;
     }
 
+// The following method used for testing ProbabilityToString
+//
+//    public static void main(String[] args) {
+//        double[] test = new double[] {.5, .0995, .00000001, 001.000, .0022341, 
+//                                      .0022351, 3.14159E-12, 
+//                                      00.999, .002351111, 22.8E-14, 0.000E-12,
+//                                      37, 0033D, 04.85, -35.3};
+//        int i = 0;
+//        while (i < test.length) {
+//            System.out.println("" + i + ": " + Double.toString(test[i]) + " -> " + ProbabilityToString(test[i],2));
+//            i++;
+//        }    
+//    }
+
     /**
      * Initialize the model checker
-     * @return
+     * @return an error code, or <code>EC.NO_ERROR</code> on success
      * @throws Throwable
      */
-    public abstract boolean doInit(boolean ignoreCancel) throws Throwable;
+    public abstract int doInit(boolean ignoreCancel) throws Throwable;
 
     /**
      * I believe this method is called after the initial states are computed
@@ -199,30 +382,20 @@ public abstract class AbstractChecker implements Cancelable
      * Create the partial state space for given starting state up
      * to the given depth or the number of states.
      */
-    public final boolean runTLC(int depth) throws Exception
+    public final int runTLC(int depth) throws Exception
     {
-        // SZ Feb 23, 2009: exit if canceled
-        if (this.cancellationFlag)
-        {
-            return false;
-        }
-
         if (depth < 2)
         {
-            return true;
+            return EC.NO_ERROR;
         }
 
-        // Start all the workers:
-        IdThread[] workers = startWorkers(this, depth);
+        workers = startWorkers(this, depth);
 
         // Check progress periodically:
         // Comment added by LL on 9 April 2012.  The coverage is printed
         // every `count' times that the progress is printed.
         int count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval;
 
-        // work to be done prior loop entry
-        runTLCPreLoop();
-
         // I added the `if (!this.done)' to the following statement.
         // I have no idea what this wait is for, but apparently
         // because of changes made by Simon, it caused TLC to wait for
@@ -253,11 +426,13 @@ public abstract class AbstractChecker implements Cancelable
         // SZ Feb 23, 2009: exit if canceled
         // added condition to run in the cycle
         // while (true) {
-        while (!this.cancellationFlag)
+        int result = EC.NO_ERROR;
+        while (true)
         {
-            if (!this.doPeriodicWork())
+            result = this.doPeriodicWork();
+            if (result != EC.NO_ERROR)
             {
-                return false;
+                return result;
             }
             synchronized (this)
             {
@@ -284,14 +459,19 @@ public abstract class AbstractChecker implements Cancelable
         {
             workers[i].join();
         }
-        return true;
-    }
-
-    public void setCancelFlag(boolean flag)
-    {
-        this.cancellationFlag = flag;
+        return EC.NO_ERROR;
     }
     
+	public final void setAllValues(int idx, IValue val) {
+		for (int i = 0; i < this.workers.length; i++) {
+			workers[i].setLocalValue(idx, val);
+		}
+	}
+
+	public final IValue getValue(int i, int idx) {
+		return workers[i].getLocalValue(idx);
+	}
+
     /**
      * Debugging support
      * @param message
@@ -307,24 +487,17 @@ public abstract class AbstractChecker implements Cancelable
      * @param checkIndex the check level (depth or level)
      * @return the array of initialized worker threads
      */
-    protected abstract IdThread[] startWorkers(AbstractChecker checker, int checkIndex);
-
-    /**
-     * Hook to run some work before entering the worker loop
-     */
-    protected void runTLCPreLoop()
-    {
-    }
+    protected abstract IWorker[] startWorkers(AbstractChecker checker, int checkIndex);
 
     /**
      * Usually
      * Check liveness: check liveness properties on the partial state graph.
      * Checkpoint: checkpoint three data structures: the state set, the
      *             state queue, and the state trace.
-     * @return
+     * @return an error code, or <code>EC.NO_ERROR</code> on success
      * @throws Exception
      */
-    public abstract boolean doPeriodicWork() throws Exception;
+    public abstract int doPeriodicWork() throws Exception;
 
     /**
      * Method called from the main worker loop
@@ -336,7 +509,55 @@ public abstract class AbstractChecker implements Cancelable
 
     /**
      * Main method of the model checker
+     * @return an error code, or <code>EC.NO_ERROR</code> on success
      * @throws Exception
      */
-    public abstract void modelCheck() throws Exception;
+    final public int modelCheck() throws Exception {
+        final int result = modelCheckImpl();
+        return (result != EC.NO_ERROR) ? result : errorCode;
+    }
+
+    protected abstract int modelCheckImpl() throws Exception;
+
+	public int getProgress() {
+		return -1;
+	}
+	
+	public void stop() {
+		throw new UnsupportedOperationException("stop not implemented");
+	}
+	
+	public void suspend() {
+		throw new UnsupportedOperationException("suspend not implemented");
+	}
+	
+	public void resume() {
+		throw new UnsupportedOperationException("resume not implemented");
+	}
+	
+	static void scheduleTermination(final TimerTask tt) {
+		// Stops model checker after the given time in seconds. If model checking
+		// terminates before stopAfter seconds, the timer task will never run.
+		// Contrary to TLCSet("exit",...) this does not require a spec modification. Is
+		// is likely of little use for regular TLC users. In other words, this is meant
+		// to be a developer only feature and thus configured via a system property and
+		// not a regular TLC parameter.
+		final long stopAfter = Long.getLong(TLC.class.getName() + ".stopAfter", -1L);
+		if (stopAfter > 0) {
+			final Timer stopTimer = new Timer("TLCStopAfterTimer");
+			stopTimer.schedule(tt, stopAfter * 1000L); // seconds to milliseconds.
+		}
+	}
+	
+	protected boolean isTimeBound() {
+		return Long.getLong(TLC.class.getName() + ".stopAfter", -1L) != -1;
+	}
+
+	public long getStateQueueSize() {
+		return -1;
+	}
+
+	public long getDistinctStatesGenerated() {
+		return -1;
+	}
 }
diff --git a/tlatools/src/tlc2/tool/Action.java b/tlatools/src/tlc2/tool/Action.java
index ba497238ed2933ab19af1d30d32c3b17f4b0984d..0a756b6a49810ccc66f5d130a2d25d21bff1f1db 100644
--- a/tlatools/src/tlc2/tool/Action.java
+++ b/tlatools/src/tlc2/tool/Action.java
@@ -5,28 +5,94 @@ package tlc2.tool;
 
 import java.io.Serializable;
 
+import tla2sany.semantic.OpDefNode;
 import tla2sany.semantic.SemanticNode;
+import tla2sany.st.Location;
+import tla2sany.st.SyntaxTreeConstants;
+import tla2sany.st.TreeNode;
+import tlc2.tool.coverage.CostModel;
 import tlc2.util.Context;
+import util.UniqueString;
 
 public final class Action implements ToolGlobals, Serializable {
+	private static final UniqueString UNNAMED_ACTION = UniqueString.uniqueStringOf("UnnamedAction");
+
   /* A TLA+ action.   */
 
   /* Fields  */
   public final SemanticNode pred;     // Expression of the action
   public final Context con;           // Context of the action
+  private final UniqueString actionName;
+  private OpDefNode opDef = null;
+  public CostModel cm = CostModel.DO_NOT_RECORD;
 
   /* Constructors */
   public Action(SemanticNode pred, Context con) {
-    this.pred = pred;
-    this.con = con;
+	  this(pred, con, UNNAMED_ACTION);
+  }
+
+  private Action(SemanticNode pred, Context con, UniqueString actionName) {
+	  this.pred = pred;
+	  this.con = con;
+	  this.actionName = actionName;
   }
 
-  /* Returns a string representation of this action.  */
+  public Action(SemanticNode pred, Context con, OpDefNode opDef) {
+	  // opDef null when action not declared, i.e. Spec == x = 0 /\ ...
+	  // See test64 and test64a and others.
+	  this(pred, con, opDef != null ? opDef.getName() : UNNAMED_ACTION);
+	  this.opDef = opDef;
+  }
+
+/* Returns a string representation of this action.  */
   public final String toString() {
     return "<Action " + pred.toString() + ">";
   }
 
   public final String getLocation() {
-    return "<Action " + pred.getLocation() + ">";
+	  // It is possible that actionName is "Action" but lets ignore it for now.
+	  if (actionName != UNNAMED_ACTION && actionName != null && !"".equals(actionName.toString())) {
+		  // If known, print the action name instead of the generic string "Action".
+	      return "<" + actionName + " " +  pred.getLocation() + ">";
+	  }
+	  return "<Action " + pred.getLocation() + ">";
+  }
+  
+  /**
+   * @return The name of this action. Can be {@link Action#UNNAMED_ACTION}.
+   */
+  public final UniqueString getName() {
+	  return actionName;
   }
+  
+	/**
+	 * @return The OpDefNode corresponding to this Action or <code>null</code> if
+	 *         the Action is not explicitly declared. I.e. "Spec == x = 42 /\ [][x'
+	 *         = x + 1]_x".
+	 */
+	public final OpDefNode getOpDef() {
+		return this.opDef;
+	}
+
+	public final boolean isDeclared() {
+		// Spec == x = 0 /\ [][x' = x + 1]_x  has no declared actions.
+		return getDeclaration() != Location.nullLoc;
+	}
+	
+	/**
+	 * @return The {@link Location} of the <code>Action</code>'s declaration or
+	 *         <code>Location.nullLoc</code> if {@link #isDeclared()} is
+	 *         false.
+	 */
+	public Location getDeclaration() {
+		if (this.opDef != null) {
+			final TreeNode tn = opDef.getTreeNode();
+			if (tn != null && tn.one() != null && tn.one().length >= 1) {
+				final TreeNode treeNode = tn.one()[0];
+				assert treeNode.isKind(SyntaxTreeConstants.N_IdentLHS);
+				return treeNode.getLocation();
+			}
+		}
+		return Location.nullLoc;
+	}
 }
diff --git a/tlatools/src/tlc2/tool/CallStack.java b/tlatools/src/tlc2/tool/CallStack.java
index efda0f2d88b00852a0f402db065c07c1ec07bb2e..2d1e4360e17cb69151d5052135db3e793aef3215 100644
--- a/tlatools/src/tlc2/tool/CallStack.java
+++ b/tlatools/src/tlc2/tool/CallStack.java
@@ -16,6 +16,7 @@ public class CallStack {
 
   private SemanticNode[] stack;    // the call stack
   private int index;               // pointer to the empty slot
+  private boolean frozen;
 
   public final void push(SemanticNode expr) {
     if (this.index == this.stack.length) {
@@ -24,47 +25,60 @@ public class CallStack {
     this.stack[this.index++] = expr;
   }
 
-  public final void pop() { this.index--; }
+  public final void pop() { if(!frozen) this.index--; }
 
-  public final int size() { return this.index; }
+  /**
+   * Calling freeze turns all subsequent pop operations into no-ops. 
+   */
+  public void freeze() {
+	  this.frozen = true;
+  }
   
+  public final int size() { return this.index; }
+
   private final void resize() {
     int len = 2 * this.stack.length;
     SemanticNode[] stack1 = new SemanticNode[len];
     System.arraycopy(this.stack, 0, stack1, 0, this.stack.length);
     this.stack = stack1;
   }
-  
+
   // Returns a string representation of this.
-  public final String toString() 
+  public final String toString()
   {
-      /*
-       * Moved in the distinction if the call stack is empty or not (from Tool) 
-       */
-      if (this.index > 0) 
-      {
-          StringBuffer sb = new StringBuffer();
-          for (int i = 0; i < this.index; i++) {
-              sb.append(i + ". ");
-              SemanticNode expr = this.stack[i];
-              Location loc = expr.getTreeNode().getLocation();
-              sb.append("Line ");
-              sb.append(loc.beginLine());
-              sb.append(", column ");
-              sb.append(loc.beginColumn());
-              sb.append(" to line ");
-              sb.append(loc.endLine());
-              sb.append(", column ");
-              sb.append(loc.endColumn());
-              sb.append(" in ");
-              sb.append(loc.source() + "\n");
-          }
-          sb.append("\n");
-          return sb.toString();
-          
-      } else {
-          return "    The error call stack is empty.\n";
+    /*
+     * Moved in the distinction if the call stack is empty or not (from Tool)
+     */
+    if (this.index > 0)
+    {
+      final StringBuffer sb = new StringBuffer();
+      SemanticNode expr = null;
+      int stackDepth = 0;
+      for (int i = 0; i < this.index; i++) {
+        if(expr == this.stack[i]) {
+        	// Skip consecutive identical SemanticNodes.
+        	continue;
+        }
+        expr = this.stack[i];
+        Location loc = expr.getTreeNode().getLocation();
+        sb.append(stackDepth + ". ");
+        sb.append("Line ");
+        sb.append(loc.beginLine());
+        sb.append(", column ");
+        sb.append(loc.beginColumn());
+        sb.append(" to line ");
+        sb.append(loc.endLine());
+        sb.append(", column ");
+        sb.append(loc.endColumn());
+        sb.append(" in ");
+        sb.append(loc.source() + "\n");
+        stackDepth++;
       }
+      sb.append("\n");
+      return sb.toString();
+    } else {
+        return "    The error call stack is empty.\n";
+    }
   }
 
 }
diff --git a/tlatools/src/tlc2/tool/Cancelable.java b/tlatools/src/tlc2/tool/Cancelable.java
deleted file mode 100644
index 0cdc618662ca8fcdc782bbd38cffa09cbe433350..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/tool/Cancelable.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package tlc2.tool;
-
-/**
- * The interface for a cancelable.
- * @author Simon Zambrovski
- * @version $Id$
- */
-public interface Cancelable
-{
-    /**
-     * Sets the flag to cancel the instance
-     * @param flag cancellation flag
-     */
-    public void setCancelFlag(boolean flag);
-}
diff --git a/tlatools/src/tlc2/tool/CheckImpl.java b/tlatools/src/tlc2/tool/CheckImpl.java
index 49337bf10cbd27d2a52b855318e547e420b9b961..9b5b91f5a3e74b9b9343fdcf8aa35b915f78731c 100644
--- a/tlatools/src/tlc2/tool/CheckImpl.java
+++ b/tlatools/src/tlc2/tool/CheckImpl.java
@@ -6,11 +6,13 @@ package tlc2.tool;
 import java.io.IOException;
 
 import tlc2.TLCGlobals;
+import tlc2.output.EC;
 import tlc2.output.StatePrinter;
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.FPSetConfiguration;
 import tlc2.tool.fp.FPSetFactory;
 import tlc2.tool.queue.DiskStateQueue;
+import tlc2.util.NoopStateWriter;
 import util.ToolIO;
 
 /**
@@ -30,15 +32,15 @@ public abstract class CheckImpl extends ModelChecker {
    * @param fpMemSize : This parameter added by Yuan Yu on 6 Apr 2010 
    * because same parameter was added to the ModelChecker constructor. 
    */
-  public CheckImpl(String specFile, String configFile, boolean deadlock,
+  public CheckImpl(ITool tool, String metadir, boolean deadlock,
 		   int depth, String fromChkpt, final FPSetConfiguration fpSetConfig)
   throws IOException {
     // SZ Feb 20, 2009: patched due to changes to ModelCheker
-    super(specFile, configFile, null, deadlock, fromChkpt, null, null, fpSetConfig); // no name resolver and no specobj
+    super(tool, metadir, new NoopStateWriter(), deadlock, fromChkpt, fpSetConfig, System.currentTimeMillis()); // no name resolver and no specobj
     this.depth = depth;
     this.curState = null;
     this.coverSet = FPSetFactory.getFPSet();
-    this.coverSet.init(TLCGlobals.getNumWorkers(), this.metadir, specFile+"_cs");
+    this.coverSet.init(TLCGlobals.getNumWorkers(), this.metadir, tool.getRootName()+"_cs");
     this.stateEnum = null;
   }
 
@@ -68,10 +70,11 @@ public abstract class CheckImpl extends ModelChecker {
       this.doInit(false);
     }
     ToolIO.out.println("Creating a partial state space of depth " +
-		       this.depth + " ... ");
-    if (!this.runTLC(this.depth)) {
+           this.depth + " ... ");
+    final int result = this.runTLC(this.depth);
+    if (result != EC.NO_ERROR) {
       ToolIO.out.println("\nExit: failed to create the partial state space.");
-      System.exit(1);
+      System.exit(EC.ExitStatus.errorConstantToExitStatus(result));
     }
     ToolIO.out.println("completed.");
     this.lastTraceTime = System.currentTimeMillis();
@@ -92,8 +95,9 @@ public abstract class CheckImpl extends ModelChecker {
     int depth1= this.trace.getLevel(st.uid) + depth;
     this.theStateQueue = new DiskStateQueue(this.metadir);
     this.theStateQueue.enqueue(st);
-    if (!this.runTLC(depth1)) {
-      System.exit(1);
+    final int result = this.runTLC(depth1);
+    if (result != EC.NO_ERROR) {
+      System.exit(EC.ExitStatus.errorConstantToExitStatus(result));
     }
   }
   
@@ -115,9 +119,9 @@ public abstract class CheckImpl extends ModelChecker {
       StatePrinter.printState(s1);
       return false;
     }
-    int cnt = this.impliedActions.length;
+    int cnt = this.tool.getImpliedActions().length;
     for (int i = 0; i < cnt; i++) {
-      if (!this.tool.isValid(this.impliedActions[i], s0, s1)) {
+      if (!this.tool.isValid(this.tool.getImpliedActions()[i], s0, s1)) {
 	ToolIO.out.println("Error: Action property " + this.tool.getImpliedActNames()[i] +
 			   " is violated.");
 	StatePrinter.printState(s0);
@@ -138,12 +142,11 @@ public abstract class CheckImpl extends ModelChecker {
     boolean seen = this.coverSet.put(fp);
     if (!seen) {
       if (!this.theFPSet.contains(fp)) {
-	long loc = this.trace.writeState(this.curState, fp);
-	state.uid = loc;
+      state.uid = this.trace.writeState(this.curState, fp);
 	// Check invariant properties of the state:
-	int cnt = this.invariants.length;
+	int cnt = this.tool.getInvariants().length;
 	for (int j = 0; j < cnt; j++) {
-	  if (!this.tool.isValid(this.invariants[j], state)) {
+	  if (!this.tool.isValid(this.tool.getInvariants()[j], state)) {
 	    // We get here because of invariant violation:
 	    ToolIO.out.println("Error: Invariant " + this.tool.getInvNames()[j] +
 			       " is violated. The behavior up to this point is:");
diff --git a/tlatools/src/tlc2/tool/CheckImplFile.java b/tlatools/src/tlc2/tool/CheckImplFile.java
index a4f6eaaecb9c82b55b19f62a667bb0e4ce91ef84..b813a91d12f3d2e2031b6c2a2b2939555b32c12f 100644
--- a/tlatools/src/tlc2/tool/CheckImplFile.java
+++ b/tlatools/src/tlc2/tool/CheckImplFile.java
@@ -19,9 +19,11 @@ import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.impl.FastTool;
 import tlc2.util.FP64;
 import util.Assert;
 import util.FileUtil;
+import util.TLAConstants;
 import util.ToolIO;
 import util.UniqueString;
 
@@ -39,10 +41,10 @@ public class CheckImplFile extends CheckImpl
      * to ModelChecker constructor.
      * 
      */
-    public CheckImplFile(String specFile, String configFile, boolean deadlock, int depth, String fromChkpt,
+    public CheckImplFile(ITool tool, String metadir, boolean deadlock, int depth, String fromChkpt,
             String traceFile, final FPSetConfiguration fpSetConfig) throws IOException
     {
-        super(specFile, configFile, deadlock, depth, fromChkpt, fpSetConfig);
+        super(tool, metadir, deadlock, depth, fromChkpt, fpSetConfig);
         this.traceFile = traceFile;
         this.states = null;
         this.sidx = 0;
@@ -178,9 +180,11 @@ public class CheckImplFile extends CheckImpl
             index++;
             if (index < args.length) {
                 configFile = args[index++];
-                int len = configFile.length();
-                if (configFile.startsWith(".cfg", len-4)) {
-                    configFile = configFile.substring(0, len-4);
+                if (configFile.endsWith(TLAConstants.Files.CONFIG_EXTENSION))
+                {
+                    configFile
+                    	= configFile.substring(0,
+                    			(configFile.length() - TLAConstants.Files.CONFIG_EXTENSION.length()));
                 }
             }
             else {
@@ -280,13 +284,12 @@ public class CheckImplFile extends CheckImpl
                 return;
             }
             if (mainFile != null) {
-                printErrorMsg(MP.getError(EC.CHECK_PARAM_UNRECOGNIZED, new String[]{mainFile, args[index] }));
+                printErrorMsg(MP.getError(EC.CHECK_PARAM_UNRECOGNIZED, new String[]{args[index], mainFile }));
                 return;
             }
             mainFile = args[index++];
-            int len = mainFile.length();
-            if (mainFile.startsWith(".tla", len-4)) {
-                mainFile = mainFile.substring(0, len-4);
+            if (mainFile.endsWith(TLAConstants.Files.TLA_EXTENSION)) {
+                mainFile = mainFile.substring(0, (mainFile.length() - TLAConstants.Files.TLA_EXTENSION.length()));
             }
         }
     }
@@ -299,6 +302,9 @@ public class CheckImplFile extends CheckImpl
     if (configFile == null) configFile = mainFile;
     if (traceFile == null) traceFile = mainFile + "_trace";
 
+    final File f = new File(mainFile);
+	String metadir = FileUtil.makeMetaDir(f.isAbsolute() ? f.getParent() : "", fromChkpt);
+
     try {
       // Initialize:
       if (fromChkpt != null) {
@@ -308,7 +314,8 @@ public class CheckImplFile extends CheckImpl
       FP64.Init(0);
       
       // Start the checker:
-      CheckImplFile checker = new CheckImplFile(mainFile, configFile, deadlock,
+      final ITool tool = new FastTool(mainFile, configFile);
+      CheckImplFile checker = new CheckImplFile(tool, metadir, deadlock,
 						depth, fromChkpt, traceFile, new FPSetConfiguration());
       checker.init();
       while (true) {
diff --git a/tlatools/src/tlc2/tool/ConcurrentTLCTrace.java b/tlatools/src/tlc2/tool/ConcurrentTLCTrace.java
new file mode 100644
index 0000000000000000000000000000000000000000..0cf1ce2b3a15d4f16e9ff95eb7b6db2911b2fc6f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/ConcurrentTLCTrace.java
@@ -0,0 +1,276 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.io.File;
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.util.LongVec;
+
+/**
+ * This implementation of a Trace is concurrent in that multiple workers can add
+ * states to it whereas with {@link TLCTrace} concurrent adds/appends block and
+ * are serialized. The latter has been identified to be a scalability bottleneck
+ * of bread-first search (safety checking).
+ * <p>
+ * {@link ConcurrentTLCTrace} - in comparison to {@link TLCTrace} - maintains
+ * one trace file per worker and thus supports concurrent appends: Each worker
+ * adds/appends an entry to its (dedicate) file. When a counter-example has to be
+ * created, the actual error-trace gets created from the union of all (partial)
+ * trace files.
+ */
+public class ConcurrentTLCTrace extends TLCTrace {
+	
+	private final Worker workers[];
+
+	public ConcurrentTLCTrace(String metadir, String specFile, TraceApp tool) throws IOException {
+		super(metadir, specFile, tool);
+		this.workers = new Worker[TLCGlobals.getNumWorkers()];
+	}
+
+	public Worker addWorker(Worker worker) {
+		this.workers[worker.myGetId()] = worker;
+		return worker;
+	}
+
+	/**
+	 * @see ConcurrentTLCTrace#getLevel()
+	 */
+	@Override
+	public final int getLevelForReporting() throws IOException {
+		return getLevel();
+	}
+
+	@Override
+	public synchronized final int getLevel() throws IOException {
+		int maxLevel = 1; // With a single, init state the level/progress/diameter is 1, not 0!
+		for (Worker worker : workers) {
+			maxLevel = Math.max(maxLevel, worker.getMaxLevel());
+		}
+		return maxLevel;
+	}
+
+	/**
+	 * @see TLCTrace#getTrace(LongVec)
+	 */
+	public TLCStateInfo[] getTrace(final TLCState state) throws IOException {
+		if (state.isInitial()) {
+			return new TLCStateInfo[] {new TLCStateInfo(state)};
+		}
+		
+		final LongVec fps = new LongVec();
+
+		// Starting at the given start fingerprint (which is the end of the
+		// trace from the point of the initial states), the sequence of
+		// predecessors fingerprints are reconstructed from the trace files up to
+		// an initial state.
+		synchronized (this) {
+			Record record = Record.getPredecessor(state, this.workers);
+			while (!record.isInitial()) {
+				fps.addElement(record.fp);
+				record = record.getPredecessor();
+			}
+			// The fp of the final initial state.
+			fps.addElement(record.fp);
+			assert 0 <= fps.size() && fps.size() <= getLevel();
+		}
+		
+		return getTrace(fps);
+	}
+	
+	public TLCStateInfo[] getTrace(final TLCState from, final TLCState to) throws IOException {
+		if (to.isInitial() || from.equals(to)) {
+			return new TLCStateInfo[] {new TLCStateInfo(to)};
+		}
+		
+		final LongVec fps = new LongVec();
+
+		// Starting at the given start fingerprint (which is the end of the
+		// trace from the point of the initial states), the sequence of
+		// predecessors fingerprints are reconstructed from the trace files up to
+		// an initial state.
+		synchronized (this) {
+			Record record = Record.getPredecessor(to, this.workers);
+			while (record.fp != from.fingerPrint()) {
+				fps.addElement(record.fp);
+				record = record.getPredecessor();
+			}
+			// The fp of the final initial state.
+			fps.addElement(record.fp);
+			assert 0 <= fps.size() && fps.size() <= getLevel();
+		}
+		
+		return getTrace(new TLCStateInfo(from), fps);
+	}
+
+	/**
+	 * Write out a sequence of states that reaches s2 from an initial state,
+	 * according to the spec. s2 is a next state of s1.
+	 * 
+	 * @param s1
+	 *            may not be null.
+	 * @param s2
+	 *            may be null.
+	 * @throws IOException
+	 * @throws WorkerException
+	 */
+	public void printTrace(final TLCState s1, final TLCState s2) throws IOException, WorkerException {
+		if (s1.isInitial()) {
+			printTrace(s1, s2, new TLCStateInfo[0]);
+		} else {
+			printTrace(s1, s2, getTrace(s1));
+		}
+	}
+
+	/* Checkpoint. */
+	
+	public synchronized void beginChkpt() throws IOException {
+		for (Worker worker : workers) {
+			worker.beginChkpt();
+		}
+	}
+
+	public void commitChkpt() throws IOException {
+		for (Worker worker : workers) {
+			worker.commitChkpt();
+		}
+		// MAK 06/08/2020:
+		// Touch MC.st.chkpt (in addition to the worker-created MC-${N}.chkpt files)
+		// that (single-worker) TLCTrace creates. The Toolbox checks for the existence
+		// of checkpoints by looking for this file (see
+		// org.lamport.tla.toolbox.tool.tlc.model.Model.getCheckpoints(boolean)). If 
+		// the Toolbox check fails, a user cannot resume/restart model-checking from
+		// the checkpoint. Addresses Github issue #469 at
+		// https://github.com/tlaplus/tlaplus/issues/469
+		//
+		// We create an empty file here instead of changing the Toolbox in case 3rd party
+		// integrations and scripts rely on the file's existence.  Reading the empty
+		// MC.st.chkpt with an older TLC release will crash and thus make a user aware
+		// that recovery fails.
+		//
+		// createNewFile is safe to call periodically when a checkpoint get taken
+		// because it does *not* throw an exception if the file already exists.
+		new File(filename + ".chkpt").createNewFile();
+	}
+
+	public void recover() throws IOException { 
+		// TODO Check that the number of disk .st files is >= workers.length. If it is
+		// lower, TLC runs with fewer workers than when the checkpoint was taken. Take
+		// this case into account.
+		
+		for (Worker worker : workers) {
+			worker.recover();
+		}
+	}
+	
+	/* Enumerator */
+	
+	public synchronized Enumerator elements() throws IOException {
+		final Worker.Enumerator[] enums = new Worker.Enumerator[workers.length];
+		for (int j = 0; j < workers.length; j++) {
+			enums[j] = workers[j].elements();
+		}
+		return new Enumerator() {
+			private int idx = 0;
+			
+			@Override
+			public long nextPos() {
+				if (idx >= workers.length) {
+					return -1L;
+				}
+				if (enums[idx].hasMoreFP()) {
+					return 42L;
+				} else if (idx + 1 >= workers.length) {
+					return -1L;
+				} else {
+					idx = idx + 1;
+					return enums[idx].hasMoreFP() ? 42L : -1L;	
+				}
+			}
+
+			@Override
+			public long nextFP() throws IOException {
+				if (!enums[idx].hasMoreFP()) {
+					idx = idx + 1;
+				}
+				return enums[idx].nextFP();
+			}
+
+			@Override
+			public void close() throws IOException {
+				for (Worker.Enumerator enumerator : enums) {
+					enumerator.close();
+				}
+			}
+
+			@Override
+			public void reset(long i) throws IOException {
+				idx = 0;
+			}
+		};
+	}
+	
+	public static class Record {
+		
+
+		static Record getPredecessor(final TLCState state, final Worker[] workers) throws IOException {
+			Record record = workers[state.workerId].readStateRecord(state.uid);
+			record.workers = workers;
+			return record.getPredecessor();
+		}
+
+		private final long ptr;
+		private final int worker;
+		private final long fp;
+		private Worker[] workers;
+
+		public Record(final long ptr, final int worker, final long fp) {
+			this.ptr = ptr;
+			this.worker = worker;
+			this.fp = fp;
+		}
+		
+		public Worker getWorker() {
+			return this.workers[this.worker];
+		}
+
+		public boolean isInitial() {
+			return ptr == 1L;
+		}
+
+		@Override
+		public String toString() {
+			return "Record [ptr=" + ptr + ", worker=" + worker + ", fp=" + fp + ", initial=" + isInitial() + "]";
+		}
+		
+		Record getPredecessor() throws IOException {
+			final Record record = getWorker().readStateRecord(this.ptr);
+			record.workers = this.workers;
+			return record;
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/tool/DFIDModelChecker.java b/tlatools/src/tlc2/tool/DFIDModelChecker.java
index 6cf83959cfe510fa255c5168eafb90180dc4e0f2..c333ed588ace262232bccedf31ab9ff82803cf8b 100644
--- a/tlatools/src/tlc2/tool/DFIDModelChecker.java
+++ b/tlatools/src/tlc2/tool/DFIDModelChecker.java
@@ -3,21 +3,25 @@
 package tlc2.tool;
 
 import java.io.IOException;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
 
-import tla2sany.modanalyzer.SpecObj;
 import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.OpDeclNode;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.fp.dfid.FPIntSet;
 import tlc2.tool.fp.dfid.MemFPIntSet;
+import tlc2.tool.impl.CallStackTool;
 import tlc2.tool.liveness.LiveException;
+import tlc2.util.IStateWriter;
 import tlc2.util.IdThread;
 import tlc2.util.LongVec;
-import tlc2.util.ObjLongTable;
+import tlc2.util.SetOfStates;
+import util.Assert;
 import util.FileUtil;
-import util.FilenameToStream;
 import util.UniqueString;
 
 /** 
@@ -35,40 +39,63 @@ public class DFIDModelChecker extends AbstractChecker
     public TLCState[] theInitStates; // the set of initial states
     public long[] theInitFPs; // ... and their fps
     public FPIntSet theFPSet; // the set of reachable states (SZ: note the type)
-    protected DFIDWorker[] workers; // the workers
+    private final AtomicLong numOfGenStates;
+
+	protected final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
+		protected Integer initialValue() {
+			return 1;
+		}
+	};
+
+	protected static final int INITIAL_CAPACITY = 16;
 
     /** 
      * Constructor for running DFID   
+     * @param startTime 
      * @param resolver 
      */
-    public DFIDModelChecker(String specFile, String configFile, String dumpFile, boolean deadlock, String fromChkpt,
-            boolean preprocess, FilenameToStream resolver, SpecObj specObj) throws EvalException, IOException
-    {
+	public DFIDModelChecker(ITool tool, String metadir, final IStateWriter stateWriter,
+			boolean deadlock, String fromChkpt, long startTime) throws EvalException, IOException {
         // call the abstract constructor
-        super(specFile, configFile, dumpFile, deadlock, fromChkpt, preprocess, resolver, specObj);
+        super(tool, metadir, stateWriter, deadlock, fromChkpt, startTime);
 
-        this.theInitStates = null;
+		// https://github.com/tlaplus/tlaplus/issues/548
+		Assert.check(TLCGlobals.getNumWorkers() == 1, EC.GENERAL,
+				"Depth-First Iterative Deepening mode does not support multiple workers (https://github.com/tlaplus/tlaplus/issues/548).  Please run TLC with a single worker.");
+		// https://github.com/tlaplus/tlaplus/issues/548
+		Assert.check(this.checkLiveness == false, EC.GENERAL,
+				"Depth-First Iterative Deepening mode does not support checking liveness properties (https://github.com/tlaplus/tlaplus/issues/548).  Please check liveness properties in Breadth-First-Search mode.");
+
+		this.theInitStates = null;
         this.theInitFPs = null;
         this.theFPSet = new MemFPIntSet(); // init the state set
-        this.theFPSet.init(TLCGlobals.getNumWorkers(), this.metadir, specFile);
+        this.theFPSet.init(TLCGlobals.getNumWorkers(), this.metadir, this.tool.getRootFile());
 
         // Initialize all the workers:
         this.workers = new DFIDWorker[TLCGlobals.getNumWorkers()];
+        this.numOfGenStates = new AtomicLong(0);
     }
 
     /**
      * This method does model checking on a TLA+ spec. All the visited
      * states are stored in the variable theFPSet.
      */
-    public void modelCheck() throws Exception
+	@Override
+    protected int modelCheckImpl() throws Exception
     {
+		int result = EC.NO_ERROR;
         boolean recovered = this.recover();
         try
         {
-            if (!this.checkAssumptions())
-                return;
-            if (!this.doInit(false))
-                return;
+			if (this.checkLiveness && liveCheck.getNumChecker() == 0) {
+				return MP.printError(EC.TLC_LIVE_FORMULA_TAUTOLOGY);
+			}
+			result = this.checkAssumptions();
+            if (result != EC.NO_ERROR)
+                return result;
+            result = this.doInit(false);
+            if (result != EC.NO_ERROR)
+                return result;
         } catch (Throwable e)
         {
             // Initial state computation fails with an exception:
@@ -81,18 +108,17 @@ public class DFIDModelChecker extends AbstractChecker
             }
 
             // Replay the error with the error stack recorded:
-            this.tool.setCallStack();
             try
             {
-                this.numOfGenStates = new AtomicLong(0);
-                this.doInit(true);
+                this.numOfGenStates.set(0);
+                this.doInit(new CallStackTool(this.tool), true);
             } catch (Throwable e1)
             {
-                MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString());
+                result = MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.toString());
             }
             this.printSummary(false);
             this.cleanup(false);
-            return;
+            return result;
         }
 
         if (recovered)
@@ -106,15 +132,15 @@ public class DFIDModelChecker extends AbstractChecker
         }
 
         // Return if there is no next state predicate:
-        if (this.actions.length == 0)
+        if (this.tool.getActions().length == 0)
         {
             this.reportSuccess();
             this.printSummary(true);
             this.cleanup(true);
-            return;
+            return result;
         }
 
-        boolean success = false;
+        result = EC.GENERAL;
         try
         {
             boolean terminated = false;
@@ -128,47 +154,56 @@ public class DFIDModelChecker extends AbstractChecker
                         // Always check liveness properties at the end:
                         if (this.checkLiveness)
                         {
-							MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS,
-									new String[] { "complete", Long.toString(this.theFPSet.size()) });
+        					// Print progress statistics prior to liveness checking.
+        					// Liveness checking can take a substantial amount of time
+        					// and thus give the user some clues at what stage safety
+        					// checking is.
+							MP.printMessage(EC.TLC_PROGRESS_STATS_DFID, new String[] {
+									String.valueOf(this.numOfGenStates), String.valueOf(theFPSet.size()) });
                             // SZ Jul 10, 2009: what for?
                             // ToolIO.out.flush();
-                            success = liveCheck.finalCheck();
-                            if (!success)
-                                return;
+                            result = liveCheck.finalCheck(tool);
+                            if (result != EC.NO_ERROR)
+                                return result;
                         }
 
                         // We get here because the checking has been completed.
-                        success = true;
+                        result = EC.NO_ERROR;
                         this.reportSuccess();
                     } else if (this.keepCallStack)
                     {
                         // Replay the error with the error stack recorded:
-                        this.tool.setCallStack();
+                    	final CallStackTool cTool = new CallStackTool(this.tool);
                         try
                         {
-                            this.doNext(this.predErrState, this.predErrState.fingerPrint(), true, new ObjLongTable(10),
+							this.doNext(cTool, this.predErrState, this.predErrState.fingerPrint(), true,
                                     new StateVec(1), new LongVec());
                         } catch (Throwable e)
                         {
-                            MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString());
+                            MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString());
                         }
                     }
                     break;
                 }
 
                 // Start working on this level:
-                MP.printMessage(EC.TLC_PROGRESS_STATS_DFID, new String[] { String.valueOf(level),
+                MP.printMessage(EC.TLC_PROGRESS_START_STATS_DFID, new String[] { String.valueOf(level),
                         String.valueOf(this.numOfGenStates), String.valueOf(this.theFPSet.size()) });
 
                 FPIntSet.incLevel();
-                success = this.runTLC(level);
-                if (!success)
-                    return;
+                result = this.runTLC(level);
+				// Recent done flag before after the workers have checked the
+				// current level in preparation for the next level.
+                synchronized (this) {
+                	this.done = false;
+				}
+                if (result != EC.NO_ERROR)
+                    return result;
 
                 // Check if we should stop at this level:
                 for (int i = 0; i < this.workers.length; i++)
                 {
-                    if (this.workers[i].isTerminated())
+                    if (((DFIDWorker) this.workers[i]).isTerminated())
                     {
                         terminated = true;
                         break;
@@ -177,7 +212,7 @@ public class DFIDModelChecker extends AbstractChecker
                 boolean moreLevel = false;
                 for (int i = 0; i < this.workers.length; i++)
                 {
-                    if (this.workers[i].hasMoreLevel())
+                    if (((DFIDWorker) this.workers[i]).hasMoreLevel())
                     {
                         moreLevel = true;
                         break;
@@ -185,16 +220,21 @@ public class DFIDModelChecker extends AbstractChecker
                 }
                 terminated = terminated || !moreLevel;
             }
+            return result;
         } catch (Exception e)
         {
             // Assert.printStack(e);
-            success = false;
-            if (!(e instanceof LiveException))
+            if (e instanceof LiveException)
+            {
+                result = ((LiveException)e).errorCode;
+            } else
             {
-                MP.printError(EC.GENERAL, e);  // LL changed call 7 April 2012
+                result = MP.printError(EC.GENERAL, e);  // LL changed call 7 April 2012
             }
+            return result;
         } finally
         {
+        	final boolean success = result == EC.NO_ERROR;
             this.printSummary(success);
             this.cleanup(success);
         }
@@ -202,7 +242,7 @@ public class DFIDModelChecker extends AbstractChecker
 
     /* Check the assumptions.  
      * This code is a clone of the same method in ModelChecker */
-    public final boolean checkAssumptions()
+    public final int checkAssumptions()
     {
         ExprNode[] assumps = this.tool.getAssumptions();
         boolean[] isAxiom = this.tool.getAssumptionIsAxiom();
@@ -212,29 +252,30 @@ public class DFIDModelChecker extends AbstractChecker
             {
                 if ((!isAxiom[i]) && !this.tool.isValid(assumps[i]))
                 {
-                    MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString());
-                    return false;
+                    return MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString());
                 }
             } catch (Exception e)
             {
                 // Assert.printStack(e);
-                MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+                return MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
                         new String[] { assumps[i].toString(), e.getMessage() });
-                return false;
             }
         }
-        return true;
+        return EC.NO_ERROR;
     }
 
     /* Compute the set of initial states.  */
     // SZ Feb 23, 2009: added ignore cancel flag
-    public final boolean doInit(boolean ignoreCancel) throws Throwable
+    public final int doInit(boolean ignoreCancel) throws Throwable {
+    	return doInit(this.tool, ignoreCancel);
+    }
+    private final int doInit(final ITool tool, boolean ignoreCancel) throws Throwable
     {
         TLCState curState = null;
         try
         {
             // Generate the initial states:
-            StateVec states = this.tool.getInitStates();
+            StateVec states = tool.getInitStates();
             this.numOfGenStates.set(states.size());
             final long l = this.numOfGenStates.get();
             //TODO casting to int is potentially dangerous
@@ -245,12 +286,11 @@ public class DFIDModelChecker extends AbstractChecker
             {
                 curState = states.elementAt(i);
                 // Check if the state is a legal state
-                if (!this.tool.isGoodState(curState))
+                if (!tool.isGoodState(curState))
                 {
-                    MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString());
-                    return false;
+                    return MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString());
                 }
-                boolean inModel = this.tool.isInModel(curState);
+                boolean inModel = tool.isInModel(curState);
                 int status = FPIntSet.NEW;
                 if (inModel)
                 {
@@ -262,40 +302,36 @@ public class DFIDModelChecker extends AbstractChecker
                         this.theInitFPs[idx++] = fp;
 
                         // Write out the state if asked
-                        if (this.allStateWriter != null)
-                        {
-                            this.allStateWriter.writeState(curState);
-                        }
+                        this.allStateWriter.writeState(curState);
 
                         // build behavior graph for liveness checking
                         if (this.checkLiveness)
                         {
-                            liveCheck.addInitState(curState, fp);
+                            liveCheck.addInitState(tool, curState, fp);
                         }
                     }
                 }
                 // Check properties of the state:
                 if (status == FPIntSet.NEW)
                 {
-                    for (int j = 0; j < this.invariants.length; j++)
+                    for (int j = 0; j < tool.getInvariants().length; j++)
                     {
-                        if (!this.tool.isValid(this.invariants[j], curState))
+                        if (!tool.isValid(tool.getInvariants()[j], curState))
                         {
                             // We get here because of invariant violation:
-                            MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] { this.tool.getInvNames()[j],
+                            MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] { tool.getInvNames()[j],
                                     curState.toString() });
                             if (!TLCGlobals.continuation)
-                                return false;
+                                return EC.TLC_INVARIANT_VIOLATED_INITIAL;
                         }
                     }
-                    for (int j = 0; j < this.impliedInits.length; j++)
+                    for (int j = 0; j < tool.getImpliedInits().length; j++)
                     {
-                        if (!this.tool.isValid(this.impliedInits[j], curState))
+                        if (!tool.isValid(tool.getImpliedInits()[j], curState))
                         {
                             // We get here because of implied-inits violation:
-                            MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, new String[] {
-                                    this.tool.getImpliedInitNames()[j], curState.toString() });
-                            return false;
+                            return MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, new String[] {
+                                    tool.getImpliedInitNames()[j], curState.toString() });
                         }
                     }
                 }
@@ -316,13 +352,12 @@ public class DFIDModelChecker extends AbstractChecker
             // Assert.printStack(e);
             if (e instanceof OutOfMemoryError)
             {
-                MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT);
-                return false;
+                return MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT);
             }
             this.errState = curState;
             throw e;
         }
-        return true;
+        return EC.NO_ERROR;
     }
 
     /**
@@ -332,18 +367,20 @@ public class DFIDModelChecker extends AbstractChecker
      * not been done in nextStates.  Return true if it finds a leaf
      * successor of curState.
      */
-    public final boolean doNext(TLCState curState, long cfp, boolean isLeaf, ObjLongTable counts, StateVec states,
+    public final boolean doNext(TLCState curState, long cfp, boolean isLeaf, StateVec states,
+            LongVec fps) throws Throwable {
+    	return doNext(this.tool, curState, cfp, isLeaf, states, fps);
+    }
+    public final boolean doNext(final ITool tool, TLCState curState, long cfp, boolean isLeaf, StateVec states,
             LongVec fps) throws Throwable
     {
         boolean deadLocked = true;
         TLCState succState = null;
-        StateVec liveNextStates = null;
-        LongVec liveNextFPs = null;
+        SetOfStates liveNextStates = null;
 
         if (this.checkLiveness && isLeaf)
         {
-            liveNextStates = new StateVec(2);
-            liveNextFPs = new LongVec(2);
+            liveNextStates = new SetOfStates(INITIAL_CAPACITY * threadLocal.get());
         }
 
         try
@@ -351,37 +388,41 @@ public class DFIDModelChecker extends AbstractChecker
             int k = 0;
             boolean allSuccDone = true;
             boolean allSuccNonLeaf = true;
-            for (int i = 0; i < this.actions.length; i++)
+            for (int i = 0; i < tool.getActions().length; i++)
             {
-                StateVec nextStates = this.tool.getNextStates(this.actions[i], curState);
+                StateVec nextStates = tool.getNextStates(tool.getActions()[i], curState);
                 int sz = nextStates.size();
-                this.incNumOfGenStates(sz);
+                this.numOfGenStates.getAndAdd(sz);
                 deadLocked = deadLocked && (sz == 0);
 
                 for (int j = 0; j < sz; j++)
                 {
                     succState = nextStates.elementAt(j);
                     // Check if the state is a legal state.
-                    if (!this.tool.isGoodState(succState))
+                    if (!tool.isGoodState(succState))
                     {
-                        if (this.setErrState(curState, succState, false))
-                        {
-                            this.printTrace(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, curState, succState);
-
-                            synchronized (this)
-                            {
-                                this.notify();
-                            }
-                        }
+						synchronized (this) {
+                            final int errorCode = EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT;
+							if (this.setErrState(curState, succState, false, errorCode)) {
+								final Set<OpDeclNode> unassigned = succState.getUnassigned();
+								String[] parameters;
+								if (tool.getActions().length == 1) {
+									parameters = new String[] { unassigned.size() > 1 ? "s are" : " is",
+											unassigned.stream().map(n -> n.getName().toString())
+													.collect(Collectors.joining(", ")) };
+								} else {
+									parameters = new String[] { tool.getActions()[i].getName().toString(),
+											unassigned.size() > 1 ? "s are" : " is",
+											unassigned.stream().map(n -> n.getName().toString())
+													.collect(Collectors.joining(", ")) };
+								}
+								this.printTrace(errorCode, parameters, curState, succState);
+							}
+						}
                         return allSuccNonLeaf;
                     }
 
-                    if (TLCGlobals.coverageInterval >= 0)
-                    {
-                        ((TLCStateMutSource) succState).addCounts(counts);
-                    }
-
-                    boolean inModel = (this.tool.isInModel(succState) && this.tool.isInActions(curState, succState));
+                    boolean inModel = (tool.isInModel(succState) && tool.isInActions(curState, succState));
                     int status = FPIntSet.NEW;
                     if (inModel)
                     {
@@ -391,10 +432,7 @@ public class DFIDModelChecker extends AbstractChecker
                         allSuccNonLeaf = allSuccNonLeaf && !FPIntSet.isLeaf(status);
 
                         // Write out the state when new and asked:
-                        if (status == FPIntSet.NEW && this.allStateWriter != null)
-                        {
-                            this.allStateWriter.writeState(succState);
-                        }
+                        this.allStateWriter.writeState(curState, succState, status == FPIntSet.NEW);
 
                         // Remember succState if it has not been completed at this level:
                         if (!FPIntSet.isCompleted(status))
@@ -406,8 +444,7 @@ public class DFIDModelChecker extends AbstractChecker
                         // For liveness checking:
                         if (this.checkLiveness && isLeaf)
                         {
-                            liveNextStates.addElement(succState);
-                            liveNextFPs.addElement(fp);
+                            liveNextStates.put(fp, succState);
                         }
                     }
 
@@ -416,10 +453,10 @@ public class DFIDModelChecker extends AbstractChecker
                     {
                         try
                         {
-                            int len = this.invariants.length;
+                            int len = tool.getInvariants().length;
                             for (k = 0; k < len; k++)
                             {
-                                if (!tool.isValid(this.invariants[k], succState))
+                                if (!tool.isValid(tool.getInvariants()[k], succState))
                                 {
                                     // We get here because of invariant violation:
                                     synchronized (this)
@@ -427,14 +464,14 @@ public class DFIDModelChecker extends AbstractChecker
                                         if (TLCGlobals.continuation)
                                         {
                                             this.printTrace(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
-                                                    new String[] { this.tool.getInvNames()[k] }, curState, succState);
+                                                    new String[] { tool.getInvNames()[k] }, curState, succState);
                                             break;
                                         } else
                                         {
-                                            if (this.setErrState(curState, succState, false))
+                                            if (this.setErrState(curState, succState, false, EC.TLC_INVARIANT_VIOLATED_BEHAVIOR))
                                             {
                                                 this.printTrace(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
-                                                        new String[] { this.tool.getInvNames()[k] }, curState,
+                                                        new String[] { tool.getInvNames()[k] }, curState,
                                                         succState);
                                                 this.notify();
                                             }
@@ -447,23 +484,25 @@ public class DFIDModelChecker extends AbstractChecker
                                 continue;
                         } catch (Exception e)
                         {
-                            if (this.setErrState(curState, succState, true))
-                            {
-                                this.printTrace(EC.TLC_INVARIANT_EVALUATION_FAILED, new String[] { this.tool
-                                        .getInvNames()[k] }, curState, succState);
-                                this.notify();
-                            }
-                            return allSuccNonLeaf;
+                        	synchronized (this) {
+		                        if (this.setErrState(curState, succState, true, EC.TLC_INVARIANT_EVALUATION_FAILED))
+		                        {
+		                            this.printTrace(EC.TLC_INVARIANT_EVALUATION_FAILED, new String[] { tool
+		                                    .getInvNames()[k] }, curState, succState);
+		                            this.notify();
+		                        }
+		                        return allSuccNonLeaf;
+                        	}
                         }
                     }
                     // Check if the state violates any implied action. We need to do it
                     // even if succState is not new.
                     try
                     {
-                        int len = this.impliedActions.length;
+                        int len = tool.getImpliedActions().length;
                         for (k = 0; k < len; k++)
                         {
-                            if (!tool.isValid(this.impliedActions[k], curState, succState))
+                            if (!tool.isValid(tool.getImpliedActions()[k], curState, succState))
                             {
                                 // We get here because of implied-action violation:
                                 synchronized (this)
@@ -472,16 +511,15 @@ public class DFIDModelChecker extends AbstractChecker
                                     {
                                         this
                                                 .printTrace(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
-                                                        new String[] { this.tool.getImpliedActNames()[k] }, curState,
+                                                        new String[] { tool.getImpliedActNames()[k] }, curState,
                                                         succState);
                                         break;
                                     } else
                                     {
-                                        if (this.setErrState(curState, succState, false))
+                                        if (this.setErrState(curState, succState, false, EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR))
                                         {
-
                                             this.printTrace(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
-                                                    new String[] { this.tool.getImpliedActNames()[k] }, curState,
+                                                    new String[] { tool.getImpliedActNames()[k] }, curState,
                                                     succState);
                                             this.notify();
                                         }
@@ -494,13 +532,15 @@ public class DFIDModelChecker extends AbstractChecker
                             continue;
                     } catch (Exception e)
                     {
-                        if (this.setErrState(curState, succState, true))
-                        {
-                            this.printTrace(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, new String[] { this.tool
-                                    .getImpliedActNames()[k] }, curState, succState);
-                            this.notify();
-                        }
-                        return allSuccNonLeaf;
+                    	synchronized (this) {
+		                    if (this.setErrState(curState, succState, true, EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED))
+		                    {
+		                        this.printTrace(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, new String[] { tool
+		                                .getImpliedActNames()[k] }, curState, succState);
+		                        this.notify();
+		                    }
+                    	}
+                    	return allSuccNonLeaf;
                     }
                 }
 
@@ -513,7 +553,7 @@ public class DFIDModelChecker extends AbstractChecker
             {
                 synchronized (this)
                 {
-                    if (this.setErrState(curState, null, false))
+                    if (this.setErrState(curState, null, false, EC.TLC_DEADLOCK_REACHED))
                     {
                         this.printTrace(EC.TLC_DEADLOCK_REACHED, null, curState, null);
                         this.notify();
@@ -525,12 +565,20 @@ public class DFIDModelChecker extends AbstractChecker
             // Finally, add curState into the behavior graph for liveness checking:
             if (this.checkLiveness && isLeaf)
             {
+            	final long curStateFP = curState.fingerPrint();
                 // Add a stuttering step for curState:
-                long curStateFP = curState.fingerPrint();
-                liveNextStates.addElement(curState);
-                liveNextFPs.addElement(curStateFP);
+                liveNextStates.put(curStateFP, curState);
+            	this.allStateWriter.writeState(curState, curState, true, IStateWriter.Visualization.STUTTERING);
                 // Add curState to the behavior graph:
-                liveCheck.addNextState(curState, curStateFP, liveNextStates, liveNextFPs);
+                liveCheck.addNextState(tool, curState, curStateFP, liveNextStates);
+
+				// Poor man's version of a controller. If necessary, try e.g.
+				// PID controller instead.
+				final int multiplier = threadLocal.get();
+				if (liveNextStates.capacity() > (multiplier * INITIAL_CAPACITY)) {
+					// Increase initial size for as long as the set has to grow
+					threadLocal.set(multiplier + 1);
+				}
             }
 
             // We set curState DONE if
@@ -548,19 +596,23 @@ public class DFIDModelChecker extends AbstractChecker
             boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError));
             synchronized (this)
             {
-                if (this.setErrState(curState, succState, !keep))
+                final int errorCode;
+                if (e instanceof StackOverflowError)
+                {
+                    errorCode = EC.SYSTEM_STACK_OVERFLOW;
+                } else if (e instanceof OutOfMemoryError)
+                {
+                    errorCode = EC.SYSTEM_OUT_OF_MEMORY;
+                } else
+                {
+                    errorCode = EC.GENERAL;
+                }
+
+                if (this.setErrState(curState, succState, !keep, errorCode))
                 {
                     String[] parameters = null;
-                    int errorCode;
-                    if (e instanceof StackOverflowError)
-                    {
-                        errorCode = EC.SYSTEM_STACK_OVERFLOW;
-                    } else if (e instanceof OutOfMemoryError)
+                    if (errorCode == EC.GENERAL)
                     {
-                        errorCode = EC.SYSTEM_OUT_OF_MEMORY;
-                    } else
-                    {
-                        errorCode = EC.GENERAL;
                         // LL changed error message on 7 April 2012
                         parameters = new String[] { 
                                 MP.ECGeneralMsg("computing the set of next states", e) }; 
@@ -575,16 +627,16 @@ public class DFIDModelChecker extends AbstractChecker
 
     private final void printTrace(int errorCode, String[] parameters, TLCState s1, TLCState s2)
     {
-        this.workers[IdThread.GetId()].printTrace(errorCode, parameters, s1, s2);
+        ((DFIDWorker) this.workers[IdThread.GetId()]).printTrace(errorCode, parameters, s1, s2);
     }
 
     /**
      * Set the error state. 
      * <strong>Note:</note> this method must be protected by lock 
      */
-    public boolean setErrState(TLCState curState, TLCState succState, boolean keep)
+    public boolean setErrState(TLCState curState, TLCState succState, boolean keep, int errorCode)
     {
-        boolean result = super.setErrState(curState, succState, keep);
+        boolean result = super.setErrState(curState, succState, keep, errorCode);
         if (!result)
         {
             return false;
@@ -603,7 +655,7 @@ public class DFIDModelChecker extends AbstractChecker
     {
         for (int i = 0; i < this.workers.length; i++)
         {
-            this.workers[i].setStop(code);
+            ((DFIDWorker) this.workers[i]).setStop(code);
         }
     }
 
@@ -613,16 +665,18 @@ public class DFIDModelChecker extends AbstractChecker
      * Checkpoint: checkpoint three data structures: the state set, the
      *             state queue, and the state trace.
      */
-    public final boolean doPeriodicWork() throws Exception
+    @Override
+    public final int doPeriodicWork() throws Exception
     {
         synchronized (this.theFPSet)
         {
             // Run liveness checking, if needed:
             if (this.checkLiveness)
             {
-                if (!liveCheck.check(false))
+                final int result = liveCheck.check(tool, false);
+                if (result != EC.NO_ERROR)
                 {
-                    return false;
+                    return result;
                 }
             }
 
@@ -647,7 +701,7 @@ public class DFIDModelChecker extends AbstractChecker
             	MP.printMessage(EC.TLC_CHECKPOINT_END);
             }
         }
-        return true;
+        return EC.NO_ERROR;
     }
 
     public final boolean recover() throws IOException
@@ -674,8 +728,7 @@ public class DFIDModelChecker extends AbstractChecker
         this.theFPSet.close();
         if (this.checkLiveness)
             liveCheck.close();
-        if (this.allStateWriter != null)
-            this.allStateWriter.close();
+        this.allStateWriter.close();
         // SZ Feb 23, 2009:
         // FileUtil.deleteDir(new File(this.metadir), success);
         FileUtil.deleteDir(this.metadir, success);
@@ -700,36 +753,30 @@ public class DFIDModelChecker extends AbstractChecker
                 String.valueOf(this.theFPSet.size()) });
     }
 
-    public final void reportSuccess() throws IOException
+    private final void reportSuccess() throws IOException
     {
-        long d = this.theFPSet.size();
-        double prob1 = (d * (this.numOfGenStates.get() - d)) / Math.pow(2, 64);
-        double prob2 = this.theFPSet.checkFPs();
-
-        MP.printMessage(EC.TLC_SUCCESS, new String[] { String.valueOf(prob1), String.valueOf(prob2) });
+        reportSuccess(this.theFPSet.size(), this.theFPSet.checkFPs(), numOfGenStates.get());
     }
 
     /**
      * Create workers
      */
-    protected IdThread[] startWorkers(AbstractChecker checker, int checkIndex)
+    protected IWorker[] startWorkers(AbstractChecker checker, int checkIndex)
     {
         for (int i = 0; i < this.workers.length; i++)
         {
             this.workers[i] = new DFIDWorker(i, checkIndex, checker);
+        }
+		// Start all workers once instantiated to avoid a race with setStop,
+		// when setStop is being called concurrently with startWorkers. This
+		// happens, if a DFIDWorker terminates immediately.
+        for (int i = 0; i < this.workers.length; i++)
+        {
             this.workers[i].start();
         }
         return this.workers;
     }
 
-    /**
-     * Run prior the worker loop
-     */
-    protected void runTLCPreLoop()
-    {
-        this.done = false;
-    }
-
     /**
      * Process calculation 
      * @param count
diff --git a/tlatools/src/tlc2/tool/DFIDWorker.java b/tlatools/src/tlc2/tool/DFIDWorker.java
index 0af5b2babad23565111a8a2d56434c209fb5b565..5b7c1be71c2456ca20759703284d5713b6e8bb7c 100644
--- a/tlatools/src/tlc2/tool/DFIDWorker.java
+++ b/tlatools/src/tlc2/tool/DFIDWorker.java
@@ -9,7 +9,6 @@ import tlc2.output.StatePrinter;
 import tlc2.tool.fp.dfid.FPIntSet;
 import tlc2.util.IdThread;
 import tlc2.util.LongVec;
-import tlc2.util.ObjLongTable;
 import tlc2.util.RandomGenerator;
 
 public class DFIDWorker extends IdThread implements IWorker {
@@ -29,7 +28,6 @@ public class DFIDWorker extends IdThread implements IWorker {
   private TLCState[] theInitStates;
   private long[] theInitFPs;
   private int initLen;
-  private ObjLongTable astCounts;
   private int toLevel;
   private int curLevel;
   private int stopCode;
@@ -55,15 +53,12 @@ public class DFIDWorker extends IdThread implements IWorker {
     this.theInitFPs = new long[this.initLen];
     System.arraycopy(this.tlc.theInitStates, 0, this.theInitStates, 0, this.initLen);
     System.arraycopy(this.tlc.theInitFPs, 0, this.theInitFPs, 0, this.initLen);
-    this.astCounts = new ObjLongTable(10);    
     this.toLevel = toLevel;
     this.curLevel = 0;
     this.stopCode = 0;
     this.moreLevel = false;
   }
 
-  public final ObjLongTable getCounts() { return this.astCounts; }
-
   public final void setStop(int code) { this.stopCode = code; }
 
   public final boolean isTerminated() { return this.stopCode == 2; }
@@ -136,6 +131,9 @@ public class DFIDWorker extends IdThread implements IWorker {
       {
           StatePrinter.printState(this.stateStack[idx], ++idx);
       }
+      // the prefix printed by the while loop should end at s1.
+      assert s1.equals(this.stateStack[idx]);
+      StatePrinter.printState(s1, ++idx);
       if (s2 != null) 
       {
           StatePrinter.printState(s2, idx+1);
@@ -167,7 +165,6 @@ public class DFIDWorker extends IdThread implements IWorker {
 	this.succFPStack[0].reset();
 	boolean isLeaf = this.toLevel < 2;
 	boolean noLeaf = this.tlc.doNext(curState, cfp, isLeaf,
-					 this.astCounts,
 					 this.succStateStack[0],
 					 this.succFPStack[0]);
 	this.moreLevel = this.moreLevel || !noLeaf;
@@ -193,7 +190,6 @@ public class DFIDWorker extends IdThread implements IWorker {
 	    this.succFPStack[this.curLevel].reset();
 	    isLeaf = (this.curLevel >= this.toLevel-1);
 	    noLeaf = this.tlc.doNext(curState, cfp, isLeaf,
-				     this.astCounts,
 				     this.succStateStack[this.curLevel],
 				     this.succFPStack[this.curLevel]);
 	    this.moreLevel = this.moreLevel || !noLeaf;
@@ -207,7 +203,7 @@ public class DFIDWorker extends IdThread implements IWorker {
       // Assert.printStack(e);
       this.tlc.setStop(2);
       synchronized(this.tlc) {
-	if (this.tlc.setErrState(curState, null, true)) {
+	if (this.tlc.setErrState(curState, null, true, EC.GENERAL)) {
           MP.printError(EC.GENERAL, e);  // LL changed call 7 April 2012
 	}
 	this.tlc.setDone();
diff --git a/tlatools/src/tlc2/tool/Defns.java b/tlatools/src/tlc2/tool/Defns.java
index 848f688ac0dfe520500b1d7f0d3e7964c2d79e2b..39e463ed8d02839241014151004f2beff1a360d5 100644
--- a/tlatools/src/tlc2/tool/Defns.java
+++ b/tlatools/src/tlc2/tool/Defns.java
@@ -47,6 +47,12 @@ public class Defns implements ToolGlobals, Serializable
         this.table = new Object[defnIdx + 32];
     }
 
+    Defns(Defns other) {
+    	this.defnIdx = other.defnIdx;
+    	this.table = new Object[other.table.length];
+        System.arraycopy(other.table, 0, this.table, 0, other.table.length);
+    }
+    
     /**
      * Returns the definition of key if its definition exists.
      * Otherwise, returns null.
@@ -111,4 +117,8 @@ public class Defns implements ToolGlobals, Serializable
     {
         this.defnIdx = index;
     }
+    
+    public Defns snapshot() {
+    	return new Defns(this);
+    }
 }
diff --git a/tlatools/src/tlc2/tool/EvalControl.java b/tlatools/src/tlc2/tool/EvalControl.java
index 4c110b0ddd3425831ba15f164ad00174260cb0c2..c7062adcb7ed03fb078ad16fe649db416aef12ef 100644
--- a/tlatools/src/tlc2/tool/EvalControl.java
+++ b/tlatools/src/tlc2/tool/EvalControl.java
@@ -8,13 +8,34 @@ package tlc2.tool;
 public class EvalControl {
 
   public static final int KeepLazy = 1;
-  public static final int Primed = 2;
-  public static final int Enabled = 4;
-
+  /**
+   * Current evaluation within a primed variable. If isPrimed is true, lookup in
+   * the Context chain terminates on a branch.
+   * 
+   * @see tlc2.util.Context.lookup(SymbolNode, boolean)
+   */
+  public static final int Primed = 1 << 1;
+  /**
+   * Current evaluation scope is within ENABLED. In the ENABLED scope, caching of
+   * LazyValue is disabled.
+   */
+  public static final int Enabled = 1 << 2;
+  /**
+   * Evaluation in the scope of {@link ITool#getInitStates()} or
+   * {@link ITool#getInitStates(IStateFunctor)}. In other words set during the
+   * generation of initial states.
+   */
+  public static final int Init = 1 << 3;
+  
   public static final int Clear = 0;
   
+  private static boolean isSet(final int control, final int constant) {
+	  // Zero all bits except constant bit(s).
+	  return (control & constant) > 0;
+  }
+  
   public static boolean isKeepLazy(int control) {
-    return (control & KeepLazy) > 0;
+    return isSet(control, KeepLazy);
   }
 
   public static int setKeepLazy(int control) {
@@ -22,15 +43,32 @@ public class EvalControl {
   }
 
   public static boolean isPrimed(int control) {
-    return (control & Primed) > 0;
+    return isSet(control, Primed);
   }
     
   public static int setPrimed(int control) {
     return control | Primed;
   }
     
+  /**
+   * Sets {@link EvalControl#Primed} iff {@link EvalControl#Enabled} is already set.
+   */
+  public static int setPrimedIfEnabled(int control) {
+	  if (isEnabled(control)) {
+		  return setPrimed(control);
+	  }
+	  return control;
+  }
+  
   public static boolean isEnabled(int control) {
-    return (control & Enabled) > 0;
+    return isSet(control, Enabled);
+  }
+
+  public static int setEnabled(int control) {
+	return  control | Enabled;
   }
 
+  public static boolean isInit(int control) {
+	return isSet(control, Init);
+  }
 }
diff --git a/tlatools/src/tlc2/tool/EvalException.java b/tlatools/src/tlc2/tool/EvalException.java
index 1a872c961848806e8a824a38b0b43ed6a6fcc944..11c64865172b6e245edd54997794f451e91698d5 100644
--- a/tlatools/src/tlc2/tool/EvalException.java
+++ b/tlatools/src/tlc2/tool/EvalException.java
@@ -20,21 +20,39 @@ public class EvalException extends RuntimeException
 //    private int type;
 
 
-    public EvalException(int errorCode, String[] parameters)
+    private final int errorCode;
+    private final String[] parameters;
+
+	public EvalException(int errorCode, String[] parameters)
     {
         super(MP.getMessage(errorCode, parameters));
+		this.errorCode = errorCode;
+		this.parameters = parameters;
     }
 
     public EvalException(int errorCode, String parameter)
     {
-        super(MP.getMessage(errorCode, parameter));
+    	this(errorCode, new String[] {parameter});
     }
 
     public EvalException(int errorCode)
     {
-        super(MP.getMessage(errorCode));        
+        super(MP.getMessage(errorCode));
+		this.errorCode = errorCode;
+		this.parameters = null;
+    }
+
+    public int getErrorCode() {
+    	return errorCode;
     }
 
+	public String[] getParameters() {
+		return parameters;
+	}
+
+	public boolean hasParameters() {
+		return parameters != null;
+	}
     
     // SZ Jul 14, 2009: refactored and deprecated, all usage changed to standard constructor 
     // public EvalException(int type, String message)
diff --git a/tlatools/src/tlc2/tool/FingerprintException.java b/tlatools/src/tlc2/tool/FingerprintException.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d468ebbcff6e3d85e20e149d62759485efcddda
--- /dev/null
+++ b/tlatools/src/tlc2/tool/FingerprintException.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Ian Morris Nieves - initial design and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.value.IValue;
+
+public class FingerprintException extends RuntimeException {
+
+  final public IValue value;
+  final public FingerprintException next;
+
+  private FingerprintException(Throwable initCauseThrowable, IValue value, FingerprintException next) {
+    initCause(initCauseThrowable);
+    this.value = value;
+    this.next = next;
+  }
+
+  public static FingerprintException getNewHead(IValue v, Throwable t){
+    FingerprintException fpe = null;
+    if(t instanceof FingerprintException)
+      fpe = ((FingerprintException) t).prependNewHead(v);
+    else
+      fpe = FingerprintException.createNewHead(v, t);
+    return fpe;
+  }
+
+  private static FingerprintException createNewHead(IValue value, Throwable initCauseThrowable){
+    if(value == null || initCauseThrowable == null)
+      return null;
+    else
+      return new FingerprintException(initCauseThrowable, value, null);
+  }
+
+  private FingerprintException prependNewHead(IValue value){
+    if(value == null)
+      return null;
+    else
+      return new FingerprintException(null, value, this);
+  }
+
+  public Throwable getRootCause(){
+    FingerprintException nextFPE = this;
+    while(nextFPE.next != null)
+      nextFPE = nextFPE.next;
+    return nextFPE.getCause();
+  }
+
+  public String getTrace(){
+    return getTraceImpl(0, null);
+  }
+
+  private String getTraceImpl(final int traceIndexLabel, final Integer lastSemanticNodeUid){
+    SemanticNode semanticNode = value.getSource();
+    if(semanticNode == null){
+      if(next == null)
+        return "";
+      else
+        return next.getTraceImpl(traceIndexLabel, lastSemanticNodeUid);
+    }
+    else{
+      Integer semanticNodeUid = semanticNode.getUid();
+      if(semanticNodeUid.equals(lastSemanticNodeUid)){ // same SemanticNode compared to current top of stack
+        if(next == null)
+          return "";
+        else
+          return next.getTraceImpl(traceIndexLabel, lastSemanticNodeUid);
+      }
+      else{ // different SemanticNode compared to current top of stack
+        String description = traceIndexLabel + ") " + semanticNode.toString() + "\n";
+        if(next == null)
+          return description;
+        else
+          return next.getTraceImpl(traceIndexLabel+1, semanticNodeUid) + description;
+      }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/tool/IActionItemList.java b/tlatools/src/tlc2/tool/IActionItemList.java
new file mode 100644
index 0000000000000000000000000000000000000000..e02279024302ef6fce51329234b3db350e5421ee
--- /dev/null
+++ b/tlatools/src/tlc2/tool/IActionItemList.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+
+public interface IActionItemList {
+
+	/**
+	 * predicate of a conjunction
+	 */
+	int CONJUNCT = 0;
+	/**
+	 * predicate
+	 */
+	int PRED = -1;
+	/**
+	 * UNCHANGED predicate
+	 */
+	int UNCHANGED = -2;
+	/**
+	 * pred' # pred
+	 */
+	int CHANGED = -3;
+	
+	IActionItemList cons(SemanticNode exprOrOpArgNode, Context c, CostModel cm, int ailconst);
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/IContextEnumerator.java b/tlatools/src/tlc2/tool/IContextEnumerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..258abbea9d3afff40a929a35a0e0467f8e5e2223
--- /dev/null
+++ b/tlatools/src/tlc2/tool/IContextEnumerator.java
@@ -0,0 +1,9 @@
+package tlc2.tool;
+
+import tlc2.util.Context;
+
+public interface IContextEnumerator {
+
+	Context nextElement();
+
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/INextStateFunctor.java b/tlatools/src/tlc2/tool/INextStateFunctor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4412d89f06590b411d1fda2be78a2ea8abda8e99
--- /dev/null
+++ b/tlatools/src/tlc2/tool/INextStateFunctor.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+public interface INextStateFunctor extends IStateFunctor {
+
+	Object addElement(final TLCState s, final Action a, final TLCState t);
+	
+	public static class InvariantViolatedException extends RuntimeException {
+		
+	}
+}
diff --git a/tlatools/src/tlc2/tool/IStateFunctor.java b/tlatools/src/tlc2/tool/IStateFunctor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bbb29c21233bdaa11696a5079248c8711dfa9c4
--- /dev/null
+++ b/tlatools/src/tlc2/tool/IStateFunctor.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+/**
+ * An {@link IStateFunctor}'s responsibility is to accept generated (init)
+ * states via addElement and to process them. As the time of writing, known
+ * implementors are StateVec which simply stores all states passed to it for
+ * later use and ModelChecker.DoInitFunctor which checks each state right
+ * away.
+ */
+public interface IStateFunctor {
+
+	Object addElement(TLCState state);
+
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/ITool.java b/tlatools/src/tlc2/tool/ITool.java
new file mode 100644
index 0000000000000000000000000000000000000000..2917e4085a1f160e89896618fd03e680891cb17e
--- /dev/null
+++ b/tlatools/src/tlc2/tool/ITool.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.io.File;
+import java.util.List;
+
+import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.ExprOrOpArgNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.SymbolNode;
+import tlc2.tool.coverage.CostModel;
+import tlc2.tool.impl.ModelConfig;
+import tlc2.util.Context;
+import tlc2.util.ObjLongTable;
+import tlc2.util.Vect;
+import tlc2.value.IFcnLambdaValue;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import util.FilenameToStream;
+
+public interface ITool extends TraceApp {
+
+	/**
+	   * This method returns the set of all possible actions of the
+	   * spec, and sets the actions field of this object. In fact, we
+	   * could simply treat the next predicate as one "giant" action.
+	   * But for efficiency, we preprocess the next state predicate by
+	   * splitting it into a set of actions for the maximum prefix
+	   * of disjunction and existential quantification.
+	   */
+	Action[] getActions();
+
+	/*
+	   * This method returns the set of possible initial states that
+	   * satisfies the initial state predicate. Initial state predicate
+	   * can be under-specified.  Too many possible initial states will
+	   * probably make tools like TLC useless.
+	   */
+	StateVec getInitStates();
+
+	void getInitStates(IStateFunctor functor);
+
+	/* Create the state specified by pred.  */
+	TLCState makeState(SemanticNode pred);
+
+	/**
+	   * This method returns the set of next states when taking the action
+	   * in the given state.
+	   */
+	StateVec getNextStates(Action action, TLCState state);
+	
+	boolean getNextStates(final INextStateFunctor functor, final TLCState state);
+
+	IValue eval(SemanticNode expr, Context c, TLCState s0);
+
+	IValue eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control);
+	
+	IValue eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm);
+
+	/**
+	   * This method determines if the argument is a valid state.  A state
+	   * is good iff it assigns legal explicit values to all the global
+	   * state variables.
+	   */
+	boolean isGoodState(TLCState state);
+
+	/* This method determines if a state satisfies the model constraints. */
+	boolean isInModel(TLCState state) throws EvalException;
+
+	/* This method determines if a pair of states satisfy the action constraints. */
+	boolean isInActions(TLCState s1, TLCState s2) throws EvalException;
+
+	boolean hasStateOrActionConstraints();
+
+	/**
+	   * This method determines if an action is enabled in the given state.
+	   * More precisely, it determines if (act.pred /\ (sub' # sub)) is
+	   * enabled in the state s and context act.con.
+	   */
+	TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1, ExprNode subscript, final int ail);
+	TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1);
+	TLCState enabled(SemanticNode pred, IActionItemList acts, Context c, TLCState s0, TLCState s1);
+	TLCState enabled(SemanticNode pred, IActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm);
+
+	/* This method determines if the action predicate is valid in (s0, s1). */
+	boolean isValid(Action act, TLCState s0, TLCState s1);
+
+	/* Returns true iff the predicate is valid in the state. */
+	boolean isValid(Action act, TLCState state);
+
+	/* Returns true iff the predicate is valid in the state. */
+	boolean isValid(Action act);
+
+	boolean isValid(ExprNode expr);
+
+	/* Reconstruct the initial state whose fingerprint is fp. */
+	TLCStateInfo getState(long fp);
+
+	/**
+		 * Reconstruct the next state of state s whose fingerprint is fp.
+		 *
+		 * @return Returns the TLCState wrapped in TLCStateInfo. TLCStateInfo stores
+		 *         the stateNumber (relative to the given sinfo) and a pointer to
+		 *         the predecessor.
+		 */
+	TLCStateInfo getState(long fp, TLCStateInfo sinfo);
+
+	/* Reconstruct the next state of state s whose fingerprint is fp. */
+	TLCStateInfo getState(long fp, TLCState s);
+
+	/* Reconstruct the info for s1.   */
+	TLCStateInfo getState(TLCState s1, TLCState s);
+
+	/* Return the set of all permutations under the symmetry assumption. */
+	IMVPerm[] getSymmetryPerms();
+
+	boolean hasSymmetry();
+
+	Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args, Context c, TLCState s0, TLCState s1, int control);
+
+	Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args, Context c, TLCState s0, TLCState s1, int control,
+			CostModel cm);
+
+	IContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0, TLCState s1, int control);
+
+	Vect<Action> getInitStateSpec();
+
+	Action[] getInvariants();
+
+	ObjLongTable<SemanticNode> getPrimedLocs();
+
+	Context getOpContext(OpDefNode odn, ExprOrOpArgNode[] args, Context ctx, boolean b);
+
+	ExprNode[] getAssumptions();
+
+	boolean[] getAssumptionIsAxiom();
+
+	int checkAssumptions();
+
+	String[] getInvNames();
+
+	String[] getImpliedActNames();
+
+	/**
+	 * @return The name of the root module.
+	 */
+	String getRootName();
+	
+	/**
+	 * @return The file name of the root module which might contain the
+	 *         full or relative path information.
+	 */
+	String getRootFile();
+
+	String getConfigFile();
+
+	String getSpecDir();
+
+	String[] getImpliedInitNames();
+
+	/**
+	 * Initial predicate of the liveness property Prop (see impliedActions above).
+	 * Most common used when checking if a Spec implements another one, i.e. ASpec
+	 * => BSpec.
+	 * <p>
+	 * See the following tests:<br>
+	 * tlc2.tool.suite.Test55
+	 * <ul>
+	 * <li>Action line 7, col 1 to line 7, col 41 of module test55</li>
+	 * <li>Action line 7, col 1 to line 7, col 41 of module test55</li>
+	 * </ul>
+	 * tlc2.tool.suite.Test63
+	 * <ul>
+	 * <li>Action line 52, col 1 to line 52, col 21 of module test63</li>
+	 * </ul>
+	 */
+	Action[] getImpliedInits();
+
+	/**
+	 * Checking a liveness property Prop (declared by the PROPERTY keyword in the
+	 * config file) means to verify Spec => Prop. An implied action is the [][A]_x
+	 * (A \/ x' = x) part of Prop where A is an action and x is a variable.
+	 * 
+	 * See the following tests:<br>
+	 * tlc2.tool.suite.Test52
+	 * <ul>
+	 * <li></li>
+	 * <li></li>
+	 * </ul>
+	 * tlc2.tool.suite.Test56
+	 * <ul>
+	 * <li></li>
+	 * </ul>
+	 */
+	Action[] getImpliedActions();
+
+	boolean livenessIsTrue();
+
+	Action[] getImpliedTemporals();
+
+	Action[] getTemporals();
+
+	Object lookup(SymbolNode opNode, Context con, boolean b);
+
+	Object lookup(SymbolNode operator);
+
+	Object getVal(ExprOrOpArgNode expr, Context con, boolean b);
+
+	Action getNextStateSpec();
+
+	SemanticNode getViewSpec();
+
+	int getId();
+
+	List<File> getModuleFiles(FilenameToStream resolver);
+
+	ModelConfig getModelConfig();
+
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/IWorker.java b/tlatools/src/tlc2/tool/IWorker.java
index f6dd6acd96aa52fddee93fff2ab9257885060697..58bfc7cdbb6f3ac0f84e39dde91c82109183cafd 100644
--- a/tlatools/src/tlc2/tool/IWorker.java
+++ b/tlatools/src/tlc2/tool/IWorker.java
@@ -1,17 +1,28 @@
 package tlc2.tool;
 
-import tlc2.util.ObjLongTable;
+import tlc2.TLCGlobals;
+import tlc2.value.IValue;
 
 /**
  * A common interface for workers
  * @author Simon Zambrovski
- * @version $Id$
  */
 public interface IWorker
 {
-    /** 
-     * extracted from Worker and DFID worker
-     * used in the {@link AbstractChecker#reportCoverage(IWorker[])} 
-     */
-    public ObjLongTable getCounts();
+	/**
+	 * @return A worker's id in the range 0 to {@link TLCGlobals#getNumWorkers()} - 1
+	 */
+	public int myGetId();
+	
+    // see Thread
+    
+	public void start();
+
+	public void join() throws InterruptedException;
+
+	// see IdThread
+	
+	public IValue getLocalValue(int idx);
+
+	public void setLocalValue(int idx, IValue val);
 }
diff --git a/tlatools/src/tlc2/tool/ModelChecker.java b/tlatools/src/tlc2/tool/ModelChecker.java
index f5fe5a09d3c13b1e142579adc5549f9a0c02ee67..53bbe671db92b82740d2f063346dbad88d4e012e 100644
--- a/tlatools/src/tlc2/tool/ModelChecker.java
+++ b/tlatools/src/tlc2/tool/ModelChecker.java
@@ -1,33 +1,39 @@
 // Copyright (c) 2003 Compaq Corporation.  All rights reserved.
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Wed  4 Jul 2007 at 17:46:34 PST by lamport  
-//      modified on Fri Jan 18 11:33:51 PST 2002 by yuanyu   
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Wed  4 Jul 2007 at 17:46:34 PST by lamport
+//      modified on Fri Jan 18 11:33:51 PST 2002 by yuanyu
 
 package tlc2.tool;
 
+import java.io.File;
 import java.io.IOException;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
 
-import tla2sany.modanalyzer.SpecObj;
-import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.OpDeclNode;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
-import tlc2.output.OutputCollector;
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.FPSetConfiguration;
 import tlc2.tool.fp.FPSetFactory;
+import tlc2.tool.impl.CallStackTool;
 import tlc2.tool.liveness.LiveCheck;
+import tlc2.tool.queue.DiskByteArrayQueue;
 import tlc2.tool.queue.DiskStateQueue;
 import tlc2.tool.queue.IStateQueue;
-import tlc2.util.IdThread;
-import tlc2.util.LongVec;
-import tlc2.util.ObjLongTable;
+import tlc2.util.IStateWriter;
+import tlc2.util.SetOfStates;
 import tlc2.util.statistics.BucketStatistics;
-import tlc2.value.Value;
+import util.Assert;
 import util.DebugPrinter;
 import util.FileUtil;
 import util.FilenameToStream;
+import util.TLAFlightRecorder;
 import util.UniqueString;
 
 /** 
@@ -41,52 +47,73 @@ import util.UniqueString;
 public class ModelChecker extends AbstractChecker
 {
 
+	protected static final boolean coverage = TLCGlobals.isCoverageEnabled();
 	/**
 	 * If the state/ dir should be cleaned up after a successful model run
 	 */
-	private static final boolean VETO_CLEANUP = Boolean.getBoolean(ModelChecker.class.getName() + ".vetoCleanup");
+	public static final boolean VETO_CLEANUP = Boolean.getBoolean(ModelChecker.class.getName() + ".vetoCleanup");
 
+	private long numberOfInitialStates;
     public FPSet theFPSet; // the set of reachable states (SZ: note the type)
     public IStateQueue theStateQueue; // the state queue
-    public TLCTrace trace; // the trace file
-    protected Worker[] workers; // the workers
+    public final ConcurrentTLCTrace trace; // the trace file
     // used to calculate the spm metric
     public long distinctStatesPerMinute, statesPerMinute = 0L;
     protected long oldNumOfGenStates, oldFPSetSize = 0L;
+    /**
+	 * The ratio between time spend on safety checking and liveness checking.
+	 */
+    private double runtimeRatio = 0d;
+	/**
+	 * Flag set via JMX if liveness checking should be triggered.
+	 */
+	private boolean forceLiveCheck = false;
 
     /* Constructors  */
+    public ModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, boolean deadlock, String fromChkpt,
+            final Future<FPSet> future, long startTime) throws EvalException, IOException, InterruptedException, ExecutionException {
+    	this(tool, metadir, stateWriter, deadlock, fromChkpt, startTime);
+    	this.theFPSet = future.get();
+
+        // Initialize all the workers:
+        this.workers = new Worker[TLCGlobals.getNumWorkers()];
+        for (int i = 0; i < this.workers.length; i++)
+        {
+            this.workers[i] = this.trace.addWorker(new Worker(i, this, this.metadir, this.tool.getRootName()));
+        }
+    }
+    
+    public ModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, boolean deadlock, String fromChkpt,
+            final FPSetConfiguration fpSetConfig, long startTime) throws EvalException, IOException {
+    	this(tool, metadir, stateWriter, deadlock, fromChkpt, startTime);
+    	this.theFPSet = FPSetFactory.getFPSet(fpSetConfig).init(TLCGlobals.getNumWorkers(), metadir, tool.getRootName());
+
+        // Initialize all the workers:
+        this.workers = new Worker[TLCGlobals.getNumWorkers()];
+        for (int i = 0; i < this.workers.length; i++)
+        {
+            this.workers[i] = this.trace.addWorker(new Worker(i, this, this.metadir, this.tool.getRootName()));
+        }
+    }
+    
     /**
      * The only used constructor of the TLA+ model checker
      * SZ Feb 20, 2009
      * @param resolver name resolver to be able to load files (specs and configs) from managed environments 
      * @param specObj external SpecObj added to enable to work on existing specification 
-     * Modified on 6 Apr 2010 by Yuan Yu to add fpMemSize parameter.
      */
-    public ModelChecker(String specFile, String configFile, String dumpFile, boolean deadlock, String fromChkpt,
-            FilenameToStream resolver, SpecObj specObj, final FPSetConfiguration fpSetConfig) throws EvalException, IOException
-    {
+	private ModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, boolean deadlock, String fromChkpt,
+			long startTime) throws EvalException, IOException    {
         // call the abstract constructor
-        super(specFile, configFile, dumpFile, deadlock, fromChkpt, true, resolver, specObj);
+        super(tool, metadir, stateWriter, deadlock, fromChkpt, startTime);
 
-        // SZ Feb 20, 2009: this is a selected alternative
-        this.theStateQueue = new DiskStateQueue(this.metadir);
+		this.theStateQueue = useByteArrayQueue()
+				? new DiskByteArrayQueue(this.metadir)
+				: new DiskStateQueue(this.metadir);
         // this.theStateQueue = new MemStateQueue(this.metadir);
 
-        //TODO why used to div by 20?
-		this.theFPSet = FPSetFactory.getFPSet(fpSetConfig);
-
-        // initialize the set
-        this.theFPSet.init(TLCGlobals.getNumWorkers(), this.metadir, specFile);
-
         // Finally, initialize the trace file:
-        this.trace = new TLCTrace(this.metadir, specFile, this.tool);
-
-        // Initialize all the workers:
-        this.workers = new Worker[TLCGlobals.getNumWorkers()];
-        for (int i = 0; i < this.workers.length; i++)
-        {
-            this.workers[i] = new Worker(i, this);
-        }
+        this.trace = new ConcurrentTLCTrace(this.metadir, this.tool.getRootName(), this.tool);
     }
 
     /**
@@ -95,30 +122,37 @@ public class ModelChecker extends AbstractChecker
      * next states have not been explored are stored in the variable
      * theStateQueue.
      */
-    public void modelCheck() throws Exception
+	@Override
+    protected int modelCheckImpl() throws Exception
     {
+		int result = EC.NO_ERROR;
         report("entering modelCheck()");
         
         // needed to calculate state/minute in final progress report
-        final long startTime = System.currentTimeMillis();
 
-        boolean recovered = this.recover();
+		boolean recovered = this.recover();
         if (!recovered)
         {
 
+			if (this.checkLiveness && liveCheck.getNumChecker() == 0) {
+				return MP.printError(EC.TLC_LIVE_FORMULA_TAUTOLOGY);
+			}
+        	
             // We start from scratch. Initialize the state queue and the
-            // state set to contain all the initial states.
-            if (!this.checkAssumptions())
-                return;
+			// state set to contain all the initial states.
+			result = this.checkAssumptions();
+            if (result != EC.NO_ERROR)
+                return result;
             try
             {
                 report("doInit(false)");
                 MP.printMessage(EC.TLC_COMPUTING_INIT);
-                // SZ Feb 23, 2009: do not ignore cancel on creation of the init states
-                if (!this.doInit(false))
+				// SZ Feb 23, 2009: do not ignore cancel on creation of the init states
+				result = this.doInit(false);
+                if (result != EC.NO_ERROR)
                 {
                     report("exiting, because init failed");
-                    return;
+                    return result;
                 }
             } catch (Throwable e)
             {
@@ -149,97 +183,122 @@ public class ModelChecker extends AbstractChecker
                 }
 
                 // Replay the error with the error stack recorded:
-                this.tool.setCallStack();
+                final CallStackTool cTool = new CallStackTool(this.tool);
                 try
                 {
-                    this.numOfGenStates = new AtomicLong(0);
+                    numberOfInitialStates = 0;
                     // SZ Feb 23, 2009: ignore cancel on error reporting
-                    this.doInit(true);
-                } catch (Throwable e1)
-                {
+					this.doInit(cTool, true);
+                } catch (FingerprintException fe){
+                    result = MP.printError(EC.TLC_FINGERPRINT_EXCEPTION, new String[]{fe.getTrace(), fe.getRootCause().getMessage()});
+                } catch (Throwable e1) {
                     // Assert.printStack(e);
-                    MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString());
+                    result = MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString());
                 }
                 this.printSummary(false, startTime);
                 this.cleanup(false);
                 report("exiting, because init failed with exception");
-                return;
+                return result;
             }
 
-            if (this.numOfGenStates.get() == this.theFPSet.size())
+            long statesGenerated = getStatesGenerated();
+            final String plural = (statesGenerated == 1) ? "" : "s";
+            if (statesGenerated == this.theFPSet.size())
             {
-                String plural = (this.numOfGenStates.get() == 1) ? "" : "s";
-                MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(this.numOfGenStates), plural });
+                MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(statesGenerated), plural });
             } else
             {
-                MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(this.numOfGenStates),
+                MP.printMessage(EC.TLC_INIT_GENERATED2, new String[] { String.valueOf(statesGenerated), plural,
                         String.valueOf(this.theFPSet.size()) });
             }
         }
 
         report("init processed");
+        
         // Finished if there is no next state predicate:
-        if (this.actions.length == 0)
+        if (this.tool.getActions().length == 0)
         {
-            reportSuccess(this.theFPSet, this.numOfGenStates.get());
-            this.printSummary(true, startTime);
+        	if (this.theStateQueue.isEmpty()) {
+        		reportSuccess(this.theFPSet, getStatesGenerated());
+        		this.printSummary(true, startTime);
+        	} else {
+        		result = MP.printError(EC.TLC_STATES_AND_NO_NEXT_ACTION);
+        	}
             this.cleanup(true);
             report("exiting with actions.length == 0");
-            return;
+            return result;
         }
 
-        boolean success = false;
+        result = EC.GENERAL;
         try
         {
             report("running TLC");
-            success = this.runTLC(Integer.MAX_VALUE);
-            if (!success)
+            result = this.runTLC(Integer.MAX_VALUE);
+            if (result != EC.NO_ERROR)
             {
                 report("TLC terminated with error");
-                return;
+                return result;
             }
             if (this.errState == null)
             {
                 // Always check liveness properties at the end:
                 if (this.checkLiveness)
                 {
-					MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS,
-							new String[] { "complete", Long.toString(this.theFPSet.size()) });
+					// Print progress statistics prior to liveness checking.
+					// Liveness checking can take a substantial amount of time
+					// and thus give the user some clues at what stage safety
+					// checking is.
+            		MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] {
+                            String.valueOf(this.trace.getLevelForReporting()),
+                            MP.format(getStatesGenerated()),
+                            MP.format(theFPSet.size()),
+                            MP.format(this.theStateQueue.size()) });
+                	
                     report("checking liveness");
-                    success = liveCheck.finalCheck();
+                    result = liveCheck.finalCheck(tool);
                     report("liveness check complete");
-                    if (!success)
+                    if (result != EC.NO_ERROR)
                     {
                         report("exiting error status on liveness check");
-                        return;
+                        return result;
                     }
                 }
 
                 // We get here because the checking has been completed.
-                success = true;
-                reportSuccess(this.theFPSet, this.numOfGenStates.get());
+                result = EC.NO_ERROR;
+                reportSuccess(this.theFPSet, getStatesGenerated());
             } else if (this.keepCallStack)
             {
                 // Replay the error with the error stack recorded:
-                this.tool.setCallStack();
+            	final CallStackTool cTool = new CallStackTool(this.tool);
                 try
                 {
-                    this.doNext(this.predErrState, new ObjLongTable(10));
+					// Not adding newly created Worker to trace#addWorker because it is not supposed
+					// to rewrite the trace file but to reconstruct actual states referenced by
+					// their fingerprints in the trace.
+					this.doNext(cTool, this.predErrState, this.checkLiveness ? new SetOfStates() : null,
+							new Worker(4223, this, this.metadir, tool.getRootName()));
+                } catch (FingerprintException e)
+                {
+                    result = MP.printError(EC.TLC_FINGERPRINT_EXCEPTION, new String[]{e.getTrace(), e.getRootCause().getMessage()});
+                } catch (EvalException e) {
+                	// Do not replace the actual error code, such as assert violation, with TLC_NESTED_EXPRESSION.
+	                MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString());
+	                result = e.getErrorCode();
                 } catch (Throwable e)
                 {
                     // Assert.printStack(e);
-                    MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString());
+                    result = MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString());
                 }
             }
         } catch (Exception e)
         {
             report("TLC terminated with error");
             // Assert.printStack(e);
-            success = false;
-            MP.printError(EC.GENERAL, e);  // LL changed call 7 April 2012
+            result = MP.printError(EC.GENERAL, e);  // LL changed call 7 April 2012
         } finally
         {
-        	
+        	final boolean success = result == EC.NO_ERROR;
         	this.printSummary(success, startTime);
 
         	if (this.checkLiveness) {
@@ -255,42 +314,19 @@ public class ModelChecker extends AbstractChecker
         	}
 
             this.cleanup(success);
-        }
 
-        report("exiting modelCheck()");
+			report("exiting modelCheck()");
+		}
+
+		return result;
     }
 
     /** 
      * Check the assumptions.  
      */
-    public boolean checkAssumptions()
+    public int checkAssumptions()
     {
-        ExprNode[] assumps = this.tool.getAssumptions();
-        boolean[] isAxiom = this.tool.getAssumptionIsAxiom();
-        boolean assumptionsAreTRUE = true;
-        for (int i = 0; i < assumps.length; i++)
-        {
-            try
-            {
-                if ((!isAxiom[i]) && !this.tool.isValid(assumps[i]))
-                {
-                	OutputCollector.addViolatedAssumption(assumps[i]);
-                    MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString());
-                    //return false;
-                    assumptionsAreTRUE = false;
-                }
-            } catch (Exception e)
-            {
-                // Assert.printStack(e);
-            	OutputCollector.addViolatedAssumption(assumps[i]);
-                MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
-                        new String[] { assumps[i].toString(), e.getMessage() });
-                //return false;
-                assumptionsAreTRUE = false;
-            }
-        }
-        //return true;
-        return assumptionsAreTRUE;
+    	return this.tool.checkAssumptions();
     }
 
     /**
@@ -298,92 +334,52 @@ public class ModelChecker extends AbstractChecker
      * @return status, if false, the processing should be stopped
      * @throws Throwable
      */
-    public final boolean doInit(boolean ignoreCancel) throws Throwable
+    @Override
+    public final int doInit(boolean ignoreCancel) throws Throwable {
+    	return doInit(this.tool, ignoreCancel);
+    }
+    
+    private final int doInit(final ITool tool, boolean ignoreCancel) throws Throwable
     {
-        // SZ Feb 23, 2009: cancel flag set, quit
-        if (!ignoreCancel && this.cancellationFlag)
-        {
-            return false;
-        }
-
-        TLCState curState = null;
-
-        try
-        {
-            // Generate the initial states:
-            StateVec theInitStates = this.tool.getInitStates();
-            this.numOfGenStates.set(theInitStates.size());
-            for (int i = 0; i < theInitStates.size(); i++)
-            {
-                curState = theInitStates.elementAt(i);
-                // Check if the state is a legal state
-                if (!this.tool.isGoodState(curState))
-                {
-                    MP.printError(EC.TLC_INITIAL_STATE, curState.toString());
-                    return false;
-                }
-                boolean inModel = this.tool.isInModel(curState);
-                boolean seen = false;
-                if (inModel)
-                {
-                    long fp = curState.fingerPrint();
-                    seen = this.theFPSet.put(fp);
-                    if (!seen)
-                    {
-                        if (this.allStateWriter != null)
-                        {
-                            this.allStateWriter.writeState(curState);
-                        }
-                        curState.uid = this.trace.writeState(fp);
-                        this.theStateQueue.enqueue(curState);
-
-                        // build behavior graph for liveness checking
-                        if (this.checkLiveness)
-                        {
-                            liveCheck.addInitState(curState, fp);
-                        }
-                    }
-                }
-                // Check properties of the state:
-                OutputCollector.setInitialState(curState);
-                if (!seen)
-                {
-                    for (int j = 0; j < this.invariants.length; j++)
-                    {
-                        if (!this.tool.isValid(this.invariants[j], curState))
-                        {
-                            // We get here because of invariant violation:
-                        	MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] {
-                                    this.tool.getInvNames()[j].toString(), curState.toString() });
-                        	if (!TLCGlobals.continuation)
-                                return false;
-                        }
-                    }
-                    for (int j = 0; j < this.impliedInits.length; j++)
-                    {
-                        if (!this.tool.isValid(this.impliedInits[j], curState))
-                        {
-                            // We get here because of implied-inits violation:
-                            MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, new String[] {
-                                    this.tool.getImpliedInitNames()[j], curState.toString() });
-                            return false;
-                        }
-                    }
-                }
-            }
-        } catch (Throwable e)
-        {
-            // Assert.printStack(e);
-            if (e instanceof OutOfMemoryError)
-            {
-                MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT);
-                return false;
-            }
-            this.errState = curState;
-            throw e;
+		// Generate the initial states.
+        //
+		// The functor is passed to getInitStates() to - instead of adding all
+		// init states into an intermediate StateVec to check and add each state
+		// in a subsequent loop - directly check each state one-by-one and add
+		// it to the queue, fingerprint set and trace file. This avoids
+		// allocating memory for StateVec (which depending on the number of init
+		// states can grow to be GBs) and the subsequent loop over StateVec.
+        final DoInitFunctor functor;
+        if (ignoreCancel) {
+			// Rerunning state space exploration to reconstruct the error stack to determine
+			// what caused Tool to fail to evaluate the init predicate expressions. Thus,
+			// re-check all invariants even if state is already known (= part of theFPSet).
+        	functor = new DoInitFunctor(tool, ignoreCancel);
+        } else {
+        	functor = new DoInitFunctor(tool);
         }
-        return true;
-    }
+		try {
+			tool.getInitStates(functor);
+		} catch (DoInitFunctor.InvariantViolatedException ive) {
+			this.errState = functor.errState;
+			return functor.returnValue;
+		} catch (Assert.TLCRuntimeException e) {
+			this.errState = functor.errState;
+			throw e;
+		}
+		
+		// Iff one of the init states' checks violates any properties, the
+		// functor will record it.
+		if (functor.errState != null) {
+			this.errState = functor.errState;
+			if (functor.e != null) {
+				throw functor.e;
+			}
+		}
+		
+		// Return whatever the functor has recorded.
+		return functor.returnValue;
+	}
 
     /**
      * Compute the set of the next states.  For each next state, check that
@@ -393,257 +389,266 @@ public class ModelChecker extends AbstractChecker
      * 
      * This method is called from the workers on every step
      */
-    public final boolean doNext(TLCState curState, ObjLongTable counts) throws Throwable
+    private final boolean doNext(final ITool tool, TLCState curState, final SetOfStates liveNextStates, final Worker worker) throws Throwable
     {
-        // SZ Feb 23, 2009: cancel the calculation
-        if (this.cancellationFlag)
-        {
-            return false;
-        }
-
         boolean deadLocked = true;
         TLCState succState = null;
-        StateVec liveNextStates = null;
-        LongVec liveNextFPs = null;
-
-        if (this.checkLiveness)
-        {
-            liveNextStates = new StateVec(2);
-            liveNextFPs = new LongVec(2);
-        }
-
         try
         {
-            int k = 0;
-            // <--
-            // <--
-            for (int i = 0; i < this.actions.length; i++)
+            for (int i = 0; i < tool.getActions().length; i++)
             {
-                // SZ Feb 23, 2009: cancel the calculation
-                if (this.cancellationFlag)
-                {
-                    return false;
-                }
-
-                StateVec nextStates = this.tool.getNextStates(this.actions[i], curState);
-                int sz = nextStates.size();
-                this.incNumOfGenStates(sz);
-                deadLocked = deadLocked && (sz == 0);
+				final Action action = tool.getActions()[i];
+				final StateVec nextStates = tool.getNextStates(action, curState);
+				final int sz = nextStates.size();
+				worker.incrementStatesGenerated(sz);
+				deadLocked = deadLocked && (sz == 0);
 
                 for (int j = 0; j < sz; j++)
                 {
-                    succState = nextStates.elementAt(j);
-                    // Check if succState is a legal state.
-                    if (!this.tool.isGoodState(succState))
-                    {
-                        if (this.setErrState(curState, succState, false))
-                        {
-                            MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT);
-                            this.trace.printTrace(curState, succState);
-                            this.theStateQueue.finishAll();
-
-                            synchronized (this)
-                            {
-                                this.notify();
-                            }
-                        }
-                        return true;
-                    }
-                    if (TLCGlobals.coverageInterval >= 0)
+					succState = nextStates.elementAt(j);
+					// Check if succState is a legal state.
+                    if (!tool.isGoodState(succState))
                     {
-                        ((TLCStateMutSource) succState).addCounts(counts);
-                    }
+                    	return doNextSetErr(curState, succState, action);
+					}
 
-                    boolean inModel = (this.tool.isInModel(succState) && this.tool.isInActions(curState, succState));
-                    boolean seen = false;
+					final boolean inModel = (tool.isInModel(succState) && tool.isInActions(curState, succState));
+					boolean unseen = true;
                     if (inModel)
                     {
-                        long fp = succState.fingerPrint();
-                        seen = this.theFPSet.put(fp);
-                        if (!seen)
-                        {
-                            // Write out succState when needed:
-                            if (this.allStateWriter != null)
-                            {
-                                this.allStateWriter.writeState(succState);
-                            }
-                            // Enqueue succState only if it satisfies the model constraints:
-                            long loc = this.trace.writeState(curState, fp);
-                            succState.uid = loc;
-                            this.theStateQueue.sEnqueue(succState);
-                        }
-                        // For liveness checking:
-                        if (this.checkLiveness)
-                        {
-                            liveNextStates.addElement(succState);
-                            liveNextFPs.addElement(fp);
-                        }
-                    }
-                    // Check if succState violates any invariant:
-                    if (!seen)
+						unseen = !isSeenState(curState, succState, action, worker, liveNextStates);
+					}
+					// Check if an unseen succState violates any invariant:
+                    if (unseen)
                     {
-                        try
-                        {
-                            int len = this.invariants.length;
-                            for (k = 0; k < len; k++)
-                            {
-                                // SZ Feb 23, 2009: cancel the calculation
-                                if (this.cancellationFlag)
-                                {
-                                    return false;
-                                }
-
-                                if (!tool.isValid(this.invariants[k], succState))
-                                {
-                                    // We get here because of invariant violation:
-                                    synchronized (this)
-                                    {
-                                        if (TLCGlobals.continuation)
-                                        {
-                                            MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
-                                                    this.tool.getInvNames()[k]);
-                                            this.trace.printTrace(curState, succState);
-                                            break;
-                                        } else
-                                        {
-                                            if (this.setErrState(curState, succState, false))
-                                            {
-                                                MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, this.tool
-                                                        .getInvNames()[k]);
-                                                this.trace.printTrace(curState, succState);
-                                                this.theStateQueue.finishAll();
-                                                this.notify();
-                                            }
-                                            return true;
-                                        }
-                                    }
-                                }
-                            }
-                            if (k < len)
-                                continue;
-                        } catch (Exception e)
-                        {
-                            if (this.setErrState(curState, succState, true))
-                            {
-                                MP.printError(EC.TLC_INVARIANT_EVALUATION_FAILED, new String[] {
-                                        this.tool.getInvNames()[k], 
-                                        (e.getMessage()==null)?e.toString():e.getMessage() });
-                                this.trace.printTrace(curState, succState);
-                                this.theStateQueue.finishAll();
-                                this.notify();
-                            }
-                            throw e;
-                        }
-                    }
+                    	if (doNextCheckInvariants(tool, curState, succState)) {
+                    		return true;
+                    	}
+					}
                     // Check if the state violates any implied action. We need to do it
                     // even if succState is not new.
-                    try
-                    {
-                        int len = this.impliedActions.length;
-                        for (k = 0; k < len; k++)
-                        {
-                            // SZ Feb 23, 2009: cancel the calculation
-                            if (this.cancellationFlag)
-                            {
-                                return false;
-                            }
-
-                            if (!tool.isValid(this.impliedActions[k], curState, succState))
-                            {
-                                // We get here because of implied-action violation:
-                                synchronized (this)
-                                {
-                                    if (TLCGlobals.continuation)
-                                    {
-                                        MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, this.tool
-                                                .getImpliedActNames()[k]);
-                                        this.trace.printTrace(curState, succState);
-                                        break;
-                                    } else
-                                    {
-                                        if (this.setErrState(curState, succState, false))
-                                        {
-                                            MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, this.tool
-                                                    .getImpliedActNames()[k]);
-                                            this.trace.printTrace(curState, succState);
-                                            this.theStateQueue.finishAll();
-                                            this.notify();
-                                        }
-                                        return true;
-                                    }
-                                }
-                            }
-                        }
-                        if (k < len)
-                            continue;
-                    } catch (Exception e)
-                    {
-                        if (this.setErrState(curState, succState, true))
-                        {
-                            MP.printError(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, new String[] {
-                                    this.tool.getImpliedActNames()[k], 
-                                    (e.getMessage()==null)?e.toString():e.getMessage() });
-                            this.trace.printTrace(curState, succState);
-                            this.theStateQueue.finishAll();
-                            this.notify();
-                        }
-                        throw e;
+                    if (doNextCheckImplied(tool, curState, succState)) {
+                    	return true;
                     }
-                }
-                // Must set state to null!!!
-                succState = null;
-            }
-            // Check for deadlock:
+                    if (inModel && unseen) {
+						// The state is inModel, unseen and neither invariants
+						// nor implied actions are violated. It is thus eligible
+						// for further processing by other workers.
+						this.theStateQueue.sEnqueue(succState);
+                    }
+				}
+				// Must set state to null!!!
+				succState = null;
+			}
+			// Check for deadlock:
             if (deadLocked && this.checkDeadlock)
             {
-                synchronized (this)
-                {
-                    if (this.setErrState(curState, null, false))
-                    {
-                        MP.printError(EC.TLC_DEADLOCK_REACHED);
-                        this.trace.printTrace(curState, null);
-                        this.theStateQueue.finishAll();
-                        this.notify();
-                    }
-                }
-                return true;
-            }
-            // Finally, add curState into the behavior graph for liveness checking:
-            if (this.checkLiveness)
+                return doNextSetErr(curState, null, false, EC.TLC_DEADLOCK_REACHED, null);
+			}
+			return false;
+        } catch (final Throwable e)
+        {
+			doNextFailed(curState, succState, e);
+			throw e;
+		}
+    }
+
+	private final boolean isSeenState(final TLCState curState, final TLCState succState,
+			final Action action, final Worker worker, final SetOfStates liveNextStates) throws IOException {
+		final long fp = succState.fingerPrint();
+		final boolean seen = this.theFPSet.put(fp);
+		// Write out succState when needed:
+		this.allStateWriter.writeState(curState, succState, !seen, action);
+		if (!seen)
+		{
+			// Write succState to trace only if it satisfies the
+			// model constraints. Do not enqueue it yet, but wait
+		    // for implied actions and invariants to be checked.
+		    // Those checks - if violated - will cause model checking
+		    // to terminate. Thus we cannot let concurrent workers start
+		    // exploring this new state. Conversely, the state has to
+		    // be in the trace in case either invariant or implied action
+		    // checks want to print the trace. 
+			worker.writeState(curState, fp, succState);
+			if (coverage) {
+				action.cm.incSecondary();
+			}
+		}
+		// For liveness checking:
+		if (this.checkLiveness)
+		{
+			liveNextStates.put(fp, succState);
+		}
+		return seen;
+	}
+
+	private final boolean doNextCheckInvariants(final ITool tool, final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception {
+        int k = 0;
+		try
+        {
+			for (k = 0; k < tool.getInvariants().length; k++)
             {
-                // Add the stuttering step:
-                long curStateFP = curState.fingerPrint();
-                liveNextStates.addElement(curState);
-                liveNextFPs.addElement(curStateFP);
-                liveCheck.addNextState(curState, curStateFP, liveNextStates, liveNextFPs);
-            }
-            return false;
-        } catch (Throwable e)
+                if (!tool.isValid(tool.getInvariants()[k], succState))
+                {
+                    // We get here because of invariant violation:
+                	if (TLCGlobals.continuation) {
+                        synchronized (this)
+                        {
+							MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
+									tool.getInvNames()[k]);
+							this.trace.printTrace(curState, succState);
+							return false;
+                        }
+                	} else {
+						return doNextSetErr(curState, succState, false,
+								EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, tool.getInvNames()[k]);
+                	}
+				}
+			}
+        } catch (Exception e)
         {
-            // Assert.printStack(e);
-            boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError));
-            synchronized (this)
+			doNextEvalFailed(curState, succState, EC.TLC_INVARIANT_EVALUATION_FAILED,
+					tool.getInvNames()[k], e);
+		}
+		return false;
+	}
+
+	private final boolean doNextCheckImplied(final ITool tool, final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception {
+		int k = 0;
+        try
+        {
+			for (k = 0; k < tool.getImpliedActions().length; k++)
             {
-                if (this.setErrState(curState, succState, !keep))
+                if (!tool.isValid(tool.getImpliedActions()[k], curState, succState))
                 {
-                    if (e instanceof StackOverflowError)
-                    {
-                        MP.printError(EC.SYSTEM_STACK_OVERFLOW, e);
-                    } else if (e instanceof OutOfMemoryError)
+                    // We get here because of implied-action violation:
+                    if (TLCGlobals.continuation)
                     {
-                        MP.printError(EC.SYSTEM_OUT_OF_MEMORY, e);
-                    } else if (e.getMessage() != null)
-                    {
-                        MP.printError(EC.GENERAL, e);  // LL changed call 7 April 2012
-                    }
-                    this.trace.printTrace(curState, succState);
-                    this.theStateQueue.finishAll();
-                    this.notify();
-                }
-            }
-            throw e;
-        }
-    }
+                        synchronized (this)
+                        {
+                            MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, tool
+                                    .getImpliedActNames()[k]);
+							this.trace.printTrace(curState, succState);
+							return false;
+                       }
+                    } else {
+						return doNextSetErr(curState, succState, false,
+								EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
+								tool.getImpliedActNames()[k]);
+                	}
+				}
+			}
+        } catch (Exception e)
+        {
+			doNextEvalFailed(curState, succState, EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED,
+					tool.getImpliedActNames()[k], e);
+		}
+        return false;
+	}
+
+	final boolean doNextSetErr(TLCState curState, TLCState succState, boolean keep, int ec, String param) throws IOException, WorkerException {
+		synchronized (this)
+		{
+		    if (this.setErrState(curState, succState, keep, ec))
+		    {
+		    	if (param == null) {
+		    		MP.printError(ec);
+		    	} else {
+		    		MP.printError(ec, param);
+		    	}
+				this.trace.printTrace(curState, succState);
+				this.theStateQueue.finishAll();
+				this.notify();
+			}
+		}
+		return true;
+	}
+
+	final boolean doNextSetErr(TLCState curState, TLCState succState, Action action) throws IOException, WorkerException {
+		synchronized (this) {
+			final int errorCode = EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT;
+			if (this.setErrState(curState, succState, false, errorCode))
+			{
+				final Set<OpDeclNode> unassigned = succState.getUnassigned();
+				if (this.tool.getActions().length == 1) {
+					MP.printError(errorCode,
+							new String[] { unassigned.size() > 1 ? "s are" : " is",
+									unassigned.stream().map(n -> n.getName().toString())
+											.collect(Collectors.joining(", ")) });
+				} else {
+					MP.printError(errorCode,
+							new String[] { action.getName().toString(),
+									unassigned.size() > 1 ? "s are" : " is",
+									unassigned.stream().map(n -> n.getName().toString())
+											.collect(Collectors.joining(", ")) });
+				}
+				this.trace.printTrace(curState, succState);
+				this.theStateQueue.finishAll();
+				this.notify();
+			}
+			return true;
+		}
+	}
+
+	final void doNextEvalFailed(TLCState curState, TLCState succState, int ec, String param, Exception e)
+			throws IOException, WorkerException, Exception {
+		synchronized (this) {
+		    if (this.setErrState(curState, succState, true, ec))
+		    {
+				MP.printError(ec, new String[] { param, (e.getMessage() == null) ? e.toString() : e.getMessage() });
+				this.trace.printTrace(curState, succState);
+				this.theStateQueue.finishAll();
+				this.notify();
+			}
+			throw e;
+		}
+	}
+
+	final void doNextFailed(TLCState curState, TLCState succState, Throwable e)
+			throws IOException, WorkerException, Throwable {
+		// Assert.printStack(e);
+		final boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError)
+		        || (e instanceof AssertionError));
+		synchronized (this)
+		{
+			final int ec;
+			if (e instanceof StackOverflowError)
+			{
+				ec = EC.SYSTEM_STACK_OVERFLOW;
+			} else if (e instanceof OutOfMemoryError)
+			{
+				ec = EC.SYSTEM_OUT_OF_MEMORY;
+			} else if (e instanceof AssertionError)
+			{
+				ec = EC.TLC_BUG;
+			} else if (e instanceof EvalException)
+			{
+				ec = ((EvalException) e).getErrorCode();
+			} else
+			{
+				ec = EC.GENERAL;
+			}
+
+			if (this.setErrState(curState, succState, !keep, ec))
+		    {
+				if (!(ec == EC.GENERAL && e.getMessage() == null))
+		        {
+					if (e instanceof EvalException && ((EvalException) e).hasParameters()) {
+						// An EvalException pretty-prints itself in its constructor, i.e. converts the
+						// parameters into the human readable string. However, MP.print* will
+						// pretty-print it a second time, which is why we pass the original parameters
+						// instead of the EvalException itself.  Exception handling in TLC is a mess!
+						MP.printError(ec, ((EvalException) e).getParameters(), e);
+					} else {
+						MP.printError(ec, e);
+					}
+		        }
+				this.trace.printTrace(curState, succState);
+				this.theStateQueue.finishAll();
+				this.notify();
+			}
+		}
+	}
 
     /**
      * Things need to be done here:
@@ -651,47 +656,114 @@ public class ModelChecker extends AbstractChecker
      * Create checkpoint: checkpoint three data structures: the state set,
      *                    the state queue, and the state trace.
      */
-    public final boolean doPeriodicWork() throws Exception
+    @Override
+    public final int doPeriodicWork() throws Exception
     {
+		// Remember if checkpointing should be run. doCheckPoint() when called
+		// internally diffs the time expired since its last invocation which is
+		// only milliseconds here when called twice.
+		final boolean createCheckPoint = TLCGlobals.doCheckPoint();
+		if ((!this.checkLiveness || runtimeRatio > TLCGlobals.livenessRatio || !liveCheck.doLiveCheck()) && !forceLiveCheck && !createCheckPoint) {
+			updateRuntimeRatio(0L);
+			
+			// Do not suspend the state queue if neither check-pointing nor
+			// liveness-checking is going to happen. Suspending is expensive.
+			// It stops all workers.
+			return EC.NO_ERROR;
+		}
+   	
         if (this.theStateQueue.suspendAll())
         {
             // Run liveness checking, if needed:
-            if (this.checkLiveness)
+			// The ratio set in TLCGlobals defines an upper bound for the
+			// runtime dedicated to liveness checking.
+            if (this.checkLiveness && (runtimeRatio < TLCGlobals.livenessRatio || forceLiveCheck))
             {
-                if (!liveCheck.check(false))
-                    return false;
+				final long preLivenessChecking = System.currentTimeMillis();
+				final int result = liveCheck.check(tool, forceLiveCheck);
+                if (result != EC.NO_ERROR)
+                {
+                    return result;
+                }
+                forceLiveCheck = false;
+                updateRuntimeRatio(System.currentTimeMillis() - preLivenessChecking);
+            } else if (runtimeRatio > TLCGlobals.livenessRatio) {
+            	updateRuntimeRatio(0L);
             }
 
-            if (TLCGlobals.doCheckPoint()) {
+            if (createCheckPoint) {
             	// Checkpoint:
-            	MP.printMessage(EC.TLC_CHECKPOINT_START, this.metadir);
-            	
-            	// start checkpointing:
-            	this.theStateQueue.beginChkpt();
-            	this.trace.beginChkpt();
-            	this.theFPSet.beginChkpt();
-            	this.theStateQueue.resumeAll();
-            	UniqueString.internTbl.beginChkpt(this.metadir);
-            	if (this.checkLiveness)
-            	{
-            		liveCheck.beginChkpt();
-            	}
-            	// commit checkpoint:
-            	this.theStateQueue.commitChkpt();
-            	this.trace.commitChkpt();
-            	this.theFPSet.commitChkpt();
-            	UniqueString.internTbl.commitChkpt(this.metadir);
-            	if (this.checkLiveness)
-            	{
-            		liveCheck.commitChkpt();
-            	}
-            	MP.printMessage(EC.TLC_CHECKPOINT_END);
+            	checkpoint();
             } else {
 				// Just resume worker threads when checkpointing is skipped
             	this.theStateQueue.resumeAll();
             }
         }
-        return true;
+        return EC.NO_ERROR;
+    }
+
+	protected void checkpoint() throws IOException {
+		// start checkpointing:
+       	MP.printMessage(EC.TLC_CHECKPOINT_START, this.metadir);
+		this.theStateQueue.beginChkpt();
+		this.trace.beginChkpt();
+		this.theFPSet.beginChkpt();
+		this.theStateQueue.resumeAll();
+		UniqueString.internTbl.beginChkpt(this.metadir);
+		if (this.checkLiveness)
+		{
+			liveCheck.beginChkpt();
+		}
+		// commit checkpoint:
+		this.theStateQueue.commitChkpt();
+		this.trace.commitChkpt();
+		this.theFPSet.commitChkpt();
+		UniqueString.internTbl.commitChkpt(this.metadir);
+		if (this.checkLiveness)
+		{
+			liveCheck.commitChkpt();
+		}
+    	MP.printMessage(EC.TLC_CHECKPOINT_END);
+	}
+
+	public void forceLiveCheck() {
+		forceLiveCheck = true;
+	}
+    
+    protected void updateRuntimeRatio(final long delta) {
+    	assert delta >= 0L;
+
+    	// Absolute runtime from TLC startup to now (includes liveness
+		// checking, even the current delta).
+		long totalRuntime = System.currentTimeMillis() - startTime;
+		
+		// Subtract a progressInterval to account for the fact that the
+		// previously recorded runtimeRatio was calculated with totalRuntime
+		// from the previous progressReporting interval. updateRuntimeRatio is
+		// called from doPeriodicWork which executes every progressIntervall.
+		// This is an approximation because the last invocation could have
+		// happened longer ago than progressInterval if e.g. checkpointing
+		// blocked the doPeriodicWork thread.
+		totalRuntime = totalRuntime - TLCGlobals.progressInterval;
+		
+		// Subtract delta from the totalRuntime
+		totalRuntime = totalRuntime - delta;
+		
+		// Absolute time spent on all liveness checks from TLC
+		// startup up to now (without delta). Iff no liveness checking has been
+		// executed so far, the absolute time is obviously 0. totalRuntime
+		// can also be negative.
+		final double absLivenessRuntime = Math.max(totalRuntime * runtimeRatio, 0);
+
+		// Sum up the absLivenessRuntime with the new delta. It is the current
+		// absolute time for liveness checking. Divide it by overall
+		// totalRuntime (including progressInterval and delta) to calculate the
+		// new ratio.
+		runtimeRatio = (delta + absLivenessRuntime) / (totalRuntime + TLCGlobals.progressInterval + delta);
+    }
+    
+    public double getRuntimeRatio() {
+    	return runtimeRatio;
     }
 
     public final boolean recover() throws IOException
@@ -703,31 +775,55 @@ public class ModelChecker extends AbstractChecker
             MP.printMessage(EC.TLC_CHECKPOINT_RECOVER_START, this.fromChkpt);
             this.trace.recover();
             this.theStateQueue.recover();
-            this.theFPSet.recover();
+            this.theFPSet.recover(this.trace);
             if (this.checkLiveness)
             {
+				// Liveness checking requires the initial states to be
+				// available as part of behaviors. Initial states are not part
+				// of the checkpoint, but we can easily recreate them.
+				// See bug #22 "Recovering from a checkpoint silently breaks
+				// liveness checking" at
+				// https://github.com/tlaplus/tlaplus/issues/22
+            	this.tool.getInitStates(new IStateFunctor() {
+					public Object addElement(TLCState state) {
+						liveCheck.addInitState(tool, state, state.fingerPrint());
+						return true;
+					}
+				});
                 liveCheck.recover();
             }
             MP.printMessage(EC.TLC_CHECKPOINT_RECOVER_END, new String[] { String.valueOf(this.theFPSet.size()),
                     String.valueOf(this.theStateQueue.size()) });
             recovered = true;
-            this.numOfGenStates.set(this.theFPSet.size());
+            // Not all states are true initial states, but who cares at this point?
+            numberOfInitialStates = this.theFPSet.size();
         }
         return recovered;
     }
 
     private final void cleanup(boolean success) throws IOException
     {
+    	boolean vetoCleanup = VETO_CLEANUP;
+    	
+		// If model checking is not done, checkpoints are (explicitly) enabled, and
+		// either and error has been found or time-bound model checking is enabled, take
+		// a snapshot to allow users to continue model checking if needed.
+		if (TLCGlobals.chkptExplicitlyEnabled()
+				&& !theStateQueue.isEmpty() && (this.errState != null || isTimeBound())) {
+			checkpoint();
+			vetoCleanup = true;
+		}
+    	
         this.theFPSet.close();
         this.trace.close();
-        if (this.checkLiveness)
-            liveCheck.close();
-        if (this.allStateWriter != null)
-            this.allStateWriter.close();
-        	if (!VETO_CLEANUP) {
-        		FileUtil.deleteDir(this.metadir, success);
-        	}
-		}
+        if (this.checkLiveness) {
+        	liveCheck.close();
+        }
+        this.allStateWriter.close();
+    	if (!vetoCleanup) {
+    		FileUtil.deleteDir(this.metadir, success);
+    	}
+	}
 
     public final void printSummary(boolean success, final long startTime) throws IOException
     {
@@ -740,18 +836,32 @@ public class ModelChecker extends AbstractChecker
          */
         if (TLCGlobals.tool)
         {	
-        	printProgresStats(startTime);
+        	printProgresStats(startTime, true);
         }
 
-        MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(this.numOfGenStates),
+        MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(getStatesGenerated()),
                 String.valueOf(this.theFPSet.size()), String.valueOf(this.theStateQueue.size()) });
         if (success)
         {
             MP.printMessage(EC.TLC_SEARCH_DEPTH, String.valueOf(this.trace.getLevelForReporting()));
+			
+        	// Aggregate outdegree from statistics maintained by individual workers. 
+        	final BucketStatistics aggOutDegree = new BucketStatistics("State Graph OutDegree");
+        	for (IWorker worker : workers) {
+				aggOutDegree.add(((Worker) worker).getOutDegree());
+			}
+        	// Print graph statistics iff data points were actually collected.
+        	if (aggOutDegree.getObservations() > 0) {
+				MP.printMessage(EC.TLC_STATE_GRAPH_OUTDEGREE,
+						new String[] { Integer.toString(aggOutDegree.getMin()),
+								Long.toString(Math.round(aggOutDegree.getMean())),
+								Long.toString(Math.round(aggOutDegree.getPercentile(.95))),
+								Integer.toString(aggOutDegree.getMax()) });
+        	}
         }
     }
     
-    private final void printProgresStats(final long startTime) throws IOException {
+    private final void printProgresStats(final long startTime, final boolean isFinal) throws IOException {
         final long fpSetSize = this.theFPSet.size();
         
         // print progress showing states per minute metric (spm)
@@ -764,232 +874,47 @@ public class ModelChecker extends AbstractChecker
         	oldFPSetSize = 0;
         	factor = (System.currentTimeMillis() - startTime) / 60000d;
         }
-		long l = numOfGenStates.get();
+		final long l = getStatesGenerated();
 		statesPerMinute = (long) ((l - oldNumOfGenStates) / factor);
         oldNumOfGenStates = l;
         distinctStatesPerMinute = (long) ((fpSetSize - oldFPSetSize) / factor);
         oldFPSetSize = fpSetSize;
         
-		MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { String.valueOf(this.trace.getLevelForReporting()),
-                String.valueOf(this.numOfGenStates), String.valueOf(fpSetSize),
-                String.valueOf(this.theStateQueue.size()), String.valueOf(statesPerMinute), String.valueOf(distinctStatesPerMinute) });
+		MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] {
+                String.valueOf(this.trace.getLevelForReporting()),
+                MP.format(l),
+                MP.format(fpSetSize),
+                MP.format(this.theStateQueue.size()),
+                MP.format(statesPerMinute),
+                MP.format(distinctStatesPerMinute) });
+		
+		TLAFlightRecorder.progress(isFinal, this.trace.getLevelForReporting(), l, fpSetSize, this.theStateQueue.size(),
+				statesPerMinute, distinctStatesPerMinute);
     }
 
     public static final void reportSuccess(final FPSet anFpSet, final long numOfGenStates) throws IOException
     {
-        final long fpSetSize = anFpSet.size();
-        final double actualProb = anFpSet.checkFPs();
-        reportSuccess(fpSetSize,  actualProb, numOfGenStates);
-    }
-    
-    public static final void reportSuccess(final long numOfDistinctStates, final double actualProb, final long numOfGenStates) throws IOException
-    {
-        // shown as 'calculated' in Toolbox
-        final double optimisticProb = numOfDistinctStates * ((numOfGenStates - numOfDistinctStates) / Math.pow(2, 64));
-        /* The following code added by LL on 3 Aug 2009 to print probabilities
-         * to only one decimal point.  Removed by LL on 17 April 2012 because it
-         * seemed to report probabilities > 10-4 as probability 0.
-         */
-         // final PrintfFormat fmt = new PrintfFormat("val = %.1G");
-         // final String optimisticProbStr = fmt.sprintf(optimisticProb);
-         // final String actualProbStr = fmt.sprintf(actualProb);
-        
-        // Following two lines added by LL on 17 April 2012
-        final String optimisticProbStr = "val = " + ProbabilityToString(optimisticProb, 2);
-        // shown as 'observed' in Toolbox
-        final String actualProbStr = "val = " + ProbabilityToString(actualProb, 2);
-        MP.printMessage(EC.TLC_SUCCESS, new String[] { optimisticProbStr, actualProbStr });
-    }
-    
-    /**
-     * This method added by LL on 17 April 2012 to replace the use of the PrintfFormat
-     * method in reportSuccess.
-     * 
-     * Returns a string representing the decimal representation of a probability to
-     * a given number of significant digits.  If the input is not a probability, or if
-     * some error is found, then it returns the result of applying Double.toString(long)
-     * to the value.
-     * 
-     * Warning: the code makes the following assumption:
-     *  - Double.toString(v) returns a decimal representation of v of the
-     *    form  [d]* ["." [d]+ ["E" [+ | -] [d]+]  where d is a decimal digit and
-     *      [x]   = 0 or 1 instance of x
-     *      [x]*  = any number of instances of x
-     *      [x]+  = any non-zero number of instances of x
-     *      x | y = an x or a y
-     * 
-     * @param val                - the probability represented as a long; must satisfy 0 <= val <= 1.
-     * @param significantDigits  - the number of significant digits to include; must be > 0.
-     * @return
-     */
-    private static final String ProbabilityToString(double val, int significantDigits) {
-        /*
-         * If val = 0 (which shouldn't happen), return "0.0"
-         */
-        if (val == 0) {
-            return "0.0";
-        }
-                
-        String valString = Double.toString(val) ;
-        int valStringLen = valString.length();
-        
-        String result = "";
-        int next = 0; // pointer to the next character in valString to examine.
-        int significantDigitsFound = 0;
-        
-        /*
-         * Skip past leading zeros.
-         */
-        while ((next < valStringLen)  && (valString.charAt(next) == '0')) {
-            next++ ;
-        }
-        
-        /*
-         * Append all the following digits to result, incrementing
-         * significantDigits for each one.  
-         */
-        while ( (next < valStringLen)  && 
-                Character.isDigit(valString.charAt(next))) {
-            result = result + valString.charAt(next);
-            significantDigitsFound++;
-            next++ ;
-         }
-        
-        /*
-         * IF next character is not "." 
-         *   THEN IF at end THEN return result
-         *                  ELSE return valString.
-         */
-        if (next == valStringLen) {
-            return result;
-        } else if (valString.charAt(next) != '.') {
-            return valString;
-        }
-        
-        
-        /*
-         * IF significantDigitsFound >= significantDigits, 
-         *    THEN skip over "." and the following digits.
-         *         (this should not happen)
-         *    ELSE append "." to result ;
-         *         IF significantDigitsFound = 0  
-         *           THEN copy each of the following "0"s of valString to result;
-         *         copy up to significantDigits - significantDigitsFound
-         *            following digits of valString to result;
-         *         IF next char of valString a digit >= "5"
-         *           THEN propagate a carry backwards over the digits of result
-         *                 -- e.g., changing ".019" to ".020";
-         *         Skip over remaining digits of valString;
-         */
-        if (significantDigitsFound >= significantDigits) {
-            next++ ;
-            while ( (next < valStringLen)  && 
-                    Character.isDigit(valString.charAt(next))) {
-                 next++ ;
-             }
+        final long numOfDistinctStates = anFpSet.size();
+        final double optimisticProb = calculateOptimisticProbability(numOfDistinctStates, numOfGenStates);
+        if (optimisticProb < 1E-10) {
+			// If the optimistic probability is sufficiently low, don't waste time
+			// calculating the actual probability.
+        	reportSuccess(numOfDistinctStates, numOfGenStates);
         } else {
-            next++;
-            result = result + ".";
-            if (significantDigitsFound == 0) {
-                while ((next < valStringLen)  && (valString.charAt(next) == '0')) {
-                    next++ ;
-                    result = result + "0";
-                }
-            }
-            while ((next < valStringLen)  && 
-                  Character.isDigit(valString.charAt(next)) &&
-                  significantDigitsFound < significantDigits ) {
-                      result = result + valString.charAt(next);
-                      next++;
-                      significantDigitsFound++;
-             }
-            if ((next < valStringLen)  &&  
-                 Character.isDigit(valString.charAt(next)) &&
-                 Character.digit(valString.charAt(next), 10) >= 5) {
-                int prev = result.length()-1; // the next digit of result to increment
-                boolean done = false;
-                while (!done) {
-                    if (prev < 0) {
-                        result = "1" + result;
-                        done = true;
-                    } else {
-                        char prevChar = result.charAt(prev);
-                        String front = result.substring(0, prev);
-                        String back = result.substring(prev+1);
-                        if (Character.isDigit(prevChar)) {
-                            if (prevChar == '9') {
-                                result = front + '0' + back;
-                            } else {
-                                result = front + Character.forDigit(Character.digit(prevChar, 10)+1, 10) + back;
-                                done = true;
-                            }
-                            
-                        } else {
-                            // prevChar must be '.', so just continue
-                        }
-                    }
-                    prev--;
-                }
-            }
-            while ((next < valStringLen)  &&  
-                    Character.isDigit(valString.charAt(next))) {
-                next++;
-            }
+        	reportSuccess(numOfDistinctStates, anFpSet.checkFPs(), numOfGenStates);
         }
-        
-        /*
-         * IF next at end of valString or at "E"
-         *   THEN copy remaining chars of valString to result;
-         *        return result
-         *   ELSE return valString
-         */
-        if (next >= valStringLen) {
-            return result;
-        }
-        if (valString.charAt(next)=='E') {
-            next++;
-            result = result + "E";
-            while (next < valStringLen) {
-                result = result + valString.charAt(next);
-                next++;
-            }
-            return result;
-        }
-        return valString;
-    }
-
-// The following method used for testing ProbabilityToString
-//
-//    public static void main(String[] args) {
-//        double[] test = new double[] {.5, .0995, .00000001, 001.000, .0022341, 
-//                                      .0022351, 3.14159E-12, 
-//                                      00.999, .002351111, 22.8E-14, 0.000E-12,
-//                                      37, 0033D, 04.85, -35.3};
-//        int i = 0;
-//        while (i < test.length) {
-//            System.out.println("" + i + ": " + Double.toString(test[i]) + " -> " + ProbabilityToString(test[i],2));
-//            i++;
-//        }    
-//    }
-
-    public final void setAllValues(int idx, Value val)
-    {
-        for (int i = 0; i < this.workers.length; i++)
-        {
-            workers[i].setLocalValue(idx, val);
-        }
-    }
-
-    public final Value getValue(int i, int idx)
-    {
-        return workers[i].getLocalValue(idx);
     }
 
     /**
      * Spawn the worker threads
      */
-    protected IdThread[] startWorkers(AbstractChecker checker, int checkIndex)
+    protected IWorker[] startWorkers(AbstractChecker checker, int checkIndex)
     {
+		// Generation of initial states is done at this point. Thus set the
+		// number of workers on the fpset, for it to adapt any synchronization
+    	// if necessary (e.g. OffHeapDiskFPSet).
+        this.theFPSet.incWorkers(this.workers.length);
+
         for (int i = 0; i < this.workers.length; i++)
         {
             this.workers[i].start();
@@ -997,14 +922,6 @@ public class ModelChecker extends AbstractChecker
         return this.workers;
     }
 
-    /**
-     * Work to be done prior entering to the worker loop
-     */
-    protected void runTLCPreLoop()
-    {
-        // nothing to do in this implementation
-    }
-
     /**
      * Process calculation.  
      * 
@@ -1026,7 +943,7 @@ public class ModelChecker extends AbstractChecker
     {
         final int level = this.trace.getLevel();
         
-    	printProgresStats(-1);
+    	printProgresStats(-1, false);
         
         if (level > depth)
         {
@@ -1060,8 +977,207 @@ public class ModelChecker extends AbstractChecker
     {
         DebugPrinter.print(e);
     }
+    
+	private static boolean useByteArrayQueue() {
+		return Boolean.getBoolean(ModelChecker.class.getName() + ".BAQueue");
+	}
+
+	public static String getStateQueueName() {
+		// Ideally, this wouldn't hard-code the simple name of the classes but we don't
+		// have access to the class file yet.
+		return useByteArrayQueue() ? "DiskByteArrayQueue" : "DiskStateQueue";
+	}
 
     public long getStatesGenerated() {
-    	return numOfGenStates.get();
+    	long sum = numberOfInitialStates;
+    	for (final IWorker worker : workers) {
+			sum += ((Worker) worker).getStatesGenerated();
+		}
+    	return sum;
     }
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.AbstractChecker#getProgress()
+	 */
+	@Override
+	public int getProgress() {
+		try {
+			return trace.getLevelForReporting();
+		} catch (IOException e) {
+			// The modelchecker trace file might be closed already (e.g. it
+			// gets closed at the end of the modelchecker run)
+			return -1;
+		}
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.AbstractChecker#stop()
+	 */
+	@Override
+	public void stop() {
+		synchronized (this) {
+			this.setDone();
+			this.theStateQueue.finishAll();
+			this.notifyAll();
+		}
+	}
+	
+	public void suspend() {
+		synchronized (this) {
+			this.theStateQueue.suspendAll();
+			this.notifyAll();
+		}
+	}
+
+	public void resume() {
+		synchronized (this) {
+			this.theStateQueue.resumeAll();
+			this.notifyAll();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.AbstractChecker#getStateQueueSize()
+	 */
+	@Override
+	public long getStateQueueSize() {
+		return theStateQueue.size();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.AbstractChecker#getDistinctStatesGenerated()
+	 */
+	@Override
+	public long getDistinctStatesGenerated() {
+		return theFPSet.size();
+	}
+   
+	/**
+	 * An implementation of {@link IStateFunctor} for
+	 * {@link ModelChecker#doInit(boolean)}.
+	 */
+	private class DoInitFunctor implements IStateFunctor {
+
+		@SuppressWarnings("serial")
+		public class InvariantViolatedException extends RuntimeException {
+		}
+		
+		/**
+		 * Non-Null iff a violation occurred.
+		 */
+		private TLCState errState;
+		private Throwable e;
+
+		/**
+		 * The return values of addElement are meaningless, but doInit wants to
+		 * know the actual outcome when all init states have been processed.
+		 * This outcome is stored as returnValue.
+		 */
+		private int returnValue = EC.NO_ERROR;
+		
+		private final boolean forceChecks;
+		private final ITool tool;
+		
+		public DoInitFunctor(ITool tool) {
+			this(tool, false);
+		}
+		
+		public DoInitFunctor(ITool tool, boolean forceChecks) {
+			this.forceChecks = forceChecks;
+			this.tool = tool;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.tool.IStateFunctor#addElement(tlc2.tool.TLCState)
+		 */
+		public Object addElement(final TLCState curState) {
+			if (Long.bitCount(numberOfInitialStates) == 1 && numberOfInitialStates > 1) {
+				MP.printMessage(EC.TLC_COMPUTING_INIT_PROGRESS, Long.toString(numberOfInitialStates));
+			}
+			numberOfInitialStates++;
+			
+			// getInitStates() does not support aborting init state generation
+			// once a violation has been found (that is why the return values of
+			// addElement are meaningless). It continues until all init
+			// states have been generated. Thus, the functor simply ignores
+			// subsequent states once a violation has been recorded.
+			if (errState != null) {
+				if (returnValue == EC.NO_ERROR)
+				  returnValue = EC.TLC_INITIAL_STATE;
+				return returnValue;
+			}
+			
+			try {
+				// Check if the state is a legal state
+				if (!tool.isGoodState(curState)) {
+					MP.printError(EC.TLC_INITIAL_STATE, new String[]{ "current state is not a legal state", curState.toString() });
+					this.errState = curState;
+					returnValue = EC.TLC_INITIAL_STATE;
+					throw new InvariantViolatedException();
+				}
+				boolean inModel = tool.isInModel(curState);
+				boolean seen = false;
+				if (inModel) {
+					long fp = curState.fingerPrint();
+					seen = theFPSet.put(fp);
+					if (!seen) {
+						allStateWriter.writeState(curState);
+						((Worker) workers[0]).writeState(curState, fp);
+						theStateQueue.enqueue(curState);
+
+						// build behavior graph for liveness checking
+						if (checkLiveness) {
+							liveCheck.addInitState(tool, curState, fp);
+						}
+					}
+				}
+				// Check properties of the state:
+				if (!seen || forceChecks) {
+					for (int j = 0; j < tool.getInvariants().length; j++) {
+						if (!tool.isValid(tool.getInvariants()[j], curState)) {
+							// We get here because of invariant violation:
+							MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL,
+									new String[] { tool.getInvNames()[j].toString(), curState.toString() });
+							if (!TLCGlobals.continuation) {
+								this.errState = curState;
+								returnValue = EC.TLC_INVARIANT_VIOLATED_INITIAL;
+								throw new InvariantViolatedException();
+							}
+						}
+					}
+					for (int j = 0; j < tool.getImpliedInits().length; j++) {
+						if (!tool.isValid(tool.getImpliedInits()[j], curState)) {
+							// We get here because of implied-inits violation:
+							MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL,
+									new String[] { tool.getImpliedInitNames()[j], curState.toString() });
+							this.errState = curState;
+							returnValue = EC.TLC_PROPERTY_VIOLATED_INITIAL;
+							throw new InvariantViolatedException();
+						}
+					}
+				}
+			} catch (InvariantViolatedException | Assert.TLCRuntimeException | EvalException e) {
+				// IVE gets thrown above when an Invariant is violated. TLCRuntimeException gets
+				// thrown when Tool fails to evaluate a statement because of e.g. too large sets
+				// or type errors such as in DoInitFunctorInvariantMinimalErrorStackTest test.
+				this.errState = curState;
+				this.e = e;
+				throw e;
+			} catch (OutOfMemoryError e) {
+				MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT);
+				returnValue = EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT;
+				return returnValue;
+			} catch (Throwable e) {
+				// Assert.printStack(e);
+				this.errState = curState;
+				this.e = e;
+			}
+			return returnValue;
+		}
+	}
+
+	public List<File> getModuleFiles(FilenameToStream resolver) {
+		return this.tool.getModuleFiles(resolver);
+	}
 }
+	
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/SimulationWorker.java b/tlatools/src/tlc2/tool/SimulationWorker.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e40e2890342e1b8dc16e84a740c51133e2e2354
--- /dev/null
+++ b/tlatools/src/tlc2/tool/SimulationWorker.java
@@ -0,0 +1,421 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   William Schultz - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.atomic.LongAdder;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ILiveCheck;
+import tlc2.util.IdThread;
+import tlc2.util.RandomGenerator;
+import util.FileUtil;
+
+/**
+ * A SimulationWorker repeatedly checks random traces of a spec.
+ * 
+ * It is constructed with an initial seed which it uses to initialize its
+ * internal random number generator. A simulation worker continually generates
+ * random traces, even if it encounters errors of any kind i.e.
+ * invariant/liveness violations, evaluation errors, etc. A worker communicates
+ * these errors by pushing its results onto a result queue, that is provided to
+ * it at construction time.
+ * 
+ * Workers may terminate in two possible ways. They will terminate if they
+ * receive an "interruption" signal, and they will terminate if they reach their
+ * maximum trace count limit. Otherwise, they will continue to run forever,
+ * generating new traces. It is the job of an external thread/client to
+ * determine if the errors produced are cause for termination or can be ignored.
+ * 
+ * Upon termination due to any cause, a worker thread will always push a final
+ * OK result onto its result queue. This acts as a way to signal external
+ * clients that this thread has terminated.
+ */
+public class SimulationWorker extends IdThread {
+	
+	// This worker's local source of randomness.
+	final RandomGenerator localRng;
+
+	// The state currently being processed.
+	TLCState curState;
+
+	// Stores a trace generated by this worker.
+	StateVec stateTrace;
+	
+	// The set of initial states for the spec. 
+	private StateVec initStates;
+	
+	// The queue that the worker places its results onto.
+	private final BlockingQueue<SimulationWorkerResult> resultQueue;
+	
+	// Tracks the number of traces that have been generated so far.
+	private int traceCnt = 0;
+	
+	// The maximum number of traces this worker should generate before terminating.
+	private final long maxTraceNum;
+	
+	// The maximum length of any generated trace.
+	private final long maxTraceDepth;
+	
+	// Should this worker check traces for deadlock.
+	private final boolean checkDeadlock;
+	
+	// The base name of the file that this worker writes out generated traces to. If it is null,
+	// no trace files are generated.
+	private final String traceFile;
+	
+	// A counter that tracks the number of generated states/traces. This counter may be
+	// shared among workers, so its count may be greater than the number of states/traces
+	// generated by this worker alone. It is the duty of this worker, though, to
+	// update it whenever it generates a new state or trace.
+	private final LongAdder numOfGenStates;
+	private final LongAdder numOfGenTraces;
+
+	private final ITool tool;
+	private final ILiveCheck liveCheck;	
+	
+	/**
+	 * Encapsulates information about an error produced by a simulation worker.
+	 */
+	 public static class SimulationWorkerError {
+		
+		public SimulationWorkerError(int errorCode, String[] parameters, TLCState state, StateVec stateTrace,
+				Exception e) {
+			this.errorCode = errorCode;
+			this.parameters = parameters;
+			this.state = state;
+			this.stateTrace = stateTrace;
+			this.exception = e;
+		}
+		
+		// The error code to report.
+		public int errorCode;
+
+		// Any additional information to be included in a reported error string.
+		public String[] parameters;
+
+		// The TLC state associated with the error.
+		public TLCState state;
+
+		// The TLC trace associated with the error.
+		public StateVec stateTrace;
+
+		// An exception associated with the error.
+		public Exception exception;
+	}
+	
+	
+	/**
+	 * Encapsulates information about a result produced by a simulation worker.
+	 * 
+	 * A result can either be an error or OK (indicating no error). Each result is
+	 * associated with a specific worker.
+	 */
+	public static class SimulationWorkerResult {
+		
+		/**
+		 * Constructs an OK result.
+		 */
+		static SimulationWorkerResult OK(int workerId) {
+			SimulationWorkerResult res = new SimulationWorkerResult();
+			res.workerId = workerId;
+			return res;
+		}
+			
+		/**
+		 * Constructs an error result.
+		 */
+		static SimulationWorkerResult Error(int workerId, SimulationWorkerError err) {
+			SimulationWorkerResult res = new SimulationWorkerResult();
+			res.error = Optional.of(err);
+			res.workerId = workerId;
+			return res;
+		}
+
+		/**
+		 * Returns whether this result is an error.
+		 */
+		public boolean isError() {
+			return error.isPresent();
+		}
+		
+		/**
+		 * Returns the error for this result. Only valid to call if an error is present.
+		 */
+		public SimulationWorkerError error() {
+			return error.get();
+		}
+		
+		public int workerId() {
+			return workerId;
+		}
+		
+		private int workerId; // The worker that generated this result.
+		private Optional<SimulationWorkerError> error = Optional.empty();
+
+	}
+	
+
+	public SimulationWorker(int id, ITool tool, BlockingQueue<SimulationWorkerResult> resultQueue,
+			long seed, long maxTraceDepth, long maxTraceNum, boolean checkDeadlock, String traceFile,
+			ILiveCheck liveCheck, LongAdder numOfGenStates, LongAdder numOfGenTraces) {
+		super(id);
+		this.localRng = new RandomGenerator(seed);
+		this.tool = tool;
+		this.maxTraceDepth = maxTraceDepth;
+		this.maxTraceNum = maxTraceNum;
+		this.resultQueue = resultQueue;
+		this.checkDeadlock = checkDeadlock;
+		this.traceFile = traceFile;
+		this.liveCheck = liveCheck;
+		this.numOfGenStates = numOfGenStates;
+		this.numOfGenTraces = numOfGenTraces;
+	}
+	
+	/**
+	 * The main worker loop. Continually generates random traces until the trace count limit
+	 * is reached or until the worker is interrupted.
+	 * 
+	 * We use the standard Java notion of thread "interruption" as a mechanism for
+	 * halting/cancelling the execution of a worker thread. There's nothing particularly
+	 * special about this. The Thread.interrupt() just sets a boolean flag that the
+	 * running thread then periodically checks via calls to Thread.interrupted(). We could
+	 * implement this manually but it's simpler to use the built-in mechanism.
+	 */
+	public final void run() {
+		while(true) {
+			try {
+				// The trace simulation method should do appropriately frequent interruption
+				// checks.
+				final Optional<SimulationWorkerError> res = simulateRandomTrace();
+				traceCnt++;
+				this.numOfGenTraces.increment();
+
+				// If we have an error result, place it on the output queue.
+				if (res.isPresent()) {
+					final SimulationWorkerError err = res.get();
+					resultQueue.put(SimulationWorkerResult.Error(this.myGetId(), err));
+				}
+
+				// Abide by the maximum trace generation count.
+				if (traceCnt >= maxTraceNum) {
+					resultQueue.put(SimulationWorkerResult.OK(this.myGetId()));
+					return;
+				}
+			} catch (final InterruptedException e) {
+				// Gracefully terminate if we were interrupted.
+				resultQueue.offer(SimulationWorkerResult.OK(this.myGetId()));
+				return;
+			} catch (final Exception e) {
+				final SimulationWorkerError err = new SimulationWorkerError(0, null, this.curState, this.stateTrace, e);
+				resultQueue.offer(SimulationWorkerResult.Error(this.myGetId(), err));
+				return;
+			}	
+		}
+	}
+
+	/**
+	 * Check to see if the worker thread has been interrupted.
+	 */
+	private void checkForInterrupt() throws InterruptedException {
+		if (Thread.interrupted()) {
+			throw new InterruptedException();
+		}
+	}
+	
+	/**
+	 * This method returns the set of next states generated by a randomly chosen
+	 * action. It returns null if there is no possible next state.
+	 */
+	public final StateVec randomNextStates(RandomGenerator rng, TLCState state) {
+		final Action[] actions = this.tool.getActions();
+		final int len = actions.length;
+		int index = (int) Math.floor(rng.nextDouble() * len);
+		final int p = rng.nextPrime();
+		for (int i = 0; i < len; i++) {
+			final StateVec pstates = this.tool.getNextStates(actions[index], state);
+			if (!pstates.empty()) {
+				return pstates;
+			}
+			index = (index + p) % len;
+		}
+		return null;
+	}
+	
+	/**
+	 * This method returns a state that is randomly chosen from the set of states.
+	 * It returns null if the set of states is empty.
+	 */
+	private final TLCState randomState(RandomGenerator rng, StateVec states) throws EvalException {
+		final int len = states.size();
+		if (len > 0) {
+			final int index = (int) Math.floor(rng.nextDouble() * len);
+			return states.elementAt(index);
+		}
+		return null;
+	}
+
+	/**
+	 * Generates a single random trace.
+	 *
+	 * The core steps of this process are as follows:
+	 * 
+	 *  a) Pick one of the initial states. 
+	 *  b) Randomly pick an action to generate the successor states (more than 1) to the current initial state. 
+	 *  c) Check all of the generated successors for their validity. 
+	 *  d) Randomly pick a generated successor and make it the new current state.
+	 *
+	 * Returns an Optional error representing the outcome of the trace generation. If the trace generation produced no error,
+	 * returns Optional.empty().
+	 *
+	 */
+	private Optional<SimulationWorkerError> simulateRandomTrace() throws Exception {
+		this.curState = null;
+		this.stateTrace = new StateVec((int) maxTraceDepth);
+		stateTrace.clear();
+
+		// a) Randomly select a state from the set of init states.
+		curState = randomState(this.localRng, initStates);
+		setCurrentState(curState);
+		
+		boolean inConstraints = tool.isInModel(curState);
+
+		// Simulate a trace up to the maximum specified length.
+		for (int traceIdx = 0; traceIdx < maxTraceDepth; traceIdx++) {
+			// We don't want this thread to run for too long without checking for
+			// interruption, so we do so on every iteration of the main trace generation
+			// loop.
+			checkForInterrupt();
+
+			// Add the curState to the trace regardless of its inModel property.
+			stateTrace.addElement(curState);
+
+			// Make sure this state satisfies the model constraints.
+			if (!inConstraints) {
+				break;
+			}
+
+			// b) Get the current state's successor states.
+			final StateVec nextStates = randomNextStates(this.localRng, curState);
+			if (nextStates == null) {
+				if (checkDeadlock) {
+					// We get here because of deadlock.
+					return Optional.of(new SimulationWorkerError(EC.TLC_DEADLOCK_REACHED, null, curState, stateTrace, null));
+				}
+				break;
+			}
+
+			// c) Check all generated next states before all but one are discarded.
+			for (int i = 0; i < nextStates.size(); i++) {
+				numOfGenStates.increment();
+				final TLCState state = nextStates.elementAt(i);
+
+				if (!tool.isGoodState(state)) {
+					return Optional.of(new SimulationWorkerError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, state,
+							stateTrace, null));
+				}
+
+				// Check invariants.
+				int idx = 0;
+				try {
+					for (idx = 0; idx < this.tool.getInvariants().length; idx++) {
+						if (!tool.isValid(this.tool.getInvariants()[idx], state)) {
+							// We get here because of an invariant violation.
+							return Optional.of(new SimulationWorkerError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
+									new String[] { tool.getInvNames()[idx] }, state, stateTrace, null));
+						}
+					}
+				} catch (final Exception e) {
+					return Optional.of(new SimulationWorkerError(EC.TLC_INVARIANT_EVALUATION_FAILED,
+							new String[] { tool.getInvNames()[idx], e.getMessage() }, state, stateTrace, null));
+				}
+
+				// Check action properties.
+				try {
+					for (idx = 0; idx < this.tool.getImpliedActions().length; idx++) {
+						if (!tool.isValid(this.tool.getImpliedActions()[idx], curState, state)) {
+							// We get here because of implied-action violation.
+							return Optional.of(new SimulationWorkerError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
+									new String[] { tool.getImpliedActNames()[idx] }, state, stateTrace, null));
+						}
+					}
+				} catch (final Exception e) {
+					return Optional.of(new SimulationWorkerError(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED,
+							new String[] { tool.getImpliedActNames()[idx], e.getMessage() }, state, stateTrace, null));
+				}
+
+			}
+
+			// At this point all generated successor states have been checked for
+			// their respective validity (isGood/isValid/impliedActions/...).
+
+			// d) Randomly select one of them and make it the current state for the next
+			// iteration of the loop.
+			final TLCState s1 = randomState(localRng, nextStates);
+			inConstraints = (tool.isInModel(s1) && tool.isInActions(curState, s1));
+			s1.setPredecessor(curState);
+			curState = s1;
+			setCurrentState(curState);
+		}
+
+		// Check for interruption once more before entering liveness checking.
+		checkForInterrupt();
+
+		// Check if the current trace satisfies liveness properties.
+		liveCheck.checkTrace(tool, stateTrace);
+		
+
+		// Write the trace out if desired. The trace is printed in the
+		// format of TLA module, so that it can be read by TLC again.
+		if (traceFile != null) {
+			// Make sure each worker outputs to its own set of trace files.
+			final String fileName = traceFile + "_" + String.valueOf(this.myGetId()) + "_" + this.traceCnt;
+			// TODO is it ok here?
+			final PrintWriter pw = new PrintWriter(FileUtil.newBFOS(fileName));
+			pw.println("---------------- MODULE " + fileName + " -----------------");
+			for (int idx = 0; idx < stateTrace.size(); idx++) {
+				pw.println("STATE_" + (idx + 1) + " == ");
+				pw.println(stateTrace.elementAt(idx) + "\n");
+			}
+			pw.println("=================================================");
+			pw.close();
+		}
+
+		// Finished trace generation without any errors.
+		return Optional.empty();
+	}
+	
+	public final StateVec getTrace() {
+		return stateTrace;
+	}
+
+	public void start(StateVec initStates) {
+		this.initStates = initStates;
+		this.start();
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/Simulator.java b/tlatools/src/tlc2/tool/Simulator.java
index 6edceb687878dad53fd38dcdd03733b9c46f6454..d972edaea356225a8a8e86dc895c802423e4d393 100644
--- a/tlatools/src/tlc2/tool/Simulator.java
+++ b/tlatools/src/tlc2/tool/Simulator.java
@@ -6,49 +6,46 @@
 package tlc2.tool;
 
 import java.io.IOException;
-import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.LongAdder;
 
-import tla2sany.modanalyzer.SpecObj;
-import tla2sany.semantic.SemanticNode;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.output.StatePrinter;
+import tlc2.tool.SimulationWorker.SimulationWorkerError;
+import tlc2.tool.SimulationWorker.SimulationWorkerResult;
+import tlc2.tool.coverage.CostModelCreator;
+import tlc2.tool.impl.FastTool;
 import tlc2.tool.liveness.ILiveCheck;
 import tlc2.tool.liveness.LiveCheck;
 import tlc2.tool.liveness.LiveCheck1;
 import tlc2.tool.liveness.LiveException;
 import tlc2.tool.liveness.NoOpLiveCheck;
-import tlc2.util.ObjLongTable;
 import tlc2.util.RandomGenerator;
 import tlc2.util.statistics.DummyBucketStatistics;
+import tlc2.value.IValue;
+import util.Assert.TLCRuntimeException;
 import util.FileUtil;
 import util.FilenameToStream;
 
-public class Simulator implements Cancelable {
+public class Simulator {
 
-	public static boolean EXPERIMENTAL_LIVENESS_SIMULATION = Boolean.getBoolean(Simulator.class.getName() + ".experimentalLiveness");
-	
+	public static boolean EXPERIMENTAL_LIVENESS_SIMULATION = Boolean
+			.getBoolean(Simulator.class.getName() + ".experimentalLiveness");
 
 	/* Constructors */
-	/**
-	 * SZ Feb 20, 2009: added the possibility to pass the SpecObject, this is
-	 * compatibility constructor
-	 * @throws IOException 
-	 *
-	 * @deprecated use
-	 *             {@link Simulator#Simulator(String, String, String, boolean, int, long, RandomGenerator, long, boolean, FilenameToStream, SpecObj)}
-	 *             instead and pass the <code>null</code> as SpecObj
-	 */
-	public Simulator(String specFile, String configFile, String traceFile, boolean deadlock, int traceDepth,
-			long traceNum, RandomGenerator rng, long seed, boolean preprocess, FilenameToStream resolver) throws IOException {
-		this(specFile, configFile, traceFile, deadlock, traceDepth, traceNum, rng, seed, preprocess, resolver, null);
-	}
 
 	// SZ Feb 20, 2009: added the possibility to pass the SpecObject
 	public Simulator(String specFile, String configFile, String traceFile, boolean deadlock, int traceDepth,
-			long traceNum, RandomGenerator rng, long seed, boolean preprocess, FilenameToStream resolver,
-			SpecObj specObj) throws IOException {
+			long traceNum, RandomGenerator rng, long seed, FilenameToStream resolver,
+			int numWorkers) throws IOException {
 		int lastSep = specFile.lastIndexOf(FileUtil.separatorChar);
 		String specDir = (lastSep == -1) ? "" : specFile.substring(0, lastSep + 1);
 		specFile = specFile.substring(lastSep + 1);
@@ -57,16 +54,11 @@ public class Simulator implements Cancelable {
 		// SZ Mar 5, 2009: removed it again because of the bug in simulator
 		// ToolIO.setUserDir(specDir);
 
-		this.tool = new Tool(specDir, specFile, configFile, resolver);
-
-		this.tool.init(preprocess, specObj); // parse and process the spec
+		this.tool = new FastTool(specDir, specFile, configFile, resolver);
 
-		this.checkDeadlock = deadlock;
+		this.checkDeadlock = deadlock && tool.getModelConfig().getCheckDeadlock();
 		this.checkLiveness = !this.tool.livenessIsTrue();
-		this.actions = this.tool.getActions();
 		this.invariants = this.tool.getInvariants();
-		this.impliedActions = this.tool.getImpliedActions();
-		this.numOfGenStates = 0;
 		if (traceDepth != -1) {
 			// this.actionTrace = new Action[traceDepth]; // SZ: never read
 			// locally
@@ -80,287 +72,347 @@ public class Simulator implements Cancelable {
 		this.rng = rng;
 		this.seed = seed;
 		this.aril = 0;
-		this.astCounts = new ObjLongTable(10);
 		// Initialization for liveness checking
 		if (this.checkLiveness) {
 			if (EXPERIMENTAL_LIVENESS_SIMULATION) {
 				final String tmpDir = System.getProperty("java.io.tmpdir");
-				liveCheck = new LiveCheck(this.tool, new Action[0], tmpDir, new DummyBucketStatistics());
+				liveCheck = new LiveCheck(this.tool, tmpDir, new DummyBucketStatistics());
 			} else {
 				liveCheck = new LiveCheck1(this.tool);
 			}
 		} else {
 			liveCheck = new NoOpLiveCheck(tool, specDir);
 		}
+
+		this.numWorkers = numWorkers;
+		this.workers = new ArrayList<>(numWorkers);
+		for (int i = 0; i < this.numWorkers; i++) {
+			this.workers.add(new SimulationWorker(i, this.tool, this.workerResultQueue, this.rng.nextLong(),
+					this.traceDepth, this.traceNum, this.checkDeadlock, this.traceFile, this.liveCheck,
+					this.numOfGenStates, this.numOfGenTraces));
+		}
+		
+		if (TLCGlobals.isCoverageEnabled()) {
+        	CostModelCreator.create(this.tool);
+        }
+		
+		//TODO Eventually derive Simulator from AbstractChecker.
+		AbstractChecker.scheduleTermination(new TimerTask() {
+			@Override
+			public void run() {
+				Simulator.this.stop();
+			}
+		});
 	}
 
 	/* Fields */
 	private final ILiveCheck liveCheck;
-	private final Tool tool;
-	private final Action[] actions; // the sub actions
+	private final ITool tool;
 	private final Action[] invariants; // the invariants to be checked
-	private final Action[] impliedActions; // the implied-actions to be checked
 	private final boolean checkDeadlock; // check deadlock?
 	private final boolean checkLiveness; // check liveness?
-	private long numOfGenStates;
+
+	// The total number of states/traces generated by all workers. May be written to
+	// concurrently, so we use a LongAdder to reduce potential contention.
+	private final LongAdder numOfGenStates = new LongAdder();
+	private final LongAdder numOfGenTraces = new LongAdder();
+
 	// private Action[] actionTrace; // SZ: never read locally
 	private final String traceFile;
+
+	// The maximum length of a simulated trace.
 	private final long traceDepth;
+
+	// The maximum number of total traces to generate.
 	private final long traceNum;
+
+	// The number of worker threads to use for simulation.
+	private int numWorkers = 1;
+
 	private final RandomGenerator rng;
 	private final long seed;
 	private long aril;
-	private final ObjLongTable astCounts;
-	private boolean isCancelled; // SZ Feb 24, 2009: cancellation added
+
+	// Each simulation worker pushes their results onto this shared queue.
+	private BlockingQueue<SimulationWorkerResult> workerResultQueue = new LinkedBlockingQueue<>();
+	
+    /**
+     * Timestamp of when simulation started.
+     */
+	private final long startTime = System.currentTimeMillis();
+	
+	private final List<SimulationWorker> workers;
+		 
+	 /**
+	 * Returns whether a given error code is considered "continuable". That is, if
+	 * any worker returns this error, should we consider continuing to run the
+	 * simulator. These errors are considered "fatal" since they most likely
+	 * indicate an error in the way the spec is written.
+	 */
+	private boolean isNonContinuableError(int ec) {
+		return ec == EC.TLC_INVARIANT_EVALUATION_FAILED || 
+			   ec == EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED ||
+			   ec == EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT;
+	}
+	
+	/**
+	 * Shut down all of the given workers and make sure they have stopped.
+	 */
+	private void shutdownAndJoinWorkers(final List<SimulationWorker> workers) throws InterruptedException {
+		for (SimulationWorker worker : workers) {
+			worker.interrupt();
+			worker.join();
+		}
+	}
 
 	/*
-	 * This method does simulation on a TLA+ spec. Its argument specifies the
-	 * main module of the TLA+ spec.
+	 * This method does random simulation on a TLA+ spec.
+	 * 
+	 * It runs until en error is encountered or we have generated the maximum number of traces.
+	 * 
+   * @return an error code, or <code>EC.NO_ERROR</code> on success
 	 */
-	public void simulate() throws Exception {
-		StateVec theInitStates = null;
+	public int simulate() throws Exception {
+		final int res = this.tool.checkAssumptions();
+		if (res != EC.NO_ERROR) {
+			return res;
+		}
+		
 		TLCState curState = null;
 
-		if (isCancelled) {
-			return;
-		}
-		// Compute the initial states:
+		// The init states are calculated only ever once and never change
+		// in the loops below. Ideally the variable would be final.
+		StateVec initStates = this.tool.getInitStates();
+
+		//
+		// Compute the initial states.
+		//
 		try {
-			// The init states are calculated only ever once and never change
-			// in the loops below. Ideally the variable would be final.
-			theInitStates = this.tool.getInitStates();
-			this.numOfGenStates = theInitStates.size();
-			for (int i = 0; i < theInitStates.size(); i++) {
-				curState = theInitStates.elementAt(i);
+
+
+			// This counter should always be initialized at zero.
+			assert (this.numOfGenStates.longValue() == 0);
+			this.numOfGenStates.add(initStates.size());
+			
+			MP.printMessage(EC.TLC_COMPUTING_INIT_PROGRESS, this.numOfGenStates.toString());
+
+			// Check all initial states for validity.
+			for (int i = 0; i < initStates.size(); i++) {
+				curState = initStates.elementAt(i);
 				if (this.tool.isGoodState(curState)) {
 					for (int j = 0; j < this.invariants.length; j++) {
 						if (!this.tool.isValid(this.invariants[j], curState)) {
-							// We get here because of invariant violation:
-							MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL,
+							// We get here because of invariant violation.
+							return MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL,
 									new String[] { this.tool.getInvNames()[j], curState.toString() });
-							return;
 						}
 					}
 				} else {
-					MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString());
-					return;
+					return MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString());
 				}
 			}
 		} catch (Exception e) {
-			// Assert.printStack(e);
+			final int errorCode;
 			if (curState != null) {
-				MP.printError(EC.TLC_INITIAL_STATE,
+				errorCode = MP.printError(EC.TLC_INITIAL_STATE,
 						new String[] { (e.getMessage() == null) ? e.toString() : e.getMessage(), curState.toString() });
 			} else {
-				MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012
+				errorCode = MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012
 			}
 
 			this.printSummary();
-			return;
+			return errorCode;
 		}
-		if (this.numOfGenStates == 0) {
-			MP.printError(EC.TLC_NO_STATES_SATISFYING_INIT);
-			return;
+
+		if (this.numOfGenStates.longValue() == 0) {
+			return MP.printError(EC.TLC_NO_STATES_SATISFYING_INIT);
 		}
+
 		// It appears deepNormalize brings the states into a canonical form to
 		// speed up equality checks.
-		theInitStates.deepNormalize();
+		initStates.deepNormalize();
 
-		// Start progress report thread:
-		ProgressReport report = new ProgressReport();
+		//
+		// Start progress report thread.
+		//
+		final ProgressReport report = new ProgressReport();
 		report.start();
 
-		// Start simulating:
-		final StateVec stateTrace = new StateVec((int) traceDepth);
-		int idx = 0;
-		try {
-			// The two loops essentially do:
-			// a) Pick one of the initial states for as long as the bahavior's length is less than traceCnt
-			// b) Randomly pick an action to generate the successor states (more than 1) to the current initial state
-			// c) Check all of the generated successors for their validity
-			// d) Randomly pick a generated successor and make it curState
-			for (int traceCnt = 1; traceCnt <= this.traceNum; traceCnt++) {
-				this.aril = rng.getAril();
-				stateTrace.clear();
-
-				// a) Randomly select a state from the set of init states.
-				curState = this.randomState(theInitStates);
-				boolean inConstraints = this.tool.isInModel(curState);
-
-				for (int traceIdx = 0; traceIdx < this.traceDepth; traceIdx++) {
-					// Add the curState to the trace regardless of its inModel
-					// property.
-					stateTrace.addElement(curState);
-
-					if (!inConstraints) {
-						break;
-					}
-
-					// b) Get the curState's successor states
-					StateVec nextStates = this.randomNextStates(curState);
-					if (nextStates == null) {
-						if (this.checkDeadlock) {
-							// We get here because of deadlock:
-							this.printBehavior(EC.TLC_DEADLOCK_REACHED, null, curState, stateTrace);
-							if (!TLCGlobals.continuation) {
-								return;
-							}
-						}
-						break;
-					}
-
-					// c) Check all generated next states before all but one are
-					// discarded
-					for (int i = 0; i < nextStates.size(); i++) {
-						this.numOfGenStates++;
-						TLCState state = nextStates.elementAt(i);
+		//
+		// Start simulating.
+		//
+		this.aril = rng.getAril();
+		
+		// Start up multiple simulation worker threads, each with their own unique seed.
+		final Set<Integer> runningWorkers = new HashSet<>();
+		for (int i = 0; i < this.workers.size(); i++) {
+			SimulationWorker worker = workers.get(i);
+			worker.start(initStates);
+			runningWorkers.add(i);
+		}
 
-						if (TLCGlobals.coverageInterval >= 0) {
-							((TLCStateMutSource) state).addCounts(this.astCounts);
-						}
+		int errorCode = EC.NO_ERROR;
+		
+		// Continuously consume results from all worker threads.
+		while (true) {
+			final SimulationWorkerResult result = workerResultQueue.take();
 
-						if (!this.tool.isGoodState(state)) {
-							this.printBehavior(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, state, stateTrace);
-							return;
-						} else {
-							try {
-								for (idx = 0; idx < this.invariants.length; idx++) {
-									if (!this.tool.isValid(this.invariants[idx], state)) {
-										// We get here because of invariant
-										// violation:
-										this.printBehavior(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
-												new String[] { this.tool.getInvNames()[idx] }, state, stateTrace);
-										if (!TLCGlobals.continuation) {
-											return;
-										}
-									}
-								}
-							} catch (Exception e) {
-								// Assert.printStack(e);
-								this.printBehavior(EC.TLC_INVARIANT_EVALUATION_FAILED,
-										new String[] { this.tool.getInvNames()[idx], e.getMessage() }, state, stateTrace);
-								return;
-							}
-
-							try {
-								for (idx = 0; idx < this.impliedActions.length; idx++) {
-									if (!this.tool.isValid(this.impliedActions[idx], curState, state)) {
-										// We get here because of implied-action
-										// violation:
-
-										this.printBehavior(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
-												new String[] { this.tool.getImpliedActNames()[idx] }, state, stateTrace);
-										if (!TLCGlobals.continuation) {
-											return;
-										}
-									}
-								}
-							} catch (Exception e) {
-								// Assert.printStack(e);
-								this.printBehavior(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED,
-										new String[] { this.tool.getImpliedActNames()[idx], e.getMessage() }, state,
-										stateTrace);
-								return;
-							}
-						}
+			// If the result is an error, print it.
+			if (result.isError()) {
+				SimulationWorkerError error = result.error();
+				
+				// We assume that if a worker threw an unexpected exception, there is a bug
+				// somewhere, so we print out the exception and terminate. In the case of a
+				// liveness error, which is reported as an exception, we also terminate.
+				if (error.exception != null) {
+					if (error.exception instanceof LiveException) {
+						// In case of a liveness error, there is no need to print out
+						// the behavior since the liveness checker should take care of that itself.
+						this.printSummary();
+						errorCode = ((LiveException)error.exception).errorCode;
+					} else if (error.exception instanceof TLCRuntimeException) {
+						final TLCRuntimeException exception = (TLCRuntimeException)error.exception;
+						printBehavior(exception, error.state, error.stateTrace);
+						errorCode = exception.errorCode;
+					} else {
+						printBehavior(EC.GENERAL, new String[] { MP.ECGeneralMsg("", error.exception) }, error.state,
+								error.stateTrace);
+						errorCode = EC.GENERAL;
 					}
-					// At this point all generated successor states have been checked for
-					// their respective validity (isGood/isValid/impliedActions/...).
-					// d) Then randomly select one of them and make it curState
-					// for the next iteration of the loop.
-					TLCState s1 = this.randomState(nextStates);
-					inConstraints = (this.tool.isInModel(s1) && this.tool.isInActions(curState, s1));
-					curState = s1;
+					break;
 				}
 				
-				// Check if the current trace satisfies liveness properties.
-				liveCheck.checkTrace(stateTrace);
-
-				// Write the trace out if desired. The trace is printed in the
-				// format of TLA module, so that it can be read by TLC again.
-				if (this.traceFile != null) {
-					String fileName = this.traceFile + traceCnt;
-					// TODO is it ok here?
-					PrintWriter pw = new PrintWriter(FileUtil.newBFOS(fileName));
-					pw.println("---------------- MODULE " + fileName + " -----------------");
-					for (idx = 0; idx < stateTrace.size(); idx++) {
-						pw.println("STATE_" + (idx + 1) + " == ");
-						pw.println(stateTrace.elementAt(idx) + "\n");
-					}
-					pw.println("=================================================");
-					pw.close();
+				// Print the trace for all other errors.
+				printBehavior(error);
+				
+				// For certain, "fatal" errors, we shut down all workers and terminate,
+				// regardless of the "continue" parameter, since these errors likely indicate a bug in the spec.
+				if (isNonContinuableError(error.errorCode)) {
+					errorCode = error.errorCode;
+					break;
+				}
+				
+				// If the 'continue' option is false, then we always terminate on the
+				// first error, shutting down all workers. Otherwise, we continue receiving
+				// results from the worker threads.
+				if (!TLCGlobals.continuation) {
+					errorCode = error.errorCode;
+					break;
+				}
+
+				if (errorCode == EC.NO_ERROR)
+				{
+					errorCode = EC.GENERAL;
 				}
 			}
-		} catch (Throwable e) {
-			// Assert.printStack(e);
-			if (e instanceof LiveException) {
-				this.printSummary();
-			} else {
-				// LL modified error message on 7 April 2012
-				this.printBehavior(EC.GENERAL, new String[] { MP.ECGeneralMsg("", e) }, curState, stateTrace);
+			// If the result is OK, this indicates that the worker has terminated, so we
+			// make note of this. If all of the workers have terminated, there is no need to
+			// continue waiting for results, so we should terminate.
+			else {
+				runningWorkers.remove(result.workerId());
+				if(runningWorkers.isEmpty()) {
+					break;
+				}
 			}
 		}
+		
+		// Shut down all workers.
+		this.shutdownAndJoinWorkers(workers);
+
+		// Do a final progress report.
+		report.isRunning = false;
+		synchronized (report) {
+			report.notify();
+		}
+		// Wait for the progress reporter thread to finish.
+		report.join();
+
+		return errorCode;
+	}
+
+
+	public final void printBehavior(final TLCRuntimeException exception, final TLCState state, final StateVec stateTrace) {
+		MP.printTLCRuntimeException(exception);
+		printBehavior(state, stateTrace);
+	}
+
+	public final void printBehavior(SimulationWorkerError error) {
+		printBehavior(error.errorCode, error.parameters, error.state, error.stateTrace);
 	}
 
 	/**
 	 * Prints out the simulation behavior, in case of an error. (unless we're at
 	 * maximum depth, in which case don't!)
 	 */
-	public final void printBehavior(int errorCode, String[] parameters, TLCState state, final StateVec stateTrace) {
-
+	public final void printBehavior(final int errorCode, final String[] parameters, final TLCState state, final StateVec stateTrace) {
 		MP.printError(errorCode, parameters);
+		printBehavior(state, stateTrace);
+		this.printSummary();
+	}
+	
+	public final void printBehavior(final TLCState state, final StateVec stateTrace) {
 		if (this.traceDepth == Long.MAX_VALUE) {
 			MP.printMessage(EC.TLC_ERROR_STATE);
 			StatePrinter.printState(state);
 		} else {
+			if (!stateTrace.isLastElement(state)) {
+				// MAK 09/24/2019: this method is called with state being the stateTrace's
+				// last element or not.
+				stateTrace.addElement(state);
+			}
+			
 			MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT);
+			// MAK 09/24/2019: For space reasons, TLCState does not store the state's action.
+			// This is why the loop below creates TLCStateInfo instances out of the pair cur
+			// -> last to print the action's name as part of the error trace. This is
+			// especially useful for Error-Trace Explorer in the Toolbox.
 			TLCState lastState = null;
+			TLCStateInfo sinfo;
+			int cnt = 1;
 			for (int i = 0; i < stateTrace.size(); i++) {
-				StatePrinter.printState(stateTrace.elementAt(i), lastState, i + 1);
-				lastState = stateTrace.elementAt(i);
+				final TLCState curState = stateTrace.elementAt(i);
+				if (lastState != null) {
+					sinfo = this.tool.getState(curState, lastState);
+				} else {
+					sinfo = new TLCStateInfo(curState, "<Initial predicate>");
+				}
+				
+				// MAK 09/25/2019: It is possible for
+				// tlc2.tool.SimulationWorker.simulateRandomTrace() to produce traces with
+				// *non-terminal* stuttering steps, i.e. it might produce traces such
+				// as s0,s1,s1,s2,s3,s3,s3,...sN* (where sN* represents an infinite suffix of
+				// "terminal" stuttering steps). In other words, it produces traces s.t.
+				// a trace can contain finite (sub-)sequence of stuttering steps.
+				// The reason is that simulateRandomTrace with non-zero probability selects
+				// a stuttering steps as the current state's successor. Guarding against it
+				// would require to fingerprint states (i.e. check equality) for each successor
+				// state selected which is considered too expensive.
+				// A trace with finite stuttering can be reduced to a shorter - hence
+				// better readable - trace with only infinite stuttering. This check makes sure
+				// we get rid of the confusing Toolbox behavior that a trace with finite
+				// stuttering is implicitly reduced by breadth-first-search when trace
+				// expressions are evaluated. 
+				if (lastState == null || curState.fingerPrint() != lastState.fingerPrint()) {
+					StatePrinter.printState(sinfo, lastState, cnt++);
+				} else {
+					assert Boolean.TRUE;
+				}
+				lastState = curState;
 			}
-			StatePrinter.printState(state, null, stateTrace.size() + 1);
 		}
-		this.printSummary();
 	}
 
-	/**
-	 * This method returns a state that is randomly chosen from the set of
-	 * states. It returns null if the set of states is empty.
-	 */
-	public final TLCState randomState(StateVec states) throws EvalException {
-		int len = states.size();
-		if (len > 0) {
-			int index = (int) Math.floor(this.rng.nextDouble() * len);
-			return states.elementAt(index);
+	public IValue getLocalValue(int idx) {
+		for (SimulationWorker w : workers) {
+			return w.getLocalValue(idx);
 		}
 		return null;
 	}
 
-	/*
-	 * (non-Javadoc)
-	 * @see tlc2.tool.Cancelable#setCancelFlag(boolean)
-	 */
-	public void setCancelFlag(boolean flag) {
-		this.isCancelled = flag;
-	}
-
-	/**
-	 * This method returns the set of next states generated by a randomly chosen
-	 * action. It returns null if there is no possible next state.
-	 */
-	public final StateVec randomNextStates(TLCState state) {
-		int len = this.actions.length;
-		int index = (int) Math.floor(this.rng.nextDouble() * len);
-		int p = this.rng.nextPrime();
-		for (int i = 0; i < len; i++) {
-			StateVec pstates = this.tool.getNextStates(this.actions[index], state);
-			if (!pstates.empty()) {
-				return pstates;
-			}
-			index = (index + p) % len;
+	public void setAllValues(int idx, IValue val) {
+		for (SimulationWorker w : workers) {
+			w.setLocalValue(idx, val);
 		}
-		return null;
 	}
 
 	/**
@@ -371,14 +423,13 @@ public class Simulator implements Cancelable {
 
 		/*
 		 * This allows the toolbox to easily display the last set of state space
-		 * statistics by putting them in the same form as all other progress
-		 * statistics.
+		 * statistics by putting them in the same form as all other progress statistics.
 		 */
 		if (TLCGlobals.tool) {
-			MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(this.numOfGenStates));
+			MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates.longValue()));
 		}
 
-		MP.printMessage(EC.TLC_STATS_SIMU, new String[] { String.valueOf(this.numOfGenStates),
+		MP.printMessage(EC.TLC_STATS_SIMU, new String[] { String.valueOf(numOfGenStates.longValue()),
 				String.valueOf(this.seed), String.valueOf(this.aril) });
 	}
 
@@ -386,21 +437,8 @@ public class Simulator implements Cancelable {
 	 * Reports coverage
 	 */
 	public final void reportCoverage() {
-		if (TLCGlobals.coverageInterval >= 0) {
-			MP.printMessage(EC.TLC_COVERAGE_START);
-			ObjLongTable counts = this.tool.getPrimedLocs();
-			ObjLongTable.Enumerator keys = this.astCounts.keys();
-			Object key;
-			while ((key = keys.nextElement()) != null) {
-				String loc = ((SemanticNode) key).getLocation().toString();
-				counts.add(loc, astCounts.get(key));
-			}
-			Object[] skeys = counts.sortStringKeys();
-			for (int i = 0; i < skeys.length; i++) {
-				long val = counts.get(skeys[i]);
-				MP.printMessage(EC.TLC_COVERAGE_VALUE, new String[] { skeys[i].toString(), String.valueOf(val) });
-			}
-			MP.printMessage(EC.TLC_COVERAGE_END);
+		if (TLCGlobals.isCoverageEnabled()) {
+            CostModelCreator.report(this.tool, this.startTime );
 		}
 	}
 
@@ -408,15 +446,17 @@ public class Simulator implements Cancelable {
 	 * Reports progress information
 	 */
 	final class ProgressReport extends Thread {
+
+		volatile boolean isRunning = true;
+
 		public void run() {
 			int count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval;
 			try {
-				while (true) {
+				while (isRunning) {
 					synchronized (this) {
 						this.wait(TLCGlobals.progressInterval);
 					}
-					MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates));
-
+					MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates.longValue()));
 					if (count > 1) {
 						count--;
 					} else {
@@ -430,4 +470,10 @@ public class Simulator implements Cancelable {
 			}
 		}
 	}
+
+	public void stop() {
+		for (SimulationWorker worker : workers) {
+			worker.interrupt();
+		}
+	}
 }
diff --git a/tlatools/src/tlc2/tool/Specs.java b/tlatools/src/tlc2/tool/Specs.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ff9e63c91c4bebaae73a8ca12a5f5bc86a357de
--- /dev/null
+++ b/tlatools/src/tlc2/tool/Specs.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.LevelNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SubstInNode;
+import tla2sany.semantic.SymbolNode;
+import tlc2.util.Context;
+import tlc2.util.List;
+import tlc2.value.impl.LazyValue;
+
+public abstract class Specs {
+
+	/** 
+	 * The level of the expression according to level checking.
+	 * static method, does not change instance state 
+	 */
+	public static int getLevel(LevelNode expr, Context c)
+	{
+	    HashSet<SymbolNode> lpSet = expr.getLevelParams();
+	    if (lpSet.isEmpty())
+	        return expr.getLevel();
+	
+	    int level = expr.getLevel();
+	    Iterator<SymbolNode> iter = lpSet.iterator();
+	    while (iter.hasNext())
+	    {
+	        SymbolNode param = (SymbolNode) iter.next();
+	        Object res = c.lookup(param, true);
+	        if (res != null)
+	        {
+	            if (res instanceof LazyValue)
+	            {
+	                LazyValue lv = (LazyValue) res;
+	                int plevel = getLevel((LevelNode) lv.expr, lv.con);
+	                level = (plevel > level) ? plevel : level;
+	            } else if (res instanceof OpDefNode)
+	            {
+	                int plevel = getLevel((LevelNode) res, c);
+	                level = (plevel > level) ? plevel : level;
+	            }
+	        }
+	    }
+	    return level;
+	}
+
+	/**
+	 * Static method, does not change instance state
+	 * @param expr
+	 * @param subs
+	 * @return
+	 */
+	public static final ExprNode addSubsts(ExprNode expr, List subs)
+	{
+	    ExprNode res = expr;
+	
+	    while (!subs.isEmpty())
+	    {
+	        SubstInNode sn = (SubstInNode) subs.car();
+	        res = new SubstInNode(sn.stn, sn.getSubsts(), res, sn.getInstantiatingModule(), sn.getInstantiatedModule());
+	        subs = subs.cdr();
+	    }
+	    return res;
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/StateVec.java b/tlatools/src/tlc2/tool/StateVec.java
index b31de9d6c166da9a1faa7a6d502f22cb62a12fa2..de75c3773f5e2f353b8f0f19f272273a3f0fa99a 100644
--- a/tlatools/src/tlc2/tool/StateVec.java
+++ b/tlatools/src/tlc2/tool/StateVec.java
@@ -5,12 +5,13 @@
 
 package tlc2.tool;
 
-import tla2sany.semantic.SemanticNode;
+import java.util.LinkedList;
+
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
-import tlc2.value.Value;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.Value;
 import util.Assert;
-import util.UniqueString;
 
 /*
  * This class represents a TLA+ state vector.
@@ -18,7 +19,7 @@ import util.UniqueString;
  * updates are used for improved performance and reduced
  * allocation.
  */
-public final class StateVec {
+public final class StateVec implements IStateFunctor, INextStateFunctor {
   private TLCState v[];
   private int size;
 
@@ -49,6 +50,10 @@ public final class StateVec {
 
   public final int size() { return this.size; }
 
+  public boolean isEmpty() {
+	return this.size == 0;
+  }
+
   public final void grow(int add) {
     int oldLen = this.v.length;
     if (oldLen >= TLCGlobals.setBound) {
@@ -64,32 +69,35 @@ public final class StateVec {
 
   public final TLCState elementAt(int i) { return this.v[i]; }
 
-  public final void clear() {
-    this.size = 0;
+  public boolean isLastElement(final TLCState state) {
+	  if (isEmpty()) {
+		  return false;
+	  }
+	  return this.elementAt(size() - 1) == state;
   }
   
-  // Add the binding to all the variables
-  public final StateVec bind(UniqueString var, Value val, SemanticNode ast) {
-    for (int i = 0; i < this.size; i++) {
-      TLCState s = this.v[i];
-      if (s.containsKey(var)) return null;
-      v[i] = s.bind(var, val, ast);
-    }
-    return this;
+  public TLCState first() {
+	return elementAt(0);
   }
 
-  // Bind a value in one of the states.
-  public final StateVec bindAt(int i, UniqueString var, Value val, SemanticNode ast) {
-    v[i] = v[i].bind(var, val, ast);
-    return this;
+  public final void clear() {
+    this.size = 0;
   }
-
+  
+  /* (non-Javadoc)
+   * @see tlc2.tool.IStateFunction#addElement(tlc2.tool.TLCState)
+   */
   public final StateVec addElement(TLCState state) {
     if (this.size >= this.v.length) { grow(1); }
     this.v[this.size++] = state;
     return this;
   }
   
+  @Override
+  public final StateVec addElement(TLCState predecessor, Action action, TLCState state) {
+	  return addElement(state);
+  }
+ 
   public final StateVec addElements(StateVec s1) {
     StateVec s0 = this;
 
@@ -119,6 +127,14 @@ public final class StateVec {
     this.size--;
   }
   
+  public void removeAt(final int index) {
+	  replaceAt(index, null);
+  }
+  
+  public void replaceAt(final int index, final TLCState state) {
+	  this.v[index] = state;
+  }
+  
   public final StateVec copy() {
     TLCState[] res = new TLCState[this.size];
     for (int i = 0; i < this.size; i++) {
@@ -158,4 +174,33 @@ public final class StateVec {
     return sb.toString();
   }
 
+  public final boolean contains(TLCState state) {
+	for (int i = 0; i < size; i++) {
+		if (this.v[i].fingerPrint() == state.fingerPrint()) {
+			return true;
+		}
+	}
+	return false;
+  }
+  
+  public final Value[] toRecords(final TLCState append) {
+	final Value[] values = new Value[size + 1];
+	for (int i = 0; i < values.length; i++) {
+		values[i] = new RecordValue(v[i]);
+	}
+	values[values.length] = new RecordValue(append);
+    return values;
+  }
+  
+  public final Value[] toRecords(final TLCState from, final TLCState append) {
+    final LinkedList<RecordValue> res = new LinkedList<>();
+    res.add(new RecordValue(append));
+	for (int i = size - 1; i >= 0; i--) {
+		res.push(new RecordValue(v[i]));
+		if (from.fingerPrint() == v[i].fingerPrint()) {
+			break;			
+		}
+	}
+    return res.toArray(new Value[res.size()]);
+  }
 }
diff --git a/tlatools/src/tlc2/tool/TLAClass.java b/tlatools/src/tlc2/tool/TLAClass.java
deleted file mode 100644
index 3f20e5eb442495857c407725493a94d4469fb74d..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/tool/TLAClass.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:18:28 PST by lamport
-//      modified on Mon Jan 29 16:21:11 PST 2001 by yuanyu
-
-package tlc2.tool;
-
-import tlc2.output.EC;
-import util.Assert;
-
-/**
- * 
- * @author Yuan Yu, Simon Zambrovski
- * @version $Id$
- */
-public class TLAClass
-{
-    /* Load a class from a file. */
-    private String pkg;
-
-    public TLAClass(String pkg)
-    {
-        if (pkg.length() != 0 && pkg.charAt(pkg.length() - 1) != '.')
-        {
-            this.pkg = pkg + '.';
-        } else
-        {
-            this.pkg = pkg;
-        }
-    }
-
-    /**
-     * This method attempts to load the java class with the given name.
-     **/
-    public synchronized Class loadClass(String name)
-    {
-        Class cl = null;
-        try
-        {
-            try
-            {
-                cl = Class.forName(name);
-            } catch (Exception e)
-            { /*SKIP*/
-            }
-            if (cl == null)
-            {
-                try
-                {
-                    cl = Class.forName(this.pkg + name);
-                } catch (Exception e)
-                { /*SKIP*/
-                }
-            }
-        } catch (Throwable e)
-        {
-            Assert.fail(EC.TLC_ERROR_REPLACING_MODULES, new String[] { name, 
-                       (e.getMessage()==null)?e.toString():e.getMessage() });
-        }
-        return cl;
-    }
-
-    public static void main(String argv[])
-    {
-        TLAClass tc = new TLAClass("tlc2.module");
-        Class c = tc.loadClass("Strings"); // must set CLASSPATH correctly
-        System.err.println("c = " + c);
-        // Class c1 = tc.loadClass("Class");
-        // System.err.println("c1 = " + c1);
-    }
-
-}
diff --git a/tlatools/src/tlc2/tool/TLAPlusExecutor.java b/tlatools/src/tlc2/tool/TLAPlusExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..8647dde9374e6471230b1e9ead6d450ab269505f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/TLAPlusExecutor.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+import tla2sany.semantic.SymbolNode;
+import tlc2.tool.impl.FastTool;
+import tlc2.util.Context;
+import tlc2.value.IValue;
+import util.SimpleFilenameToStream;
+
+public class TLAPlusExecutor {
+
+	public class Mapping {
+		private final Action action;
+		private final Map<SymbolNode, IValue> params;
+		private final Map<String, SymbolNode> symbol2node;
+
+		public Mapping(final Action a, final String... params) {
+			this.action = a;
+			this.params = new HashMap<>();
+			this.symbol2node = new HashMap<>();
+			for (String param : params) {
+				final SymbolNode node = action.con.lookupName(s -> s.getName().equals(param));
+				this.symbol2node.put(param, node);
+			}
+		}
+
+		public Mapping set(final String param, final IValue value) {
+			final SymbolNode node = this.symbol2node.get(param);
+			this.params.put(node, value);
+			return this;
+		}
+
+		public Context getContext() {
+			// Replace TLC's values in ctx with the given ones by extending the pointer list
+			// of ctxs.
+			Context ctx = action.con;
+			for (Map.Entry<SymbolNode, IValue> entry : params.entrySet()) {
+				ctx = ctx.cons(entry.getKey(), entry.getValue());
+			}
+			return ctx;
+		}
+	}
+
+	private final ReentrantLock lock = new ReentrantLock();
+	private final FastTool tool;
+
+	private TLCState state;
+
+	public TLAPlusExecutor(String spec, String config) {
+		this.tool = new FastTool(spec, config, new SimpleFilenameToStream());
+
+		// Initialize the TLA+ executor by generating the initial state.
+		this.state = this.tool.getInitStates().first();
+	}
+
+	public Mapping map(final String actionName, final String processNode, final IValue v, final String... params) {
+		final Action[] actions = tool.getActions();
+		for (Action action : actions) {
+			if (action.getName().equals(actionName)) {
+				final Object lookup = action.con.lookup(s -> s.getName().equals(processNode));
+				if (lookup != null) {
+					if (lookup.equals(v)) {
+						return new Mapping(action, params);
+					}
+				}
+			}
+		}
+		throw new RuntimeException("failed to create mapping");
+	}
+
+	public Object step(final Mapping m) throws Exception {
+		final Context ctx = m.getContext();
+		lock.lock();
+		try {
+			while (true) {
+				final StateVec nextStates = this.tool.getNextStates(m.action, ctx, state);
+				assert !nextStates.isEmpty();
+				this.state = nextStates.first();
+
+				// Check invariants.
+				for (int k = 0; k < this.tool.getInvariants().length; k++) {
+					if (!tool.isValid(this.tool.getInvariants()[k], this.state)) {
+						throw new INextStateFunctor.InvariantViolatedException();
+					}
+				}
+
+				final Object result = this.state.execCallable();
+				if (result != null) {
+					return result;
+				}
+			}
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	public ReentrantLock getLock() {
+		return lock;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/TLAPlusExecutorState.java b/tlatools/src/tlc2/tool/TLAPlusExecutorState.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c9434e003117b8c655f62c663098fd514a2743f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/TLAPlusExecutorState.java
@@ -0,0 +1,392 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+
+import tla2sany.semantic.OpDeclNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.SymbolNode;
+import tlc2.TLCGlobals;
+import tlc2.util.Context;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.UniqueString;
+import util.WrongInvocationException;
+
+public final class TLAPlusExecutorState extends TLCState {
+
+	private Callable<?> callable;
+
+	@Override
+	public Object execCallable() throws Exception {
+		if (callable != null) {
+			return callable.call();
+		}
+		return null;
+	}
+
+	@Override
+	public void setCallable(Callable<?> f) {
+		this.callable = f;
+	}
+
+	//* Everything below this line is a verbatim - except for the type - copy of TLCStateMut. *// 
+
+	public static void setVariables(OpDeclNode[] variables) {
+		vars = variables;
+		Empty = new TLAPlusExecutorState(new IValue[vars.length]);
+	}
+	
+	public TLCState createEmpty() {
+		return new TLAPlusExecutorState(new IValue[vars.length]);
+	}
+
+	protected TLAPlusExecutorState(final IValue[] vals) {
+		this.values = vals;
+	}
+	
+	  private IValue values[];
+	  private static ITool mytool = null;
+
+	  /**
+	   * If non-null, viewMap denotes the function to be applied to
+	   * a state before its fingerprint is computed.
+	   */
+	  private static SemanticNode viewMap = null;
+
+	  /**
+	   * If non-null, perms denotes the set of permutations under the
+	   * symmetry assumption.
+	   */
+	  private static IMVPerm[] perms = null;
+
+	  public static void setTool(ITool tool) {
+	    mytool = tool;
+	    viewMap = tool.getViewSpec();
+	    perms = tool.getSymmetryPerms();
+	  }
+
+	  //TODO equals without hashcode!
+	  public final boolean equals(Object obj) {
+	    if (obj instanceof TLAPlusExecutorState) {
+	      TLAPlusExecutorState state = (TLAPlusExecutorState)obj;
+	      for (int i = 0; i < this.values.length; i++) {
+		if (this.values[i] == null) {
+		  if (state.values[i] != null) return false;
+		}
+		else if (state.values[i] == null ||
+			 !this.values[i].equals(state.values[i])) {
+		  return false;
+		}
+	      }
+	      return true;
+	    }
+	    return false;
+	  }
+	  
+	  public final TLCState bind(UniqueString name, IValue value) {
+		  // Note, tla2sany.semantic.OpApplNode.toString(Value) relies on this ordering.
+	    int loc = name.getVarLoc();
+	    this.values[loc] = value;
+	    return this;
+	  }
+
+	  public final TLCState bind(SymbolNode id, IValue value) {
+	    throw new WrongInvocationException("SharedTLCState.bind: This is a TLC bug.");
+	  }
+	  
+	  public final TLCState unbind(UniqueString name) {
+	    int loc = name.getVarLoc();
+	    this.values[loc] = null;
+	    return this;
+	  }
+
+	  public final IValue lookup(UniqueString var) {
+	    int loc = var.getVarLoc();
+	    if (loc < 0) return null;
+	    return this.values[loc];
+	  }
+
+	  public final boolean containsKey(UniqueString var) {
+	    return (this.lookup(var) != null);
+	  }
+
+	  public final TLCState copy() {
+	    int len = this.values.length;
+	    IValue[] vals = new IValue[len];
+	    for (int i = 0; i < len; i++) {
+	      vals[i] = this.values[i];
+	    }
+	    return new TLAPlusExecutorState(vals);
+	  }
+
+	  public final TLCState deepCopy() {
+	    int len = this.values.length;
+	    IValue[] vals = new IValue[len];
+	    for (int i = 0; i < len; i++) {
+	      IValue val = this.values[i];
+	      if (val != null) {
+		vals[i] = val.deepCopy();
+	      }
+	    }
+	    return new TLAPlusExecutorState(vals);
+	  }
+
+	  public final StateVec addToVec(StateVec states) {
+	    return states.addElement(this.copy());
+	  }
+	  
+	  public final void deepNormalize() {
+	    for (int i = 0; i < this.values.length; i++) {
+	      IValue val = this.values[i];
+	      if (val != null) {
+		val.deepNormalize();
+	      }
+	    }
+	  }
+
+	  /**
+	   * This method returns the fingerprint of this state. We fingerprint
+	   * the values in the state according to the order given by vars.
+	   * This guarantees the same state has the same fingerprint.
+	   *
+	   * Since the values in this state can be shared by multiple threads
+	   * via the state queue. They have to be normalized before adding to
+	   * the state queue.  We do that here.
+	   */
+		public final long fingerPrint() {
+			int sz = this.values.length;
+
+			// TLC supports symmetry reduction. Symmetry reduction works by defining classes
+			// of symmetrically equivalent states for which TLC only checks a
+			// single representative of the equivalence class (orbit). E.g. in a two
+			// process mutual exclusion problem, the process ids are - most of the time -
+			// not relevant with regards to mutual exclusion: We don't care if process A or
+			// B is in the critical section as long as only a single process is in CS. Thus
+			// two states are symmetric that only differ in the process id variable value.
+			// Symmetry can also be observed graphically in the state graph s.t. subgraphs
+			// are isomorphic (Graph Isomorphism). Instead of enumerating the complete state
+			// graph, TLC enumerates one of the isomorphic subgraphs whose state correspond
+			// to the representatives. With respect to the corresponding Kripke structure M,
+			// the resulting Kripke M' is called the "quotient structure" (see "Exploiting
+			// Symmetry in Temporal Logic Model Checking" by Clarke et al).
+			// 
+			// The definition of equivalence classes (orbits) is provided manually by the
+			// user at startup by defining 1 to n symmetry sets. Thus TLC has to find
+			// representative at runtime only which happens below. Given any state s, TLC
+			// evaluates rep(s) to find the lexicographically smallest state ss = rep(s)
+			// with regards to the variable values. The state ss is then fingerprint instead
+			// of s.
+			//
+			// Evaluating rep(s) - to reduce s to ss - requires to apply all permutations in
+			// the group this.perms (derived from the user-defined orbit). This is known as
+			// the constructive orbit problem and is NP-hard. The loop has O(|perms| * |this.values|)
+			// with |prems| = |symmetry set 1|! * |symmetry set 2|! * ... * |symmetry set n|. 
+	        //		
+			// minVals is what is used to calculate/generate the fingerprint below.
+			// If this state is not the lexicographically smallest state ss, its current
+			// minVals will be replaced temporarily with the values of ss for the
+			// calculation of the fingerprint.
+			IValue[] minVals = this.values;
+			if (perms != null) {
+				IValue[] vals = new IValue[sz];
+				// The following for loop converges to the smallest state ss under symmetry by
+				// looping over all permutations applying each. If the outcome turns out to be
+				// lexicographically smaller than the currently smallest, it replaces the
+				// current smallest. Once all permutations (perms) have been processed, we know
+				// we have found the smallest state.
+				NEXT_PERM: for (int i = 0; i < perms.length; i++) {
+					int cmp = 0;
+					// For each value in values succinctly permute the current value
+					// and compare it to its corresponding minValue in minVals.
+					for (int j = 0; j < sz; j++) {
+						vals[j] = this.values[j].permute(perms[i]);
+						if (cmp == 0) {
+							// Only compare unless an earlier compare has found a
+							// difference already (if a difference has been found
+							// earlier, still permute the remaining values of the
+							// state to fully permute all state values).
+							cmp = vals[j].compareTo(minVals[j]);
+							if (cmp > 0) {
+								// When cmp evaluates to >0, all subsequent
+								// applications of perms[i] for the remaining values
+								// won't make the resulting vals[] smaller than
+								// minVals. Thus, exit preemptively from the loop
+								// over vals. This works because perms is the cross
+								// product of all symmetry sets.
+								continue NEXT_PERM;
+							}
+						}
+					}
+					// cmp < 0 means the current state is part of a symmetry
+					// permutation set/group and not the "smallest" one.
+					if (cmp < 0) {
+						if (minVals == this.values) {
+							minVals = vals;
+							vals = new IValue[sz];
+						} else {
+							IValue[] temp = minVals;
+							minVals = vals;
+							vals = temp;
+						}
+					}
+				}
+			}
+			// Fingerprint the state:
+			long fp = FP64.New();
+			if (viewMap == null) {
+				for (int i = 0; i < sz; i++) {
+					fp = minVals[i].fingerPrint(fp);
+				}
+				if (this.values != minVals) {
+					for (int i = 0; i < sz; i++) {
+						this.values[i].deepNormalize();
+					}
+				}
+			} else {
+				for (int i = 0; i < sz; i++) {
+					this.values[i].deepNormalize();
+				}
+				TLAPlusExecutorState state = this;
+				if (minVals != this.values) {
+					state = new TLAPlusExecutorState(minVals);
+				}
+				IValue val = mytool.eval(viewMap, Context.Empty, state);
+				fp = val.fingerPrint(fp);
+			}
+			return fp;
+		}
+
+	  public final boolean allAssigned() {
+	    int len = this.values.length;    
+	    for (int i = 0; i < len; i++) {
+	      if (values[i] == null) return false;
+	    }
+	    return true;
+	  }
+	  
+		public final Set<OpDeclNode> getUnassigned() {
+			// Return sorted set (lexicographical).
+			final Set<OpDeclNode> unassignedVars = new TreeSet<OpDeclNode>(new Comparator<OpDeclNode>() {
+				@Override
+				public int compare(OpDeclNode o1, OpDeclNode o2) {
+					return o1.getName().toString().compareTo(o2.getName().toString());
+				}
+			});
+			int len = this.values.length;
+			for (int i = 0; i < len; i++) {
+				if (values[i] == null) {
+					unassignedVars.add(vars[i]);
+				}
+			}
+			return unassignedVars;
+		}
+
+	  public final void read(IValueInputStream vis) throws IOException {
+	    super.read(vis);
+	    int len = this.values.length;
+	    for (int i = 0; i < len; i++) {
+	      this.values[i] = vis.read();
+	    }
+	  }
+
+	  public final void write(IValueOutputStream vos) throws IOException {
+	    super.write(vos);
+	    int len = this.values.length;
+	    for (int i = 0; i < len; i++) {
+	    	this.values[i].write(vos);
+	    }
+	  }
+	  
+	  /* Returns a string representation of this state.  */
+	  public final String toString() {
+	    if (TLCGlobals.useView && viewMap != null) {
+	      IValue val = mytool.eval(viewMap, Context.Empty, this);
+	      return viewMap.toString(val);
+	    }
+	    StringBuffer result = new StringBuffer();
+	    int vlen = vars.length;
+	    if (vlen == 1) {
+	      UniqueString key = vars[0].getName();
+	      IValue val = this.lookup(key);
+	      result.append(key.toString());
+	      result.append(" = ");
+	      result.append(Values.ppr(val));
+	      result.append("\n");
+	    }
+	    else {
+	      for (int i = 0; i < vlen; i++) {
+		UniqueString key = vars[i].getName();
+		IValue val = this.lookup(key);
+		result.append("/\\ ");
+		result.append(key.toString());
+	    result.append(" = ");
+	    result.append(Values.ppr(val));
+	    result.append("\n");
+	      }
+	    }
+	    return result.toString();
+	  }
+	  
+	  /* Returns a string representation of this state.  */
+	  public final String toString(TLCState lastState) {
+	    StringBuffer result = new StringBuffer();
+	    TLAPlusExecutorState lstate = (TLAPlusExecutorState)lastState;
+
+	    int vlen = vars.length;
+	    if (vlen == 1) {
+	      UniqueString key = vars[0].getName();
+	      IValue val = this.lookup(key);
+	      IValue lstateVal = lstate.lookup(key);
+	      if (!lstateVal.equals(val)) {
+		result.append(key.toString());
+		result.append(" = " + Values.ppr(val) + "\n");
+	      }
+	    }
+	    else {
+	      for (int i = 0; i < vlen; i++) {
+		UniqueString key = vars[i].getName();
+		IValue val = this.lookup(key);
+		IValue lstateVal = lstate.lookup(key);
+		if (!lstateVal.equals(val)) {
+		  result.append("/\\ ");
+		  result.append(key.toString());
+		  result.append(" = " + Values.ppr(val) + "\n");
+		}
+	      }
+	    }
+	    return result.toString();
+	  }
+}
diff --git a/tlatools/src/tlc2/tool/TLCState.java b/tlatools/src/tlc2/tool/TLCState.java
index c7a4e96c2ed8b934e6f23b538589c0e8e4f4429e..67c5fa72575a904595e2b71ffa02dce1d861bc52 100644
--- a/tlatools/src/tlc2/tool/TLCState.java
+++ b/tlatools/src/tlc2/tool/TLCState.java
@@ -7,50 +7,69 @@ package tlc2.tool;
 
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
 
 import tla2sany.semantic.OpDeclNode;
-import tla2sany.semantic.SemanticNode;
 import tla2sany.semantic.SymbolNode;
-import tlc2.value.Value;
-import tlc2.value.ValueInputStream;
-import tlc2.value.ValueOutputStream;
+import tlc2.output.EC;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import util.Assert;
 import util.UniqueString;
 
 public abstract class TLCState implements Cloneable, Serializable {
+  public short workerId = Short.MAX_VALUE; // Must be set to a non-negative number. Valid worker ids \in [0,Short.MAX_VALUE] and start at 0.
   public long uid = -1;   // Must be set to a non-negative number
+  // The level of an initial state is initialized with 1 to assert that
+  // TLCGet("level") in the first evaluation of the next-state relation equals 1.
+  // The successor states of initial states have level 2.  During the evaluation
+  // of the initial *predicate* - which generates the initial states - the level
+  // is defined to be zero.
+  public static final int INIT_LEVEL = 1;
+  private int level = INIT_LEVEL;
   
   // Set by subclasses. Cannot set until we know what the variables are.
   public static TLCState Empty = null;
 
   // The state variables.
-  public static OpDeclNode[] vars = null;
-  
-  public static void setVariables(OpDeclNode[] variables) 
-  {
-      vars = variables;
-      // SZ 10.04.2009: since this method is called exactly one from Spec#processSpec
-      // moved the call of UniqueString#setVariables to that place
-      
-      // UniqueString[] varNames = new UniqueString[variables.length];
-      // for (int i = 0; i < varNames.length; i++)
-      // {
-      //  varNames[i] = variables[i].getName();
-      //}
-      //UniqueString.setVariables(varNames);
-  }
+  protected static OpDeclNode[] vars = null;
 
-  public void read(ValueInputStream vis) throws IOException {
-    this.uid = vis.readLongNat();
+  public void read(IValueInputStream vis) throws IOException {
+	this.workerId = vis.readShortNat();
+	this.uid = vis.readLongNat();
+    this.level = vis.readShortNat();
+    assert this.level >= 0; // Should never overflow.
   }
   
-  public void write(ValueOutputStream vos) throws IOException {
-    vos.writeLongNat(this.uid);
-  }
+	public void write(IValueOutputStream vos) throws IOException {
+		if (this.level > Short.MAX_VALUE) {
+			// The on-disk representation of TLCState limits the diameter/level to
+			// Short.MAX_VALUE whereas the in-memory rep supports int. The underlying
+			// assumption being that state spaces with a diameter beyond 32767 AND which
+			// require TLC to swap the state queue to disk are infeasible to check anyway.
+			// However, one can easily come up with a spec that corresponds to few, very long
+			// behaviors which can be kept in memory.
+			Assert.fail(EC.TLC_TRACE_TOO_LONG, this.toString());
+		}
+		vos.writeShortNat(this.workerId);
+		vos.writeLongNat(this.uid);
+		vos.writeShortNat((short) this.level);
+	}
 
-  public abstract TLCState bind(UniqueString name, Value value, SemanticNode expr);
-  public abstract TLCState bind(SymbolNode id, Value value, SemanticNode expr);  
+  public abstract TLCState bind(UniqueString name, IValue value);
+  public abstract TLCState bind(SymbolNode id, IValue value);  
   public abstract TLCState unbind(UniqueString name);
-  public abstract Value lookup(UniqueString var);
+  /**
+   * Convenience method when performance doesn't matter.
+   */
+  public IValue lookup(String var) {
+	  return lookup(UniqueString.uniqueStringOf(var));
+  }
+  public abstract IValue lookup(UniqueString var);
   public abstract boolean containsKey(UniqueString var);
   public abstract TLCState copy();
   public abstract TLCState deepCopy();
@@ -58,9 +77,54 @@ public abstract class TLCState implements Cloneable, Serializable {
   public abstract void deepNormalize();
   public abstract long fingerPrint();
   public abstract boolean allAssigned();
+  public abstract Set<OpDeclNode> getUnassigned();
   public abstract TLCState createEmpty();
+  
+  /** 
+   * Returns a mapping of variable names to their assigned values in this state.
+   */ 
+  public final Map<UniqueString, IValue> getVals() {
+	final Map<UniqueString, IValue> valMap = new HashMap<UniqueString, IValue>();
+	for(int i = 0; i < vars.length; i++) {
+        UniqueString key = vars[i].getName();
+        IValue val = this.lookup(key);
+        valMap.put(key, val);
+    }
+    return valMap;
+  }
+  
+  public final OpDeclNode[] getVars() {
+	  return vars;
+  }
+
+  public final void setPredecessor(final TLCState predecessor) {
+	  // This method only keeps the level instead of the predecessor, because a) we
+	  // don't need the predecessor and b) keeping predecessors would mean that we
+	  // eventually have all states of the state graph in memory.
+	  if (predecessor.getLevel() == Integer.MAX_VALUE) {
+		  Assert.fail(EC.TLC_TRACE_TOO_LONG, this.toString());
+	  }
+	  this.level = predecessor.getLevel() + 1;
+  }
 
+  public final int getLevel() {
+	return this.level;  
+  }
+  
+  public final boolean isInitial() {
+	return this.level == INIT_LEVEL;
+  }
+  
   /* Returns a string representation of this state.  */
   public abstract String toString();
   public abstract String toString(TLCState lastState);
+  
+  public Object execCallable() throws Exception {
+	  // no-op - see TLAPlusExecutorState
+	  return null;
+  }
+  
+  public void setCallable(Callable<?> cl) {
+	  // no-op - see TLAPlusExecutorState
+  }
 }
diff --git a/tlatools/src/tlc2/tool/TLCStateFun.java b/tlatools/src/tlc2/tool/TLCStateFun.java
index 5c8c50c1831729e231b430f1d1b9510a56ae6a18..389ae6e07d45124130b674ca632c46d7974455da 100644
--- a/tlatools/src/tlc2/tool/TLCStateFun.java
+++ b/tlatools/src/tlc2/tool/TLCStateFun.java
@@ -6,13 +6,16 @@
 package tlc2.tool;
 
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
 
-import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.OpDeclNode;
 import tla2sany.semantic.SymbolNode;
 import tlc2.util.Context;
-import tlc2.value.Value;
-import tlc2.value.ValueInputStream;
-import tlc2.value.ValueOutputStream;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.impl.Value;
 import util.UniqueString;
 import util.WrongInvocationException;
 
@@ -24,12 +27,12 @@ import util.WrongInvocationException;
  */
 public final class TLCStateFun extends TLCState {
   private SymbolNode name;
-  private Value value;
+  private IValue value;
   private TLCStateFun next;
 
   public final static TLCState Empty = new TLCStateFun(null, null, null);
   
-  private TLCStateFun(SymbolNode name, Value value, TLCStateFun state) {
+  private TLCStateFun(SymbolNode name, IValue value, TLCStateFun state) {
     this.name = name;
     this.value = value;
     this.next = state;
@@ -37,11 +40,11 @@ public final class TLCStateFun extends TLCState {
 
   public final TLCState createEmpty() { return Empty; }
 
-  public final TLCState bind(UniqueString name, Value value, SemanticNode expr) {
+  public final TLCState bind(UniqueString name, IValue value) {
       throw new WrongInvocationException("TLCStateFun.bind: This is a TLC bug.");
   }
 
-  public final TLCState bind(SymbolNode id, Value value, SemanticNode expr) {
+  public final TLCState bind(SymbolNode id, IValue value) {
     return new TLCStateFun(id, value, this);
   }
   
@@ -49,7 +52,7 @@ public final class TLCStateFun extends TLCState {
       throw new WrongInvocationException("TLCStateFun.unbind: This is a TLC bug.");
   }
   
-  public final Value lookup(UniqueString var) {
+  public final IValue lookup(UniqueString var) {
     for (TLCStateFun cur = this; cur != Empty; cur = cur.next) {
       if (var == cur.name.getName()) return cur.value;
     }
@@ -80,6 +83,8 @@ public final class TLCStateFun extends TLCState {
   }
 
   public final boolean allAssigned() { return true; }  
+  
+  public final Set<OpDeclNode> getUnassigned() { return new HashSet<OpDeclNode>(); }
 
   public final Context addToContext(Context c) {
     Context c1 = c;
@@ -93,11 +98,11 @@ public final class TLCStateFun extends TLCState {
     return states.addElement(this);
   }
   
-  public final void read(ValueInputStream vis) throws IOException {
+  public final void read(IValueInputStream vis) throws IOException {
       throw new WrongInvocationException("TLCStateFun.read: This is a TLC bug.");
   }
 
-  public final void write(ValueOutputStream vos) throws IOException {
+  public final void write(IValueOutputStream vos) throws IOException {
       throw new WrongInvocationException("TLCStateFun.write: This is a TLC bug.");
   }
   
diff --git a/tlatools/src/tlc2/tool/TLCStateInfo.java b/tlatools/src/tlc2/tool/TLCStateInfo.java
index e76fa69aca581122db6ee9f0baba32e153e13901..b2dc8216438a6335a7de146f98cc30acf389dff7 100644
--- a/tlatools/src/tlc2/tool/TLCStateInfo.java
+++ b/tlatools/src/tlc2/tool/TLCStateInfo.java
@@ -10,17 +10,49 @@ public class TLCStateInfo {
   public long stateNumber;
   public TLCState state;
   public Object info;
+  public Long fp;
+
+	public TLCStateInfo(TLCState initialState) {
+		this.state = initialState;
+		this.info = "<Initial predicate>";
+		this.stateNumber = 1;
+		this.fp = initialState.fingerPrint();
+	}
 
   public TLCStateInfo(TLCState s, Object info) {
     this.state = s;
     this.info = info;
   }
 
+  public TLCStateInfo(TLCState s, String info, int stateNum, long fp) {
+	  this(s, info);
+	  stateNumber = stateNum;
+	  this.fp = fp;
+  }
+
   public final long fingerPrint() {
-    return this.state.fingerPrint();
+	  if (fp == null) {
+		  fp = this.state.fingerPrint();
+	  }
+	  return fp.longValue();
   }
 
   public final String toString() {
     return this.state.toString();
   }
+  
+  public boolean equals(Object other) {
+	  if (other instanceof TLCStateInfo) {
+		  TLCStateInfo sinfo = (TLCStateInfo) other;
+		  return this.state.equals(sinfo.state);
+	  } else if (other instanceof TLCState) {
+		  TLCState state = (TLCState) other;
+		  return this.state.equals(state);
+	  }
+	  return false;
+  }
+
+  public int hashCode() {
+	  return this.state.hashCode();
+  }
 }
diff --git a/tlatools/src/tlc2/tool/TLCStateMut.java b/tlatools/src/tlc2/tool/TLCStateMut.java
index 985f3e638c6b0d0ba45d1eb755bb1b93b0661cdf..acf1645bf6f4767bb7b4877ba892f2f93a389cb0 100644
--- a/tlatools/src/tlc2/tool/TLCStateMut.java
+++ b/tlatools/src/tlc2/tool/TLCStateMut.java
@@ -7,16 +7,21 @@ package tlc2.tool;
 
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
 
+import tla2sany.semantic.OpDeclNode;
 import tla2sany.semantic.SemanticNode;
 import tla2sany.semantic.SymbolNode;
 import tlc2.TLCGlobals;
 import tlc2.util.Context;
 import tlc2.util.FP64;
-import tlc2.value.MVPerm;
-import tlc2.value.Value;
-import tlc2.value.ValueInputStream;
-import tlc2.value.ValueOutputStream;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
 import util.UniqueString;
 import util.WrongInvocationException;
 
@@ -29,8 +34,8 @@ import util.WrongInvocationException;
  * The viewMap was added by Rajeev Joshi.
  */
 public final class TLCStateMut extends TLCState implements Cloneable, Serializable {
-  private Value values[];
-  private static Tool mytool = null;
+  private IValue values[];
+  private static ITool mytool = null;
 
   /**
    * If non-null, viewMap denotes the function to be applied to
@@ -42,23 +47,39 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
    * If non-null, perms denotes the set of permutations under the
    * symmetry assumption.
    */
-  private static MVPerm[] perms = null;
+  private static IMVPerm[] perms = null;
 
-  private TLCStateMut(Value[] vals) { this.values = vals; }
+  private TLCStateMut(IValue[] vals) { this.values = vals; }
+  
+  public static void setVariables(OpDeclNode[] variables) 
+  {
+      vars = variables;
+      IValue[] vals = new IValue[vars.length];
+      Empty = new TLCStateMut(vals);
+
+      // SZ 10.04.2009: since this method is called exactly one from Spec#processSpec
+      // moved the call of UniqueString#setVariables to that place
+      
+      // UniqueString[] varNames = new UniqueString[variables.length];
+      // for (int i = 0; i < varNames.length; i++)
+      // {
+      //  varNames[i] = variables[i].getName();
+      //}
+      //UniqueString.setVariables(varNames);
+  }
 
-  public static void init(Tool tool) {
+  public static void setTool(ITool tool) {
     mytool = tool;
-    Value[] vals = new Value[vars.length];
-    Empty = new TLCStateMut(vals);
     viewMap = tool.getViewSpec();
     perms = tool.getSymmetryPerms();
   }
 
   public final TLCState createEmpty() {
-    Value[] vals = new Value[vars.length];
+	  IValue[] vals = new IValue[vars.length];
     return new TLCStateMut(vals);
   }
 
+  //TODO equals without hashcode!
   public final boolean equals(Object obj) {
     if (obj instanceof TLCStateMut) {
       TLCStateMut state = (TLCStateMut)obj;
@@ -76,13 +97,14 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
     return false;
   }
   
-  public final TLCState bind(UniqueString name, Value value, SemanticNode expr) {
+  public final TLCState bind(UniqueString name, IValue value) {
+	  // Note, tla2sany.semantic.OpApplNode.toString(Value) relies on this ordering.
     int loc = name.getVarLoc();
     this.values[loc] = value;
     return this;
   }
 
-  public final TLCState bind(SymbolNode id, Value value, SemanticNode expr) {
+  public final TLCState bind(SymbolNode id, IValue value) {
     throw new WrongInvocationException("TLCStateMut.bind: This is a TLC bug.");
   }
   
@@ -92,7 +114,7 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
     return this;
   }
 
-  public final Value lookup(UniqueString var) {
+  public final IValue lookup(UniqueString var) {
     int loc = var.getVarLoc();
     if (loc < 0) return null;
     return this.values[loc];
@@ -104,7 +126,7 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
 
   public final TLCState copy() {
     int len = this.values.length;
-    Value[] vals = new Value[len];
+    IValue[] vals = new IValue[len];
     for (int i = 0; i < len; i++) {
       vals[i] = this.values[i];
     }
@@ -113,9 +135,9 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
 
   public final TLCState deepCopy() {
     int len = this.values.length;
-    Value[] vals = new Value[len];
+    IValue[] vals = new IValue[len];
     for (int i = 0; i < len; i++) {
-      Value val = this.values[i];
+      IValue val = this.values[i];
       if (val != null) {
 	vals[i] = val.deepCopy();
       }
@@ -129,7 +151,7 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
   
   public final void deepNormalize() {
     for (int i = 0; i < this.values.length; i++) {
-      Value val = this.values[i];
+      IValue val = this.values[i];
       if (val != null) {
 	val.deepNormalize();
       }
@@ -145,59 +167,108 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
    * via the state queue. They have to be normalized before adding to
    * the state queue.  We do that here.
    */
-  public final long fingerPrint() {
-    int sz = this.values.length;
+	public final long fingerPrint() {
+		int sz = this.values.length;
 
-    Value[] minVals = this.values;
-    if (perms != null) {
-      Value[] vals = new Value[sz];
-      // Find the "smallest" state under the symmetry permutations:
-      for (int i = 0; i < perms.length; i++) {
-	int cmp = 0;
-	for (int j = 0; j < sz; j++) {
-	  vals[j] = this.values[j].permute(perms[i]);
-	  if (cmp == 0) {
-	    cmp = vals[j].compareTo(minVals[j]);
-	  }
+		// TLC supports symmetry reduction. Symmetry reduction works by defining classes
+		// of symmetrically equivalent states for which TLC only checks a
+		// single representative of the equivalence class (orbit). E.g. in a two
+		// process mutual exclusion problem, the process ids are - most of the time -
+		// not relevant with regards to mutual exclusion: We don't care if process A or
+		// B is in the critical section as long as only a single process is in CS. Thus
+		// two states are symmetric that only differ in the process id variable value.
+		// Symmetry can also be observed graphically in the state graph s.t. subgraphs
+		// are isomorphic (Graph Isomorphism). Instead of enumerating the complete state
+		// graph, TLC enumerates one of the isomorphic subgraphs whose state correspond
+		// to the representatives. With respect to the corresponding Kripke structure M,
+		// the resulting Kripke M' is called the "quotient structure" (see "Exploiting
+		// Symmetry in Temporal Logic Model Checking" by Clarke et al).
+		// 
+		// The definition of equivalence classes (orbits) is provided manually by the
+		// user at startup by defining 1 to n symmetry sets. Thus TLC has to find
+		// representative at runtime only which happens below. Given any state s, TLC
+		// evaluates rep(s) to find the lexicographically smallest state ss = rep(s)
+		// with regards to the variable values. The state ss is then fingerprint instead
+		// of s.
+		//
+		// Evaluating rep(s) - to reduce s to ss - requires to apply all permutations in
+		// the group this.perms (derived from the user-defined orbit). This is known as
+		// the constructive orbit problem and is NP-hard. The loop has O(|perms| * |this.values|)
+		// with |prems| = |symmetry set 1|! * |symmetry set 2|! * ... * |symmetry set n|. 
+        //		
+		// minVals is what is used to calculate/generate the fingerprint below.
+		// If this state is not the lexicographically smallest state ss, its current
+		// minVals will be replaced temporarily with the values of ss for the
+		// calculation of the fingerprint.
+		IValue[] minVals = this.values;
+		if (perms != null) {
+			IValue[] vals = new IValue[sz];
+			// The following for loop converges to the smallest state ss under symmetry by
+			// looping over all permutations applying each. If the outcome turns out to be
+			// lexicographically smaller than the currently smallest, it replaces the
+			// current smallest. Once all permutations (perms) have been processed, we know
+			// we have found the smallest state.
+			NEXT_PERM: for (int i = 0; i < perms.length; i++) {
+				int cmp = 0;
+				// For each value in values succinctly permute the current value
+				// and compare it to its corresponding minValue in minVals.
+				for (int j = 0; j < sz; j++) {
+					vals[j] = this.values[j].permute(perms[i]);
+					if (cmp == 0) {
+						// Only compare unless an earlier compare has found a
+						// difference already (if a difference has been found
+						// earlier, still permute the remaining values of the
+						// state to fully permute all state values).
+						cmp = vals[j].compareTo(minVals[j]);
+						if (cmp > 0) {
+							// When cmp evaluates to >0, all subsequent
+							// applications of perms[i] for the remaining values
+							// won't make the resulting vals[] smaller than
+							// minVals. Thus, exit preemptively from the loop
+							// over vals. This works because perms is the cross
+							// product of all symmetry sets.
+							continue NEXT_PERM;
+						}
+					}
+				}
+				// cmp < 0 means the current state is part of a symmetry
+				// permutation set/group and not the "smallest" one.
+				if (cmp < 0) {
+					if (minVals == this.values) {
+						minVals = vals;
+						vals = new IValue[sz];
+					} else {
+						IValue[] temp = minVals;
+						minVals = vals;
+						vals = temp;
+					}
+				}
+			}
+		}
+		// Fingerprint the state:
+		long fp = FP64.New();
+		if (viewMap == null) {
+			for (int i = 0; i < sz; i++) {
+				fp = minVals[i].fingerPrint(fp);
+			}
+			if (this.values != minVals) {
+				for (int i = 0; i < sz; i++) {
+					this.values[i].deepNormalize();
+				}
+			}
+		} else {
+			for (int i = 0; i < sz; i++) {
+				this.values[i].deepNormalize();
+			}
+			TLCStateMut state = this;
+			if (minVals != this.values) {
+				state = new TLCStateMut(minVals);
+			}
+			IValue val = mytool.eval(viewMap, Context.Empty, state);
+			fp = val.fingerPrint(fp);
+		}
+		return fp;
 	}
-	if (cmp < 0) {
-	  if (minVals == this.values) {
-	    minVals = vals;
-	    vals = new Value[sz];
-	  }
-	  else {
-	    Value[] temp = minVals;
-	    minVals = vals;
-	    vals = temp;
-	  }
-	}
-      }
-    }
-    // Fingerprint the state:
-    long fp = FP64.New();      
-    if (viewMap == null) {
-      for (int i = 0; i < sz; i++) {
-	fp = minVals[i].fingerPrint(fp);
-      }
-      if (this.values != minVals) {
-	for (int i = 0; i < sz; i++) {
-	  this.values[i].deepNormalize();
-	}
-      }
-    }
-    else {
-      for (int i = 0; i < sz; i++) {
-	this.values[i].deepNormalize();
-      }
-      TLCStateMut state = this;
-      if (minVals != this.values) {
-	state = new TLCStateMut(minVals);
-      }
-      Value val = mytool.eval(viewMap, Context.Empty, state);
-      fp = val.fingerPrint(fp);
-    }
-    return fp;
-  }
 
   public final boolean allAssigned() {
     int len = this.values.length;    
@@ -206,8 +277,25 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
     }
     return true;
   }
+  
+	public final Set<OpDeclNode> getUnassigned() {
+		// Return sorted set (lexicographical).
+		final Set<OpDeclNode> unassignedVars = new TreeSet<OpDeclNode>(new Comparator<OpDeclNode>() {
+			@Override
+			public int compare(OpDeclNode o1, OpDeclNode o2) {
+				return o1.getName().toString().compareTo(o2.getName().toString());
+			}
+		});
+		int len = this.values.length;
+		for (int i = 0; i < len; i++) {
+			if (values[i] == null) {
+				unassignedVars.add(vars[i]);
+			}
+		}
+		return unassignedVars;
+	}
 
-  public final void read(ValueInputStream vis) throws IOException {
+  public final void read(IValueInputStream vis) throws IOException {
     super.read(vis);
     int len = this.values.length;
     for (int i = 0; i < len; i++) {
@@ -215,37 +303,39 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
     }
   }
 
-  public final void write(ValueOutputStream vos) throws IOException {
+  public final void write(IValueOutputStream vos) throws IOException {
     super.write(vos);
     int len = this.values.length;
     for (int i = 0; i < len; i++) {
-      vos.write(this.values[i]);
+    	this.values[i].write(vos);
     }
   }
   
   /* Returns a string representation of this state.  */
   public final String toString() {
     if (TLCGlobals.useView && viewMap != null) {
-      Value val = mytool.eval(viewMap, Context.Empty, this);
-      return Value.ppr(val.toString());
+      IValue val = mytool.eval(viewMap, Context.Empty, this);
+      return viewMap.toString(val);
     }
     StringBuffer result = new StringBuffer();
     int vlen = vars.length;
     if (vlen == 1) {
       UniqueString key = vars[0].getName();
-      Value val = this.lookup(key);
-      String val_str = (val == null) ? "null" : Value.ppr(val.toString());
+      IValue val = this.lookup(key);
       result.append(key.toString());
-      result.append(" = " + val_str + "\n");
+      result.append(" = ");
+      result.append(Values.ppr(val));
+      result.append("\n");
     }
     else {
       for (int i = 0; i < vlen; i++) {
 	UniqueString key = vars[i].getName();
-	Value val = this.lookup(key);
-	String val_str = (val == null) ? "null" : Value.ppr(val.toString());
+	IValue val = this.lookup(key);
 	result.append("/\\ ");
 	result.append(key.toString());
-	result.append(" = " + val_str + "\n");
+    result.append(" = ");
+    result.append(Values.ppr(val));
+    result.append("\n");
       }
     }
     return result.toString();
@@ -259,24 +349,22 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab
     int vlen = vars.length;
     if (vlen == 1) {
       UniqueString key = vars[0].getName();
-      Value val = this.lookup(key);
-      Value lstateVal = lstate.lookup(key);
+      IValue val = this.lookup(key);
+      IValue lstateVal = lstate.lookup(key);
       if (!lstateVal.equals(val)) {
-	String val_str = (val == null) ? "null" : Value.ppr(val.toString());
 	result.append(key.toString());
-	result.append(" = " + val_str + "\n");
+	result.append(" = " + Values.ppr(val) + "\n");
       }
     }
     else {
       for (int i = 0; i < vlen; i++) {
 	UniqueString key = vars[i].getName();
-	Value val = this.lookup(key);
-	Value lstateVal = lstate.lookup(key);
+	IValue val = this.lookup(key);
+	IValue lstateVal = lstate.lookup(key);
 	if (!lstateVal.equals(val)) {
-	  String val_str = (val == null) ? "null" : Value.ppr(val.toString());
 	  result.append("/\\ ");
 	  result.append(key.toString());
-	  result.append(" = " + val_str + "\n");
+	  result.append(" = " + Values.ppr(val) + "\n");
 	}
       }
     }
diff --git a/tlatools/src/tlc2/tool/TLCStateMutSource.java b/tlatools/src/tlc2/tool/TLCStateMutSource.java
index 295fcd2e492f86d8688e244775699f73aa259d5e..8f9a2a18496afbccad7d89e7618bdf59d9a48a6e 100644
--- a/tlatools/src/tlc2/tool/TLCStateMutSource.java
+++ b/tlatools/src/tlc2/tool/TLCStateMutSource.java
@@ -1,319 +1,344 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 15:30:02 PST by lamport
-//      modified on Fri Aug 24 15:08:38 PDT 2001 by yuanyu
-
-package tlc2.tool;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-
-import tla2sany.semantic.SemanticNode;
-import tla2sany.semantic.SymbolNode;
-import tlc2.TLCGlobals;
-import tlc2.util.Context;
-import tlc2.util.FP64;
-import tlc2.util.ObjLongTable;
-import tlc2.value.MVPerm;
-import tlc2.value.Value;
-import tlc2.value.ValueInputStream;
-import tlc2.value.ValueOutputStream;
-import util.UniqueString;
-import util.WrongInvocationException;
-
-/**
- * This class represents a TLA+ state, which simply is an assignment
- * of explicit values to all the global variables.  This is the
- * mutable version, which means that in-place updates are used for
- * improved performance and reduced allocation.
- *
- * The viewMap was added by Rajeev Joshi.
- */
-public final class TLCStateMutSource extends TLCState
-implements Cloneable, Serializable {
-  private Value[] values;
-  private SemanticNode[] asts;
-  private static Tool mytool = null;
-
-  /**
-   * If non-null, viewMap denotes the function to be applied to
-   * a state before its fingerprint is computed.
-   */
-  private static SemanticNode viewMap = null;
-
-  /**
-   * If non-null, perms denotes the set of permutations under
-   * the symmetry assumption.
-   */
-  private static MVPerm[] perms = null;
-
-  private TLCStateMutSource(Value[] vals, SemanticNode[] asts) {
-    this.values = vals;
-    this.asts = asts;
-  }
-
-  public static void init(Tool tool) {
-    mytool = tool;
-    Value[] vals = new Value[vars.length];
-    SemanticNode[] snodes = new SemanticNode[vars.length];
-    Empty = new TLCStateMutSource(vals, snodes);
-    viewMap = tool.getViewSpec();
-    perms = tool.getSymmetryPerms();
-  }
-
-  public final TLCState createEmpty() {
-    int sz = vars.length;
-    return new TLCStateMutSource(new Value[sz], new SemanticNode[sz]);
-  }
-
-  public final boolean equals(Object obj) {
-    if (obj instanceof TLCStateMutSource) {
-      TLCStateMutSource state = (TLCStateMutSource)obj;
-      for (int i = 0; i < this.values.length; i++) {
-	if (this.values[i] == null) {
-	  if (state.values[i] != null) return false;
-	}
-	else if (state.values[i] == null ||
-		 !this.values[i].equals(state.values[i])) {
-	  return false;
-	}
-      }
-      return true;
-    }
-    return false;
-  }
-
-  public final TLCState bind(UniqueString name, Value value, SemanticNode ast) {
-    int loc = name.getVarLoc();
-    this.values[loc] = value;
-    if (this.asts != null) {
-      this.asts[loc] = ast;
-    }
-    return this;
-  }
-
-  public final TLCState bind(SymbolNode id, Value value, SemanticNode expr) {
-    throw new WrongInvocationException("TLCStateMutSource.bind: This is a TLC bug.");
-  }
-  
-  public final TLCState unbind(UniqueString name) {
-    int loc = name.getVarLoc();
-    this.values[loc] = null;
-    this.asts[loc] = null;
-    return this;
-  }
-  
-  public final Value lookup(UniqueString var) {
-    int loc = var.getVarLoc();
-    if (loc < 0) return null;    
-    return this.values[loc];
-  }
-
-  public final boolean containsKey(UniqueString var) {
-    return (this.lookup(var) != null);
-  }
-
-  public final TLCState copy() {
-    int len = this.values.length;
-    Value[] vals = new Value[len];
-    SemanticNode[] astClone = new SemanticNode[len];
-    for (int i = 0; i < len; i++) {
-      vals[i] = this.values[i];
-      astClone[i] = this.asts[i];
-    }
-    return new TLCStateMutSource(vals, astClone);
-  }
-
-  public final TLCState deepCopy() {
-    int len = this.values.length;
-    Value[] vals = new Value[len];
-    SemanticNode[] astClone = new SemanticNode[len];
-    for (int i = 0; i < len; i++) {
-      Value val = this.values[i];
-      if (val != null) {
-	vals[i] = val.deepCopy();
-	astClone[i] = this.asts[i];
-      }
-    }
-    return new TLCStateMutSource(vals, astClone);
-  }
-
-  public final StateVec addToVec(StateVec states) {
-    return states.addElement(this.copy());
-  }
-  
-  public final void deepNormalize() {
-    for (int i = 0; i < this.values.length; i++) {
-      Value val = this.values[i];
-      if (val != null) {
-	val.deepNormalize();
-      }
-    }
-  }
-
-  private final void readObject(ObjectInputStream ois)
-  throws IOException, ClassNotFoundException {
-    this.values = (Value[])ois.readObject();
-    this.asts = null;
-  }
-
-  private final void writeObject(ObjectOutputStream oos) throws IOException {
-    oos.writeObject(this.values);
-  }
-  
-  /**
-   * This method returns the fingerprint of this state. We fingerprint
-   * the values in the state according to the order given by vars.
-   * This guarantees the same state has the same fingerprint.
-   *
-   * Since the values in this state can be shared by multiple threads
-   * via the state queue. They have to be normalized before adding to
-   * the state queue.  We do that here.   
-   */
-  public final long fingerPrint() {
-    int sz = this.values.length;
-
-    Value[] minVals = this.values;
-    if (perms != null) {
-      Value[] vals = new Value[sz];
-      // Find the "smallest" state under the symmetry permutations:
-      for (int i = 0; i < perms.length; i++) {
-	int cmp = 0;
-	for (int j = 0; j < sz; j++) {
-	  vals[j] = this.values[j].permute(perms[i]);
-	  if (cmp == 0) {
-	    cmp = vals[j].compareTo(minVals[j]);
-	  }
-	}
-	if (cmp < 0) {
-	  if (minVals == this.values) {
-	    minVals = vals;
-	    vals = new Value[sz];
-	  }
-	  else {
-	    Value[] temp = minVals;
-	    minVals = vals;
-	    vals = temp;
-	  }
-	}
-      }
-    }
-    // Fingerprint the state:
-    long fp = FP64.New();      
-    if (viewMap == null) {
-      for (int i = 0; i < sz; i++) {
-	fp = minVals[i].fingerPrint(fp);
-      }
-      if (this.values != minVals) {
-	for (int i = 0; i < sz; i++) {
-	  this.values[i].deepNormalize();
-	}
-      }
-    }
-    else {
-      for (int i = 0; i < sz; i++) {
-	this.values[i].deepNormalize();
-      }
-      TLCStateMutSource state = this;
-      if (minVals != this.values) {
-	state = new TLCStateMutSource(minVals, this.asts);
-      }
-      Value val = mytool.eval(viewMap, Context.Empty, state);
-      fp = val.fingerPrint(fp);
-    }
-    return fp;
-  }
-
-  public final void addCounts(ObjLongTable counts) {
-    for (int i = 0; i < this.asts.length; i++) {
-      counts.add(this.asts[i], 1);
-    }      
-  }
-
-  public final boolean allAssigned() {
-    int len = this.values.length;    
-    for (int i = 0; i < len; i++) {
-      if (values[i] == null) return false;
-    }
-    return true;
-  }
-
-  public final void read(ValueInputStream vis) throws IOException {
-    super.read(vis);    
-    int len = this.values.length;
-    for (int i = 0; i < len; i++) {
-      this.values[i] = vis.read();
-    }
-  }
-
-  public final void write(ValueOutputStream vos) throws IOException {
-    super.write(vos);    
-    int len = this.values.length;
-    for (int i = 0; i < len; i++) {
-      vos.write(this.values[i]);
-    }
-  }
-  
-  /* Returns a string representation of this state.  */
-  public final String toString() {
-    if (TLCGlobals.useView && viewMap != null) {
-      Value val = mytool.eval(viewMap, Context.Empty, this);
-      return Value.ppr(val.toString());
-    }
-    StringBuffer result = new StringBuffer();
-    int vlen = vars.length;
-    if (vlen == 1) {
-      UniqueString key = vars[0].getName();
-      Value val = lookup(key);
-      String val_str = (val == null) ? "null" : Value.ppr(val.toString());
-      result.append(key.toString());
-      result.append(" = " + val_str + "\n");
-    }
-    else {
-      for (int i = 0; i < vlen; i++) {
-	UniqueString key = vars[i].getName();
-	Value val = lookup(key);
-	String val_str = (val == null) ? "null" : Value.ppr(val.toString());
-	result.append("/\\ ");
-	result.append(key.toString());
-	result.append(" = " + val_str + "\n");
-      }
-    }
-    return result.toString();
-  }
-
-  /* Returns a string representation of this state.  */
-  public final String toString(TLCState lastState) {
-    StringBuffer result = new StringBuffer();
-    TLCStateMutSource lstate = (TLCStateMutSource)lastState;
-
-    int vlen = vars.length;
-    if (vlen == 1) {
-      UniqueString key = vars[0].getName();
-      Value val = this.lookup(key);
-      Value lstateVal = lstate.lookup(key);
-      if (val == null || !val.equals(lstateVal)) {
-	String val_str = (val == null) ? "null" : Value.ppr(val.toString());
-	result.append(key.toString());
-	result.append(" = " + val_str + "\n");
-      }
-    }
-    else {
-      for (int i = 0; i < vlen; i++) {
-	UniqueString key = vars[i].getName();
-	Value val = this.lookup(key);
-	Value lstateVal = lstate.lookup(key);
-	if (val == null || !val.equals(lstateVal)) {
-	  String val_str = (val == null) ? "null" : Value.ppr(val.toString());
-	  result.append("/\\ ");
-	  result.append(key.toString());
-	  result.append(" = " + val_str + "\n");
-	}
-      }
-    }
-    return result.toString();
-  }
-
-}
+//// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+//// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+//// Last modified on Mon 30 Apr 2007 at 15:30:02 PST by lamport
+////      modified on Fri Aug 24 15:08:38 PDT 2001 by yuanyu
+//
+//package tlc2.tool;
+//
+//import java.io.IOException;
+//import java.io.ObjectInputStream;
+//import java.io.ObjectOutputStream;
+//import java.io.Serializable;
+//import java.util.Comparator;
+//import java.util.Set;
+//import java.util.TreeSet;
+//
+//import tla2sany.semantic.OpDeclNode;
+//import tla2sany.semantic.SemanticNode;
+//import tla2sany.semantic.SymbolNode;
+//import tlc2.TLCGlobals;
+//import tlc2.tool.coverage.CostModel;
+//import tlc2.util.Context;
+//import tlc2.util.FP64;
+//import tlc2.util.ObjLongTable;
+//import tlc2.value.MVPerm;
+//import tlc2.value.Value;
+//import tlc2.value.ValueInputStream;
+//import tlc2.value.ValueOutputStream;
+//import util.UniqueString;
+//import util.WrongInvocationException;
+//
+///**
+// * This class represents a TLA+ state, which simply is an assignment
+// * of explicit values to all the global variables.  This is the
+// * mutable version, which means that in-place updates are used for
+// * improved performance and reduced allocation.
+// *
+// * The viewMap was added by Rajeev Joshi.
+// */
+//public final class TLCStateMutSource extends TLCState
+//implements Cloneable, Serializable {
+//  private Value[] values;
+//  private SemanticNode[] asts;
+//  private static Tool mytool = null;
+//
+//  /**
+//   * If non-null, viewMap denotes the function to be applied to
+//   * a state before its fingerprint is computed.
+//   */
+//  private static SemanticNode viewMap = null;
+//
+//  /**
+//   * If non-null, perms denotes the set of permutations under
+//   * the symmetry assumption.
+//   */
+//  private static MVPerm[] perms = null;
+//
+//  private TLCStateMutSource(Value[] vals, SemanticNode[] asts) {
+//    this.values = vals;
+//    this.asts = asts;
+//  }
+//
+//  public static void init(Tool tool) {
+//    mytool = tool;
+//    Value[] vals = new Value[vars.length];
+//    SemanticNode[] snodes = new SemanticNode[vars.length];
+//    Empty = new TLCStateMutSource(vals, snodes);
+//    viewMap = tool.getViewSpec();
+//    perms = tool.getSymmetryPerms();
+//  }
+//
+//  public final TLCState createEmpty() {
+//    int sz = vars.length;
+//    return new TLCStateMutSource(new Value[sz], new SemanticNode[sz]);
+//  }
+//
+//  public final boolean equals(Object obj) {
+//    if (obj instanceof TLCStateMutSource) {
+//      TLCStateMutSource state = (TLCStateMutSource)obj;
+//      for (int i = 0; i < this.values.length; i++) {
+//	if (this.values[i] == null) {
+//	  if (state.values[i] != null) return false;
+//	}
+//	else if (state.values[i] == null ||
+//		 !this.values[i].equals(state.values[i])) {
+//	  return false;
+//	}
+//      }
+//      return true;
+//    }
+//    return false;
+//  }
+//
+//  public final TLCState bind(UniqueString name, Value value, SemanticNode ast, CostModel cm) {
+//    int loc = name.getVarLoc();
+//    this.values[loc] = value;
+//    if (this.asts != null) {
+//      this.asts[loc] = ast;
+//    }
+//    return this;
+//  }
+//
+//  public final TLCState bind(SymbolNode id, Value value, SemanticNode expr, CostModel cm) {
+//    throw new WrongInvocationException("TLCStateMutSource.bind: This is a TLC bug.");
+//  }
+//  
+//  public final TLCState unbind(UniqueString name) {
+//    int loc = name.getVarLoc();
+//    this.values[loc] = null;
+//    this.asts[loc] = null;
+//    return this;
+//  }
+//  
+//  public final Value lookup(UniqueString var) {
+//    int loc = var.getVarLoc();
+//    if (loc < 0) return null;    
+//    return this.values[loc];
+//  }
+//
+//  public final boolean containsKey(UniqueString var) {
+//    return (this.lookup(var) != null);
+//  }
+//
+//  public final TLCState copy() {
+//    int len = this.values.length;
+//    Value[] vals = new Value[len];
+//    SemanticNode[] astClone = new SemanticNode[len];
+//    for (int i = 0; i < len; i++) {
+//      vals[i] = this.values[i];
+//      astClone[i] = this.asts[i];
+//    }
+//    return new TLCStateMutSource(vals, astClone);
+//  }
+//
+//  public final TLCState deepCopy() {
+//    int len = this.values.length;
+//    Value[] vals = new Value[len];
+//    SemanticNode[] astClone = new SemanticNode[len];
+//    for (int i = 0; i < len; i++) {
+//      Value val = this.values[i];
+//      if (val != null) {
+//	vals[i] = val.deepCopy();
+//	astClone[i] = this.asts[i];
+//      }
+//    }
+//    return new TLCStateMutSource(vals, astClone);
+//  }
+//
+//  public final StateVec addToVec(StateVec states) {
+//    return states.addElement(this.copy());
+//  }
+//  
+//  public final void deepNormalize() {
+//    for (int i = 0; i < this.values.length; i++) {
+//      Value val = this.values[i];
+//      if (val != null) {
+//	val.deepNormalize();
+//      }
+//    }
+//  }
+//
+//  private final void readObject(ObjectInputStream ois)
+//  throws IOException, ClassNotFoundException {
+//    this.values = (Value[])ois.readObject();
+//    this.asts = null;
+//  }
+//
+//  private final void writeObject(ObjectOutputStream oos) throws IOException {
+//    oos.writeObject(this.values);
+//  }
+//  
+//  /**
+//   * This method returns the fingerprint of this state. We fingerprint
+//   * the values in the state according to the order given by vars.
+//   * This guarantees the same state has the same fingerprint.
+//   *
+//   * Since the values in this state can be shared by multiple threads
+//   * via the state queue. They have to be normalized before adding to
+//   * the state queue.  We do that here.   
+//   * 
+//   * @see TLCStateMut#fingerPrint() for a commented version of this
+//   *      implementation.
+//   */
+//  public final long fingerPrint() {
+//    int sz = this.values.length;
+//
+//    Value[] minVals = this.values;
+//    if (perms != null) {
+//      Value[] vals = new Value[sz];
+//      // Find the "smallest" state under the symmetry permutations:
+//      for (int i = 0; i < perms.length; i++) {
+//	int cmp = 0;
+//	for (int j = 0; j < sz; j++) {
+//	  vals[j] = this.values[j].permute(perms[i]);
+//	  if (cmp == 0) {
+//	    cmp = vals[j].compareTo(minVals[j]);
+//	  }
+//	}
+//	if (cmp < 0) {
+//	  if (minVals == this.values) {
+//	    minVals = vals;
+//	    vals = new Value[sz];
+//	  }
+//	  else {
+//	    Value[] temp = minVals;
+//	    minVals = vals;
+//	    vals = temp;
+//	  }
+//	}
+//      }
+//    }
+//    // Fingerprint the state:
+//    long fp = FP64.New();      
+//    if (viewMap == null) {
+//      for (int i = 0; i < sz; i++) {
+//	fp = minVals[i].fingerPrint(fp);
+//      }
+//      if (this.values != minVals) {
+//	for (int i = 0; i < sz; i++) {
+//	  this.values[i].deepNormalize();
+//	}
+//      }
+//    }
+//    else {
+//      for (int i = 0; i < sz; i++) {
+//	this.values[i].deepNormalize();
+//      }
+//      TLCStateMutSource state = this;
+//      if (minVals != this.values) {
+//	state = new TLCStateMutSource(minVals, this.asts);
+//      }
+//      Value val = mytool.eval(viewMap, Context.Empty, state);
+//      fp = val.fingerPrint(fp);
+//    }
+//    return fp;
+//  }
+//
+//  public final void addCounts(final ObjLongTable<SemanticNode> counts) {
+//    for (int i = 0; i < this.asts.length; i++) {
+//      counts.add(this.asts[i], 1);
+//    }      
+//  }
+//
+//  public final boolean allAssigned() {
+//    int len = this.values.length;    
+//    for (int i = 0; i < len; i++) {
+//      if (values[i] == null) return false;
+//    }
+//    return true;
+//  }
+//
+//	public final Set<OpDeclNode> getUnassigned() {
+//		// Return sorted set (lexicographical).
+//		final Set<OpDeclNode> unassignedVars = new TreeSet<OpDeclNode>(new Comparator<OpDeclNode>() {
+//			@Override
+//			public int compare(OpDeclNode o1, OpDeclNode o2) {
+//				return o1.getName().toString().compareTo(o2.getName().toString());
+//			}
+//		});
+//		int len = this.values.length;
+//		for (int i = 0; i < len; i++) {
+//			if (values[i] == null) {
+//				unassignedVars.add(vars[i]);
+//			}
+//		}
+//		return unassignedVars;
+//	}
+//
+//  public final void read(ValueInputStream vis) throws IOException {
+//    super.read(vis);    
+//    int len = this.values.length;
+//    for (int i = 0; i < len; i++) {
+//      this.values[i] = vis.read();
+//    }
+//  }
+//
+//  public final void write(ValueOutputStream vos) throws IOException {
+//    super.write(vos);    
+//    int len = this.values.length;
+//    for (int i = 0; i < len; i++) {
+//      vos.write(this.values[i]);
+//    }
+//  }
+//  
+//  /* Returns a string representation of this state.  */
+//  public final String toString() {
+//    if (TLCGlobals.useView && viewMap != null) {
+//      Value val = mytool.eval(viewMap, Context.Empty, this);
+//      return viewMap.toString(val);
+//    }
+//    StringBuffer result = new StringBuffer();
+//    int vlen = vars.length;
+//    if (vlen == 1) {
+//      UniqueString key = vars[0].getName();
+//      Value val = lookup(key);
+//      result.append(key.toString());
+//      result.append(" = ");
+//      result.append(Value.ppr(val));
+//      result.append("\n");
+//    }
+//    else {
+//      for (int i = 0; i < vlen; i++) {
+//	UniqueString key = vars[i].getName();
+//	Value val = lookup(key);
+//	result.append("/\\ ");
+//	result.append(key.toString());
+//    result.append(" = ");
+//    result.append(Value.ppr(val));
+//    result.append("\n");
+//      }
+//    }
+//    return result.toString();
+//  }
+//
+//  /* Returns a string representation of this state.  */
+//  public final String toString(TLCState lastState) {
+//    StringBuffer result = new StringBuffer();
+//    TLCStateMutSource lstate = (TLCStateMutSource)lastState;
+//
+//    int vlen = vars.length;
+//    if (vlen == 1) {
+//      UniqueString key = vars[0].getName();
+//      Value val = this.lookup(key);
+//      Value lstateVal = lstate.lookup(key);
+//      if (val == null || !val.equals(lstateVal)) {
+//	result.append(key.toString());
+//	result.append(" = " + Value.ppr(val) + "\n");
+//      }
+//    }
+//    else {
+//      for (int i = 0; i < vlen; i++) {
+//	UniqueString key = vars[i].getName();
+//	Value val = this.lookup(key);
+//	Value lstateVal = lstate.lookup(key);
+//	if (val == null || !val.equals(lstateVal)) {
+//	  result.append("/\\ ");
+//	  result.append(key.toString());
+//	  result.append(" = " + Value.ppr(val) + "\n");
+//	}
+//      }
+//    }
+//    return result.toString();
+//  }
+//
+//}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/TLCTrace.java b/tlatools/src/tlc2/tool/TLCTrace.java
index 2f05a1842eacd7b43192850e3295b70367a8b315..157e37f27319bc9d116f4dce0cccb5e5ad491c78 100644
--- a/tlatools/src/tlc2/tool/TLCTrace.java
+++ b/tlatools/src/tlc2/tool/TLCTrace.java
@@ -10,155 +10,187 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
 import tlc2.output.EC;
 import tlc2.output.MP;
-import tlc2.output.OutputCollector;
 import tlc2.output.StatePrinter;
 import tlc2.util.BufferedRandomAccessFile;
 import tlc2.util.LongVec;
+import tlc2.value.RandomEnumerableValues;
 import util.FileUtil;
 
 public class TLCTrace {
 
-  private static String filename;
-  private BufferedRandomAccessFile raf;
-  private long lastPtr;
-  private TraceApp tool;
-
-  public TLCTrace(String metadir, String specFile, TraceApp tool)
-  throws IOException {
-    filename = metadir + FileUtil.separator + specFile + ".st";
-    this.raf = new BufferedRandomAccessFile(filename, "rw");
-    this.lastPtr = 1L;
-    this.tool = tool;
-  }
-
-  /**
-   * @param fp A finger print of a state without a predecessor (init state)
-   * @return The new location (pointer) for the given finger print (state)
-   * @throws IOException
-   */
-  public final synchronized long writeState(final long aFingerprint)
-  throws IOException {
-	  return writeState(1, aFingerprint);
-  }
-
-  /**
-   * @param predecessor The predecessor state
-   * @param fp A finger print
-   * @return The new location (pointer) for the given finger print (state)
-   * @throws IOException
-   */
-  public final synchronized long writeState(final TLCState predecessor, final long aFingerprint)
-  throws IOException {
-	  return writeState(predecessor.uid, aFingerprint);
-  }
-  
-  /**
-   * @param predecessorLoc The location of the state predecessor
-   * @param fp A finger print
-   * @return The new location (pointer) for the given finger print (state)
-   * @throws IOException
-   */
-  private final synchronized long writeState(long predecessorLoc, long fp)
-  throws IOException {
-	//TODO Remove synchronization as all threads content for this lock
-    this.lastPtr = this.raf.getFilePointer();
-    this.raf.writeLongNat(predecessorLoc);
-    this.raf.writeLong(fp);
-    return this.lastPtr;
-  }
-
-  public final void close() throws IOException {
-    this.raf.close();
-  }
-
-  private synchronized long getPrev(long loc) throws IOException {
-    this.raf.seek(loc);
-    return this.raf.readLongNat();
-  }
-
-  private synchronized long getFP(long loc) throws IOException {
-    this.raf.seek(loc);
-    this.raf.readLongNat();    /*drop*/
-    return this.raf.readLong();
-  }
-
-  /**
-   * Returns the level (monotonically increasing)!
-   * 
-   * LL: The user has no real need of an accurate tree height. Breadth-first
-   * search is good because it provides the shortest possible error trace.
-   * Usually approximately breadth-first search is just as good because it
-   * makes little difference if the error trace isn't quite as short as
-   * possible. I believe that in most applications, after a short initial
-   * period, the height of the tree grows slowly. All workers are usually
-   * working on states of the same height except for brief periods when the
-   * height changes, and then the heights will differ by at most one.
-   * Reporting the height to the user gives him some information about how
-   * fast model checking is going. He will have no problem getting used to the
-   * idea that it's only an approximation. (I expect that few users even know
-   * what it means.) I'd like to make the reported value be monotonic because,
-   * if it's not, users may worry and people already have enough things in
-   * life to worry about.
-   * 
-   * @see TLCTrace#getLevel()
-   */
-  public final int getLevelForReporting() throws IOException {
-    final int calculatedLevel = getLevel(this.lastPtr);
-	if(calculatedLevel > previousLevel) {
-		previousLevel = calculatedLevel;
+	static final String EXT = ".st";
+	protected static String filename;
+	private final BufferedRandomAccessFile raf;
+	private long lastPtr;
+	private TraceApp tool;
+
+	public TLCTrace(String metadir, String specFile, TraceApp tool) throws IOException {
+		filename = metadir + FileUtil.separator + specFile + EXT;
+		this.raf = new BufferedRandomAccessFile(filename, "rw");
+		this.lastPtr = 1L;
+		this.tool = tool;
 	}
-	return previousLevel;
-  }
-  
-  /**
-   * Stores the previous level reported to guarantee that it is monotonic
-   */
-  private int previousLevel;
-  
-  /**
-   * @see TLCTrace#getLevel(long)
-   */
-  public final int getLevel() throws IOException {
-	// This assumption (lastPtr) only holds for the TLC in non-parallel mode.
-	// Generally the last line (logically a state) is not necessarily 
-  	// on the highest level of the state tree. This is only the case if  
-	// states are explored strictly by breadth-first search.
-	return getLevel(this.lastPtr);
-  }
-
-  /**
-   * @param startLoc The start location (pointer) from where the level (height) of the state tree should be calculated
-   * @return The level (height) of the state tree. 
-   * @throws IOException
-   */
-  public synchronized final int getLevel(long startLoc) throws IOException {
-	// keep current location
-    long currentFilePointer = this.raf.getFilePointer();
-
-    // calculate level/depth based on start location
-    int level = 0;
+
+	/**
+	 * @param fp
+	 *            A finger print of a state without a predecessor (init state)
+	 * @return The new location (pointer) for the given finger print (state)
+	 * @throws IOException
+	 */
+	public final synchronized long writeState(final long aFingerprint) throws IOException {
+		return writeState(1, aFingerprint);
+	}
+
+	/**
+	 * @param predecessor
+	 *            The predecessor state
+	 * @param fp
+	 *            A finger print
+	 * @return The new location (pointer) for the given finger print (state)
+	 * @throws IOException
+	 */
+	public final synchronized long writeState(final TLCState predecessor, final long aFingerprint) throws IOException {
+		return writeState(predecessor.uid, aFingerprint);
+	}
+
+	/**
+	 * @param predecessorLoc
+	 *            The location of the state predecessor
+	 * @param fp
+	 *            A finger print
+	 * @return The new location (pointer) for the given finger print (state)
+	 * @throws IOException
+	 */
+	private final synchronized long writeState(long predecessorLoc, long fp) throws IOException {
+		this.lastPtr = this.raf.getFilePointer();
+		this.raf.writeLongNat(predecessorLoc);
+		this.raf.writeLong(fp);
+		return this.lastPtr;
+	}
+
+	public void close() throws IOException {
+		this.raf.close();
+	}
+
+	private synchronized long getPrev(long loc) throws IOException {
+		this.raf.seek(loc);
+		return this.raf.readLongNat();
+	}
+
+	private synchronized long getFP(long loc) throws IOException {
+		this.raf.seek(loc);
+		this.raf.readLongNat(); /* drop */
+		return this.raf.readLong();
+	}
+
+	/**
+	 * Returns the level (monotonically increasing)!
+	 * 
+	 * LL: The user has no real need of an accurate tree height. Breadth-first
+	 * search is good because it provides the shortest possible error trace.
+	 * Usually approximately breadth-first search is just as good because it
+	 * makes little difference if the error trace isn't quite as short as
+	 * possible. I believe that in most applications, after a short initial
+	 * period, the height of the tree grows slowly. All workers are usually
+	 * working on states of the same height except for brief periods when the
+	 * height changes, and then the heights will differ by at most one.
+	 * Reporting the height to the user gives him some information about how
+	 * fast model checking is going. He will have no problem getting used to the
+	 * idea that it's only an approximation. (I expect that few users even know
+	 * what it means.) I'd like to make the reported value be monotonic because,
+	 * if it's not, users may worry and people already have enough things in
+	 * life to worry about.
+	 * 
+	 * @see TLCTrace#getLevel()
+	 */
+	public synchronized int getLevelForReporting() throws IOException {
+		final int calculatedLevel = getLevel(this.lastPtr);
+		if (calculatedLevel > previousLevel) {
+			previousLevel = calculatedLevel;
+		}
+		return previousLevel;
+	}
+
+	/**
+	 * Stores the previous level reported to guarantee that it is monotonic
+	 */
+	private int previousLevel;
+
+	/**
+	 * @see TLCTrace#getLevel(long)
+	 * @return 1 to the length of the longest behavior found so far.
+	 */
+	public int getLevel() throws IOException {
+	    // This assumption (lastPtr) only holds for the TLC in non-parallel mode.
+		// Generally the last line (logically a state) is not necessarily
+		// on the highest level of the state tree, which is only true if
+		// states are explored with strict breadth-first search.
+		//
+		// The (execution) trace is a forest of one to n trees, where each path 
+		// in the forest represents the order in which states have been generated
+		// by the workers.
+		// The algorithm, with which the diameter is approximated from the trace,
+		// is pretty simple; too simple. The trace is constantly written to the .st
+		// file where each "record" in the file is a link from a successor state to
+		// its predecessor state. Thus, the link is a position in the trace file
+		// where the predecessor state - actually its fingerprint - is stored. At
+		// the end of the trace file, there are up to m leaf records for which no
+		// successors have been appended (yet, assuming there are any).
+		//
+		// Once the workers have terminated, TLC traverses the trace from a leaf record
+		// back to a root record. This height is what is reported as the diameter. 
+		// The selection, from what leaf record TLC starts the traversal, is based on
+		// the last record inserted into the trace file. If this record is one with a
+		// low height (because its corresponding worker waited most of the time), the
+		// diameter will thus be underreport. If, on the other hand, the last record 
+		// happens to be one with a large height, the diameter will be overreported.
+		// 
+		// The selection of the leaf record is the source of the algorithm's 
+		// non-determinism. With a single worker, the last record in the trace is
+		// always the same which always corresponds to the longest behavior found so 
+		// far (strict BFS). This invariant does not hold with multiple workers.
+        //
+		// Obviously, with multiple workers the approximation of the diameter will
+		// improve with the size of the state graph. Assuming a well-shaped state graph,
+		// we can argue that the approximation is good enough and document, that its 
+		// value can be anything from 1 to the longest behavior found so far.
+		return getLevel(this.lastPtr);
+	}
+
+	/**
+	 * @param startLoc
+	 *            The start location (pointer) from where the level (height) of
+	 *            the path in the execution tree should be calculated.
+	 * @return The level (height) of the path in the execution tree (the trace)
+	 *         starting at startLoc.
+	 * @throws IOException
+	 */
+	public synchronized final int getLevel(long startLoc) throws IOException {
+		// keep current location
+		long currentFilePointer = this.raf.getFilePointer();
+
+		// calculate level/depth based on start location
+		int level = 0;
 	for (long predecessorLoc = startLoc; predecessorLoc != 1; predecessorLoc = this
 				.getPrev(predecessorLoc)) {
-      level++;
-    }
-		
-	// rewind to current location
-    this.raf.seek(currentFilePointer);
-    return level;
-  }
-  
-  /**
-   * @return All states in the trace file
-   * @throws IOException
-   */
-  public final TLCStateInfo[] getTrace() throws IOException {
+			level++;
+		}
+
+		// rewind to current location
+		this.raf.seek(currentFilePointer);
+		return level;
+	}
+
+	/**
+	 * @return All states in the trace file
+	 * @throws IOException
+	 */
+	public final TLCStateInfo[] getTrace() throws IOException {
 		final Map<Long, TLCStateInfo> locToState = new HashMap<Long, TLCStateInfo>();
 
 		synchronized (this) {
@@ -167,16 +199,16 @@ public class TLCTrace {
 				long length = this.raf.length();
 				// go to first byte
 				this.raf.seek(0);
-				
+
 				// read init state
-				this.raf.readLongNat(); /* drop predecessor of init state*/
+				this.raf.readLongNat(); /* drop predecessor of init state */
 				TLCStateInfo state = this.tool.getState(this.raf.readLong());
 				locToState.put(0L, state);
-				
-				for (long location = 12; location < length; location+=12) {
+
+				for (long location = 12; location < length; location += 12) {
 					final long predecessorLocation = this.raf.readLongNat();
 					final long fp = this.raf.readLong();
-					
+
 					// read predecessor from map
 					final TLCStateInfo predecessor = locToState.get(predecessorLocation);
 
@@ -186,7 +218,7 @@ public class TLCTrace {
 					// chain to predecessor
 					state.predecessorState = predecessor;
 					state.stateNumber = location / 12;
-					
+
 					// store in map
 					locToState.put(location, state);
 				}
@@ -196,242 +228,290 @@ public class TLCTrace {
 				this.raf.seek(curLoc);
 			}
 		}
-		
+
 		return locToState.values().toArray(new TLCStateInfo[locToState.size()]);
-  }
-  
-  /**
-   * @param loc The start location (pointer) from where the trace should be computed
-   * @param included true if the start location state should be included
-   * @return An array of predecessor states
-   * @throws IOException
-   */
-  public final TLCStateInfo[] getTrace(long loc, boolean included)
-  throws IOException {
-    LongVec fps = new LongVec();
-
-    synchronized(this) {
-      long curLoc = this.raf.getFilePointer();
-      long loc1 = (included) ? loc : this.getPrev(loc);
-      for (long ploc = loc1; ploc != 1; ploc = this.getPrev(ploc)) {
-	fps.addElement(this.getFP(ploc));
-      }
-      this.raf.seek(curLoc);
-    }
-
-    int stateNum = 0;
-    int len = fps.size();
-    TLCStateInfo[] res = new TLCStateInfo[len];
-    if (len > 0) {
-      long fp = fps.elementAt(len-1);
-      TLCStateInfo sinfo = this.tool.getState(fp);
-      if (sinfo == null) 
-      {
-          MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
-          MP.printError(EC.TLC_BUG, "1");
-          System.exit(1);
-      }
-      res[stateNum++] = sinfo;
-      for (int i = len - 2; i >= 0; i--) {
-	fp = fps.elementAt(i);
-	sinfo = this.tool.getState(fp, sinfo.state);
-	if (sinfo == null) {
-	    /*
-	     * The following error message is misleading, because it's triggered
-	     * when TLC can't find a non-initial state from its fingerprint
-	     * when it's generating an error trace.  LL 7 Mar 2012
-	     */
-        MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
-        MP.printError(EC.TLC_BUG, "2");
-	  System.exit(1);
 	}
-	res[stateNum++] = sinfo;
-      }
-    }
-    return res;
-  }
-
-  /**
-   * Write out a sequence of states that reaches s2 from an initial
-   * state, according to the spec. s2 is a next state of s1.
-   * 
-   * @param s1 may not be null.
-   * @param s2 may be null.
-   * @throws IOException
-   * @throws WorkerException
-   */
-  public synchronized final void printTrace(final TLCState s1, final TLCState s2)
-  throws IOException, WorkerException 
-  {
-	  ArrayList<TLCStateInfo> trace = new ArrayList<TLCStateInfo>(); // collecting the whole error trace
-	  
-	  MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT);
-      // Print the prefix leading to s1:
-      long loc1 = s1.uid; 
-      TLCState lastState = null;
-      TLCStateInfo[] prefix = this.getTrace(loc1, false);
-      int idx = 0;
-      while (idx < prefix.length) 
-      {
-          StatePrinter.printState(prefix[idx], lastState, idx+1);
-          lastState = prefix[idx].state;
-          trace.add(prefix[idx]);
-          idx++;
-      }
-
-      // Print s1:
-      TLCStateInfo sinfo;
-      if (prefix.length == 0) {
-          sinfo = this.tool.getState(s1.fingerPrint());
-          if (sinfo == null) 
-          {
-              MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
-              MP.printError(EC.TLC_BUG, "3");
-              System.exit(1);
-          }
-      }
-      else 
-      {
-          TLCState s0 = prefix[prefix.length-1].state;
-          sinfo = this.tool.getState(s1.fingerPrint(), s0);
-          if (sinfo == null) 
-          {
-              MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
-              MP.printError(EC.TLC_BUG, "4");
-              StatePrinter.printState(s1); 
-              System.exit(1);
-          }
-      }
-      if (s2 == null) 
-      { 
-          lastState = null; 
-      }
-      StatePrinter.printState(sinfo, lastState, ++idx);
-      lastState = sinfo.state;
-      trace.add(sinfo);
-      
-      // Print s2:
-      if (s2 != null) {
-          sinfo = this.tool.getState(s2, s1);
-          if (sinfo == null) 
-          {
-              MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
-              MP.printError(EC.TLC_BUG, "5");
-              StatePrinter.printState(s2);
-              System.exit(1);
-          }
-          StatePrinter.printState(sinfo, null, ++idx);
-          trace.add(sinfo);
-      }
-      OutputCollector.setTrace(trace);
-  }
-
-
-  /**
-   * Returns a sequence of states that reaches, but excludes the
-   * state with fingerprint fp.
-   */
-  @SuppressWarnings("unused")
-  private final TLCStateInfo[] printPrefix(long fp) throws IOException {
-    // First, find the location for fp:
-    this.raf.seek(0);
-    this.raf.readLongNat();    /*drop*/
-
-    while (this.raf.readLong() != fp) {
-      this.raf.readLongNat();  /*drop*/
-    }
-    
-    // Print the states corresponding to the fps:
-    TLCState lastState = null;
-    TLCStateInfo[] prefix = this.getTrace(this.lastPtr, false);
-    int idx = 0;
-    while (idx < prefix.length) {
-        StatePrinter.printState(prefix[idx], lastState, idx+1);
-      lastState = prefix[idx].state;
-      idx++;
-    }
-    return prefix;
-  }
-  
-  /* Checkpoint.  */
-  public synchronized final void beginChkpt() throws IOException {
-    this.raf.flush();
-    // SZ Feb 24, 2009: FileUtil introduced
-    DataOutputStream dos = FileUtil.newDFOS(filename + ".tmp");
-    dos.writeLong(this.raf.getFilePointer());
-    dos.writeLong(this.lastPtr);
-    dos.close();
-  }
-
-  public final void commitChkpt() throws IOException {
-    File oldChkpt = new File(filename + ".chkpt");
-    File newChkpt = new File(filename + ".tmp");
-    if ((oldChkpt.exists() && !oldChkpt.delete()) ||
-	!newChkpt.renameTo(oldChkpt)) {
-      throw new IOException("Trace.commitChkpt: cannot delete " + oldChkpt);
-    }
-  }
-
-  public final void recover() throws IOException {
-    // SZ Feb 24, 2009: FileUtil introduced
-    DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt");
-    long filePos = dis.readLong();
-    this.lastPtr = dis.readLong();
-    dis.close();
-    this.raf.seek(filePos);
-  }
-
-  public static String getFilename() { return filename; }
-
-  public static long getRecoverPtr() throws IOException {
-      // SZ Feb 24, 2009: FileUtil introduced
-    DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt");
-    long res = dis.readLong();
-    dis.close();
-    return res;
-  }
-
-  @SuppressWarnings("unused")
-  private long[] addBlock(long fp[], long prev[]) throws IOException {
-    // Reuse prev.
-    for (int i = 0; i < fp.length; i++) {
-      prev[i] = this.writeState(prev[i], fp[i]);
-    }
-    return prev;
-  }
-
-  public synchronized final Enumerator elements() throws IOException {
-    return new Enumerator();
-  }
-
-  final class Enumerator {
-    long len;
-    BufferedRandomAccessFile enumRaf;
-    
-    Enumerator() throws IOException {
-      this.len = raf.length();
-      this.enumRaf = new BufferedRandomAccessFile(filename, "r");
-    }
-
-    final void reset(long pos) throws IOException {
-      this.len = raf.length();
-      if (pos == -1) {
-	pos = this.enumRaf.getFilePointer();
-      }
-      this.enumRaf = new BufferedRandomAccessFile(filename, "r");
-      this.enumRaf.seek(pos);
-    }
-    
-    final long nextPos() {
-      long fpos = this.enumRaf.getFilePointer();
-      if (fpos < this.len) { return fpos; }
-      return -1;
-    }
-
-    final long nextFP() throws IOException {
-      this.enumRaf.readLongNat();    /*drop*/
-      return this.enumRaf.readLong();
-    }
-  }
+
+	/**
+	 * @param loc
+	 *            The start location (pointer) from where the trace should be
+	 *            computed
+	 * @param included
+	 *            true if the start location state should be included
+	 * @return An array of predecessor states
+	 * @throws IOException
+	 */
+	protected TLCStateInfo[] getTrace(long loc, boolean included) throws IOException {
+		LongVec fps = new LongVec();
+
+		// Starting at the given start fingerprint (which is the end of the
+		// trace from the point of the initial states), the chain of
+		// predecessors fingerprints are reconstructed from the trace file up to
+		// the initial state.
+		synchronized (this) {
+			long curLoc = this.raf.getFilePointer();
+			long loc1 = (included) ? loc : this.getPrev(loc);
+			for (long ploc = loc1; ploc != 1; ploc = this.getPrev(ploc)) {
+				fps.addElement(this.getFP(ploc));
+			}
+			this.raf.seek(curLoc);
+		}
+		
+		return getTrace(fps);
+	}
+
+	/**
+	 * This method is *not* safe to call multiple times iff the spec being checked
+	 * consumed randomness, ie. TLC!RandomElement or through the Randomization
+	 * module. In other words, such specs are incompatible with TLC's -continue
+	 * mode.
+	 * <p>
+	 * To implement this correctly, state space exploration would either have to
+	 * halt while the fingerprints are resolved to TLCStates below or ITool has
+	 * to offer additional API s.t. the seed of RandomEnumerableValues gets
+	 * passed as part of the method call.
+	 */
+	protected final TLCStateInfo[] getTrace(final LongVec fps) {
+		return getTrace(null, fps);
+	}
+
+	protected final TLCStateInfo[] getTrace(TLCStateInfo sinfo, final LongVec fps) {
+		// Re-Initialize the rng with the seed value recorded and used during the model
+		// checking phase. Otherwise, we won't be able to reconstruct the error trace
+		// because the set of initial states is likely to be different.
+		// This is only necessary though, if TLCGlobals.enumFraction was < 1 during
+		// the generation of inits.
+		RandomEnumerableValues.reset();
+		
+		// The vector of fingerprints is now being followed forward from the
+		// initial state (which is the last state in the long vector), to the
+		// end state.
+		//
+		// At each fingerprint of the sequence, the equivalent state gets
+		// reconstructed. For the initial state it's just the fingerprint, for
+		// successor states the predecessor p to the successor state s and the
+		// fingerprint that are passed to Tool. Tool generates *all* next states
+		// of p and throws away all except the one that has a matching
+		// fingerprint.
+		int stateNum = 0;
+		final int len = fps.size();
+		final TLCStateInfo[] res = new TLCStateInfo[len];
+		if (len > 0) {
+			if (sinfo == null) {
+				// Recreate initial state from its fingerprint.
+				final long fp = fps.elementAt(fps.size() - 1);
+				sinfo = this.tool.getState(fp);
+			}
+			// Recover successor states from its predecessor and its fingerprint.
+			res[stateNum++] = sinfo;
+			for (int i = len - 2; i >= 0; i--) {
+				long fp = fps.elementAt(i);
+				sinfo = this.tool.getState(fp, sinfo.state);
+				if (sinfo == null) {
+					/*
+					 * The following error message is misleading, because it's triggered when TLC
+					 * can't find a non-initial state from its fingerprint when it's generating an
+					 * error trace. LL 7 Mar 2012
+					 */
+					MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
+					MP.printError(EC.TLC_BUG, "2 " + Long.toString(fp));
+					System.exit(1);
+				}
+				res[stateNum++] = sinfo;
+			}
+		}
+		return res;
+	}
+
+	/**
+	 * Write out a sequence of states that reaches s2 from an initial state,
+	 * according to the spec. s2 is a next state of s1.
+	 * 
+	 * @param s1
+	 *            may not be null.
+	 * @param s2
+	 *            may be null.
+	 * @throws IOException
+	 * @throws WorkerException
+	 */
+	public void printTrace(final TLCState s1, final TLCState s2) throws IOException, WorkerException {
+		printTrace(s1, s2, getTrace(s1.uid, false));
+	}
+	
+	protected synchronized final void printTrace(final TLCState s1, final TLCState s2, final TLCStateInfo[] prefix)
+			throws IOException, WorkerException {
+		if (s1.isInitial()) {
+			// Do not recreate the potentially expensive error trace - e.g. when the set of
+			// initial states is huge such as during inductive invariant checking. Instead
+			// use the two states s1 and s2 directly.
+			MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT);
+			StatePrinter.printState(new TLCStateInfo(s1));
+			if (s2 != null) {
+				// Create TLCStateInfo instance to include corresponding action in output.
+				StatePrinter.printState(this.tool.getState(s2, s1), s1, 2);
+			}
+			return;
+		}
+
+		MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT);
+		// Print the prefix leading to s1:
+		TLCState lastState = null;
+		int idx = 0;
+		while (idx < prefix.length) {
+			StatePrinter.printState(prefix[idx], lastState, idx + 1);
+			lastState = prefix[idx].state;
+			idx++;
+		}
+
+		// Print s1:
+		TLCStateInfo sinfo;
+		// If the prefix is of length zero, s1 is an initial state. If the
+		// prefix has elements, use the last state in prefix as the predecessor
+		// to s1.
+		if (prefix.length == 0) {
+			sinfo = this.tool.getState(s1.fingerPrint());
+			if (sinfo == null) {
+				MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
+				MP.printError(EC.TLC_BUG, "3");
+				System.exit(1);
+			}
+		} else {
+			TLCState s0 = prefix[prefix.length - 1].state;
+			sinfo = this.tool.getState(s1.fingerPrint(), s0);
+			if (sinfo == null) {
+				MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
+				MP.printError(EC.TLC_BUG, "4");
+				StatePrinter.printState(s1);
+				System.exit(1);
+			}
+		}
+		if (s2 == null) {
+			lastState = null;
+		}
+		StatePrinter.printState(sinfo, lastState, ++idx);
+		lastState = sinfo.state;
+
+		// Print s2:
+		// The predecessor to s2 is s1.
+		if (s2 != null) {
+			sinfo = this.tool.getState(s2, s1);
+			if (sinfo == null) {
+				MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
+				MP.printError(EC.TLC_BUG, "5");
+				StatePrinter.printState(s2);
+				System.exit(1);
+			}
+			StatePrinter.printState(sinfo, null, ++idx);
+		}
+	}
+
+	/**
+	 * Returns a sequence of states that reaches, but excludes the state with
+	 * fingerprint fp.
+	 */
+	@SuppressWarnings("unused")
+	private final TLCStateInfo[] printPrefix(long fp) throws IOException {
+		// First, find the location for fp:
+		this.raf.seek(0);
+		this.raf.readLongNat(); /* drop */
+
+		while (this.raf.readLong() != fp) {
+			this.raf.readLongNat(); /* drop */
+		}
+
+		// Print the states corresponding to the fps:
+		TLCState lastState = null;
+		TLCStateInfo[] prefix = this.getTrace(this.lastPtr, false);
+		int idx = 0;
+		while (idx < prefix.length) {
+			StatePrinter.printState(prefix[idx], lastState, idx + 1);
+			lastState = prefix[idx].state;
+			idx++;
+		}
+		return prefix;
+	}
+
+	/* Checkpoint. */
+	public synchronized void beginChkpt() throws IOException {
+		this.raf.flush();
+		// SZ Feb 24, 2009: FileUtil introduced
+		DataOutputStream dos = FileUtil.newDFOS(filename + ".tmp");
+		dos.writeLong(this.raf.getFilePointer());
+		dos.writeLong(this.lastPtr);
+		dos.close();
+	}
+
+	public void commitChkpt() throws IOException {
+		File oldChkpt = new File(filename + ".chkpt");
+		File newChkpt = new File(filename + ".tmp");
+		if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) {
+			throw new IOException("Trace.commitChkpt: cannot delete " + oldChkpt);
+		}
+	}
+
+	public void recover() throws IOException {
+		// SZ Feb 24, 2009: FileUtil introduced
+		DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt");
+		long filePos = dis.readLong();
+		this.lastPtr = dis.readLong();
+		dis.close();
+		this.raf.seek(filePos);
+	}
+
+	@SuppressWarnings("unused")
+	private long[] addBlock(long fp[], long prev[]) throws IOException {
+		// Reuse prev.
+		for (int i = 0; i < fp.length; i++) {
+			prev[i] = this.writeState(prev[i], fp[i]);
+		}
+		return prev;
+	}
+
+	public synchronized Enumerator elements() throws IOException {
+		return new TLCTraceEnumerator();
+	}
+
+	public interface Enumerator {
+		long nextPos();
+		long nextFP() throws IOException;
+		void close() throws IOException;
+		void reset(long i) throws IOException;
+	}
+	
+	public class TLCTraceEnumerator implements Enumerator {
+		long len;
+		BufferedRandomAccessFile enumRaf;
+
+		TLCTraceEnumerator() throws IOException {
+			this.len = raf.length();
+			this.enumRaf = new BufferedRandomAccessFile(filename, "r");
+		}
+
+		public final void reset(long pos) throws IOException {
+			this.len = raf.length();
+			if (pos == -1) {
+				pos = this.enumRaf.getFilePointer();
+			}
+			this.enumRaf = new BufferedRandomAccessFile(filename, "r");
+			this.enumRaf.seek(pos);
+		}
+
+		public final long nextPos() {
+			long fpos = this.enumRaf.getFilePointer();
+			if (fpos < this.len) {
+				return fpos;
+			}
+			return -1L;
+		}
+
+		public final long nextFP() throws IOException {
+			this.enumRaf.readLongNat(); /* drop */
+			return this.enumRaf.readLong();
+		}
+		
+		public final void close() throws IOException {
+			this.enumRaf.close();
+		}
+	}
 
 }
diff --git a/tlatools/src/tlc2/tool/Tool.java b/tlatools/src/tlc2/tool/Tool.java
deleted file mode 100644
index 594fbc36dfdc032ce84823b761d35939004b6724..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/tool/Tool.java
+++ /dev/null
@@ -1,2990 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Thu  2 Aug 2007 at 10:25:48 PST by lamport 
-//      modified on Fri Jan  4 22:46:57 PST 2002 by yuanyu 
-
-package tlc2.tool;
-
-import tla2sany.modanalyzer.SpecObj;
-import tla2sany.semantic.ExprNode;
-import tla2sany.semantic.ExprOrOpArgNode;
-import tla2sany.semantic.FormalParamNode;
-import tla2sany.semantic.LabelNode;
-import tla2sany.semantic.LetInNode;
-import tla2sany.semantic.ModuleNode;
-import tla2sany.semantic.OpApplNode;
-import tla2sany.semantic.OpArgNode;
-import tla2sany.semantic.OpDeclNode;
-import tla2sany.semantic.OpDefNode;
-import tla2sany.semantic.SemanticNode;
-import tla2sany.semantic.Subst;
-import tla2sany.semantic.SubstInNode;
-import tla2sany.semantic.APSubstInNode;
-import tla2sany.semantic.SymbolNode;
-import tla2sany.semantic.ThmOrAssumpDefNode;
-import tlc2.TLCGlobals;
-import tlc2.output.EC;
-import tlc2.output.MP;
-import tlc2.util.Context;
-import tlc2.util.Vect;
-import tlc2.value.Applicable;
-import tlc2.value.BoolValue;
-import tlc2.value.Enumerable;
-import tlc2.value.FcnLambdaValue;
-import tlc2.value.FcnParams;
-import tlc2.value.FcnRcdValue;
-import tlc2.value.LazyValue;
-import tlc2.value.MVPerm;
-import tlc2.value.MethodValue;
-import tlc2.value.OpLambdaValue;
-import tlc2.value.OpValue;
-import tlc2.value.RecordValue;
-import tlc2.value.Reducible;
-import tlc2.value.SetCapValue;
-import tlc2.value.SetCupValue;
-import tlc2.value.SetDiffValue;
-import tlc2.value.SetEnumValue;
-import tlc2.value.SetOfFcnsValue;
-import tlc2.value.SetOfRcdsValue;
-import tlc2.value.SetOfTuplesValue;
-import tlc2.value.SetPredValue;
-import tlc2.value.StringValue;
-import tlc2.value.SubsetValue;
-import tlc2.value.TupleValue;
-import tlc2.value.UnionValue;
-import tlc2.value.Value;
-import tlc2.value.ValueConstants;
-import tlc2.value.ValueEnumeration;
-import tlc2.value.ValueExcept;
-import tlc2.value.ValueVec;
-import util.Assert;
-import util.FilenameToStream;
-import util.UniqueString;
-
-/**
- * This class provides useful methods for tools like model checker
- * and simulator.
- * 
- * It's instance serves as a spec handle
- * This is one of two places in TLC, where not all messages are retrieved from the message printer,
- * but constructed just here in the code. 
- * 
- * @version $Id$
- */
-public class Tool 
-    extends Spec 
-    implements ValueConstants, ToolGlobals, TraceApp 
-{
-  protected Action[] actions;     // the list of TLA actions.
-  private CallStack callStack;    // the call stack.
-
-  private Vect actionVec = new Vect(10);
-  
-  /**
-   * Creates a new tool handle 
-   * @param specDir
-   * @param specFile
-   * @param configFile
-   */
-  public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver) 
-  {
-      super(specDir, specFile, configFile, resolver);
-      this.actions = null;
-      this.callStack = null;
-  }
-
-  /**
-   * Initialization. Any Tool object must call it before doing anything.
-   * @param spec - <code>null</code> or a filled spec object from previous SANY run
-   */
-  public final SpecObj init(boolean preprocess, SpecObj spec) 
-  {
-      
-      // Parse and process this spec. 
-      // It takes care of all overrides.
-      // SZ Feb 20, 2009: added spec reference,
-      // if not null it is just used instead of re-parsing
-      SpecObj processSpec = super.processSpec(spec);
-
-      // Initialize state.
-      if (TLCGlobals.coverageInterval >= 0) {
-          TLCStateMutSource.init(this);
-      }
-      else {
-          TLCStateMut.init(this);
-      }
-
-      // Pre-evaluate all the definitions in the spec that are constants.
-      if (preprocess) {
-          this.processConstantDefns();
-      }
-
-      // Finally, process the config file.
-      super.processConfig();
-      
-      return processSpec;
-  }
-
-  public final void setCallStack() 
-  { 
-      this.callStack = new CallStack(); 
-  }
-
-  public final CallStack getCallStack() 
-  {
-      return this.callStack;
-  }
-   
-  /**
-   * This method returns the set of all possible actions of the 
-   * spec, and sets the actions field of this object. In fact, we
-   * could simply treat the next predicate as one "giant" action.
-   * But for efficiency, we preprocess the next state predicate by 
-   * splitting it into a set of actions for the maximum prefix
-   * of disjunction and existential quantification.
-   */
-  public final Action[] getActions() {
-    if (this.actions == null) {
-      Action next = this.getNextStateSpec();
-      if (next == null) {
-        this.actions = new Action[0];
-      }
-      else {
-        this.getActions(next.pred, next.con);
-        int sz = this.actionVec.size();
-        this.actions = new Action[sz];
-        for (int i = 0; i < sz; i++) {
-          this.actions[i] = (Action)this.actionVec.elementAt(i);
-        }
-      }
-    }
-    return this.actions;
-  }
-
-  
-  
-  private final void getActions(SemanticNode next, Context con) {
-    switch (next.getKind()) {
-    case OpApplKind:
-      {
-        OpApplNode next1 = (OpApplNode)next;
-        this.getActionsAppl(next1, con);
-        return;
-      }
-    case LetInKind:
-      {
-        LetInNode next1 = (LetInNode)next;
-        this.getActions(next1.getBody(), con);
-        return;
-      }
-    case SubstInKind:
-      {
-        SubstInNode next1 = (SubstInNode)next;
-        Subst[] substs = next1.getSubsts();
-        if (substs.length == 0) {
-          this.getActions(next1.getBody(), con);
-        }
-        else {
-          Action action = new Action(next1, con);
-          this.actionVec.addElement(action);
-        }
-        return;
-      }
-
-      // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-      case APSubstInKind:
-        {
-          APSubstInNode next1 = (APSubstInNode)next;
-          Subst[] substs = next1.getSubsts();
-          if (substs.length == 0) {
-            this.getActions(next1.getBody(), con);
-          }
-          else {
-            Action action = new Action(next1, con);
-            this.actionVec.addElement(action);
-          }
-          return;
-        }
-
-    /***********************************************************************
-    * LabelKind class added by LL on 13 Jun 2007.                          *
-    ***********************************************************************/
-    case LabelKind:
-      {
-        LabelNode next1 = (LabelNode)next;
-        this.getActions(next1.getBody(), con);
-        return;
-      }
-    default:
-      {
-        Assert.fail("The next state relation is not a boolean expression.\n" + next);
-      }
-    }
-  }
-
-  private final void getActionsAppl(OpApplNode next, Context con) {
-    ExprOrOpArgNode[] args = next.getArgs();
-    SymbolNode opNode = next.getOperator();
-    int opcode = BuiltInOPs.getOpCode(opNode.getName());
-
-    if (opcode == 0) {
-      Object val = this.lookup(opNode, con, false);
-
-      if (val instanceof OpDefNode) {
-        OpDefNode opDef = (OpDefNode)val;
-        opcode = BuiltInOPs.getOpCode(opDef.getName());
-        if (opcode == 0) {
-          try {
-            FormalParamNode[] formals = opDef.getParams();
-            int alen = args.length;
-            int argLevel = 0;
-            for (int i = 0; i < alen; i++) {
-              argLevel = args[i].getLevel();
-              if (argLevel != 0) break;
-            }
-            if (argLevel == 0) {
-              Context con1 = con;
-              for (int i = 0; i < alen; i++) {
-                Value aval = this.eval(args[i], con, TLCState.Empty);
-                con1 = con1.cons(formals[i], aval);
-              }
-              this.getActions(opDef.getBody(), con1);
-              return;
-            }
-          }
-          catch (Throwable e) { /*SKIP*/ }
-        }
-      }
-      if (opcode == 0) {
-        Action action = new Action(next, con);
-        this.actionVec.addElement(action);
-        return;
-      }
-    }
-
-    switch (opcode) {
-    case OPCODE_be:     // BoundedExists
-      {
-        int cnt = this.actionVec.size();
-        try {
-          ContextEnumerator Enum =
-            this.contexts(next, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear);
-          Context econ;
-          while ((econ = Enum.nextElement()) != null) {
-            this.getActions(args[0], econ);
-          }
-        }
-        catch (Throwable e) {
-          Action action = new Action(next, con);
-          this.actionVec.removeAll(cnt);
-          this.actionVec.addElement(action);
-        }
-        return;
-      }
-    case OPCODE_dl:     // DisjList
-    case OPCODE_lor:      
-      {
-        for (int i = 0; i < args.length; i++) {
-          this.getActions(args[i], con);
-        }
-        return;
-      }
-    default:
-      {
-        // We handle all the other builtin operators here.
-        Action action = new Action(next, con);
-        this.actionVec.addElement(action);
-        return;
-      }
-    }
-  }
-
-  /*
-   * This method returns the set of possible initial states that
-   * satisfies the initial state predicate. Initial state predicate
-   * can be under-specified.  Too many possible initial states will
-   * probably make tools like TLC useless.
-   */
-  public final StateVec getInitStates() {
-    Vect init = this.getInitStateSpec();
-    ActionItemList acts = ActionItemList.Empty;
-    StateVec initStates = new StateVec(0);
-    for (int i = 1; i < init.size(); i++) {
-      Action elem = (Action)init.elementAt(i);
-      acts = acts.cons(elem.pred, elem.con, -1);
-    }
-    if (init.size() != 0) {
-      Action elem = (Action)init.elementAt(0);
-      TLCState ps = TLCState.Empty.createEmpty();
-      this.getInitStates(elem.pred, acts, elem.con, ps, initStates);
-    }
-    return initStates;
-  }
-
-  /* Create the state specified by pred.  */
-  public final TLCState makeState(SemanticNode pred) {
-    ActionItemList acts = ActionItemList.Empty;
-    TLCState ps = TLCState.Empty.createEmpty();
-    StateVec states = new StateVec(0);
-    this.getInitStates(pred, acts, Context.Empty, ps, states);
-    if (states.size() != 1) {
-      Assert.fail("The predicate does not specify a unique state." + pred);
-    }
-    TLCState state = states.elementAt(0);
-    if (!this.isGoodState(state)) {
-      Assert.fail("The state specified by the predicate is not complete." + pred);
-    }
-    return state;
-  }
-
-  private final void getInitStates(SemanticNode init, ActionItemList acts,
-                                   Context c, TLCState ps, StateVec states) {
-    switch (init.getKind()) {
-    case OpApplKind:
-      {
-        OpApplNode init1 = (OpApplNode)init;
-        this.getInitStatesAppl(init1, acts, c, ps, states);
-        return;
-      }
-    case LetInKind:
-      {
-        LetInNode init1 = (LetInNode)init;
-        this.getInitStates(init1.getBody(), acts, c, ps, states);
-        return;
-      }
-    case SubstInKind:
-      {
-        SubstInNode init1 = (SubstInNode)init;
-        Subst[] subs = init1.getSubsts();
-        Context c1 = c;
-        for (int i = 0; i < subs.length; i++) {
-          Subst sub = subs[i];
-          c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false));
-        }
-        this.getInitStates(init1.getBody(), acts, c1, ps, states);
-        return;
-      }
-
-      // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-      case APSubstInKind:
-        {
-          APSubstInNode init1 = (APSubstInNode)init;
-          Subst[] subs = init1.getSubsts();
-          Context c1 = c;
-          for (int i = 0; i < subs.length; i++) {
-            Subst sub = subs[i];
-            c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false));
-          }
-          this.getInitStates(init1.getBody(), acts, c1, ps, states);
-          return;
-        }
-
-
-    /***********************************************************************
-    * LabelKind class added by LL on 13 Jun 2007.                          *
-    ***********************************************************************/
-    case LabelKind:
-      {
-        LabelNode init1 = (LabelNode)init;
-        this.getInitStates(init1.getBody(), acts, c, ps, states);
-        return;
-      }
-    default:
-      {
-        Assert.fail("The init state relation is not a boolean expression.\n" + init);
-      }
-    }
-  }
-
-  private final void getInitStates(ActionItemList acts, TLCState ps, StateVec states) {
-    if (acts.isEmpty()) {
-      states.addElement(ps.copy());
-    }
-    else {
-      // Assert.check(act.kind > 0 || act.kind == -1);
-      ActionItemList acts1 = acts.cdr();
-      this.getInitStates(acts.carPred(), acts1, acts.carContext(), ps, states);
-    }
-  }
-
-  private final void getInitStatesAppl(OpApplNode init, ActionItemList acts,
-                                       Context c, TLCState ps, StateVec states) {
-    ExprOrOpArgNode[] args = init.getArgs();
-    int alen = args.length;
-    SymbolNode opNode = init.getOperator();
-    int opcode = BuiltInOPs.getOpCode(opNode.getName());
-
-    if (opcode == 0) {
-      // This is a user-defined operator with one exception: it may
-      // be substed by a builtin operator. This special case occurs
-      // when the lookup returns an OpDef with opcode # 0.
-      Object val = this.lookup(opNode, c, ps, false);
-
-      if (val instanceof OpDefNode) {
-        OpDefNode opDef = (OpDefNode)val;
-        opcode = BuiltInOPs.getOpCode(opDef.getName());
-        if (opcode == 0) {
-          // Context c1 = this.getOpContext(opDef, args, c, false);
-          Context c1 = this.getOpContext(opDef, args, c, true);
-          this.getInitStates(opDef.getBody(), acts, c1, ps, states);
-          return;
-        }
-      }
-      // Added 13 Nov 2009 by LL to fix Yuan's fix.
-      /*********************************************************************
-      * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
-      * imported with parameterized instantiation.                         *
-      *********************************************************************/
-      if (val instanceof ThmOrAssumpDefNode) {
-          ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val;
-          opcode = BuiltInOPs.getOpCode(opDef.getName());
-          Context c1 = this.getOpContext(opDef, args, c, true);
-          this.getInitStates(opDef.getBody(), acts, c1, ps, states);
-          return;
-        }
-
-      if (val instanceof LazyValue) {
-        LazyValue lv = (LazyValue)val;
-        if (lv.val == null || lv.val == ValUndef) {
-          this.getInitStates(lv.expr, acts, lv.con, ps, states);
-          return;
-        }
-        val = lv.val;
-      }
-
-      Object bval = val;
-      if (alen == 0) {
-        if (val instanceof MethodValue) {
-          bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear);
-        }
-      }
-      else {
-        if (val instanceof OpValue) {
-          Applicable opVal = (Applicable)val;
-          Value[] argVals = new Value[alen];
-          // evaluate the actuals:
-          for (int i = 0; i < alen; i++) {
-            argVals[i] = this.eval(args[i], c, ps);
-          }
-          // apply the operator:
-          bval = opVal.apply(argVals, EvalControl.Clear);
-        }
-      }
-
-            if (opcode == 0)
-            {
-                if (!(bval instanceof BoolValue))
-                {
-                    Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "initial states", "boolean",
-                            bval.toString(), init.toString() });
-                }
-                if (((BoolValue) bval).val)
-                {
-                    this.getInitStates(acts, ps, states);
-                }
-                return;
-            }
-        }
-
-    switch (opcode) {
-    case OPCODE_dl:     // DisjList
-    case OPCODE_lor:
-      {
-        for (int i = 0; i < alen; i++) {
-          this.getInitStates(args[i], acts, c, ps, states);
-        }
-        return;
-      }
-    case OPCODE_cl:     // ConjList
-    case OPCODE_land:
-      {
-        for (int i = alen-1; i > 0; i--) {
-          acts = acts.cons(args[i], c, i);
-        }
-        this.getInitStates(args[0], acts, c, ps, states);
-        return;
-      }
-    case OPCODE_be:     // BoundedExists
-      {
-        SemanticNode body = args[0];
-        ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear);
-        Context c1;
-        while ((c1 = Enum.nextElement()) != null) {
-          this.getInitStates(body, acts, c1, ps, states);
-        }
-        return;
-      }
-    case OPCODE_bf:     // BoundedForall
-      {
-        SemanticNode body = args[0];
-        ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear);
-        Context c1 = Enum.nextElement();
-        if (c1 == null) {
-          this.getInitStates(acts, ps, states);
-        }
-        else {
-          ActionItemList acts1 = acts;
-          Context c2;
-          while ((c2 = Enum.nextElement()) != null) {
-            acts1 = acts1.cons(body, c2, -1);
-          }
-          this.getInitStates(body, acts1, c1, ps, states);
-        }
-        return;
-      }
-    case OPCODE_ite:    // IfThenElse
-      {
-        Value guard = this.eval(args[0], c, ps);
-        if (!(guard instanceof BoolValue)) {
-          Assert.fail("In computing initial states, a non-boolean expression (" +
-                      guard.getKindString() + ") was used as the condition " +
-                      "of an IF.\n" + init);
-        }
-        int idx = (((BoolValue)guard).val) ? 1 : 2;
-        this.getInitStates(args[idx], acts, c, ps, states);
-        return;
-      }
-    case OPCODE_case:   // Case
-      {
-        SemanticNode other = null;
-        for (int i = 0; i < alen; i++) {
-          OpApplNode pair = (OpApplNode)args[i];
-          ExprOrOpArgNode[] pairArgs = pair.getArgs();
-          if (pairArgs[0] == null) {
-            other = pairArgs[1];
-          }
-          else {
-            Value bval = this.eval(pairArgs[0], c, ps);
-            if (!(bval instanceof BoolValue)) {
-              Assert.fail("In computing initial states, a non-boolean expression (" +
-                          bval.getKindString() + ") was used as a guard condition" +
-                          " of a CASE.\n" + pairArgs[1]);
-            }
-            if (((BoolValue)bval).val) {
-              this.getInitStates(pairArgs[1], acts, c, ps, states);
-              return;
-            }
-          }
-        }
-        if (other == null) {
-          Assert.fail("In computing initial states, TLC encountered a CASE with no" +
-                      " conditions true.\n" + init);
-        }
-        this.getInitStates(other, acts, c, ps, states);
-        return;
-      }
-    case OPCODE_fa:     // FcnApply
-      {
-        Value fval = this.eval(args[0], c, ps);
-        if (fval instanceof FcnLambdaValue) {
-          FcnLambdaValue fcn = (FcnLambdaValue)fval;
-          if (fcn.fcnRcd == null) {
-            Context c1 = this.getFcnContext(fcn, args, c, ps, TLCState.Empty, EvalControl.Clear);
-            this.getInitStates(fcn.body, acts, c1, ps, states);
-            return;
-          }
-          fval = fcn.fcnRcd;
-        }
-        else if (!(fval instanceof Applicable)) {
-          Assert.fail("In computing initial states, a non-function (" +
-                      fval.getKindString() + ") was applied as a function.\n" + init);
-        }
-            Applicable fcn = (Applicable) fval;
-            Value argVal = this.eval(args[1], c, ps);
-            Value bval = fcn.apply(argVal, EvalControl.Clear);
-            if (!(bval instanceof BoolValue))
-            {
-                Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "initial states", "boolean",
-                        init.toString() });
-            }
-        if (((BoolValue)bval).val) {
-          this.getInitStates(acts, ps, states);
-        }
-        return;
-      }
-    case OPCODE_eq:
-      {
-        SymbolNode var = this.getVar(args[0], c, false);
-        if (var == null || var.getName().getVarLoc() < 0) {
-          Value bval = this.eval(init, c, ps);
-          if (!((BoolValue)bval).val) return;
-        }
-        else {
-          UniqueString varName = var.getName();   
-          Value lval = ps.lookup(varName);
-          Value rval = this.eval(args[1], c, ps);
-          if (lval == null) {
-            ps = ps.bind(varName, rval, init);
-            this.getInitStates(acts, ps, states);
-            ps.unbind(varName);
-            return;
-          }
-          else {
-            if (!lval.equals(rval)) return;
-          }
-        }
-        this.getInitStates(acts, ps, states);
-        return;
-      }
-    case OPCODE_in:
-      {
-        SymbolNode var = this.getVar(args[0], c, false);
-        if (var == null || var.getName().getVarLoc() < 0) {
-          Value bval = this.eval(init, c, ps);
-          if (!((BoolValue)bval).val) return;
-        }
-        else {
-          UniqueString varName = var.getName();
-          Value lval = ps.lookup(varName);
-          Value rval = this.eval(args[1], c, ps);
-          if (lval == null) {
-            if (!(rval instanceof Enumerable)) {
-              Assert.fail("In computing initial states, the right side of \\IN" +
-                          " is not enumerable.\n" + init);
-            }
-            ValueEnumeration Enum = ((Enumerable)rval).elements();
-            Value elem;
-            while ((elem = Enum.nextElement()) != null) {
-              ps.bind(varName, elem, init);
-              this.getInitStates(acts, ps, states);
-              ps.unbind(varName);
-            }
-            return;
-          }
-          else {
-            if (!rval.member(lval)) return;
-          }
-        }
-        this.getInitStates(acts, ps, states);
-        return;
-      }
-    case OPCODE_implies:
-      {
-        Value lval = this.eval(args[0], c, ps);
-        if (!(lval instanceof BoolValue)) {
-          Assert.fail("In computing initial states of a predicate of form" +
-                      " P => Q, P was " + lval.getKindString() + "\n." + init);
-        }
-        if (((BoolValue)lval).val) {
-          this.getInitStates(args[1], acts, c, ps, states);
-        }
-        else {
-          this.getInitStates(acts, ps, states);
-        }
-        return;
-      }
-    // The following case added by LL on 13 Nov 2009 to handle subexpression names.
-    case OPCODE_nop:
-    {
-       this.getInitStates(args[0], acts, c, ps, states); 
-       return;
-    }
-    default:
-      {
-        // For all the other builtin operators, simply evaluate:
-        Value bval = this.eval(init, c, ps);
-        if (!(bval instanceof BoolValue)) {
-            
-          Assert.fail("In computing initial states, TLC expected a boolean expression," +
-                      "\nbut instead found " + bval + ".\n" + init);
-        }
-        if (((BoolValue)bval).val) {
-          this.getInitStates(acts, ps, states);
-        }
-        return;
-      }
-    }
-  }
-
-  /**
-   * This method returns the set of next states when taking the action
-   * in the given state.
-   */
-  public final StateVec getNextStates(Action action, TLCState state) {
-    ActionItemList acts = ActionItemList.Empty;
-    TLCState s1 = TLCState.Empty.createEmpty();
-    StateVec nss = new StateVec(0);
-    this.getNextStates(action.pred, acts, action.con, state, s1, nss);
-    return nss;
-  }
-
-  private final TLCState getNextStates(SemanticNode pred, ActionItemList acts, Context c,
-                                       TLCState s0, TLCState s1, StateVec nss) {
-    switch (pred.getKind()) {
-    case OpApplKind:
-      {
-        OpApplNode pred1 = (OpApplNode)pred;
-        return this.getNextStatesAppl(pred1, acts, c, s0, s1, nss);
-      }
-    case LetInKind:
-      {
-        LetInNode pred1 = (LetInNode)pred;
-        return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss);
-      }
-    case SubstInKind:
-      {
-        SubstInNode pred1 = (SubstInNode)pred;
-        Subst[] subs = pred1.getSubsts();
-        int slen = subs.length;
-        Context c1 = c;
-        for (int i = 0; i < slen; i++) {
-          Subst sub = subs[i];
-          c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false));
-        }
-        return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss);
-      }
-
-      // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-      case APSubstInKind:
-        {
-          APSubstInNode pred1 = (APSubstInNode)pred;
-          Subst[] subs = pred1.getSubsts();
-          int slen = subs.length;
-          Context c1 = c;
-          for (int i = 0; i < slen; i++) {
-            Subst sub = subs[i];
-            c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false));
-          }
-          return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss);
-        }
-
-
-    /***********************************************************************
-    * LabelKind class added by LL on 13 Jun 2007.                          *
-    ***********************************************************************/
-    case LabelKind:
-      {
-        LabelNode pred1 = (LabelNode)pred;
-        return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss);
-      }
-    default:
-      {
-        Assert.fail("The next state relation is not a boolean expression.\n" + pred);
-      }
-    }
-    return s1;
-  }
-
-  private final TLCState getNextStates(ActionItemList acts, TLCState s0, TLCState s1,
-                                       StateVec nss) {
-    TLCState resState = s1;
-
-    if (acts.isEmpty()) {
-      nss.addElement(s1);
-      resState = s1.copy();
-    }
-    else {
-      int kind = acts.carKind();
-      SemanticNode pred = acts.carPred();
-      Context c = acts.carContext();
-      ActionItemList acts1 = acts.cdr();
-      if (kind > 0) {
-        if (this.callStack != null) this.callStack.push(pred);
-        resState = this.getNextStates(pred, acts1, c, s0, s1, nss);
-        if (this.callStack != null) this.callStack.pop();       
-      }
-      else if (kind == -1) {
-        resState = this.getNextStates(pred, acts1, c, s0, s1, nss);
-      }
-      else if (kind == -2) {
-        resState = this.processUnchanged(pred, acts1, c, s0, s1, nss);
-      }
-      else {
-        Value v1 = this.eval(pred, c, s0);
-        Value v2 = this.eval(pred, c, s1);
-        if (!v1.equals(v2)) {
-          resState = this.getNextStates(acts1, s0, s1, nss);
-        }
-      }
-    }
-
-    return resState;
-  }
-  
-  private final TLCState getNextStatesAppl(OpApplNode pred, ActionItemList acts, Context c,
-                                           TLCState s0, TLCState s1, StateVec nss) {
-    ExprOrOpArgNode[] args = pred.getArgs();
-    int alen = args.length;
-    SymbolNode opNode = pred.getOperator();
-    int opcode = BuiltInOPs.getOpCode(opNode.getName());
-
-    if (opcode == 0) {
-      // This is a user-defined operator with one exception: it may
-      // be substed by a builtin operator. This special case occurs
-      // when the lookup returns an OpDef with opcode # 0.
-      Object val = this.lookup(opNode, c, s0, false);
-
-      if (val instanceof OpDefNode) {
-        OpDefNode opDef = (OpDefNode)val;
-        opcode = BuiltInOPs.getOpCode(opDef.getName());
-        if (opcode == 0) {
-          // Context c1 = this.getOpContext(opDef, args, c, false);
-          Context c1 = this.getOpContext(opDef, args, c, true);   
-          return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss);
-        }
-      }
-      
-      // Added by LL 13 Nov 2009 to fix Yuan's fix 
-      /*********************************************************************
-       * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
-       * imported with parameterized instantiation.                         *
-       *********************************************************************/
-      if (val instanceof ThmOrAssumpDefNode) {
-          ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val;
-          Context c1 = this.getOpContext(opDef, args, c, true);
-          return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss);
-        }
-
-      if (val instanceof LazyValue) {
-        LazyValue lv = (LazyValue)val;
-        if (lv.val == null || lv.val == ValUndef) {
-          return this.getNextStates(lv.expr, acts, lv.con, s0, s1, nss);
-        }
-        val = lv.val;
-      }
-
-      Object bval = val;
-      if (alen == 0) {
-        if (val instanceof MethodValue) {
-          bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear);
-        }
-      }
-      else {
-        if (val instanceof OpValue) {
-          Applicable opVal = (Applicable)val;
-          Value[] argVals = new Value[alen];
-          // evaluate the actuals:
-          for (int i = 0; i < alen; i++) {
-            argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Clear);
-          }
-          // apply the operator:
-          bval = opVal.apply(argVals, EvalControl.Clear);
-        }
-      }
-
-            if (opcode == 0)
-            {
-                if (!(bval instanceof BoolValue))
-                {
-                    Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean",
-                            bval.toString(), pred.toString() });
-                }
-                if (((BoolValue) bval).val)
-                {
-                    return this.getNextStates(acts, s0, s1, nss);
-                }
-                return s1;
-            }
-        }
-
-    TLCState resState = s1;
-    switch (opcode) {
-    case OPCODE_cl:     // ConjList
-    case OPCODE_land:      
-      {
-        ActionItemList acts1 = acts;
-        for (int i = alen - 1; i > 0; i--) {
-          acts1 = acts1.cons(args[i], c, i);
-        }
-        if (this.callStack != null) this.callStack.push(args[0]);
-        resState = this.getNextStates(args[0], acts1, c, s0, s1, nss);
-        if (this.callStack != null) this.callStack.pop();
-        return resState;
-      }
-    case OPCODE_dl:     // DisjList
-    case OPCODE_lor:      
-      {
-        for (int i = 0; i < alen; i++) {
-          if (this.callStack != null) this.callStack.push(args[i]);       
-          resState = this.getNextStates(args[i], acts, c, s0, resState, nss);
-          if (this.callStack != null) this.callStack.pop();       
-        }
-        return resState;
-      }
-    case OPCODE_be:     // BoundedExists
-      {
-        SemanticNode body = args[0];
-        ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear);
-        Context c1;
-        while ((c1 = Enum.nextElement()) != null) {
-          resState = this.getNextStates(body, acts, c1, s0, resState, nss);
-        }
-        return resState;
-      }
-    case OPCODE_bf:     // BoundedForall
-      {
-        SemanticNode body = args[0];
-        ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear);
-        Context c1 = Enum.nextElement();
-        if (c1 == null) {
-          resState = this.getNextStates(acts, s0, s1, nss);
-        }
-        else {
-          ActionItemList acts1 = acts;
-          Context c2;
-          while ((c2 = Enum.nextElement()) != null) {
-            acts1 = acts1.cons(body, c2, -1);
-          }
-          resState = this.getNextStates(body, acts1, c1, s0, s1, nss);
-        }
-        return resState;
-      }
-    case OPCODE_fa:     // FcnApply
-      {
-        Value fval = this.eval(args[0], c, s0, s1, EvalControl.KeepLazy);
-        if (fval instanceof FcnLambdaValue) {
-          FcnLambdaValue fcn = (FcnLambdaValue)fval;
-          if (fcn.fcnRcd == null) {
-            Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Clear);
-            return this.getNextStates(fcn.body, acts, c1, s0, s1, nss);
-          }
-          fval = fcn.fcnRcd;
-        }
-        if (!(fval instanceof Applicable)) {
-          Assert.fail("In computing next states, a non-function (" +
-                      fval.getKindString() + ") was applied as a function.\n" + pred);
-        }
-        Applicable fcn = (Applicable)fval;
-        Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Clear);
-        Value bval = fcn.apply(argVal, EvalControl.Clear);
-        if (!(bval instanceof BoolValue)) {
-            Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "next states", "boolean",
-                    pred.toString() });
-        }
-        if (((BoolValue)bval).val) {
-          return this.getNextStates(acts, s0, s1, nss);
-        }
-        return resState;
-      }
-    case OPCODE_aa:     // AngleAct <A>_e
-      {
-        ActionItemList acts1 = acts.cons(args[1], c, -3);
-        return this.getNextStates(args[0], acts1, c, s0, s1, nss);
-      }
-    case OPCODE_sa:     // [A]_e
-      {
-        /* The following two lines of code did not work, and were changed by
-         * YuanYu to mimic the way \/ works.  Change made
-         *  11 Mar 2009, with LL sitting next to him.
-         */
-          //    this.getNextStates(args[0], acts, c, s0, s1, nss);
-          //    return this.processUnchanged(args[1], acts, c, s0, s1, nss);
-        resState = this.getNextStates(args[0], acts, c, s0, resState, nss);
-        return this.processUnchanged(args[1], acts, c, s0, resState, nss);
-      }
-    case OPCODE_ite:    // IfThenElse
-      {
-        Value guard = this.eval(args[0], c, s0, s1, EvalControl.Clear);
-        if (!(guard instanceof BoolValue)) {
-          Assert.fail("In computing next states, a non-boolean expression (" +
-                      guard.getKindString() + ") was used as the condition of" +
-                      " an IF." + pred);
-        }
-        if (((BoolValue)guard).val) {
-          return this.getNextStates(args[1], acts, c, s0, s1, nss);
-        }
-        else {
-          return this.getNextStates(args[2], acts, c, s0, s1, nss);
-        }
-      }
-    case OPCODE_case:   // Case
-      {
-        SemanticNode other = null;
-        for (int i = 0; i < alen; i++) {
-          OpApplNode pair = (OpApplNode)args[i];
-          ExprOrOpArgNode[] pairArgs = pair.getArgs();
-          if (pairArgs[0] == null) {
-            other = pairArgs[1];
-          }
-          else {
-            Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Clear);
-            if (!(bval instanceof BoolValue)) {
-              Assert.fail("In computing next states, a non-boolean expression (" +
-                          bval.getKindString() + ") was used as a guard condition" +
-                          " of a CASE.\n" + pairArgs[1]);
-            }
-            if (((BoolValue)bval).val) {
-              return this.getNextStates(pairArgs[1], acts, c, s0, s1, nss);
-            }
-          }
-        }
-        if (other == null) {
-          Assert.fail("In computing next states, TLC encountered a CASE with no" +
-                      " conditions true.\n" + pred);
-        }
-        return this.getNextStates(other, acts, c, s0, s1, nss);
-      }
-    case OPCODE_eq:
-      {
-        SymbolNode var = this.getPrimedVar(args[0], c, false);
-        // Assert.check(var.getName().getVarLoc() >= 0);
-        if (var == null) {
-          Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear);
-          if (!((BoolValue)bval).val) {
-            return resState;
-          }
-        }
-        else {
-          UniqueString varName = var.getName();
-          Value lval = s1.lookup(varName);
-          Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear);
-          if (lval == null) {
-            resState.bind(varName, rval, pred);
-            resState = this.getNextStates(acts, s0, resState, nss);
-            resState.unbind(varName);
-            return resState;
-          }
-          else if (!lval.equals(rval)) {
-            return resState;
-          }
-        }
-        return this.getNextStates(acts, s0, s1, nss);
-      }
-    case OPCODE_in:
-      {
-        SymbolNode var = this.getPrimedVar(args[0], c, false);
-        // Assert.check(var.getName().getVarLoc() >= 0);        
-        if (var == null) {
-          Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear);
-          if (!((BoolValue)bval).val) {
-            return resState;
-          }
-        }
-        else {
-          UniqueString varName = var.getName();
-          Value lval = s1.lookup(varName);
-          Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear);
-          if (lval == null) {
-            if (!(rval instanceof Enumerable)) {
-              Assert.fail("In computing next states, the right side of \\IN" +
-                          " is not enumerable.\n" + pred);
-            }
-            ValueEnumeration Enum = ((Enumerable)rval).elements();
-            Value elem;
-            while ((elem = Enum.nextElement()) != null) {
-              resState.bind(varName, elem, pred);
-              resState = this.getNextStates(acts, s0, resState, nss);
-              resState.unbind(varName);
-            }
-            return resState;
-          }
-          else if (!rval.member(lval)) {
-            return resState;
-          }
-        }
-        return this.getNextStates(acts, s0, s1, nss);
-      }
-    case OPCODE_implies:
-      {
-        Value bval = this.eval(args[0], c, s0, s1, EvalControl.Clear);
-        if (!(bval instanceof BoolValue)) {
-            Assert.fail("In computing next states of a predicate of the form" +
-                        " P => Q, P was\n" + bval.getKindString() + ".\n" + pred);
-        }
-        if (((BoolValue)bval).val) {
-          return this.getNextStates(args[1], acts, c, s0, s1, nss);
-        }
-        else {
-          return this.getNextStates(acts, s0, s1, nss);
-        }
-      }
-    case OPCODE_unchanged:
-      {
-        return this.processUnchanged(args[0], acts, c, s0, s1, nss);
-      }
-    case OPCODE_cdot:
-      {
-        Assert.fail("The current version of TLC does not support action composition.");
-        /***
-        TLCState s01 = TLCStateFun.Empty;
-        StateVec iss = new StateVec(0);
-        this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss);
-        int sz = iss.size();
-        for (int i = 0; i < sz; i++) {
-          s01 = iss.elementAt(i);
-          this.getNextStates(args[1], acts, c, s01, s1, nss);
-        }
-        ***/
-        return s1;
-      }
-    // The following case added by LL on 13 Nov 2009 to handle subexpression names.
-    case OPCODE_nop:
-    {
-        return this.getNextStates(args[0], acts, c, s0, s1, nss); 
-    }
-    default:
-      {
-        // We handle all the other builtin operators here.
-        Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear);
-        if (!(bval instanceof BoolValue)) {
-            Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean",
-                    bval.toString(), pred.toString() });
-        }
-        if (((BoolValue)bval).val) {
-          resState = this.getNextStates(acts, s0, s1, nss);
-        }
-        return resState;
-      }
-    }
-  }
-
-  private final TLCState processUnchanged(SemanticNode expr, ActionItemList acts, Context c,
-                                          TLCState s0, TLCState s1, StateVec nss) {
-    SymbolNode var = this.getVar(expr, c, false);
-    TLCState resState = s1;
-    if (var != null) {
-      // expr is a state variable:
-      UniqueString varName = var.getName();
-      Value val0 = s0.lookup(varName);
-      Value val1 = s1.lookup(varName);
-      if (val1 == null) {
-        resState.bind(varName, val0, expr);
-        resState = this.getNextStates(acts, s0, resState, nss);
-        resState.unbind(varName);
-      }
-      else if (val0.equals(val1)) {
-        resState = this.getNextStates(acts, s0, s1, nss);
-      }
-      else {
-          MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString(), expr.toString()});
-      }
-      return resState;
-    }
-      
-    if (expr instanceof OpApplNode) {
-      OpApplNode expr1 = (OpApplNode)expr;
-      ExprOrOpArgNode[] args = expr1.getArgs();
-      int alen = args.length;
-      SymbolNode opNode = expr1.getOperator();
-      UniqueString opName = opNode.getName();
-      int opcode = BuiltInOPs.getOpCode(opName);
-
-      if (opcode == OPCODE_tup) {
-        // a tuple:
-        if (alen != 0) {
-          ActionItemList acts1 = acts;
-          for (int i = alen-1; i > 0; i--) {
-            acts1 = acts1.cons(args[i], c, -2);
-          }
-          return this.processUnchanged(args[0], acts1, c, s0, s1, nss);
-        }
-        return this.getNextStates(acts, s0, s1, nss);
-      }
-
-      if (opcode == 0 && alen == 0) {
-        // a 0-arity operator:
-        Object val = this.lookup(opNode, c, false);
-
-        if (val instanceof OpDefNode) {
-          return this.processUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1, nss);
-        }
-        else if (val instanceof LazyValue) {
-          LazyValue lv = (LazyValue)val;
-          return this.processUnchanged(lv.expr, acts, lv.con, s0, s1, nss);
-        }
-        else {
-          Assert.fail("In computing next states, TLC found the identifier\n" +
-                      opName + " undefined in an UNCHANGED expression at\n" + expr);
-        }
-        return this.getNextStates(acts, s0, s1, nss);
-      }
-    }
-    
-    Value v0 = this.eval(expr, c, s0);
-    Value v1 = this.eval(expr, c, s1, null, EvalControl.Clear);
-    if (v0.equals(v1)) {
-      resState = this.getNextStates(acts, s0, s1, nss);
-    }
-    return resState;
-  }
-
-  /* Special version of eval for state expressions. */
-  public final Value eval(SemanticNode expr, Context c, TLCState s0) {
-    return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear);
-  }
-  
-  /*
-   * This method evaluates the expression expr in the given context,
-   * current state, and partial next state.
-   */
-  public final Value eval(SemanticNode expr, Context c, TLCState s0,
-                          TLCState s1, int control) {
-    switch (expr.getKind()) {
-    /***********************************************************************
-    * LabelKind class added by LL on 13 Jun 2007.                          *
-    ***********************************************************************/
-    case LabelKind:
-      {
-        LabelNode expr1 = (LabelNode) expr;
-        return this.eval( expr1.getBody(), c, s0, s1, control) ;
-      }
-    case OpApplKind:
-      {
-        OpApplNode expr1 = (OpApplNode)expr;
-        return this.evalAppl(expr1, c, s0, s1, control);
-      }
-    case LetInKind:
-      {
-        LetInNode expr1 = (LetInNode)expr;
-        OpDefNode[] letDefs = expr1.getLets();
-        int letLen = letDefs.length;
-        Context c1 = c;
-        for (int i = 0; i < letLen; i++) {
-          OpDefNode opDef = letDefs[i];
-          if (opDef.getArity() == 0) {
-            Value rhs = new LazyValue(opDef.getBody(), c1);
-            c1 = c1.cons(opDef, rhs);
-          }
-        }
-        return this.eval(expr1.getBody(), c1, s0, s1, control);
-      }
-    case SubstInKind:
-      {
-        SubstInNode expr1 = (SubstInNode)expr;
-        Subst[] subs = expr1.getSubsts();
-        int slen = subs.length;
-        Context c1 = c;
-        for (int i = 0; i < slen; i++) {
-          Subst sub = subs[i];
-          c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true));
-        }
-        return this.eval(expr1.getBody(), c1, s0, s1, control);
-      }
-
-      // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-      case APSubstInKind:
-        {
-          APSubstInNode expr1 = (APSubstInNode)expr;
-          Subst[] subs = expr1.getSubsts();
-          int slen = subs.length;
-          Context c1 = c;
-          for (int i = 0; i < slen; i++) {
-            Subst sub = subs[i];
-            c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true));
-          }
-          return this.eval(expr1.getBody(), c1, s0, s1, control);
-        }
-
-
-    case NumeralKind:
-    case DecimalKind:
-    case StringKind:
-      {
-        return Value.getValue(expr);
-      }
-    case AtNodeKind:
-      {
-        return (Value)c.lookup(EXCEPT_AT);
-      }
-    case OpArgKind:
-      {
-        OpArgNode expr1 = (OpArgNode)expr;
-        SymbolNode opNode = expr1.getOp();
-        Object val = this.lookup(opNode, c, false);
-
-        if (val instanceof OpDefNode) {
-          return new OpLambdaValue((OpDefNode)val, this, c, s0, s1);
-        }
-        return (Value)val;
-      }
-    default:
-      {
-        Assert.fail("Attempted to evaluate an expression that cannot be evaluated.\n" +
-                    expr);
-        return null;     // make compiler happy
-      }
-    }
-  }
-
-  public final Value evalAppl(OpApplNode expr, Context c, TLCState s0,
-                              TLCState s1, int control) {
-    ExprOrOpArgNode[] args = expr.getArgs();
-    SymbolNode opNode = expr.getOperator();
-    int opcode = BuiltInOPs.getOpCode(opNode.getName());
-
-    if (opcode == 0) {
-      // This is a user-defined operator with one exception: it may
-      // be substed by a builtin operator. This special case occurs
-      // when the lookup returns an OpDef with opcode # 0.
-      if (this.callStack != null) this.callStack.push(expr);
-      Object val = this.lookup(opNode, c, s0, EvalControl.isPrimed(control));
-
-      // First, unlazy if it is a lazy value. We cannot use the cached
-      // value when s1 == null or isEnabled(control).
-      if (val instanceof LazyValue) {
-        LazyValue lv = (LazyValue)val;
-        if (s1 == null ||
-            lv.val == ValUndef ||
-            EvalControl.isEnabled(control)) {
-          val = this.eval(lv.expr, lv.con, s0, s1, control);
-        }
-        else {
-          if (lv.val == null) {
-            lv.val = this.eval(lv.expr, lv.con, s0, s1, control);
-          }
-          val = lv.val;
-        }
-      }
-
-      Value res = null;
-      if (val instanceof OpDefNode) {
-        OpDefNode opDef = (OpDefNode)val;
-        opcode = BuiltInOPs.getOpCode(opDef.getName());
-        if (opcode == 0) {
-          Context c1 = this.getOpContext(opDef, args, c, true);
-          res = this.eval(opDef.getBody(), c1, s0, s1, control);
-        }
-      }
-      else if (val instanceof Value) {
-        res = (Value)val;
-        int alen = args.length;
-        if (alen == 0) {
-          if (val instanceof MethodValue) {
-            res = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear);
-          }
-        }
-        else {
-          if (val instanceof OpValue) {
-            Applicable opVal = (Applicable)val;
-            Value[] argVals = new Value[alen];
-            // evaluate the actuals:
-            for (int i = 0; i < alen; i++) {
-              argVals[i] = this.eval(args[i], c, s0, s1, control);
-            }
-            // apply the operator:
-            res = opVal.apply(argVals, control);
-          }
-        }
-      }
-      /*********************************************************************
-      * The following added by Yuan Yu on 13 Nov 2009 to allow theorem an  *
-      * assumption names to be used as expressions.                        *
-      *                                                                    *
-      * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
-      * imported with parameterized instantiation.                         *
-      *********************************************************************/
-      else if (val instanceof ThmOrAssumpDefNode) {
-//        Assert.fail("Trying to evaluate the theorem or assumption name `"
-//                     + opNode.getName() + "'. \nUse `" + opNode.getName() 
-//                     + "!:' instead.\n" +expr);
-        ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val ;
-        Context c1 = this.getOpContext(opDef, args, c, true);
-        return this.eval(opDef.getBody(), c1, s0, s1, control);
-        }
-      else {
-        Assert.fail("In evaluation, the identifier " + opNode.getName() + " is either" +
-                    " undefined or not an operator.\n" + expr);
-      }
-      if (this.callStack != null) this.callStack.pop();
-      if (opcode == 0) return res;
-    }
-
-    switch (opcode) {
-    case OPCODE_bc:     // BoundedChoose
-      {
-        SemanticNode pred = args[0];
-        SemanticNode inExpr = expr.getBdedQuantBounds()[0];
-        Value inVal = this.eval(inExpr, c, s0, s1, control);
-        if (!(inVal instanceof Enumerable)) {
-          Assert.fail("Attempted to compute the value of an expression of\n" +
-                      "form CHOOSE x \\in S: P, but S was not enumerable.\n" + expr);
-        }
-
-        // To fix Bugzilla Bug 279 : TLC bug caused by TLC's not preserving the semantics of CHOOSE,
-        // the statement 
-        //
-        //    inVal.normalize();
-        //
-        // was replaced by the following by LL on 7 Mar 2012.  This fix has not yet received
-        // the blessing of Yuan Yu, so it should be considered to be provisional.
-        //
-        Value convertedVal = SetEnumValue.convert(inVal);
-        if (convertedVal != null) {
-            inVal = convertedVal;
-        } else {
-            inVal.normalize();
-        }
-        // end of fix.
-        
-        ValueEnumeration enumSet = ((Enumerable)inVal).elements();
-        FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; 
-        boolean isTuple = expr.isBdedQuantATuple()[0];
-        if (isTuple) {
-          // Identifier tuple case:
-          int cnt = bvars.length;
-          Value val;
-          while ((val = enumSet.nextElement()) != null) {
-            TupleValue tv = TupleValue.convert(val);
-            if (tv == null || tv.size() != cnt) {
-              Assert.fail("Attempted to compute the value of an expression of form\n" +
-                          "CHOOSE <<x1, ... , xN>> \\in S: P, but S was not a set\n" +
-                          "of N-tuples.\n" + expr);
-            }
-            Context c1 = c;
-            for (int i = 0; i < cnt; i++) {
-              c1 = c1.cons(bvars[i], tv.elems[i]);
-            }
-            Value bval = this.eval(pred, c1, s0, s1, control);
-            if (!(bval instanceof BoolValue)) {
-                Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
-            }
-            if (((BoolValue)bval).val) return val;
-          }
-        }
-        else {
-          // Simple identifier case:
-          SymbolNode name = bvars[0];
-          Value val;
-          while ((val = enumSet.nextElement()) != null) {
-            Context c1 = c.cons(name, val);
-            Value bval = this.eval(pred, c1, s0, s1, control);
-            if (!(bval instanceof BoolValue)) {
-                Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
-            }
-            if (((BoolValue)bval).val) return val;
-          }
-        }
-        Assert.fail("Attempted to compute the value of an expression of form\n" +
-                    "CHOOSE x \\in S: P, but no element of S satisfied P.\n" + expr);
-        return null;    // make compiler happy
-      }
-    case OPCODE_be:     // BoundedExists
-      {
-        ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control);
-        SemanticNode body = args[0];
-        Context c1;
-        while ((c1 = Enum.nextElement()) != null) {
-          Value bval = this.eval(body, c1, s0, s1, control);
-          if (!(bval instanceof BoolValue)) {
-              Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
-          }
-          if (((BoolValue)bval).val) return ValTrue;
-        }
-        return ValFalse;        
-      }
-    case OPCODE_bf:     // BoundedForall
-      {
-        ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control);
-        SemanticNode body = args[0];
-        Context c1;
-        while ((c1 = Enum.nextElement()) != null) {
-          Value bval = this.eval(body, c1, s0, s1, control);
-          if (!(bval instanceof BoolValue)) {
-              Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
-          }
-          if (!((BoolValue)bval).val) return ValFalse;
-        }
-        return ValTrue;
-      }
-    case OPCODE_case:   // Case
-      {
-        int alen = args.length;
-        SemanticNode other = null;
-        for (int i = 0; i < alen; i++) {
-          OpApplNode pairNode = (OpApplNode)args[i];
-          ExprOrOpArgNode[] pairArgs = pairNode.getArgs();
-          if (pairArgs[0] == null) {
-            other = pairArgs[1];
-          }
-          else {
-            Value bval = this.eval(pairArgs[0], c, s0, s1, control);
-            if (!(bval instanceof BoolValue)) {
-              Assert.fail("A non-boolean expression (" + bval.getKindString() +
-                          ") was used as a condition of a CASE. " + pairArgs[0]);
-            }
-            if (((BoolValue)bval).val) {
-              return this.eval(pairArgs[1], c, s0, s1, control);
-            }
-          }
-        }
-        if (other == null) {
-          Assert.fail("Attempted to evaluate a CASE with no conditions true.\n" + expr);
-        }
-        return this.eval(other, c, s0, s1, control);
-      }
-    case OPCODE_cp:     // CartesianProd
-      {
-        int alen = args.length;
-        Value[] sets = new Value[alen];
-        for (int i = 0; i < alen; i++) {
-          sets[i] = this.eval(args[i], c, s0, s1, control);
-        }
-        return new SetOfTuplesValue(sets);
-      }
-    case OPCODE_cl:     // ConjList
-      {
-        int alen = args.length;
-        for (int i = 0; i < alen; i++) {
-          if (this.callStack != null) this.callStack.push(args[i]);
-          Value bval = this.eval(args[i], c, s0, s1, control);
-          if (!(bval instanceof BoolValue)) {
-            Assert.fail("A non-boolean expression (" + bval.getKindString() +
-                        ") was used as a formula in a conjunction.\n" + args[i]);
-          }
-          if (this.callStack != null) this.callStack.pop();
-          if (!((BoolValue)bval).val) return ValFalse;
-        }
-        return ValTrue;
-      }
-    case OPCODE_dl:     // DisjList
-      {
-        int alen = args.length;
-        for (int i = 0; i < alen; i++) {
-          if (this.callStack != null) this.callStack.push(args[i]);
-          Value bval = this.eval(args[i], c, s0, s1, control);
-          if (!(bval instanceof BoolValue)) {
-            Assert.fail("A non-boolean expression (" + bval.getKindString() +
-                        ") was used as a formula in a disjunction.\n" + args[i]);
-          }
-          if (this.callStack != null) this.callStack.pop();
-          if (((BoolValue)bval).val) return ValTrue;
-        }
-        return ValFalse;
-      }
-    case OPCODE_exc:    // Except
-      {
-        int alen = args.length;
-        Value result = this.eval(args[0], c, s0, s1, control);
-        // SZ: variable not used ValueExcept[] expts = new ValueExcept[alen-1];
-        for (int i = 1; i < alen; i++) {
-          OpApplNode pairNode = (OpApplNode)args[i];
-          ExprOrOpArgNode[] pairArgs = pairNode.getArgs();
-          SemanticNode[] cmpts = ((OpApplNode)pairArgs[0]).getArgs();
-
-          Value[] lhs = new Value[cmpts.length];
-          for (int j = 0; j < lhs.length; j++) {
-            lhs[j] = this.eval(cmpts[j], c, s0, s1, control);
-          }
-          Value atVal = result.select(lhs);
-          if (atVal == null) {
-            // Do nothing but warn:
-              MP.printWarning(EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD, new String[]{args[0].toString()});
-          }
-          else {
-            Context c1 = c.cons(EXCEPT_AT, atVal);
-            Value rhs = this.eval(pairArgs[1], c1, s0, s1, control);
-            ValueExcept vex = new ValueExcept(lhs, rhs);
-            result = result.takeExcept(vex);
-          }
-        }
-        return result;
-      }
-    case OPCODE_fa:     // FcnApply
-      {
-        Value result = null;
-        if (this.callStack != null) this.callStack.push(expr);
-        Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(control));
-        if ((fval instanceof FcnRcdValue) ||
-            (fval instanceof FcnLambdaValue)) {
-          Applicable fcn = (Applicable)fval;
-          Value argVal = this.eval(args[1], c, s0, s1, control);
-          result = fcn.apply(argVal, control);
-        }
-        else if ((fval instanceof TupleValue) ||
-                 (fval instanceof RecordValue)) {
-          Applicable fcn = (Applicable)fval;
-          if (args.length != 2) {
-            Assert.fail("Attempted to evaluate an expression of form f[e1, ... , eN]" +
-                        "\nwith f a tuple or record and N > 1.\n" + expr);
-          }
-          Value aval = this.eval(args[1], c, s0, s1, control);
-          result = fcn.apply(aval, control);
-        }
-        else {
-          Assert.fail("A non-function (" + fval.getKindString() + ") was applied" +
-                      " as a function.\n" + expr);
-        }
-        if (this.callStack != null) this.callStack.pop();
-        return result;
-      }
-    case OPCODE_fc:     // FcnConstructor
-    case OPCODE_nrfs:   // NonRecursiveFcnSpec
-    case OPCODE_rfs:    // RecursiveFcnSpec
-      {
-        FormalParamNode[][] formals = expr.getBdedQuantSymbolLists();
-        boolean[] isTuples = expr.isBdedQuantATuple();
-        ExprNode[] domains = expr.getBdedQuantBounds();
-
-        Value[] dvals = new Value[domains.length];
-        boolean isFcnRcd = true;
-        for (int i = 0; i < dvals.length; i++) {
-          dvals[i] = this.eval(domains[i], c, s0, s1, control);
-          isFcnRcd = isFcnRcd && (dvals[i] instanceof Reducible);
-        }
-        FcnParams params = new FcnParams(formals, isTuples, dvals);
-
-        SemanticNode fbody = args[0];
-        FcnLambdaValue fval = new FcnLambdaValue(params, fbody, this, c, s0, s1, control);
-        if (opcode == OPCODE_rfs) {
-          SymbolNode fname = expr.getUnbdedQuantSymbols()[0];
-          fval.makeRecursive(fname);
-          isFcnRcd = false;
-        }
-        if (isFcnRcd && !EvalControl.isKeepLazy(control)) {
-          return fval.toFcnRcd();
-        }
-        return fval;
-      }
-    case OPCODE_ite:    // IfThenElse
-      {
-        Value bval = this.eval(args[0], c, s0, s1, control);
-        if (!(bval instanceof BoolValue)) {
-          Assert.fail("A non-boolean expression (" + bval.getKindString() +
-                      ") was used as the condition of an IF.\n" + expr);
-        }
-        if (((BoolValue)bval).val) {
-          return this.eval(args[1], c, s0, s1, control);
-        }
-        return this.eval(args[2], c, s0, s1, control);
-      }
-    case OPCODE_rc:     // RcdConstructor
-      {
-        int alen = args.length;
-        UniqueString[] names = new UniqueString[alen];
-        Value[] vals = new Value[alen];
-        for (int i = 0; i < alen; i++) {
-          OpApplNode pairNode = (OpApplNode)args[i];
-          ExprOrOpArgNode[] pair = pairNode.getArgs();
-          names[i] = ((StringValue)Value.getValue(pair[0])).getVal();
-          vals[i] = this.eval(pair[1], c, s0, s1, control);
-        }
-        return new RecordValue(names, vals, false);
-      }
-    case OPCODE_rs:     // RcdSelect
-      {
-        Value rval = this.eval(args[0], c, s0, s1, control);
-        Value sval = Value.getValue(args[1]);
-        if (rval instanceof RecordValue) {
-          Value result = ((RecordValue)rval).select(sval);
-          if (result == null) {
-            Assert.fail("Attempted to select nonexistent field " + sval + " from the" +
-                        " record\n" + Value.ppr(rval.toString()) + "\n" + expr);
-          }
-          return result;
-        }
-        else {
-          FcnRcdValue fcn = FcnRcdValue.convert(rval);
-          if (fcn == null) {
-            Assert.fail("Attempted to select field " + sval + " from a non-record" +
-                        " value " + Value.ppr(rval.toString()) + "\n" + expr);
-          }
-          return fcn.apply(sval, control);
-        }
-      }
-    case OPCODE_se:     // SetEnumerate
-      {
-        int alen = args.length;
-        ValueVec vals = new ValueVec(alen);
-        for (int i = 0; i < alen; i++) {
-          vals.addElement(this.eval(args[i], c, s0, s1, control));
-        }
-        return new SetEnumValue(vals, false);
-      }
-    case OPCODE_soa:    // SetOfAll: {e(x) : x \in S} 
-      {
-        ValueVec vals = new ValueVec();
-        ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control);
-        SemanticNode body = args[0];
-        Context c1;
-        while ((c1 = Enum.nextElement()) != null) {
-          Value val = this.eval(body, c1, s0, s1, control);
-          vals.addElement(val);
-          // vals.addElement1(val);
-        }
-        return new SetEnumValue(vals, false);
-      }
-    case OPCODE_sor:    // SetOfRcds
-      {
-        int alen = args.length;
-        UniqueString names[] = new UniqueString[alen];
-        Value vals[] = new Value[alen];
-        for (int i = 0; i < alen; i++) {
-          OpApplNode pairNode = (OpApplNode)args[i];
-          ExprOrOpArgNode[] pair = pairNode.getArgs();
-          names[i] = ((StringValue)Value.getValue(pair[0])).getVal();
-          vals[i] = this.eval(pair[1], c, s0, s1, control);
-        }
-        return new SetOfRcdsValue(names, vals, false);
-      }
-    case OPCODE_sof:    // SetOfFcns
-      {
-        Value lhs = this.eval(args[0], c, s0, s1, control);
-        Value rhs = this.eval(args[1], c, s0, s1, control);
-        return new SetOfFcnsValue(lhs, rhs);
-      }
-    case OPCODE_sso:    // SubsetOf
-      {
-        SemanticNode pred = args[0];
-        SemanticNode inExpr = expr.getBdedQuantBounds()[0];
-        Value inVal = this.eval(inExpr, c, s0, s1, control);
-        boolean isTuple = expr.isBdedQuantATuple()[0];
-        FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0];
-        if (inVal instanceof Reducible) {
-          ValueVec vals = new ValueVec();
-          ValueEnumeration enumSet = ((Enumerable)inVal).elements();
-          Value elem;
-          if (isTuple) {
-            while ((elem = enumSet.nextElement()) != null) {
-              Context c1 = c;
-              Value[] tuple = ((TupleValue)elem).elems;
-              for (int i = 0; i < bvars.length; i++) {
-                c1 = c1.cons(bvars[i], tuple[i]);
-              }
-              Value bval = this.eval(pred, c1, s0, s1, control);
-              if (!(bval instanceof BoolValue)) {
-                Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" +
-                            " when P was " + bval.getKindString() + ".\n" + pred);
-              }
-              if (((BoolValue)bval).val) {
-                vals.addElement(elem);
-              }
-            }
-          }
-          else {
-            SymbolNode idName = bvars[0];
-            while ((elem = enumSet.nextElement()) != null) {
-              Context c1 = c.cons(idName, elem);
-              Value bval = this.eval(pred, c1, s0, s1, control);
-              if (!(bval instanceof BoolValue)) {
-                Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" +
-                            " when P was " + bval.getKindString() + ".\n" + pred);
-              }
-              if (((BoolValue)bval).val) {
-                vals.addElement(elem);
-              }
-            }
-          }
-          return new SetEnumValue(vals, inVal.isNormalized());
-        }
-        else if (isTuple) {
-          return new SetPredValue(bvars, inVal, pred, this, c, s0, s1, control);
-        }
-        else {
-          return new SetPredValue(bvars[0], inVal, pred, this, c, s0, s1, control);
-        }
-      }
-    case OPCODE_tup:    // Tuple
-      {
-        int alen = args.length;
-        Value[] vals = new Value[alen];
-        for (int i = 0; i < alen; i++) {
-          vals[i] = this.eval(args[i], c, s0, s1, control);
-        }
-        return new TupleValue(vals);
-      }
-    case OPCODE_uc:     // UnboundedChoose
-      {
-        Assert.fail("TLC attempted to evaluate an unbounded CHOOSE.\n" +
-                    "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" +
-                    expr);
-        return null;    // make compiler happy
-      }      
-    case OPCODE_ue:     // UnboundedExists
-      {
-        Assert.fail("TLC attempted to evaluate an unbounded \\E.\n" +
-                    "Make sure that the expression is of form \\E x \\in S: P(x).\n" +
-                    expr);
-        return null;    // make compiler happy
-      }
-    case OPCODE_uf:     // UnboundedForall
-      {
-        Assert.fail("TLC attempted to evaluate an unbounded \\A.\n" +
-                    "Make sure that the expression is of form \\A x \\in S: P(x).\n" +
-                    expr);
-        return null;    // make compiler happy
-      }
-    case OPCODE_lnot:
-      {
-        Value arg = this.eval(args[0], c, s0, s1, control);
-        if (!(arg instanceof BoolValue)) {
-          Assert.fail("Attempted to apply the operator ~ to a non-boolean\n(" +
-                      arg.getKindString() + ")\n" + expr);
-        }
-        return (((BoolValue)arg).val) ? ValFalse : ValTrue;
-      }
-    case OPCODE_subset:
-      {
-        Value arg = this.eval(args[0], c, s0, s1, control);
-        return new SubsetValue(arg);
-      }
-    case OPCODE_union:
-      {
-        Value arg = this.eval(args[0], c, s0, s1, control);
-        return UnionValue.union(arg);   
-      }
-    case OPCODE_domain:
-      {
-        Value arg = this.eval(args[0], c, s0, s1, control);
-        if (!(arg instanceof Applicable)) {
-          Assert.fail("Attempted to apply the operator DOMAIN to a non-function\n(" +
-                      arg.getKindString() + ")\n" + expr);
-        }
-        return ((Applicable)arg).getDomain();
-      }
-    case OPCODE_enabled:
-      {
-        TLCState sfun = TLCStateFun.Empty;
-        Context c1 = Context.branch(c);
-        sfun = this.enabled(args[0], ActionItemList.Empty, c1, s0, sfun);
-        return (sfun != null) ? ValTrue : ValFalse;
-      }
-    case OPCODE_eq:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        return (arg1.equals(arg2)) ? ValTrue : ValFalse;
-      }
-    case OPCODE_land:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        if (!(arg1 instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form P /\\ Q" +
-                      " when P was\n" + arg1.getKindString() + ".\n" + expr);
-        }
-        if (((BoolValue)arg1).val) {
-          Value arg2 = this.eval(args[1], c, s0, s1, control);
-          if (!(arg2 instanceof BoolValue)) {
-            Assert.fail("Attempted to evaluate an expression of form P /\\ Q" +
-                        " when Q was\n" + arg2.getKindString() + ".\n" + expr);
-          }
-          return arg2;
-        }
-        return ValFalse;
-      }
-    case OPCODE_lor:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        if (!(arg1 instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form P \\/ Q" +
-                      " when P was\n" + arg1.getKindString() + ".\n" + expr);
-        }
-        if (((BoolValue)arg1).val) return ValTrue;
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        if (!(arg2 instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form P \\/ Q" +
-                      " when Q was\n" + arg2.getKindString() + ".\n" + expr);
-        }
-        return arg2;
-      }
-    case OPCODE_implies:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        if (!(arg1 instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form P => Q" +
-                      " when P was\n" + arg1.getKindString() + ".\n" + expr);
-        }
-        if (((BoolValue)arg1).val) {
-          Value arg2 = this.eval(args[1], c, s0, s1, control);
-          if (!(arg2 instanceof BoolValue)) {
-            Assert.fail("Attempted to evaluate an expression of form P => Q" +
-                        " when Q was\n" + arg2.getKindString() + ".\n" + expr);
-          }
-          return arg2;
-        }
-        return ValTrue;
-      }
-    case OPCODE_equiv:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        if (!(arg1 instanceof BoolValue) || !(arg2 instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form P <=> Q" +
-                      " when P or Q was not a boolean.\n" + expr);
-        }
-        BoolValue bval1 = (BoolValue)arg1;
-        BoolValue bval2 = (BoolValue)arg2;
-        return (bval1.val == bval2.val) ? ValTrue : ValFalse;
-      }
-    case OPCODE_noteq:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        return arg1.equals(arg2) ? ValFalse : ValTrue;
-      }
-    case OPCODE_subseteq:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        if (!(arg1 instanceof Enumerable)) {
-          Assert.fail("Attempted to evaluate an expression of form S \\subseteq T," +
-                      " but S was not enumerable.\n" + expr);
-        }
-        ValueEnumeration Enum = ((Enumerable)arg1).elements();
-        Value elem;
-        while ((elem = Enum.nextElement()) != null) {
-          if (!arg2.member(elem)) return ValFalse;
-        }
-        return ValTrue;
-      }
-    case OPCODE_in:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        return (arg2.member(arg1)) ? ValTrue : ValFalse;
-      }
-    case OPCODE_notin:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        return (arg2.member(arg1)) ? ValFalse : ValTrue;
-      }
-    case OPCODE_setdiff:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        if (arg1 instanceof Reducible) {
-          return ((Reducible)arg1).diff(arg2);
-        }
-        return new SetDiffValue(arg1, arg2);
-      }
-    case OPCODE_cap:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        if (arg1 instanceof Reducible) {
-          return ((Reducible)arg1).cap(arg2);
-        }
-        else if (arg2 instanceof Reducible) {
-          return ((Reducible)arg2).cap(arg1);
-        }
-        return new SetCapValue(arg1, arg2);
-      }
-
-    case OPCODE_nop:
-      /*********************************************************************
-      * Added by LL on 2 Aug 2007.                                         *
-      *********************************************************************/
-      { return eval(args[0], c, s0, s1, control) ;
-      }
-
-    case OPCODE_cup:
-      {
-        Value arg1 = this.eval(args[0], c, s0, s1, control);
-        Value arg2 = this.eval(args[1], c, s0, s1, control);
-        if (arg1 instanceof Reducible) {
-          return ((Reducible)arg1).cup(arg2);
-        }
-        else if (arg2 instanceof Reducible) {
-          return ((Reducible)arg2).cup(arg1);
-        }
-        return new SetCupValue(arg1, arg2);
-      }
-    case OPCODE_prime:
-      {
-        if (EvalControl.isEnabled(control)) {
-          // We are now in primed and enabled.
-          return this.eval(args[0], c, s1, null, EvalControl.setPrimed(control));
-        }
-        return this.eval(args[0], c, s1, null, control);
-      }
-    case OPCODE_unchanged:
-      {
-        Value v0 = this.eval(args[0], c, s0, TLCState.Empty, control);
-        if (EvalControl.isEnabled(control)) {
-          // We are now in primed and enabled.
-          control = EvalControl.setPrimed(control);
-        }
-        Value v1 = this.eval(args[0], c, s1, null, control);
-        return (v0.equals(v1)) ? ValTrue : ValFalse;
-      }
-    case OPCODE_aa:     // <A>_e          
-      {
-        Value res = this.eval(args[0], c, s0, s1, control);
-        if (!(res instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form <A>_e," +
-                      " but A was not a boolean.\n" + expr);
-        }
-        if (!((BoolValue)res).val) return ValFalse;
-        Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control);
-        if (EvalControl.isEnabled(control)) {
-          // We are now in primed and enabled. 
-          control = EvalControl.setPrimed(control);
-        }
-        Value v1 = this.eval(args[1], c, s1, null, control);
-        return v0.equals(v1) ? ValFalse : ValTrue;
-      }
-    case OPCODE_sa:     // [A]_e
-      {
-        Value res = this.eval(args[0], c, s0, s1, control);
-        if (!(res instanceof BoolValue)) {
-          Assert.fail("Attempted to evaluate an expression of form [A]_e," +
-                      " but A was not a boolean.\n" + expr);
-        }
-        if (((BoolValue)res).val) return ValTrue;
-        Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control);
-        if (EvalControl.isEnabled(control)) {
-          // We are now in primed and enabled.
-          control = EvalControl.setPrimed(control);
-        }
-        Value v1 = this.eval(args[1], c, s1, null, control);
-        return (v0.equals(v1)) ? ValTrue : ValFalse;
-      }
-    case OPCODE_cdot:
-      {
-        Assert.fail("The current version of TLC does not support action composition.");
-        /***
-        TLCState s01 = TLCStateFun.Empty;
-        StateVec iss = new StateVec(0);
-        this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss);
-        int sz = iss.size();
-        for (int i = 0; i < sz; i++) {
-          s01 = iss.elementAt(i);
-          this.eval(args[1], c, s01, s1, control);
-        }
-        ***/
-        return null;    // make compiler happy
-      }
-    case OPCODE_sf:     // SF
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"SF", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_wf:     // WF
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"WF", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_te:     // TemporalExists
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\EE", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_tf:     // TemporalForAll
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\AA", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_leadto:
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a ~> b", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_arrow:
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a -+-> formula", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_box:
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"[]A", expr.toString()});
-        return null;    // make compiler happy
-      }
-    case OPCODE_diamond:
-      {
-          Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"<>A", expr.toString()});
-        return null;    // make compiler happy
-      }
-
-    default:
-      {
-        Assert.fail("TLC BUG: could not evaluate this expression.\n" + expr);
-        return null;
-      }
-    }
-  }
-
-  /**
-   * This method determines if the argument is a valid state.  A state
-   * is good iff it assigns legal explicit values to all the global
-   * state variables.
-   */
-  public final boolean isGoodState(TLCState state) {
-    return state.allAssigned();
-  }
-
-  /* This method determines if a state satisfies the model constraints. */
-  public final boolean isInModel(TLCState state) throws EvalException {
-    ExprNode[] constrs = this.getModelConstraints();
-    for (int i = 0; i < constrs.length; i++) {
-      Value bval = this.eval(constrs[i], Context.Empty, state);
-      if (!(bval instanceof BoolValue)) {
-          Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()});
-      }
-      if (!((BoolValue)bval).val) return false;
-    }
-    return true;
-  }
-
-  /* This method determines if a pair of states satisfy the action constraints. */
-  public final boolean isInActions(TLCState s1, TLCState s2) throws EvalException {
-    ExprNode[] constrs = this.getActionConstraints();
-    for (int i = 0; i < constrs.length; i++) {
-      Value bval = this.eval(constrs[i], Context.Empty, s1, s2, EvalControl.Clear);
-      if (!(bval instanceof BoolValue)) {
-          Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()});
-      }
-      if (!((BoolValue)bval).val) return false;
-    }
-    return true;
-  }
-
-  /**
-   * This method determines if an action is enabled in the given state.
-   * More precisely, it determines if (act.pred /\ (sub' # sub)) is
-   * enabled in the state s and context act.con.
-   */
-  public final TLCState enabled(SemanticNode pred, ActionItemList acts,
-                                Context c, TLCState s0, TLCState s1) {
-    switch (pred.getKind()) {
-    case OpApplKind:
-      {
-        OpApplNode pred1 = (OpApplNode)pred;
-        return this.enabledAppl(pred1, acts, c, s0, s1);
-      }
-    case LetInKind:
-      {
-        LetInNode pred1 = (LetInNode)pred;
-        OpDefNode[] letDefs = pred1.getLets();
-        Context c1 = c;
-        for (int i = 0; i < letDefs.length; i++) {
-          OpDefNode opDef = letDefs[i];
-          if (opDef.getArity() == 0) {
-            Value rhs = new LazyValue(opDef.getBody(), c1);
-            c1 = c1.cons(opDef, rhs);
-          }
-        }
-        return this.enabled(pred1.getBody(), acts, c1, s0, s1);
-      }
-    case SubstInKind:
-      {
-        SubstInNode pred1 = (SubstInNode)pred;
-        Subst[] subs = pred1.getSubsts();
-        int slen = subs.length;
-        Context c1 = c;
-        for (int i = 0; i < slen; i++) {
-          Subst sub = subs[i];
-          c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false));
-        }
-        return this.enabled(pred1.getBody(), acts, c1, s0, s1);
-      }
-
-      // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-      case APSubstInKind:
-        {
-          APSubstInNode pred1 = (APSubstInNode)pred;
-          Subst[] subs = pred1.getSubsts();
-          int slen = subs.length;
-          Context c1 = c;
-          for (int i = 0; i < slen; i++) {
-            Subst sub = subs[i];
-            c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false));
-          }
-          return this.enabled(pred1.getBody(), acts, c1, s0, s1);
-        }
-
-
-    /***********************************************************************
-    * LabelKind class added by LL on 13 Jun 2007.                          *
-    ***********************************************************************/
-    case LabelKind:
-      {
-        LabelNode pred1 = (LabelNode)pred;
-        return this.enabled(pred1.getBody(), acts, c, s0, s1);
-      }
-    default:
-      {
-        // We should not compute enabled on anything else.
-        Assert.fail("Attempted to compute ENABLED on a non-boolean expression.\n" + pred);
-        return null;    // make compiler happy
-      }
-    }
-  }
-
-  private final TLCState enabled(ActionItemList acts, TLCState s0, TLCState s1) {
-    if (acts.isEmpty()) return s1;
-
-    int kind = acts.carKind();
-    SemanticNode pred = acts.carPred();
-    Context c = acts.carContext();
-    ActionItemList acts1 = acts.cdr();
-    if (kind > 0) {
-      if (this.callStack != null) this.callStack.push(acts.carPred());
-      TLCState res = this.enabled(pred, acts1, c, s0, s1);
-      if (this.callStack != null) this.callStack.pop();
-      return res;
-    }
-    else if (kind == -1) {
-      return this.enabled(pred, acts1, c, s0, s1);
-    }
-    if (kind == -2) {
-      return this.enabledUnchanged(pred, acts1, c, s0, s1);
-    }
-
-    Value v1 = this.eval(pred, c, s0, TLCState.Empty, EvalControl.Enabled);
-    // We are now in ENABLED and primed state.
-    Value v2 = this.eval(pred, c, s1, null, EvalControl.Primed);
-
-    if (v1.equals(v2)) return null;
-    return this.enabled(acts1, s0, s1);
-  }
-
-    private final TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1)
-    {
-        ExprOrOpArgNode[] args = pred.getArgs();
-        int alen = args.length;
-        SymbolNode opNode = pred.getOperator();
-        int opcode = BuiltInOPs.getOpCode(opNode.getName());
-
-        if (opcode == 0)
-        {
-            // This is a user-defined operator with one exception: it may
-            // be substed by a builtin operator. This special case occurs
-            // when the lookup returns an OpDef with opcode # 0.
-            Object val = this.lookup(opNode, c, s0, false);
-
-            if (val instanceof OpDefNode)
-            {
-                OpDefNode opDef = (OpDefNode) val;
-                opcode = BuiltInOPs.getOpCode(opDef.getName());
-                if (opcode == 0)
-                {
-                    // Context c1 = this.getOpContext(opDef, args, c, false);
-                    Context c1 = this.getOpContext(opDef, args, c, true);
-                    return this.enabled(opDef.getBody(), acts, c1, s0, s1);
-                }
-            }
-            
-
-            // Added 13 Nov 2009 by LL to handle theorem or assumption names
-            /*********************************************************************
-            * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
-            * imported with parameterized instantiation.                         *
-            *********************************************************************/
-            if (val instanceof ThmOrAssumpDefNode)
-            {
-                ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val;
-                Context c1 = this.getOpContext(opDef, args, c, true);
-                return this.enabled(opDef.getBody(), acts, c1, s0, s1);
-            }
-
-
-            if (val instanceof LazyValue)
-            {
-                LazyValue lv = (LazyValue) val;
-                return this.enabled(lv.expr, acts, lv.con, s0, s1);
-            }
-
-            Object bval = val;
-            if (alen == 0)
-            {
-                if (val instanceof MethodValue)
-                {
-                    bval = ((MethodValue) val).apply(EmptyArgs, EvalControl.Clear);
-                }
-            } else
-            {
-                if (val instanceof OpValue)
-                {
-                    Applicable op = (Applicable) val;
-                    Value[] argVals = new Value[alen];
-                    // evaluate the actuals:
-                    for (int i = 0; i < alen; i++)
-                    {
-                        argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Enabled);
-                    }
-                    // apply the operator:
-                    bval = op.apply(argVals, EvalControl.Enabled);
-                }
-            }
-
-            if (opcode == 0)
-            {
-                if (!(bval instanceof BoolValue))
-                {
-                    Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean",
-                            bval.toString(), pred.toString() });
-                }
-                if (((BoolValue) bval).val)
-                {
-                    return this.enabled(acts, s0, s1);
-                }
-                return null;
-            }
-        }
-
-        switch (opcode) {
-        case OPCODE_aa: // AngleAct <A>_e
-        {
-            ActionItemList acts1 = acts.cons(args[1], c, -3);
-            return this.enabled(args[0], acts1, c, s0, s1);
-        }
-        case OPCODE_be: // BoundedExists
-        {
-            SemanticNode body = args[0];
-            ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled);
-            Context c1;
-            while ((c1 = Enum.nextElement()) != null)
-            {
-                TLCState s2 = this.enabled(body, acts, c1, s0, s1);
-                if (s2 != null)
-                    return s2;
-            }
-            return null;
-        }
-        case OPCODE_bf: // BoundedForall
-        {
-            SemanticNode body = args[0];
-            ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled);
-            Context c1 = Enum.nextElement();
-            if (c1 == null)
-            {
-                return this.enabled(acts, s0, s1);
-            }
-            ActionItemList acts1 = acts;
-            Context c2;
-            while ((c2 = Enum.nextElement()) != null)
-            {
-                acts1 = acts1.cons(body, c2, -1);
-            }
-            return this.enabled(body, acts1, c1, s0, s1);
-        }
-        case OPCODE_case: // Case
-        {
-            SemanticNode other = null;
-            for (int i = 0; i < alen; i++)
-            {
-                OpApplNode pair = (OpApplNode) args[i];
-                ExprOrOpArgNode[] pairArgs = pair.getArgs();
-                if (pairArgs[0] == null)
-                {
-                    other = pairArgs[1];
-                } else
-                {
-                    Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Enabled);
-                    if (!(bval instanceof BoolValue))
-                    {
-                        Assert.fail("In computing ENABLED, a non-boolean expression(" + bval.getKindString()
-                                + ") was used as a guard condition" + " of a CASE.\n" + pairArgs[1]);
-                    }
-                    if (((BoolValue) bval).val)
-                    {
-                        return this.enabled(pairArgs[1], acts, c, s0, s1);
-                    }
-                }
-            }
-            if (other == null)
-            {
-                Assert.fail("In computing ENABLED, TLC encountered a CASE with no" + " conditions true.\n" + pred);
-            }
-            return this.enabled(other, acts, c, s0, s1);
-        }
-        case OPCODE_cl: // ConjList
-        case OPCODE_land: {
-            ActionItemList acts1 = acts;
-            for (int i = alen - 1; i > 0; i--)
-            {
-                acts1 = acts1.cons(args[i], c, i);
-            }
-            if (this.callStack != null)
-                this.callStack.push(args[0]);
-            TLCState res = this.enabled(args[0], acts1, c, s0, s1);
-            if (this.callStack != null)
-                this.callStack.pop();
-            return res;
-        }
-        case OPCODE_dl: // DisjList
-        case OPCODE_lor: {
-            for (int i = 0; i < alen; i++)
-            {
-                if (this.callStack != null)
-                    this.callStack.push(args[i]);
-                TLCState s2 = this.enabled(args[i], acts, c, s0, s1);
-                if (this.callStack != null)
-                    this.callStack.pop();
-                if (s2 != null)
-                    return s2;
-            }
-            return null;
-        }
-        case OPCODE_fa: // FcnApply
-        {
-            Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(EvalControl.Enabled));
-            if (fval instanceof FcnLambdaValue)
-            {
-                FcnLambdaValue fcn = (FcnLambdaValue) fval;
-                if (fcn.fcnRcd == null)
-                {
-                    Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Enabled);
-                    return this.enabled(fcn.body, acts, c1, s0, s1);
-                }
-                fval = fcn.fcnRcd;
-            }
-            if (fval instanceof Applicable)
-            {
-                Applicable fcn = (Applicable) fval;
-                Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Enabled);
-                Value bval = fcn.apply(argVal, EvalControl.Enabled);
-                if (!(bval instanceof BoolValue))
-                {
-                    Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "ENABLED", "boolean",
-                            pred.toString() });
-
-                }
-                if (!((BoolValue) bval).val)
-                    return null;
-            } else
-            {
-                Assert.fail("In computing ENABLED, a non-function (" + fval.getKindString()
-                        + ") was applied as a function.\n" + pred);
-            }
-            return this.enabled(acts, s0, s1);
-        }
-        case OPCODE_ite: // IfThenElse
-        {
-            Value guard = this.eval(args[0], c, s0, s1, EvalControl.Enabled);
-            if (!(guard instanceof BoolValue))
-            {
-                Assert.fail("In computing ENABLED, a non-boolean expression(" + guard.getKindString()
-                        + ") was used as the guard condition" + " of an IF.\n" + pred);
-            }
-            int idx = (((BoolValue) guard).val) ? 1 : 2;
-            return this.enabled(args[idx], acts, c, s0, s1);
-        }
-        case OPCODE_sa: // SquareAct [A]_e
-        {
-            TLCState s2 = this.enabled(args[0], acts, c, s0, s1);
-            if (s2 != null)
-                return s2;
-            return this.enabledUnchanged(args[1], acts, c, s0, s1);
-        }
-        case OPCODE_te: // TemporalExists
-        case OPCODE_tf: // TemporalForAll
-        {
-            Assert.fail("In computing ENABLED, TLC encountered temporal quantifier.\n" + pred);
-            return null; // make compiler happy
-        }
-        case OPCODE_uc: // UnboundedChoose
-        {
-            Assert.fail("In computing ENABLED, TLC encountered unbounded CHOOSE. "
-                    + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + pred);
-            return null; // make compiler happy
-        }
-        case OPCODE_ue: // UnboundedExists
-        {
-            Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. "
-                    + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + pred);
-            return null; // make compiler happy
-        }
-        case OPCODE_uf: // UnboundedForall
-        {
-            Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. "
-                    + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + pred);
-            return null; // make compiler happy
-        }
-        case OPCODE_sf: // SF
-        {
-            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[]{ "SF", pred.toString()});
-            return null; // make compiler happy
-        }
-        case OPCODE_wf: // WF
-        {
-            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "WF", pred.toString() });
-            return null; // make compiler happy
-        }
-        case OPCODE_box: {
-            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "[]", pred.toString() });
-            return null; // make compiler happy
-        }
-        case OPCODE_diamond: {
-            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "<>", pred.toString() });
-            return null; // make compiler happy
-        }
-        case OPCODE_unchanged: {
-            return this.enabledUnchanged(args[0], acts, c, s0, s1);
-        }
-        case OPCODE_eq: {
-            SymbolNode var = this.getPrimedVar(args[0], c, true);
-            if (var == null)
-            {
-                Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled);
-                if (!((BoolValue) bval).val)
-                    return null;
-            } else
-            {
-                UniqueString varName = var.getName();
-                Value lval = s1.lookup(varName);
-                Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled);
-                if (lval == null)
-                {
-                    TLCState s2 = s1.bind(var, rval, pred);
-                    return this.enabled(acts, s0, s2);
-                } else
-                {
-                    if (!lval.equals(rval))
-                        return null;
-                }
-            }
-            return this.enabled(acts, s0, s1);
-        }
-        case OPCODE_implies: {
-            Value bval = this.eval(args[0], c, s0, s1, EvalControl.Enabled);
-            if (!(bval instanceof BoolValue))
-            {
-                Assert.fail("While computing ENABLED of an expression of the form" + " P => Q, P was "
-                        + bval.getKindString() + ".\n" + pred);
-            }
-            if (((BoolValue) bval).val)
-            {
-                return this.enabled(args[1], acts, c, s0, s1);
-            }
-            return this.enabled(acts, s0, s1);
-        }
-        case OPCODE_cdot: {
-            Assert.fail("The current version of TLC does not support action composition.");
-            /***
-            TLCState s01 = TLCStateFun.Empty;
-            StateVec iss = new StateVec(0);
-            this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss);
-            int sz = iss.size();
-            for (int i = 0; i < sz; i++) {
-              s01 = iss.elementAt(i);
-              TLCState s2 = this.enabled(args[1], acts, c, s01, s1);
-              if (s2 != null) return s2;
-            }
-            ***/
-            return null; // make compiler happy
-        }
-        case OPCODE_leadto: {
-            Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a ~> b).\n" + pred);
-            return null; // make compiler happy
-        }
-        case OPCODE_arrow: {
-            Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a -+-> formula).\n" + pred);
-            return null; // make compiler happy
-        }
-        case OPCODE_in: {
-            SymbolNode var = this.getPrimedVar(args[0], c, true);
-            if (var == null)
-            {
-                Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled);
-                if (!((BoolValue) bval).val)
-                    return null;
-            } else
-            {
-                UniqueString varName = var.getName();
-                Value lval = s1.lookup(varName);
-                Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled);
-                if (lval == null)
-                {
-                    if (!(rval instanceof Enumerable))
-                    {
-                        Assert.fail("The right side of \\IN is not enumerable.\n" + pred);
-                    }
-                    ValueEnumeration Enum = ((Enumerable) rval).elements();
-                    Value val;
-                    while ((val = Enum.nextElement()) != null)
-                    {
-                        TLCState s2 = s1.bind(var, val, pred);
-                        s2 = this.enabled(acts, s0, s2);
-                        if (s2 != null)
-                            return s2;
-                    }
-                    return null;
-                } else
-                {
-                    if (!rval.member(lval))
-                        return null;
-                }
-            }
-            return this.enabled(acts, s0, s1);
-        }
-        // The following case added by LL on 13 Nov 2009 to handle subexpression names.
-        case OPCODE_nop:
-        {
-            return this.enabled(args[0], acts, c, s0, s1);
-        }
-        
-        default: {
-            // We handle all the other builtin operators here.
-            Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled);
-            if (!(bval instanceof BoolValue))
-            {
-                Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean",
-                        bval.toString(), pred.toString() });
-            }
-            if (((BoolValue) bval).val)
-            {
-                return this.enabled(acts, s0, s1);
-            }
-            return null;
-        }
-        }
-    }
-
-  private final TLCState enabledUnchanged(SemanticNode expr, ActionItemList acts,
-                                          Context c, TLCState s0, TLCState s1) {
-    SymbolNode var = this.getVar(expr, c, true);
-    if (var != null) {
-      // a state variable:
-      UniqueString varName = var.getName();
-      Value v0 = this.eval(expr, c, s0, s1, EvalControl.Enabled);
-      Value v1 = s1.lookup(varName);
-      if (v1 == null) {
-        s1 = s1.bind(var, v0, expr);
-        return this.enabled(acts, s0, s1);
-      }
-      if (v1.equals(v0)) {
-        return this.enabled(acts, s0, s1);
-      }
-      MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString() , expr.toString()});
-      return null;
-    }
-      
-    if (expr instanceof OpApplNode) {
-      OpApplNode expr1 = (OpApplNode)expr;
-      ExprOrOpArgNode[] args = expr1.getArgs();
-      int alen = args.length;
-      SymbolNode opNode = expr1.getOperator();
-      UniqueString opName = opNode.getName();
-      int opcode = BuiltInOPs.getOpCode(opName);
-
-      if (opcode == OPCODE_tup) {
-        // a tuple:
-        if (alen != 0) {
-          ActionItemList acts1 = acts;
-          for (int i = 1; i < alen; i++) {
-            acts1 = acts1.cons(args[i], c, -2);
-          }
-          return this.enabledUnchanged(args[0], acts1, c, s0, s1);
-        }
-        return this.enabled(acts, s0, s1);      
-      }
-
-      if (opcode == 0 && alen == 0) {
-        // a 0-arity operator:
-        Object val = this.lookup(opNode, c, false);
-
-        if (val instanceof LazyValue) {
-          LazyValue lv = (LazyValue)val;
-          return this.enabledUnchanged(lv.expr, acts, lv.con, s0, s1);
-        }
-        else if (val instanceof OpDefNode) {
-          return this.enabledUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1);
-        }
-        else if (val == null) {
-          Assert.fail("In computing ENABLED, TLC found the undefined identifier\n" +
-                      opName + " in an UNCHANGED expression at\n" + expr);
-        }
-        return this.enabled(acts, s0, s1);
-      }
-    }
-
-    Value v0 = this.eval(expr, c, s0, TLCState.Empty, EvalControl.Enabled);
-    Value v1 = this.eval(expr, c, s1, TLCState.Empty, EvalControl.Primed);
-    if (!v0.equals(v1)) return null;
-    return this.enabled(acts, s0, s1);
-  }
-
-  /* This method determines if the action predicate is valid in (s0, s1). */
-  public final boolean isValid(Action act, TLCState s0, TLCState s1) {
-    Value val = this.eval(act.pred, act.con, s0, s1, EvalControl.Clear);
-    if (!(val instanceof BoolValue)) {
-        Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", act.pred.toString()});
-    }
-    return ((BoolValue)val).val;
-  }
-  
-  /* Returns true iff the predicate is valid in the state. */
-  public final boolean isValid(Action act, TLCState state) {
-    return this.isValid(act, state, TLCState.Empty);
-  }
-
-  /* Returns true iff the predicate is valid in the state. */
-  public final boolean isValid(Action act) {
-    return this.isValid(act, TLCState.Empty, TLCState.Empty);
-  }
-
-  public final boolean isValid(ExprNode expr) {
-    Value val = this.eval(expr, Context.Empty, TLCState.Empty);
-    if (!(val instanceof BoolValue)) {
-        Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
-    }
-    return ((BoolValue)val).val;
-  }
-  
-  /* Reconstruct the initial state whose fingerprint is fp. */
-  public final TLCStateInfo getState(long fp) {
-    StateVec initStates = this.getInitStates();
-    for (int i = 0; i < initStates.size(); i++) {
-      TLCState state = initStates.elementAt(i);
-      long nfp = state.fingerPrint();
-      if (fp == nfp) {
-        String info = "<Initial predicate>";
-        return new TLCStateInfo(state, info);
-      }
-    }
-    return null;
-  }
-  
-  /* Reconstruct the next state of state s whose fingerprint is fp. */
-  public final TLCStateInfo getState(long fp, TLCState s) {
-    for (int i = 0; i < this.actions.length; i++) {
-      Action curAction = this.actions[i];
-      StateVec nextStates = this.getNextStates(curAction, s);
-      for (int j = 0; j < nextStates.size(); j++) {
-        TLCState state = nextStates.elementAt(j);
-        long nfp = state.fingerPrint();
-        if (fp == nfp) {
-          return new TLCStateInfo(state, curAction.getLocation());
-        }
-      }
-    }
-    return null;
-  }
-
-  /* Reconstruct the info for s1.   */
-  public final TLCStateInfo getState(TLCState s1, TLCState s) {
-    for (int i = 0; i < this.actions.length; i++) {
-      Action curAction = this.actions[i];
-      StateVec nextStates = this.getNextStates(curAction, s);
-      for (int j = 0; j < nextStates.size(); j++) {
-        TLCState state = nextStates.elementAt(j);
-        if (s1.equals(state)) {
-          return new TLCStateInfo(state, curAction.getLocation());
-        }
-      }
-    }
-    return null;
-  }
-
-  /* Return the set of all permutations under the symmetry assumption. */
-  public final MVPerm[] getSymmetryPerms() {
-    String name = this.config.getSymmetry();
-    if (name.length() == 0) return null;
-    Object symm = this.defns.get(name);
-    if (symm == null) {
-        Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "symmetry function", name});
-    }
-    if (!(symm instanceof OpDefNode)) {
-      Assert.fail("The symmetry function " + name + " must specify a set of permutations.");
-    }
-    Value fcns = this.eval(((OpDefNode)symm).getBody(), Context.Empty, TLCState.Empty);
-    if (!(fcns instanceof Enumerable)) {
-      Assert.fail("The symmetry operator must specify a set of functions.");
-    }
-    ValueEnumeration Enum = ((Enumerable)fcns).elements();
-    return MVPerm.permutationSubgroup(Enum);
-  }
-
-  public final Context getFcnContext(FcnLambdaValue fcn, ExprOrOpArgNode[] args,
-                                     Context c, TLCState s0, TLCState s1,
-                                     int control) {
-    Context fcon = fcn.con;
-    int plen = fcn.params.length();
-    FormalParamNode[][] formals = fcn.params.formals;
-    Value[] domains = fcn.params.domains;
-    boolean[] isTuples = fcn.params.isTuples;
-    Value argVal = this.eval(args[1], c, s0, s1, control);
-    
-    if (plen == 1) {
-      if (!domains[0].member(argVal)) {
-        Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) +
-                    ",\nthe first argument is:\n" + Value.ppr(argVal.toString()) +
-                    "which is not in its domain.\n" + args[0]);
-      }
-      if (isTuples[0]) {
-        FormalParamNode[] ids = formals[0];
-        TupleValue tv = TupleValue.convert(argVal);
-        if (tv == null || argVal.size() != ids.length) {
-          Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-                      ",\nthe argument is:\n" + Value.ppr(argVal.toString()) +
-                      "which does not match its formal parameter.\n" + args[0]);
-        }
-        Value[] elems = tv.elems;
-        for (int i = 0; i < ids.length; i++) {
-          fcon = fcon.cons(ids[i], elems[i]);
-        }
-      }
-      else {
-        fcon = fcon.cons(formals[0][0], argVal);
-      }
-    }
-    else {
-      TupleValue tv = TupleValue.convert(argVal);
-      if (tv == null) {
-        Assert.fail("Attempted to apply a function to an argument not in its" +
-                    " domain.\n" + args[0]);
-      }
-      int argn = 0;      
-      Value[] elems = tv.elems;
-      for (int i = 0; i < formals.length; i++) {
-        FormalParamNode[] ids = formals[i];
-        Value domain = domains[i];        
-        if (isTuples[i]) {
-          if (!domain.member(elems[argn])) {
-            Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) +
-                        ",\nthe argument number " + (argn+1) + " is:\n" +
-                        Value.ppr(elems[argn].toString()) +
-                        "\nwhich is not in its domain.\n" + args[0]);
-          }
-          TupleValue tv1 = TupleValue.convert(elems[argn++]);
-          if (tv1 == null || tv1.size() != ids.length) {
-            Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) +
-                        ",\nthe argument number " + argn + " is:\n" +
-                        Value.ppr(elems[argn-1].toString()) +
-                        "which does not match its formal parameter.\n" + args[0]);
-          }
-          Value[] avals = tv1.elems;
-          for (int j = 0; j < ids.length; j++) {
-            fcon = fcon.cons(ids[j], avals[j]);
-          }       
-        }
-        else {
-          for (int j = 0; j < ids.length; j++) {
-            if (!domain.member(elems[argn])) {
-              Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) +
-                          ",\nthe argument number " + (argn+1) + " is:\n" +
-                          Value.ppr(elems[argn].toString()) +
-                          "which is not in its domain.\n" + args[0]);
-            }
-            fcon = fcon.cons(ids[j], elems[argn++]);
-          }
-        }
-      }
-    }
-    return fcon;
-  }
-
-  /* A context enumerator for an operator application. */
-  public final ContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0,
-                                          TLCState s1, int control) {
-    FormalParamNode[][] formals = appl.getBdedQuantSymbolLists();
-    boolean[] isTuples = appl.isBdedQuantATuple();
-    ExprNode[] domains = appl.getBdedQuantBounds();
-
-    int flen = formals.length;
-    int alen = 0;
-    for (int i = 0; i < flen; i++) {
-      alen += (isTuples[i]) ? 1 : formals[i].length;
-    }
-    Object[] vars = new Object[alen];
-    ValueEnumeration[] enums = new ValueEnumeration[alen];
-    int idx = 0;
-    for (int i = 0; i < flen; i++) {
-      Value boundSet = this.eval(domains[i], c, s0, s1, control);
-      if (!(boundSet instanceof Enumerable)) {
-        Assert.fail("TLC encountered a non-enumerable quantifier bound\n" +
-                    Value.ppr(boundSet.toString()) + ".\n" + domains[i]);
-      }
-      FormalParamNode[] farg = formals[i];
-      if (isTuples[i]) {
-        vars[idx] = farg;
-        enums[idx++] = ((Enumerable)boundSet).elements();
-      }
-      else {
-        for (int j = 0; j < farg.length; j++) {
-          vars[idx] = farg[j];
-          enums[idx++] = ((Enumerable)boundSet).elements();
-        }
-      }
-    }
-    return new ContextEnumerator(vars, enums, c);
-  }
-
-  /**
-   * This method converts every definition that is constant into TLC
-   * value. By doing this, TLC avoids evaluating the same expression
-   * multiple times.
-   * 
-   * The method runs for every module in the module tables.
-   * 
-   * Modified by LL on 23 July 2013 so it is not run for modules that are
-   * instantiated and have parameters (CONSTANT or VARIABLE declarations)
-   */
-  private void processConstantDefns() {
-      ModuleNode[] mods = this.moduleTbl.getModuleNodes();
-      for (int i = 0; i < mods.length; i++) {
-    	  if (   (! mods[i].isInstantiated())
-    		  || (   (mods[i].getConstantDecls().length == 0)
-    			  && (mods[i].getVariableDecls().length == 0) ) ) {
-            this.processConstantDefns(mods[i]);
-    	  }
-      }
-  }
-
-  /**
-   * Converts the constant definitions in the corresponding value for the 
-   * module -- that is, it "converts" (which seems to mean calling deepNormalize)
-   * the values substituted for the declared constants.  On 17 Mar 2012 it was
-   * modified by LL to evaluate the OpDefNode when a defined operator is substituted
-   * for an ordinary declared constant (not a declared operator constant).  Without this
-   * evaluation, the definition gets re-evaluated every time TLC evaluates the declared
-   * constant.  LL also added a check that an operator substituted for the declared
-   * constant also has the correct arity.
-   *   
-   * @param mod the module to run on
-   */
-  private void processConstantDefns(ModuleNode mod) {
-
-      // run for constant definitions
-      OpDeclNode[] consts = mod.getConstantDecls();
-      for (int i = 0; i < consts.length; i++) {
-          Object val = consts[i].getToolObject(TLCGlobals.ToolId);
-          if (val != null && val instanceof Value) {
-              ((Value)val).deepNormalize();
-              // System.err.println(consts[i].getName() + ": " + val);        
-          } // The following else clause was added by LL on 17 March 2012.
-            else if (val != null && val instanceof OpDefNode) {
-              OpDefNode opDef = (OpDefNode) val;
-              // The following check logically belongs in Spec.processSpec, but it's not there.
-              // So, LL just added it here.  This error cannot occur when running TLC from
-              // the Toolbox.
-              Assert.check(opDef.getArity() == consts[i].getArity(), 
-                           EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS,
-                           new String[] {consts[i].getName().toString(), opDef.getName().toString()});
-                            
-              if (opDef.getArity() == 0) {
-                Value defVal = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty);
-                defVal.deepNormalize();
-                consts[i].setToolObject(TLCGlobals.ToolId, defVal);
-              }
-          }
-      }
-
-      // run for constant operator definitions
-      OpDefNode[] opDefs = mod.getOpDefs();
-      for (int i = 0; i < opDefs.length; i++) {
-          OpDefNode opDef = opDefs[i]; 
-          
-          // The following variable evaluate and its value added by LL on 24 July 2013 
-          // to prevent pre-evaluation of a definition from an EXTENDS of a module that 
-          // is also instantiated.
-          ModuleNode moduleNode = opDef.getOriginallyDefinedInModuleNode() ;
-          boolean evaluate =    (moduleNode == null)
-        		             || (! moduleNode.isInstantiated())
-        		             || (   (moduleNode.getConstantDecls().length == 0)
-        		                 && (moduleNode.getVariableDecls().length == 0) ) ;
-
-          if (evaluate && opDef.getArity() == 0) {
-              Object realDef = this.lookup(opDef, Context.Empty, false);
-              if (realDef instanceof OpDefNode) {
-                  opDef = (OpDefNode)realDef;
-                  if (this.getLevelBound(opDef.getBody(), Context.Empty) == 0) {
-                      try {
-                          UniqueString opName = opDef.getName();
-                          // System.err.println(opName);
-                          Value val = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty);
-                          val.deepNormalize();
-                          // System.err.println(opName + ": " + val);
-                          opDef.setToolObject(TLCGlobals.ToolId, val);
-                          Object def = this.defns.get(opName);
-                          if (def == opDef) {
-                              this.defns.put(opName, val);
-                          }
-                      }
-                      catch (Throwable e) {
-                          // Assert.printStack(e);
-                      }
-                  }
-              }
-          }
-      }
-
-      // run for all inner modules
-      ModuleNode[] imods = mod.getInnerModules();
-      for (int i = 0; i < imods.length; i++) {
-          this.processConstantDefns(imods[i]);
-      }
-  }
-
-}
diff --git a/tlatools/src/tlc2/tool/ToolGlobals.java b/tlatools/src/tlc2/tool/ToolGlobals.java
index 90fe0a8a45eb9ed741168dff078dfb23a3fc1388..2d6fa5ed720ffe8903351a1677360bb0d3e080ba 100644
--- a/tlatools/src/tlc2/tool/ToolGlobals.java
+++ b/tlatools/src/tlc2/tool/ToolGlobals.java
@@ -7,7 +7,6 @@ package tlc2.tool;
 
 import tla2sany.semantic.ASTConstants;
 import tla2sany.semantic.OpDefNode;
-import tlc2.value.Value;
 import util.UniqueString;
 
 public interface ToolGlobals extends ASTConstants {
@@ -15,8 +14,6 @@ public interface ToolGlobals extends ASTConstants {
    * This interface provides useful globals for the implementation
    * of the tools.
    */
-
-  public static final Value[] EmptyArgs = new Value[0];
   // SZ 11.04.2009: changed the method to the equivalent 
   public static final OpDefNode EXCEPT_AT = new OpDefNode(UniqueString.uniqueStringOf("@"));
   
diff --git a/tlatools/src/tlc2/tool/Worker.java b/tlatools/src/tlc2/tool/Worker.java
index 3b98529ead182a74fc13c6b90bf1f0f5fc473a86..43d76b3320b5157f1d357d52f94591f583c00c31 100644
--- a/tlatools/src/tlc2/tool/Worker.java
+++ b/tlatools/src/tlc2/tool/Worker.java
@@ -5,53 +5,69 @@
 
 package tlc2.tool;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.fp.FPSet;
+import tlc2.tool.impl.FastTool;
 import tlc2.tool.queue.IStateQueue;
+import tlc2.util.BufferedRandomAccessFile;
+import tlc2.util.IStateWriter;
 import tlc2.util.IdThread;
-import tlc2.util.ObjLongTable;
-import tlc2.value.Value;
+import tlc2.util.SetOfStates;
+import tlc2.util.statistics.FixedSizedBucketStatistics;
+import tlc2.util.statistics.IBucketStatistics;
+import util.Assert.TLCRuntimeException;
+import util.FileUtil;
+import util.WrongInvocationException;
+
+public final class Worker extends IdThread implements IWorker, INextStateFunctor {
 
-public class Worker extends IdThread implements IWorker {
+	protected static final boolean coverage = TLCGlobals.isCoverageEnabled();
+	private static final int INITIAL_CAPACITY = 16;
 	
 	/**
 	 * Multi-threading helps only when running on multiprocessors. TLC can
 	 * pretty much eat up all the cycles of a processor running single threaded.
 	 * We expect to get linear speedup with respect to the number of processors.
 	 */
-	private ModelChecker tlc;
-	private IStateQueue squeue;
-	private ObjLongTable astCounts;
-	private Value[] localValues;
+	private final ModelChecker tlc;
+	private final FastTool tool;
+	private final IStateQueue squeue;
+	private final FPSet theFPSet;
+	private final IStateWriter allStateWriter;
+	private final IBucketStatistics outDegree;
+	private final String filename;
+	private final BufferedRandomAccessFile raf;
+	private final boolean checkDeadlock;
+
+	private long lastPtr;
+	private long statesGenerated;
+	private int unseenSuccessorStates = 0;
+	private volatile int maxLevel = 0;
 
 	// SZ Feb 20, 2009: changed due to super type introduction
-	public Worker(int id, AbstractChecker tlc) {
+	public Worker(int id, AbstractChecker tlc, String metadir, String specFile) throws IOException {
 		super(id);
 		// SZ 12.04.2009: added thread name
 		this.setName("TLC Worker " + id);
 		this.tlc = (ModelChecker) tlc;
+		this.checkLiveness = this.tlc.checkLiveness;
+		this.checkDeadlock = this.tlc.checkDeadlock;
+		this.tool = (FastTool) this.tlc.tool;
 		this.squeue = this.tlc.theStateQueue;
-		this.astCounts = new ObjLongTable(10);
-		this.localValues = new Value[4];
+		this.theFPSet = this.tlc.theFPSet;
+		this.allStateWriter = this.tlc.allStateWriter;
+		this.outDegree = new FixedSizedBucketStatistics(this.getName(), 32); // maximum outdegree of 32 appears sufficient for now.
 		this.setName("TLCWorkerThread-" + String.format("%03d", id));
-	}
-
-  public final ObjLongTable getCounts() { return this.astCounts; }
-
-	public Value getLocalValue(int idx) {
-		if (idx < this.localValues.length) {
-			return this.localValues[idx];
-		}
-		return null;
-	}
 
-	public void setLocalValue(int idx, Value val) {
-		if (idx >= this.localValues.length) {
-			Value[] vals = new Value[idx + 1];
-			System.arraycopy(this.localValues, 0, vals, 0, this.localValues.length);
-			this.localValues = vals;
-		}
-		this.localValues[idx] = val;
+		this.filename = metadir + FileUtil.separator + specFile + "-" + myGetId();
+		this.raf = new BufferedRandomAccessFile(filename + TLCTrace.EXT, "rw");
 	}
 
 	/**
@@ -59,11 +75,11 @@ public class Worker extends IdThread implements IWorker {
    * possible next states of the state, checks the invariants, and
    * updates the state set and state queue.
 	 */
-	public final void run() {
+	public void run() {
 		TLCState curState = null;
 		try {
 			while (true) {
-				curState = (TLCState) this.squeue.sDequeue();
+				curState = this.squeue.sDequeue();
 				if (curState == null) {
 					synchronized (this.tlc) {
 						this.tlc.setDone();
@@ -72,16 +88,44 @@ public class Worker extends IdThread implements IWorker {
 					this.squeue.finishAll();
 					return;
 				}
-				if (this.tlc.doNext(curState, this.astCounts))
-					return;
+				setCurrentState(curState);
+				
+				if (this.checkLiveness) {
+					// Allocate iff liveness is checked.
+					setOfStates = createSetOfStates();
+				}
+				
+				final long preNext = this.statesGenerated;
+				try {
+					this.tool.getNextStates(this, curState);
+				} catch (TLCRuntimeException | EvalException e) {
+					// The next-state relation couldn't be evaluated.
+					this.tlc.doNextFailed(curState, null, e);
+				}
+				
+				if (this.checkDeadlock && preNext == this.statesGenerated) {
+					// A deadlock is defined as a state without (seen or unseen) successor
+					// states. In other words, evaluating the next-state relation for a state
+					// yields no states.
+	                this.tlc.doNextSetErr(curState, null, false, EC.TLC_DEADLOCK_REACHED, null);
+				}
+				
+	            // Finally, add curState into the behavior graph for liveness checking:
+	            if (this.checkLiveness)
+	            {
+					doNextCheckLiveness(curState, setOfStates);
+	            }
+				
+				this.outDegree.addSample(unseenSuccessorStates);
+				unseenSuccessorStates = 0;
 			}
 		} catch (Throwable e) {
 			// Something bad happened. Quit ...
 			// Assert.printStack(e);
+			resetCurrentState();
 			synchronized (this.tlc) {
-				if (this.tlc.setErrState(curState, null, true)) {
-					MP.printError(EC.GENERAL, e); // LL changed call 7 April
-													// 2012
+				if (this.tlc.setErrState(curState, null, true, EC.GENERAL)) {
+					MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012
 				}
 				this.squeue.finishAll();
 				this.tlc.notify();
@@ -89,4 +133,332 @@ public class Worker extends IdThread implements IWorker {
 			return;
 		}
 	}
+	
+	/* Liveness */
+	
+	private int multiplier = 1;
+
+	private SetOfStates setOfStates;
+
+	private final boolean checkLiveness;
+
+	private final void doNextCheckLiveness(TLCState curState, SetOfStates liveNextStates) throws IOException {
+		final long curStateFP = curState.fingerPrint();
+
+		// Add the stuttering step:
+		liveNextStates.put(curStateFP, curState);
+		this.tlc.allStateWriter.writeState(curState, curState, true, IStateWriter.Visualization.STUTTERING);
+
+		this.tlc.liveCheck.addNextState(tlc.tool, curState, curStateFP, liveNextStates);
+
+		if (liveNextStates.capacity() > (multiplier * INITIAL_CAPACITY)) {
+			// Increase initial size for as long as the set has to grow
+			multiplier++;
+		}
+	}
+	
+	private final SetOfStates createSetOfStates() {
+		return new SetOfStates(multiplier * INITIAL_CAPACITY);
+	}
+	
+	/* Statistics */
+
+	final void incrementStatesGenerated(long l) {
+		this.statesGenerated += l;		
+	}
+	
+	final long getStatesGenerated() {
+		return this.statesGenerated;
+	}
+
+	public final IBucketStatistics getOutDegree() {
+		return this.outDegree;
+	}
+	
+	public final int getMaxLevel() {
+		return maxLevel;
+	}
+
+	final void setLevel(int level) {
+		maxLevel = level;
+	}
+
+	/* Maintain trace file (to reconstruct error-trace) */
+	
+	/*
+	 * Synchronize reads and writes to read a consistent union of all trace file
+	 * fragments when one worker W wants to create the counter-example. Otherwise, we
+	 * might not be able to correctly trace the path from state to an initial state.
+	 * The W thread holds ModelChecker.this. The other workers might either: a) Wait
+	 * on IStateQueue#sDequeue (waiting for a new state to be read from disk or
+	 * added to the queue) b) Wait on ModelChecker.this (because they also found
+	 * another counter-example but are blocked until we are done printing it) c)
+	 * Wait on ModelChecker.this in Worker#run because the state queue is empty and
+	 * they which to terminate. d) Run state space exploration The on-disk file of
+	 * each worker's trace fragment is potentially inconsistent because the worker's
+	 * cache in BufferedRandomAccessFile hasn't been flushed out.
+	 */
+	
+	public final synchronized void writeState(final TLCState initialState, final long fp) throws IOException {
+		// Write initial state to trace file.
+		this.lastPtr = this.raf.getFilePointer();
+		this.raf.writeLongNat(1L);
+		this.raf.writeShortNat(myGetId());
+		this.raf.writeLong(fp);
+		
+		// Add predecessor pointer to success state.
+		initialState.workerId = (short) myGetId();
+		initialState.uid = this.lastPtr;
+	}
+
+	public final synchronized void writeState(final TLCState curState, final long sucStateFp, final TLCState sucState) throws IOException {
+		// Keep track of maximum diameter.
+		maxLevel = Math.max(curState.getLevel() + 1, maxLevel);
+		
+		// Write to trace file.
+		this.lastPtr = this.raf.getFilePointer();
+		this.raf.writeLongNat(curState.uid);
+		this.raf.writeShortNat(curState.workerId);
+		this.raf.writeLong(sucStateFp);
+		
+		// Add predecessor pointer to success state.
+		sucState.workerId = (short) myGetId();
+		sucState.uid = this.lastPtr;
+		
+		sucState.setPredecessor(curState);
+		
+    	unseenSuccessorStates++;
+		
+//		System.err.println(String.format("<<%s, %s>>: pred=<<%s, %s>>, %s -> %s", myGetId(), this.lastPtr, 
+//				curState.uid, curState.workerId,
+//				curState.fingerPrint(), sucStateFp));
+	}
+
+	// Read from previously written (see writeState) trace file.
+	public final synchronized ConcurrentTLCTrace.Record readStateRecord(final long ptr) throws IOException {
+		// Remember current tip of the file before we rewind.
+		this.raf.mark();
+		
+		// rewind to position we want to read from.
+		this.raf.seek(ptr);
+		
+		final long prev = this.raf.readLongNat();
+		assert 0 <= ptr;
+		
+		final int worker = this.raf.readShortNat();
+		assert 0 <= worker && worker < tlc.workers.length;
+			
+		final long fp = this.raf.readLong();
+		assert tlc.theFPSet.contains(fp);
+		
+		// forward/go back back to tip of file.
+		// This is only necessary iff TLC runs with '-continue'. In other words, state
+		// space exploration continues after an error trace has been written.
+		this.raf.seek(this.raf.getMark());
+		
+		return new ConcurrentTLCTrace.Record(prev, worker, fp);
+	}
+	
+	/* Checkpointing */
+
+	public final synchronized void beginChkpt() throws IOException {
+		this.raf.flush();
+		final DataOutputStream dos = FileUtil.newDFOS(filename + ".tmp");
+		dos.writeLong(this.raf.getFilePointer());
+		dos.writeLong(this.lastPtr);
+		dos.close();
+	}
+
+	public final synchronized void commitChkpt() throws IOException {
+		final File oldChkpt = new File(filename + ".chkpt");
+		final File newChkpt = new File(filename + ".tmp");
+		if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) {
+			throw new IOException("Trace.commitChkpt: cannot delete " + oldChkpt);
+		}
+	}
+
+	public final void recover() throws IOException {
+		final DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt");
+		final long filePos = dis.readLong();
+		this.lastPtr = dis.readLong();
+		dis.close();
+		this.raf.seek(filePos);
+	}
+	
+	/* Enumerator */
+	
+	public final Enumerator elements() throws IOException {
+		return new Enumerator();
+	}
+
+	public class Enumerator {
+
+		private final long len;
+		private final BufferedRandomAccessFile enumRaf;
+
+		Enumerator() throws IOException {
+			this.len = raf.getFilePointer();
+			this.enumRaf = new BufferedRandomAccessFile(filename + TLCTrace.EXT, "r");
+		}
+
+		public boolean hasMoreFP() {
+			final long fpos = this.enumRaf.getFilePointer();
+			if (fpos < this.len) {
+				return true;
+			}
+			return false;
+		}
+
+		public long nextFP() throws IOException {
+			this.enumRaf.readLongNat(); /* drop */
+			this.enumRaf.readShortNat(); /* drop */
+			return this.enumRaf.readLong();
+		}
+
+		public void close() throws IOException {
+			this.enumRaf.close();
+		}
+	}
+	
+	//**************************************************************//
+
+	@Override
+	public final Object addElement(final TLCState state) {
+		throw new WrongInvocationException("tlc2.tool.Worker.addElement(TLCState) should not be called");
+	}
+
+	@Override
+	public final Object addElement(final TLCState curState, final Action action, final TLCState succState) {
+	    if (coverage) { action.cm.incInvocations(); }
+		this.statesGenerated++;
+		
+		try {
+			if (!this.tool.isGoodState(succState)) {
+				this.tlc.doNextSetErr(curState, succState, action);
+				throw new InvariantViolatedException();
+			}
+			
+			// Check if state is excluded by a state or action constraint.
+			final boolean inModel = (this.tool.isInModel(succState) && this.tool.isInActions(curState, succState));
+			
+			// Check if state is new or has been seen earlier.
+			boolean unseen = true;
+			if (inModel) {
+				unseen = !isSeenState(curState, succState, action);
+			}
+			
+			// Check if succState violates any invariant:
+			if (unseen) {
+				if (this.doNextCheckInvariants(curState, succState)) {
+					throw new InvariantViolatedException();
+				}
+			}
+			
+			// Check if the state violates any implied action. We need to do it
+			// even if succState is not new.
+			if (this.doNextCheckImplied(curState, succState)) {
+				throw new InvariantViolatedException();
+			}
+			
+			if (inModel && unseen) {
+				// The state is inModel, unseen and neither invariants
+				// nor implied actions are violated. It is thus eligible
+				// for further processing by other workers.
+				this.squeue.sEnqueue(succState);
+			}
+			return this;
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private final boolean isSeenState(final TLCState curState, final TLCState succState, final Action action)
+			throws IOException {
+		final long fp = succState.fingerPrint();
+		final boolean seen = this.theFPSet.put(fp);
+		// Write out succState when needed:
+		this.allStateWriter.writeState(curState, succState, !seen, action);
+		if (!seen) {
+			// Write succState to trace only if it satisfies the
+			// model constraints. Do not enqueue it yet, but wait
+			// for implied actions and invariants to be checked.
+			// Those checks - if violated - will cause model checking
+			// to terminate. Thus we cannot let concurrent workers start
+			// exploring this new state. Conversely, the state has to
+			// be in the trace in case either invariant or implied action
+			// checks want to print the trace.
+			this.writeState(curState, fp, succState);
+			if (coverage) {	action.cm.incSecondary(); }
+		}
+		// For liveness checking:
+		if (this.checkLiveness)
+		{
+			this.setOfStates.put(fp, succState);
+		}
+		return seen;
+	}
+
+	private final boolean doNextCheckInvariants(final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception {
+        int k = 0;
+		try
+        {
+			for (k = 0; k < this.tool.getInvariants().length; k++)
+            {
+                if (!tool.isValid(this.tool.getInvariants()[k], succState))
+                {
+                    // We get here because of invariant violation:
+                	if (TLCGlobals.continuation) {
+                        synchronized (this.tlc)
+                        {
+							MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
+									this.tool.getInvNames()[k]);
+							this.tlc.trace.printTrace(curState, succState);
+							return false;
+                        }
+                	} else {
+						return this.tlc.doNextSetErr(curState, succState, false,
+								EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, this.tool.getInvNames()[k]);
+                	}
+				}
+			}
+        } catch (Exception e)
+        {
+			this.tlc.doNextEvalFailed(curState, succState, EC.TLC_INVARIANT_EVALUATION_FAILED,
+					this.tool.getInvNames()[k], e);
+		}
+		return false;
+	}
+
+	private final boolean doNextCheckImplied(final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception {
+		int k = 0;
+        try
+        {
+			for (k = 0; k < this.tool.getImpliedActions().length; k++)
+            {
+                if (!tool.isValid(this.tool.getImpliedActions()[k], curState, succState))
+                {
+                    // We get here because of implied-action violation:
+                    if (TLCGlobals.continuation)
+                    {
+                        synchronized (this.tlc)
+                        {
+                            MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, this.tool
+                                    .getImpliedActNames()[k]);
+                            this.tlc.trace.printTrace(curState, succState);
+							return false;
+                       }
+                    } else {
+						return this.tlc.doNextSetErr(curState, succState, false,
+								EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
+								this.tool.getImpliedActNames()[k]);
+                	}
+				}
+			}
+        } catch (Exception e)
+        {
+        	this.tlc.doNextEvalFailed(curState, succState, EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED,
+					this.tool.getImpliedActNames()[k], e);
+		}
+        return false;
+	}
 }
diff --git a/tlatools/src/tlc2/tool/coverage/ActionWrapper.java b/tlatools/src/tlc2/tool/coverage/ActionWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..ead40c50e5947345682f5c02c0a32472b4e85edf
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/ActionWrapper.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import tla2sany.semantic.LetInNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.SubstInNode;
+import tla2sany.st.Location;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.Action;
+
+public final class ActionWrapper extends CostModelNode {
+
+	public enum Relation {
+		INIT, NEXT, PROP;
+	}
+	
+	private final Action action;
+	private final Relation relation;
+	
+	public ActionWrapper(final Action action, Relation rel) {
+		this.action = action;
+		this.relation = rel;
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.coverage.CostModelNode#getLocation()
+	 */
+	@Override
+	protected Location getLocation() {
+		if (this.action.isDeclared()) {
+			return this.action.getDeclaration();
+		}
+		return this.action.pred.getLocation();
+	}
+
+	private String printLocation() {
+		if (!this.action.isDeclared()) {
+			// Safeguard for legacy cases if any.
+			return this.action.toString();
+		}
+		// Determine if the mapping from the action's name/identifier/declaration to the
+		// action's definition is 1:1 or 1:N.
+		//
+		// Act == /\ x  = 23
+		//        /\ x' = 42
+		// vs
+		// Act == \/ /\ x  = 23
+		//           /\ x' = 42
+		//        \/ /\ x  = 123
+		//           /\ x' = 4711
+		// or
+		// Act == (x  = 23 /\ x' = 42) \/ (x  = 123 /\ x' = 4711)
+		//
+		// For a 1:1 mapping this prints just the location of Act. For a 1:N mapping it
+		// prints the location of Act _and_ the location (in shortened form) of the actual
+		// disjunct.
+		final Location declaration = this.action.getDeclaration();
+		final Location definition = this.action.getOpDef().getBody().getLocation();
+		final Location actual = this.action.pred.getLocation();
+		if (definition.equals(actual)) {
+			// 1:1
+			return String.format("<%s %s>", this.action.getName(), declaration);
+		} else {
+			// 1:N
+			return String.format("<%s %s (%s %s %s %s)>", this.action.getName(), declaration, actual.beginLine(),
+					actual.beginColumn(), actual.endLine(), actual.endColumn());
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.coverage.CostModelNode#getRoot()
+	 */
+	@Override
+	public CostModelNode getRoot() {
+		return this;
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.CostModel#get(tla2sany.semantic.SemanticNode)
+	 */
+	@Override
+	public final CostModel get(final SemanticNode eon) {
+		// returns this instance in case no match is found in children. As a result, the
+		// CostModel will be incorrect which is not as severe as running into an NPE.
+		if (eon instanceof SubstInNode) {
+			final SubstInNode sin = (SubstInNode) eon;
+			return this.children.getOrDefault(sin.getBody(), this);
+		} else if (eon instanceof LetInNode) {
+			final LetInNode lin = (LetInNode) eon;
+			return this.children.getOrDefault(lin.getBody(), this);
+		}
+		return this.children.getOrDefault(eon, this);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.coverage.CostModelNode#getNode()
+	 */
+	@Override
+	SemanticNode getNode() {
+		return action.pred;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.coverage.CostModelNode#isRoot()
+	 */
+	@Override
+	boolean isRoot() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.coverage.CostModel#report()
+	 */
+	public CostModel report() {
+		// Report count for action itself.
+		if (relation == Relation.PROP) {
+			assert getEvalCount() == 0L && this.secondary.getCount() == 0L;
+			MP.printMessage(EC.TLC_COVERAGE_PROPERTY, new String[] { printLocation() });
+		} else if (relation == Relation.INIT) {
+			// TODO Eventually coverage for init and next should consistently report states
+			// found and distinct states into the same counters.
+			MP.printMessage(EC.TLC_COVERAGE_INIT, new String[] { printLocation(), String.valueOf(getEvalCount()),
+					String.valueOf(getEvalCount() + this.secondary.getCount()) });
+		} else {
+			MP.printMessage(EC.TLC_COVERAGE_NEXT, new String[] { printLocation(),
+					String.valueOf(this.secondary.getCount()), String.valueOf(getEvalCount()) });
+		}
+
+		// An action has single child which is the OpApplNodeWrapper with the OpApplNode
+		// for this OpDefNode unless the action's pred is a substitution or a let/in expr.
+		assert !(this.action.pred instanceof SubstInNode || this.action.pred instanceof LetInNode)
+				? this.children.size() == 1
+				: !this.children.isEmpty();
+		// Let children report.
+		// Could disable here if decided to implement action-only coverage at the TLC
+		// level (see org.lamport.tla.toolbox.tool.tlc.model.Model.Coverage).
+		this.children.values().forEach(c -> c.report());
+		return this;
+	}
+
+	public boolean is(Relation r) {
+		return r == this.relation;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/coverage/CostModel.java b/tlatools/src/tlc2/tool/coverage/CostModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdfbaf6c7415447c394315e1e87ceb71909e78c5
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/CostModel.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import tla2sany.semantic.SemanticNode;
+
+public interface CostModel {
+
+	CostModel DO_NOT_RECORD = new CostModel() {
+
+		@Override
+		public final CostModel report() {
+			// no-op
+			return this;
+		}
+
+		@Override
+		public final CostModel get(final SemanticNode sn) {
+			return this;
+		}
+		
+		@Override
+		public final CostModel getRoot() {
+			return this;
+		}
+
+		@Override
+		public final CostModel incInvocations() {
+			// no-op
+			return this;
+		}
+
+		@Override
+		public final CostModel incInvocations(final long value) {
+			// no-op
+			return this;
+		}
+
+		@Override
+		public final CostModel incSecondary() {
+			// no-op
+			return this;
+		}
+
+		@Override
+		public final CostModel incSecondary(long value) {
+			// no-op
+			return null;
+		}
+
+		@Override
+		public final CostModel getAndIncrement(SemanticNode eon) {
+			// no-op
+			return this;
+		}
+		
+		@Override
+		public final String toString() {
+			return "DO_NOT_RECORD";
+		}
+	};
+
+	CostModel incInvocations();
+
+	CostModel incInvocations(final long value);
+
+	CostModel incSecondary();
+	
+	CostModel incSecondary(final long value);
+
+	CostModel report();
+
+	CostModel get(final SemanticNode sn);
+	
+	CostModel getAndIncrement(final SemanticNode eon);
+	
+	CostModel getRoot();
+}
diff --git a/tlatools/src/tlc2/tool/coverage/CostModelCreator.java b/tlatools/src/tlc2/tool/coverage/CostModelCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..a70c7187074f80a4da80e9272abb24e913fbd13f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/CostModelCreator.java
@@ -0,0 +1,465 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static tlc2.tool.ToolGlobals.OPCODE_unchanged;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.explorer.ExplorerVisitor;
+import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.ExprOrOpArgNode;
+import tla2sany.semantic.LetInNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.Subst;
+import tla2sany.semantic.SubstInNode;
+import tla2sany.semantic.SymbolNode;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.Action;
+import tlc2.tool.ITool;
+import tlc2.tool.coverage.ActionWrapper.Relation;
+import tlc2.util.Context;
+import tlc2.util.ObjLongTable;
+import tlc2.util.Vect;
+
+/**
+ * <h1>Why a CostModel:</h1> Why a CostModelCreator to traverses the semantic
+ * graph to create a forest of OpApplNode trees (rooted at an action)?<br>
+ * The semantic graph is no fit to store cost metrics. This is due to the fact
+ * that the semantic graph has only a single node for each OpDefNode and thus
+ * OpApplNode even when an OpApplNode is invoked from different actions and thus
+ * different call trees. If we were to store costs inside the semantic graph, it
+ * would thus not be possible to show costs per action. Therefore,
+ * CostModelCreate creates a tree for each action whose subtree is the set of
+ * OpApplNodes reachable from this particular action.
+ * <p>
+ * A CostModel tree of an action gets traversed by Tool in lockstep (except that
+ * the tree only contains OpApplNodes thus <code>return this</code> in
+ * OpApplNodeWrapper#get) when Tool traverses the semantic graph to evaluate an
+ * action.
+ * <p>
+ * As part of the work on the CostModel, the ExplorerVisitor received an extension
+ * to export the semantic graph into dot notation, which can be rendered with
+ * GraphViz:
+ * <code>java -cp tla2tools.jartla2sany.SANY -d ATLA+Spec.tla dot</code> It
+ * optionally includes line numbers if the system property
+ * <code>tla2sany.explorer.DotExplorerVisitor.includeLineNumbers=true</code>
+ * is set.
+ * <p>
+ * -----------------------------------------------------------------------------
+ * <h2>Note on performance:</h2> Spec/Tool and Value turn isCoverageEnabled into
+ * constants. This should help the runtime to optimize away most calls to
+ * CostModel unless coverage is enabled.
+ * <p>
+ * Macro-benchmarks on a real-world PlusCal [1] model show a 2% performance drop
+ * in terms of throughput due to the addition of the "if (coverage) {...}" calls
+ * when coverage is turned off (it is in the range of %40 when turned on). In
+ * other words, the CostModel data collector has a non-zero overhead. The
+ * reasons are unknown and further investigations have been abandoned. Instead,
+ * the problem has been side-stepped by refactorings in ModelChecker and Tool.
+ * [1]
+ * https://bitbucket.org/parvmor/tarjanconcurrentscc/src/unionfind/specifications/MCBloemenSCC.tla
+ * altered to terminate TLC after five minutes with TLCSet("exit",...)
+ * <p>
+ * The refactorings essentially decomposed large methods into smaller ones (see
+ * git history for commits). This allows the runtime to emit more efficient code
+ * s.t. hot methods can be inlined to reduce invocations. The net gain is in the
+ * range of 5%; enough to make up for the 2% drop introduced by the CostModel
+ * collector.<br>
+ * Methods were identified by:
+ * <code>java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining
+ * -jar tla2tools.jar -workers 1 | grep "hot method too big"</code>
+ * <p>
+ * A far more elegant implementation and zero-overhead solution of the CostModel
+ * collector uses aspect-programming (based on e.g. AspectJ used in other places
+ * of TLC). This approach has been prototyped in CostModelAspect.java. The
+ * aspects in CostModelAspect are woven into TLC by AspectJ at the defined
+ * pointcuts when needed.<br>
+ * Weaving can either be done at compile-time (CTW) or launch-time (LTW) where
+ * LTW is preferable in our case to only load the CostModel code when a user
+ * enables it. Unfortunately, benchmarks revealed a huge performance drops in
+ * the order of a magnitude with LTW. Inspections of the generated bytecode with
+ * JitWatch did not reveal the root cause for this major performance drop. As a
+ * side-effect, LTW would require TLC to include the AspectJ runtime - and even
+ * bigger in size - the ASM bytecode instrumentation. Users would also have to
+ * pass the -javaagent parameter to TLC. In other words, using coverage wouldn't
+ * be as transparent to the user as it is today.
+ * <p>
+ * Experiments with CTW - compared to LTW - resulted in much better performance
+ * with coverage enabled. Still, the overhead of CTW with coverage disabled was
+ * non-negligible. We therefore went as far as hacking the class-loading in
+ * TLC.java to load vanilla TLC with coverage off and instrumented code with
+ * coverage on. However, we considered this approach to be too hack-ish and
+ * abandoned it in favor of the aforementioned refactorings (it also broke the
+ * model.ModelInJarfeature). It additionally increased the size of tla2tools.jar
+ * two-fold.<br>
+ * Generally it appears as if the code generation in AspectJ is not optimized to
+ * emit the most efficient code. Most pointcuts result in superfluous object
+ * allocations inside the generated bytecode - to pass along method parameters -
+ * for *every* method invocation. Future versions of AspectJ might produce more
+ * efficient code though.
+ * <p>
+ * -----------------------------------------------------------------------------
+ * Changing tla2sany.semantic.Generator to generate a forest of call trees -
+ * s.t. there is one OpDefNode & OpApplNode instance per call tree instead of
+ * one global graph - appears to be the implementation with minimal overhead. We
+ * did not follow up on this idea however, because of the inherent risk of
+ * introducing subtle side-effect into the semantics of TLA+ expression
+ * evaluation. An optimization - to only generate a forest when coverage is
+ * enabled - that uses dynamic proxies (JDK dynamic proxies, CGLib, ASM) proved
+ * impossible unless the complete class hierarchy of SemanticNode gets
+ * refactored into a hierarchy with interfaces (proxies can practically only be
+ * generated for interfaces)
+ */
+public class CostModelCreator extends ExplorerVisitor {
+
+	private final Deque<CostModelNode> stack = new ArrayDeque<>();
+	private final Map<ExprOrOpArgNode, Subst> substs = new HashMap<>();
+	private final Map<OpApplNode, Set<OpApplNodeWrapper>> node2Wrapper = new HashMap<>();
+	private final Set<OpDefNode> opDefNodes = new HashSet<>();
+	// Set of OpDefNodes occurring in LetIns and their OpApplNodes.
+	private final Map<ExprNode, ExprNode> letIns = new HashMap<>();
+	// OpAppNode does not implement equals/hashCode which causes problem when added
+	// to sets or maps. E.g. for a test, an OpApplNode instance belonging to
+	// Sequences.tla showed up in coverage output.
+	private final Set<OpApplNodeWrapper> nodes = new HashSet<>();
+	private final ITool tool;
+	
+	private ActionWrapper root;
+	private Context ctx = Context.Empty;
+	
+	private CostModelCreator(final SemanticNode root, final ITool tool) {
+		this.tool = tool;
+		this.stack.push(new RecursiveOpApplNodeWrapper());
+		root.walkGraph(new CoverageHashTable(opDefNodes), this);
+	}
+
+	// root cannot be type OpApplNode but has to be SemanticNode (see Test216).
+	private CostModelCreator(final ITool tool) {
+		this.tool = tool;
+		// MAK 10/08/2018: Annotate OApplNodes in the semantic tree that correspond to
+		// primed vars. It is unclear why OpApplNodes do not get marked as primed when
+		// instantiated. The logic in Tool#getPrimedLocs is too obscure to tell.
+		final ObjLongTable<SemanticNode>.Enumerator<SemanticNode> keys = tool.getPrimedLocs().keys();
+		SemanticNode sn;
+		while ((sn = keys.nextElement()) != null) {
+			this.nodes.add(new OpApplNodeWrapper((OpApplNode) sn, null));
+		}
+	}
+
+	private CostModel getCM(final Action act, final ActionWrapper.Relation relation) {
+		this.substs.clear();
+		this.node2Wrapper.clear();
+		this.opDefNodes.clear();
+		this.letIns.clear();
+		this.stack.clear();
+		this.ctx = Context.Empty;
+		
+		this.root = new ActionWrapper(act, relation);
+		this.stack.push(root);
+		act.pred.walkGraph(new CoverageHashTable(opDefNodes), this);
+		
+		assert this.stack.peek().isRoot();
+		return this.stack.peek().getRoot();
+	}
+
+	@Override
+	public void preVisit(final ExploreNode exploreNode) {
+		if (exploreNode instanceof OpApplNode) {
+			final OpApplNode opApplNode = (OpApplNode) exploreNode;
+			if (opApplNode.isStandardModule()) {
+				return;
+			}
+			
+	        final OpApplNodeWrapper oan;
+			if (opApplNode.hasOpcode(OPCODE_unchanged)) {
+				oan = new UnchangedOpApplNodeWrapper(opApplNode, this.root);
+			} else {
+				oan = new OpApplNodeWrapper(opApplNode, this.root);
+			}
+			
+			if (nodes.contains(oan)) {
+				oan.setPrimed();
+			}
+			
+			// A (recursive) function definition nested in LetIn:
+			//   LET F[n \in S] == e
+			//   IN F[...]
+			// with e either built from F or not.
+			if (letIns.containsKey(opApplNode)) {
+				// At the visit of the LETIN node in the walk over the semantic graph we stored
+				// the mapping from the LET part to the IN part in this.lets (see LetInNode below).
+				// Here, we add the LET parts(s) to the lets of the IN part if it is found on
+				// the stack (this is more involved because we have to find the OANWrappers and
+				// not just the OANs). 
+				final ExprNode in = letIns.get(opApplNode);
+				for (CostModelNode cmn : stack) {
+					final SemanticNode node = cmn.getNode();
+					if (node == in && cmn instanceof OpApplNodeWrapper) {
+						// addLets instead of addChild because lets can be added multiple times
+						// whereas addChild asserts a child to be added only once.
+						((OpApplNodeWrapper) cmn).addLets(oan);
+					}
+				}
+			}
+			
+			// CONSTANT operators (including definition overrides...)
+			final SymbolNode operator = opApplNode.getOperator();
+			final Object val = tool.lookup(operator);
+			if (val instanceof OpDefNode && operator != val) { // second conjunct bc lookup returns operator when nothing else found.
+				final OpDefNode odn = (OpDefNode) val;
+				final ExprNode body = odn.getBody();
+				if (body instanceof OpApplNode) {
+					final CostModelCreator substitution = new CostModelCreator(body, tool);
+					oan.addChild((OpApplNodeWrapper) substitution.getModel());
+				}
+			}			
+			
+			// RECURSIVE
+			if (operator instanceof OpDefNode) {
+				final OpDefNode odn = (OpDefNode) operator;
+				if (odn.getInRecursive()) {
+					final OpApplNodeWrapper recursive = (OpApplNodeWrapper) stack.stream()
+							.filter(w -> w.getNode() != null && w.getNode() instanceof OpApplNode
+									&& ((OpApplNode) w.getNode()).getOperator() == odn)
+							.findFirst().orElse(null);
+					if (recursive != null) {
+						oan.setRecursive(recursive);
+					}
+				}
+			}
+
+			// Higher-order operators/Operators as arguments (LAMBDA, ...)
+			//
+			// line X: Foo(Op(_), S) == \A s \in S: Op(s)
+			// line ?: ...
+			// line Y: Bar == Foo(LAMBDA e: e..., {1,2,3})
+			//
+			// This is the most involved part of CMC: The task is to make the OANW
+			// corresponding to the RHS of the LAMBDA expression on line Y a child of Op(s)
+			// on line X. However, the graph exploration is DFS which means that we haven't
+			// seen the LAMBDA on line Y when we are at the Op(s) on line X and we've
+			// (mostly) forgotten about Op(s) when we see the LAMBDA. ToolImpl - as part of
+			// its DFS over the semantic graph - passes a context along which gets extended
+			// or *branched*. Here, we cannot pass a Context along the decent but instead
+			// keep a single, global context. 
+			//
+			// The global context does not create a problem with regards to correctness, but
+			// can lead to long context chains for larger specifications. Therefore, only
+			// extend the context when opApplNode.argsContainOpArgNodes() is true, i.e. when
+			// one or more arguments of an operator are also operators (such as a LAMBDA).
+			// Without this safeguard, the time to create the CostModel for the SchedMono
+			// specification took approximately 60 seconds. With the safeguard, it is down
+			// to a second or two.
+			//
+			// To summarize, this is a clutch that has been hacked to work good enough!
+			// 
+			// if-branches 1., 2., and 3. below are evaluated in three distinct
+			// invocation of outer preVisit for different ExploreNodes.
+			if (tool != null && operator instanceof OpDefNode && opApplNode.hasOpcode(0)
+					&& opApplNode.argsContainOpArgNodes()) {
+				// 1) Maintain Context for all OpApplNode iff one or more of its args are of
+				// type OpArgNode. This is more restrictive than Tool.
+				final OpDefNode odn = (OpDefNode) operator;
+				if (odn.hasOpcode(0) && !odn.isStandardModule()) {
+					this.ctx = tool.getOpContext(odn, opApplNode.getArgs(), ctx, false);
+				}
+			}
+			final Object lookup = this.ctx.lookup(opApplNode.getOperator());
+			if (lookup instanceof OpDefNode) {
+				// 2) Context has an entry for the given body where body is 'LAMBDA e: e...' and
+				// oan is 'Op(s)'. Remember for later.
+				final ExprNode body = ((OpDefNode) lookup).getBody();
+				if (body instanceof OpApplNode) {
+					// Design choice:
+					// Might as well store the mapping from body to oan via
+					// body#setToolObject(tla2sany.semantic.FrontEnd.getToolId(), oan) instead of in
+					// node2Wrapper. However, node2Wrapper can be gc'ed after the CostModel has been
+					// created and before state space exploration.
+					this.node2Wrapper.computeIfAbsent((OpApplNode) body, key -> new HashSet<>()).add(oan);
+				}
+			}
+			if (this.node2Wrapper.containsKey(opApplNode)) {
+				// 3) Now it's later. Connect w and oan where
+				// w is 'Op(s)' and oan is 'LAMBDA e: e...'
+				this.node2Wrapper.get(opApplNode).forEach(w -> w.addChild(oan));
+			}
+			// End of Higher-order operators/Operators as arguments (LAMBDA, ...) 
+			
+			// Substitutions
+			if (this.substs.containsKey(exploreNode)) {
+				final Subst subst = this.substs.get(exploreNode);
+				assert subst.getExpr() == oan.getNode();
+				subst.setCM(oan);
+			}
+			
+			final CostModelNode parent = stack.peek();
+			parent.addChild(oan.setLevel(parent.getLevel() + 1));
+			stack.push(oan);
+		} else if (exploreNode instanceof SubstInNode) {
+			final SubstInNode sin = (SubstInNode) exploreNode;
+			final Subst[] substs = sin.getSubsts();
+			for (Subst subst : substs) {
+				this.substs.put(subst.getExpr(), subst);
+			}
+		} else if (exploreNode instanceof LetInNode) {
+			final LetInNode lin = (LetInNode) exploreNode;
+			for (OpDefNode opDefNode : lin.getLets()) {
+				letIns.put(opDefNode.getBody(), lin.getBody());
+			}
+		} else if (exploreNode instanceof OpDefNode) {
+			//TODO Might suffice to just keep RECURSIVE ones.
+			opDefNodes.add((OpDefNode) exploreNode);
+		}
+	}
+
+	@Override
+	public void postVisit(final ExploreNode exploreNode) {
+		if (exploreNode instanceof OpApplNode) {
+			if (((OpApplNode) exploreNode).isStandardModule()) {
+				return;
+			}
+			final CostModelNode pop = stack.pop();
+			assert pop.getNode() == exploreNode;
+		} else if (exploreNode instanceof OpDefNode) {
+			final boolean removed = opDefNodes.remove((OpDefNode) exploreNode);
+			assert removed;
+		}
+	}
+
+	private CostModel getModel() {
+		assert this.stack.peek().isRoot();
+		return this.stack.peek().getRoot();
+	}
+	
+	public static final void create(final ITool tool) {
+		final CostModelCreator collector = new CostModelCreator(tool);
+
+		// TODO Start from the ModuleNode similar to how the Explorer works. It is
+		// unclear how to lookup the corresponding subtree in the global CM graph
+		// in getNextState and getInitStates of the model checker.
+		final Vect<Action> init = tool.getInitStateSpec();
+		for (int i = 0; i < init.size(); i++) {
+			final Action initAction = init.elementAt(i);
+			initAction.cm = collector.getCM(initAction, Relation.INIT);
+		}
+
+		final Map<SemanticNode, CostModel> cms = new HashMap<>();
+		for (Action nextAction : tool.getActions()) {
+			if (cms.containsKey(nextAction.pred)) {
+				nextAction.cm = cms.get(nextAction.pred);
+			} else {
+				nextAction.cm = collector.getCM(nextAction, Relation.NEXT);
+				cms.put(nextAction.pred, nextAction.cm);
+			}
+		}
+
+		for (Action invariant : tool.getInvariants()) {
+			invariant.cm = collector.getCM(invariant, Relation.PROP);
+		}
+		
+        // https://github.com/tlaplus/tlaplus/issues/413#issuecomment-577304602
+        if (Boolean.getBoolean(CostModelCreator.class.getName() + ".implied")) {
+    		for (Action impliedInits : tool.getImpliedInits()) {
+    			impliedInits.cm = collector.getCM(impliedInits, Relation.PROP);
+    		}
+    		for (Action impliedActions : tool.getImpliedActions()) {
+    			impliedActions.cm = collector.getCM(impliedActions, Relation.PROP);
+    		}
+        }
+	}
+	
+	public static void report(final ITool tool, final long startTime) {
+        MP.printMessage(EC.TLC_COVERAGE_START);
+    	final Vect<Action> init = tool.getInitStateSpec();
+    	for (int i = 0; i < init.size(); i++) {
+    		final Action initAction = init.elementAt(i);
+    		initAction.cm.report();
+    	}
+
+		// Order next-state actions based on location to print in order of location.
+		// Note that Action[] actions may contain action instances with identical
+		// location which is the case for actions that are evaluated in the scope of a
+		// Context, i.e. \E s \in ProcSet: action(s) \/ ...
+    	// However, actions with identical location share the ActionWrapper instance
+    	// which is why we can non-deterministically choose to report one of it without
+    	// producing bogus results (see CostModelCreator.preVisit(ExploreNode) above).
+    	final Action[] actions = tool.getActions();
+        final Set<CostModel> reported = new HashSet<>();
+        final Set<Action> sortedActions = new TreeSet<>(new Comparator<Action>() {
+			@Override
+			public int compare(Action o1, Action o2) {
+				return o1.pred.getLocation().compareTo(o2.pred.getLocation());
+			}
+		});
+        sortedActions.addAll(Arrays.asList(actions));
+        for (Action action : sortedActions) {
+        	if (!reported.contains(action.cm)) {
+        		action.cm.report();
+        		reported.add(action.cm);
+        	}
+		}
+        
+        for (Action invariant : tool.getInvariants()) {
+        	//TODO May need to be ordered similar to next-state actions above.
+        	invariant.cm.report();
+		}	
+        
+        // https://github.com/tlaplus/tlaplus/issues/413#issuecomment-577304602
+        if (Boolean.getBoolean(CostModelCreator.class.getName() + ".implied")) {
+    		for (Action impliedInits : tool.getImpliedInits()) {
+    			impliedInits.cm.report();
+    		}
+    		for (Action impliedActions : tool.getImpliedActions()) {
+    			impliedActions.cm.report();
+    		}
+        }
+       
+		// Notify users about the performance overhead related to coverage collection
+		// after N minutes of model checking. The assumption is that a user has little
+		// interest in coverage for a large (long-running) model anyway.  In the future
+        // it is hopefully possible to switch from profiling to sampling to relax the
+        // performance overhead of coverage and cost statistics.
+		final long l = System.currentTimeMillis() - startTime;
+		if (l > (5L * 60L * 1000L)) {
+			MP.printMessage(EC.TLC_COVERAGE_END_OVERHEAD);
+		} else {
+			MP.printMessage(EC.TLC_COVERAGE_END);
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/tool/coverage/CostModelNode.java b/tlatools/src/tlc2/tool/coverage/CostModelNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b4700eb5bfc9b2482b11bca912e39c7965f349f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/CostModelNode.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import tla2sany.semantic.SemanticNode;
+import tla2sany.st.Location;
+import tlc2.TLCGlobals;
+import tlc2.util.statistics.CounterStatistic;
+
+public abstract class CostModelNode implements CostModel {
+	
+	// children has to preserve order to later traverse tree in the module location
+	// order when reporting coverage. Thus, use LinkedHashMap here.
+	protected final Map<SemanticNode, CostModelNode> children = new LinkedHashMap<>();
+
+	protected final CounterStatistic stats = CounterStatistic.getInstance(() -> TLCGlobals.isCoverageEnabled());
+	protected final CounterStatistic secondary = CounterStatistic.getInstance(() -> TLCGlobals.isCoverageEnabled());
+	
+	// ---------------- Statistics ---------------- //
+
+	protected long getEvalCount() {
+		return this.stats.getCount();
+	}
+
+	protected long getSecondary() {
+		return this.secondary.getCount();
+	}
+
+	protected abstract Location getLocation();
+
+	// -- --//
+	
+	void addChild(final CostModelNode child) {
+		final boolean newlyInserted = this.children.put(child.getNode(), child) == null;
+		assert newlyInserted;
+	}
+
+	abstract SemanticNode getNode();
+	
+	@Override
+	public abstract CostModelNode getRoot();
+	
+	boolean isRoot() {
+		return false;
+	}
+
+	int getLevel() {
+		return 0;
+	}
+	
+	// -- -- //
+	
+	@Override
+	public final CostModel getAndIncrement(final SemanticNode eon) {
+		return get(eon).incInvocations();
+	}
+
+	@Override
+	public final CostModel incInvocations(long size) {
+		this.stats.add(size);
+		return this;
+	}
+
+	@Override
+	public final CostModel incInvocations() {
+		this.stats.increment();
+		return this;
+	}
+
+	public final CostModel incSecondary() {
+		this.secondary.increment();
+		return this;
+	}
+
+	@Override
+	public final CostModel incSecondary(final long value) {
+		this.secondary.add(value);
+		return this;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/coverage/CoverageHashTable.java b/tlatools/src/tlc2/tool/coverage/CoverageHashTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4b3fede60cd40bd3a2f452e564d9eaa6f8cabdf
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/CoverageHashTable.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import java.util.Set;
+
+import tla2sany.explorer.ExploreNode;
+import tla2sany.semantic.OpDefNode;
+
+@SuppressWarnings("serial")
+class CoverageHashTable extends java.util.Hashtable<Integer, ExploreNode> {
+	private final Set<OpDefNode> nodes;
+
+	public CoverageHashTable(final Set<OpDefNode> nodes) {
+		this.nodes = nodes;
+	}
+
+	@Override
+	public ExploreNode get(Object key) {
+		// Return null here to visit an OpDefNode D multiple times if D is "called" from
+		// multiple OpApplNodes. However, stop endless recursion if D is a RECURSIVE
+		// operator.
+		final ExploreNode v = super.get(key);
+		if (v instanceof OpDefNode) {
+			final OpDefNode odn = (OpDefNode) v;
+			if (odn.getInRecursive()) {
+				if (nodes.contains(odn)) {
+					// RECURSIVE operators
+					return v;
+				}
+			}
+		}
+		return null;
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/coverage/OpApplNodeWrapper.java b/tlatools/src/tlc2/tool/coverage/OpApplNodeWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac2233cf32037dc7f55d5e58f2ba07b7eaacb50a
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/OpApplNodeWrapper.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.st.Location;
+import tlc2.TLCGlobals;
+import tlc2.output.EC;
+import tlc2.output.MP;
+
+public class OpApplNodeWrapper extends CostModelNode implements Comparable<OpApplNodeWrapper>, CostModel {
+
+	private final Set<Long> childCounts = new HashSet<>();
+	private final CostModelNode root;
+	private final OpApplNode node;
+	// Periodic coverage reporting executes concurrently with the evaluation of the
+	// init and next-state relation. Traversing the CostModel trees to collect the
+	// individual eval counts creates thus an inconsistent snapshot. To reduce the
+	// inconsistency, freeze the eval count for all tree nodes on the first tree
+	// traversal while the child counts are calculated. The snapshot is still
+	// inconsistent from the perspective of the evaluation, but at least the
+	// reporting in print (eval counts reported to parent - childCounts - and eval
+	// counts printed is consistent. Alternatively, evaluation of init and next
+	// could be suspended for the duration of the snapshot, but that seems overkill.
+	private long snapshotEvalCount = 0;
+	private boolean primed = false;
+	private int level;
+	private OpApplNodeWrapper recursive;
+	protected final Map<SemanticNode, CostModelNode> lets = new LinkedHashMap<>();
+
+	OpApplNodeWrapper(OpApplNode node, CostModelNode root) {
+		super();
+		this.node = node;
+		this.root = root;
+		this.level = 0;
+	}
+
+	// For unit testing only.
+	OpApplNodeWrapper() {
+		this(null, null);
+	}
+
+	// For unit testing only.
+	OpApplNodeWrapper(OpApplNode node, long samples) {
+		this(node, null);
+		this.incInvocations(samples);
+	}
+
+	// ---------------- Identity... ---------------- //
+	
+	@Override
+	public int compareTo(OpApplNodeWrapper arg0) {
+		return this.getLocation().compareTo(arg0.getLocation());
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((node.getLocation() == null) ? 0 : node.getLocation().hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		if (getClass() != obj.getClass()) {
+			return false;
+		}
+		OpApplNodeWrapper other = (OpApplNodeWrapper) obj;
+		if (node.getLocation() == null) {
+			if (other.node.getLocation() != null)
+				return false;
+		} else if (!node.getLocation().equals(other.node.getLocation())) {
+			return false;
+		}
+		return true;
+
+	}
+
+	@Override
+	public String toString() {
+		if (this.node == null) {
+			return "root";
+		}
+		return node.toString();
+	}
+	
+	// ----------------  ---------------- //
+
+	@Override
+	protected Location getLocation() {
+		return this.node != null ? this.node.getLocation() : Location.nullLoc;
+	}
+
+	public OpApplNode getNode() {
+		return this.node;
+	}
+	
+	public boolean isRoot() {
+		return this.node == null;
+	}
+
+	// ---------------- Parent <> Child ---------------- //
+	
+	public OpApplNodeWrapper addLets(OpApplNodeWrapper lets) {
+		this.lets.put(lets.getNode(), lets);
+		return this;
+	}
+
+	public OpApplNodeWrapper setRecursive(OpApplNodeWrapper recursive) {
+		assert this.recursive == null;
+		this.recursive = recursive;
+		return this;
+	}
+	
+	@Override
+	public CostModelNode getRoot() {
+		assert this.root instanceof ActionWrapper;
+		return this.root;
+	}
+	
+	private final Set<Integer> seen = new HashSet<>();
+	
+	@Override
+	public final CostModelNode get(final SemanticNode eon) {
+		if (eon == this.node || !(eon instanceof OpApplNode)) {
+			return this;
+		}
+		
+		CostModelNode child = children.get(eon);
+		if (child != null) {
+			return child;
+		}
+		
+		if (recursive != null) {
+			child = recursive.children.get(eon);
+			if (child != null) {
+				return child;
+			}
+		}
+		
+		if (lets != null) {
+			child = lets.get(eon);
+			if (child != null) {
+				return child;
+			}
+		}
+		
+		// TODO Not all places in Tool lookup the correct CM yet. This should only be an
+		// engineering effort though.
+		if (seen.add(eon.myUID)) {
+			//...only report it once to not spam the Toolbox console.
+			MP.printMessage(EC.TLC_COVERAGE_MISMATCH, new String[] { eon.toString(), this.toString() });
+		}
+		return this;
+	}
+
+	// ---------------- Level ---------------- //
+
+	public int getLevel() {
+		return this.level;
+	}
+
+	public OpApplNodeWrapper setLevel(int level) {
+		this.level = level;
+		return this;
+	}
+
+	// ---------------- Primed ---------------- //
+	
+	public OpApplNodeWrapper setPrimed() {
+		assert !isPrimed();
+		this.primed = true;
+		return this;
+	}
+
+	protected boolean isPrimed() {
+		return this.primed;
+	}
+	
+	// ---------------- Print ---------------- //
+
+	protected long getEvalCount(Calculate fresh) {
+		if (fresh == Calculate.FRESH) {
+			return super.getEvalCount();
+		} else {
+			return snapshotEvalCount;
+		}
+	}
+
+	public CostModel report() {
+		print(0, Calculate.FRESH);
+		return this;
+	}
+
+	protected void print(int level, final Calculate fresh) {
+		final Set<Long> collectedEvalCounts = new HashSet<>();
+		this.collectChildren(collectedEvalCounts, fresh);
+		if (collectedEvalCounts.isEmpty()) {
+			// Subtree has nothing to report.
+			if (getEvalCount(fresh) == 0l && !isPrimed()) {
+				// ..this node neither.
+				return;
+			} else {
+				printSelf(level++);
+				return; // Do not invoke printSelf(...) again below.
+			}
+		}
+
+		if (collectedEvalCounts.size() == 1) {
+			final long count = getCount(collectedEvalCounts);
+
+			if (count < getEvalCount(fresh)) {
+				// Cannot collapse subtree because inconsistent with this node.
+				printSelf(level++);
+				printChildren(level);
+				return;
+			}
+			if (!isPrimed() && getEvalCount(fresh) == 0l && count != 0l) {
+				// Collapse consistent subtree into this node unless this node is primed.
+				printSelf(level++, count);
+				return;
+			}
+			if (getEvalCount(fresh) == count && count == 0l) {
+				if (isPrimed()) {
+					printSelf(level++);
+				}
+				// Have a primed in subtree.
+				printChildren(level);
+				return;
+			}
+			if (getEvalCount(fresh) == count) {
+				// Have a primed in subtree.
+				printSelf(level++);
+				return;
+			}
+		}
+
+		// Subtree is inconsistent and needs to report itself.
+		if (getEvalCount(fresh) > 0 || isPrimed()) {
+			printSelf(level++);
+		}
+		printChildren(level);
+	}
+
+	private long getCount(Set<Long> collectWeights) {
+		assert collectWeights.size() == 1;
+		for (Long l : collectWeights) {
+			return l;
+		}
+		return -1l; // make compiler happy
+	}
+	
+	protected void printChildren(final int level) {
+		for (CostModelNode cmn : children.values()) {
+			((OpApplNodeWrapper) cmn).print(level, Calculate.CACHED);
+		}
+	}
+
+	protected void printSelf(final int level, final long count) {
+		MP.printMessage(EC.TLC_COVERAGE_VALUE, new String[] {
+				indentBegin(level, TLCGlobals.coverageIndent, getLocation().toString()), String.valueOf(count) });
+	}
+	
+	protected void printSelf(final int level, final long count, final long cost) {
+		MP.printMessage(EC.TLC_COVERAGE_VALUE_COST,
+				new String[] { indentBegin(level, TLCGlobals.coverageIndent, getLocation().toString()),
+						String.valueOf(count), String.valueOf(cost) });
+	}
+
+	protected void printSelf(final int level) {
+		if (getSecondary() > 0) {
+			printSelf(level, getEvalCount(), getSecondary());
+		} else {
+			printSelf(level, getEvalCount());
+		}
+	}
+
+	protected static String indentBegin(final int n, final char c, final String str) {
+		assert n >= 0;
+		final String whitespaces = new String(new char[n]).replace('\0', c);
+		return whitespaces + str;
+	}
+	
+	// ---------------- Child counts ---------------- //
+	
+	protected enum Calculate {
+		FRESH, CACHED;
+	}
+
+	protected void collectChildren(final Set<Long> result, Calculate c) {
+		for (CostModelNode cmn : children.values()) {
+			((OpApplNodeWrapper) cmn).collectAndFreezeEvalCounts(result, c);
+		}
+	}
+
+	protected void collectAndFreezeEvalCounts(final Set<Long> result, final Calculate c) {
+		if (c == Calculate.FRESH) {
+			snapshotEvalCount = this.getEvalCount(c);
+			childCounts.clear();
+			if (snapshotEvalCount > 0 || this.isPrimed()) {
+				childCounts.add(snapshotEvalCount);
+			}
+			collectChildren(childCounts, c);
+		}
+		result.addAll(childCounts);
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/coverage/RecursiveOpApplNodeWrapper.java b/tlatools/src/tlc2/tool/coverage/RecursiveOpApplNodeWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..98ab4bcfa7c5d5adad94e569b4b2989e4b54cc79
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/RecursiveOpApplNodeWrapper.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) Nov 20, 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+public final class RecursiveOpApplNodeWrapper extends OpApplNodeWrapper {
+
+	RecursiveOpApplNodeWrapper() {
+		super(null, null);
+	}
+
+	@Override
+	public boolean isRoot() {
+		assert this.getNode() == null;
+		return true;
+	}
+
+	@Override
+	public CostModelNode getRoot() {
+		assert this.children.size() == 1;
+		return this.children.values().iterator().next();
+	}
+}
diff --git a/tlatools/src/tlc2/tool/coverage/UnchangedOpApplNodeWrapper.java b/tlatools/src/tlc2/tool/coverage/UnchangedOpApplNodeWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..58a02229e49c932a39d6148c85fc2bf0af0bff46
--- /dev/null
+++ b/tlatools/src/tlc2/tool/coverage/UnchangedOpApplNodeWrapper.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import tla2sany.semantic.OpApplNode;
+
+public class UnchangedOpApplNodeWrapper extends OpApplNodeWrapper {
+
+	public UnchangedOpApplNodeWrapper(OpApplNode opApplNode, ActionWrapper root) {
+		super(opApplNode, root);
+	}
+
+	@Override
+	protected boolean isPrimed() {
+		return true;
+	}
+
+	@Override
+	protected void print(int level, final Calculate fresh) {
+		final Set<Long> collectedEvalCounts = new HashSet<>();
+		this.collectChildren(collectedEvalCounts, fresh);
+		collectedEvalCounts.remove(0L); 
+		if (collectedEvalCounts.isEmpty()) {
+			printSelf(level++);
+			return;
+		} else {
+			printSelf(level, Math.max(getEvalCount(), collectedEvalCounts.iterator().next()));
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/tool/distributed/DistApp.java b/tlatools/src/tlc2/tool/distributed/DistApp.java
index da2df940491f058d6df1803f87466c8574c95b1e..2f02277a4f47f4c4d5f78443b23307e2870e5bd5 100644
--- a/tlatools/src/tlc2/tool/distributed/DistApp.java
+++ b/tlatools/src/tlc2/tool/distributed/DistApp.java
@@ -7,6 +7,7 @@ package tlc2.tool.distributed;
 
 import java.rmi.RemoteException;
 
+import tlc2.tool.IStateFunctor;
 import tlc2.tool.TLCState;
 import tlc2.tool.TLCStateInfo;
 import tlc2.tool.TraceApp;
@@ -14,7 +15,6 @@ import tlc2.tool.WorkerException;
 
 /**
  * @author Simon Zambrovski
- * @version $Id$
  */
 public abstract class DistApp implements TraceApp {
 
@@ -35,7 +35,7 @@ public abstract class DistApp implements TraceApp {
 
 	// Returns a list of initial states.
 	// TLCServer
-	public abstract TLCState[] getInitStates() throws WorkerException;
+	public abstract void getInitStates(IStateFunctor functor);
 
 	// Returns a list of successor states of the state s. /
 	// TLCServer
diff --git a/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java b/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java
index f2160bcac1874f4537a2b74e88f136604bef932f..be69d22bde492547e6a0faf7544aac68da2aea73 100644
--- a/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java
+++ b/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java
@@ -52,7 +52,7 @@ public class RMIFilenameToStreamResolver implements FilenameToStream {
 
 		// read the file from the server
 		// strip off path
-		final String name = new File(filename).getName();
+		final String name = new TLAFile(filename, this).getName();
 
 		File file = fileCache.get(name);
 		// not in cache
@@ -102,7 +102,7 @@ public class RMIFilenameToStreamResolver implements FilenameToStream {
 	}
 
 	private File writeToNewTempFile(String name, byte[] bs) {
-		final File f = new File(rndPrefix + File.separator + name);
+		final File f = new TLAFile(rndPrefix + File.separator + name, this);
 		f.deleteOnExit();
 
 		FileOutputStream outputStream = null;
diff --git a/tlatools/src/tlc2/tool/distributed/TLCApp.java b/tlatools/src/tlc2/tool/distributed/TLCApp.java
index 84c45fad11a51057e1ce1c46cf0397e32de8411c..7e2bce787f2552c5c06000015e48b267431e916e 100644
--- a/tlatools/src/tlc2/tool/distributed/TLCApp.java
+++ b/tlatools/src/tlc2/tool/distributed/TLCApp.java
@@ -2,33 +2,33 @@
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
 // Last modified on Mon 30 Apr 2007 at 15:29:57 PST by lamport  
 //      modified on Fri Jul 27 10:47:59 PDT 2001 by yuanyu   
-
 package tlc2.tool.distributed;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
 
 import model.InJarFilenameToStream;
 import model.ModelInJar;
 import tlc2.TLCGlobals;
 import tlc2.tool.Action;
+import tlc2.tool.IStateFunctor;
+import tlc2.tool.ITool;
 import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
 import tlc2.tool.TLCStateInfo;
-import tlc2.tool.Tool;
 import tlc2.tool.WorkerException;
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.impl.CallStackTool;
+import tlc2.tool.impl.FastTool;
 import tlc2.util.FP64;
-import tlc2.value.Value;
 import util.FileUtil;
 import util.FilenameToStream;
+import util.TLAConstants;
 import util.ToolIO;
 import util.UniqueString;
 
-/**
- * @version $Id$
- */
 public class TLCApp extends DistApp {
 
 	private String config;
@@ -36,25 +36,25 @@ public class TLCApp extends DistApp {
 	/* Constructors */
 	public TLCApp(String specFile, String configFile, boolean deadlock,
 			String fromChkpt, FPSetConfiguration fpSetConfig) throws IOException {
-		this(specFile, configFile, deadlock, true, null);
+		this(specFile, configFile, deadlock, null);
 
 		this.fromChkpt = fromChkpt;
-		this.metadir = FileUtil.makeMetaDir(this.tool.specDir, fromChkpt);
+		this.metadir = FileUtil.makeMetaDir(this.tool.getSpecDir(), fromChkpt);
 		this.fpSetConfig = fpSetConfig;
 	}
 	
 	public TLCApp(String specFile, String configFile, boolean deadlock,
 			String fromChkpt, FPSetConfiguration fpSetConfig, FilenameToStream fts) throws IOException {
-		this(specFile, configFile, deadlock, true, fts);
+		this(specFile, configFile, deadlock, fts);
 
 		this.fromChkpt = fromChkpt;
-		this.metadir = FileUtil.makeMetaDir(this.tool.specDir, fromChkpt);
+		this.metadir = FileUtil.makeMetaDir(this.tool.getSpecDir(), fromChkpt);
 		this.fpSetConfig = fpSetConfig;
 	}
 
 	// TODO too many constructors redefinitions, replace with this(..) calls
 	public TLCApp(String specFile, String configFile,
-			Boolean deadlock, Boolean preprocess, FilenameToStream fts) throws IOException {
+			Boolean deadlock, FilenameToStream fts) throws IOException {
 
 		// get the spec dir from the spec file
 		int lastSep = specFile.lastIndexOf(File.separatorChar);
@@ -64,15 +64,11 @@ public class TLCApp extends DistApp {
 		
 		this.config = configFile;
 		
-		// TODO NameResolver
-		this.tool = new Tool(specDir, specFile, configFile, fts);
-		// SZ Feb 24, 2009: setup the user directory
-		ToolIO.setUserDir(specDir);
-
+		
 		this.checkDeadlock = deadlock.booleanValue();
-		this.preprocess = preprocess.booleanValue();
-		// SZ Feb 20, 2009: added null reference to SpecObj
-		this.tool.init(this.preprocess, null);
+		this.preprocess = true;
+		this.tool = new FastTool(specDir, specFile, configFile, fts);
+
 		this.impliedInits = this.tool.getImpliedInits();
 		this.invariants = this.tool.getInvariants();
 		this.impliedActions = this.tool.getImpliedActions();
@@ -80,13 +76,13 @@ public class TLCApp extends DistApp {
 	}
 
 	/* Fields */
-	public Tool tool;
+	public ITool tool;
 	public Action[] invariants; // the invariants to be checked
 	public Action[] impliedInits; // the implied-inits to be checked
 	public Action[] impliedActions; // the implied-actions to be checked
 	public Action[] actions; // the subactions
 	private boolean checkDeadlock; // check deadlock?
-	private boolean preprocess; // preprocess?
+	private final boolean preprocess; // preprocess?
 	private String fromChkpt = null; // recover from this checkpoint
 	private String metadir = null; // the directory pathname for metadata
 	private FPSetConfiguration fpSetConfig;
@@ -109,14 +105,14 @@ public class TLCApp extends DistApp {
 	 * @see tlc2.tool.distributed.DistApp#getFileName()
 	 */
 	public final String getFileName() {
-		return this.tool.rootFile;
+		return this.tool.getRootFile();
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.DistApp#getSpecDir()
 	 */
 	public String getSpecDir() {
-		return this.tool.specDir;
+		return this.tool.getSpecDir();
 	}
 
 	/* (non-Javadoc)
@@ -139,23 +135,16 @@ public class TLCApp extends DistApp {
 	public final boolean canRecover() {
 		return this.fromChkpt != null;
 	}
+	
+	public List<File> getModuleFiles() {
+		return this.tool.getModuleFiles(new InJarFilenameToStream(ModelInJar.PATH));
+    }
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.distributed.DistApp#getInitStates()
+	 * @see tlc2.tool.distributed.DistApp#getInitStates(tlc2.tool.IStateFunctor)
 	 */
-	public final TLCState[] getInitStates() throws WorkerException {
-		StateVec theInitStates = this.tool.getInitStates();
-		TLCState[] res = new TLCState[theInitStates.size()];
-		for (int i = 0; i < theInitStates.size(); i++) {
-			TLCState curState = theInitStates.elementAt(i);
-			if (!this.tool.isGoodState(curState)) {
-				String msg = "Error: Initial state is not completely specified by the"
-						+ " initial predicate.";
-				throw new WorkerException(msg, curState, null, false);
-			}
-			res[i] = (TLCState) curState;
-		}
-		return res;
+	public final void getInitStates(IStateFunctor functor) {
+		this.tool.getInitStates(functor);
 	}
 
 	/* (non-Javadoc)
@@ -183,6 +172,10 @@ public class TLCApp extends DistApp {
 						+ " the next-state action.";
 				throw new WorkerException(msg, curState, succState, false);
 			}
+			// isInModel, isInAction and invariant and implied action checks are
+			// carried out later in TLCWorker#getNextStates(..) and below in
+			// checkState(State, State) when it is know that the states are new
+			// (after the call to the fingerprint set).
 			res[i] = succState;
 		}
 		return res;
@@ -268,16 +261,14 @@ public class TLCApp extends DistApp {
 	 * @see tlc2.tool.distributed.DistApp#setCallStack()
 	 */
 	public final void setCallStack() {
-		this.tool.setCallStack();
+		this.tool = new CallStackTool(this.tool);
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.DistApp#printCallStack()
 	 */
 	public final String printCallStack() {
-		// SZ Jul 10, 2009: check if this is ok
-		// changed the method signature
-		return this.tool.getCallStack().toString();
+		return this.tool.toString();
 	}
 
 	@SuppressWarnings("deprecation")
@@ -296,9 +287,9 @@ public class TLCApp extends DistApp {
 				index++;
 				if (index < args.length) {
 					configFile = args[index];
-					int len = configFile.length();
-					if (configFile.startsWith(".cfg", len - 4)) {
-						configFile = configFile.substring(0, len - 4);
+					if (configFile.endsWith(TLAConstants.Files.CONFIG_EXTENSION)) {
+						configFile = configFile.substring(0,
+								(configFile.length() - TLAConstants.Files.CONFIG_EXTENSION.length()));
 					}
 					index++;
 				} else {
@@ -340,12 +331,18 @@ public class TLCApp extends DistApp {
 				index++;
 				if (index < args.length) {
 					try {
-						TLCGlobals.coverageInterval = Integer
-								.parseInt(args[index]) * 1000;
-						if (TLCGlobals.coverageInterval < 0) {
-							printErrorMsg("Error: expect a nonnegative integer for -coverage option.");
-							return null;
-						}
+						// Coverage reporting is broken is distributed TLC. Thus
+						// warn the user and continue ignoring the parameter.
+						// Consume its value though.
+						ToolIO.out.println(
+								"Warning: coverage reporting not supported in distributed TLC, ignoring -coverage "
+										+ args[index] + " parameter.");
+//						TLCGlobals.coverageInterval = Integer
+//						.parseInt(args[index]) * 1000;
+//						if (TLCGlobals.coverageInterval < 0) {
+//							printErrorMsg("Error: expect a nonnegative integer for -coverage option.");
+//							return null;
+//						}
 						index++;
 					} catch (Exception e) {
 						printErrorMsg("Error: An integer for coverage report interval required."
@@ -358,7 +355,7 @@ public class TLCApp extends DistApp {
 				}
 			} else if (args[index].equals("-terse")) {
 				index++;
-				Value.expand = false;
+				TLCGlobals.expand = false;
 			} else if (args[index].equals("-nowarning")) {
 				index++;
 				TLCGlobals.warn = false;
@@ -497,9 +494,8 @@ public class TLCApp extends DistApp {
 					return null;
 				}
 				specFile = args[index++];
-				int len = specFile.length();
-				if (specFile.startsWith(".tla", len - 4)) {
-					specFile = specFile.substring(0, len - 4);
+				if (specFile.endsWith(TLAConstants.Files.TLA_EXTENSION)) {
+					specFile = specFile.substring(0, (specFile.length() - TLAConstants.Files.TLA_EXTENSION.length()));
 				}
 			}
 		}
@@ -509,12 +505,13 @@ public class TLCApp extends DistApp {
 			// indicator to check the in-jar model/ folder for a spec.
 			// If a spec is found, use it instead.
 			if (ModelInJar.hasModel()) {
+				ModelInJar.loadProperties(); // Reads result.mail.address and so on.
 				TLCGlobals.tool = true; // always run in Tool mode (to parse output by Toolbox later)
 				TLCGlobals.chkptDuration = 0; // never use checkpoints with distributed TLC (highly inefficient)
 				FP64.Init(fpIndex);
 				FilenameToStream resolver = new InJarFilenameToStream(ModelInJar.PATH);
-				return new TLCApp("MC", "MC", deadlock, fromChkpt,
-						fpSetConfig, resolver);
+				return new TLCApp(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, TLAConstants.Files.MODEL_CHECK_FILE_BASENAME,
+						deadlock, fromChkpt, fpSetConfig, resolver);
 			}
 			
 			printErrorMsg("Error: Missing input TLA+ module.");
diff --git a/tlatools/src/tlc2/tool/distributed/TLCServer.java b/tlatools/src/tlc2/tool/distributed/TLCServer.java
index 51649a13303d0f1f668f38d2de5d3cd8d9c1fe64..e6b199d3bd03d0b0622f0c00aedef9ef5d41252a 100644
--- a/tlatools/src/tlc2/tool/distributed/TLCServer.java
+++ b/tlatools/src/tlc2/tool/distributed/TLCServer.java
@@ -10,30 +10,36 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.URI;
+import java.rmi.AccessException;
+import java.rmi.ConnectException;
 import java.rmi.NoSuchObjectException;
 import java.rmi.NotBoundException;
 import java.rmi.RemoteException;
+import java.rmi.ServerException;
 import java.rmi.registry.LocateRegistry;
 import java.rmi.registry.Registry;
 import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
 import java.util.ConcurrentModificationException;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicLong;
 
+import model.InJarFilenameToStream;
+import model.ModelInJar;
+import tlc2.TLC;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.EvalException;
+import tlc2.tool.IStateFunctor;
 import tlc2.tool.ModelChecker;
 import tlc2.tool.TLCState;
 import tlc2.tool.TLCTrace;
-import tlc2.tool.WorkerException;
 import tlc2.tool.distributed.fp.FPSetManager;
 import tlc2.tool.distributed.fp.FPSetRMI;
 import tlc2.tool.distributed.fp.IFPSetManager;
@@ -48,15 +54,24 @@ import tlc2.tool.queue.DiskStateQueue;
 import tlc2.tool.queue.IStateQueue;
 import tlc2.util.FP64;
 import util.Assert;
+import util.Assert.TLCRuntimeException;
 import util.FileUtil;
 import util.MailSender;
-import util.SimpleFilenameToStream;
 import util.UniqueString;
 
 @SuppressWarnings("serial")
 public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		InternRMI {
 
+	/**
+	 * Name by which {@link FPSetRMI} lookup the {@link TLCServer} (master).
+	 */
+	public static final String SERVER_NAME = "TLCServer";
+	/**
+	 * Name by which {@link TLCWorker} lookup the {@link TLCServer} (master).
+	 */
+	public static final String SERVER_WORKER_NAME = SERVER_NAME + "WORKER";
+
 	/**
 	 * Prefix master and worker heavy workload threads with this prefix and an incrementing counter to
 	 * make the threads identifiable in jmx2munin statistics, which uses simple string matching.  
@@ -187,7 +202,7 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		final FPSet fpSet = FPSetFactory.getFPSet(work.getFPSetConfiguration());
 		fpSet.init(1, metadir, work.getFileName());
 		return new NonDistributedFPSetManager(fpSet, InetAddress.getLocalHost()
-				.getCanonicalHostName());
+				.getCanonicalHostName(), trace);
 	}
 
 	/* (non-Javadoc)
@@ -274,7 +289,7 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		 * leaving the TimerTask running. This occurs by design if the TimerTask
 		 * has already been marked for execution)
 		 * 
-		 * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=216
+		 * @see Bug #216 in general/bugzilla/index.html
 		 */
 		if (worker != null) {
 			MP.printMessage(EC.TLC_DISTRIBUTED_WORKER_DEREGISTERED, thread.getUri().toString());
@@ -360,40 +375,17 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 	}
 
 	/**
-	 * @throws Exception
+	 * @throws Throwable 
 	 */
-	private final Set<Long> doInit() throws Exception {
-		final SortedSet<Long> set = new TreeSet<Long>();
-		TLCState curState = null;
-		try {
-			TLCState[] initStates = work.getInitStates();
-			for (int i = 0; i < initStates.length; i++) {
-				curState = initStates[i];
-				boolean inConstraints = work.isInModel(curState);
-				boolean seen = false;
-				if (inConstraints) {
-					long fp = curState.fingerPrint();
-					seen = !set.add(fp);
-					if (!seen) {
-						initStates[i].uid = trace.writeState(fp);
-						stateQueue.enqueue(initStates[i]);
-					}
-				}
-				if (!inConstraints || !seen) {
-					work.checkState(null, curState);
-				}
-			}
-		} catch (Exception e) {
-			this.errState = curState;
-			this.keepCallStack = true;
-			if (e instanceof WorkerException) {
-				this.errState = ((WorkerException) e).state2;
-				this.keepCallStack = ((WorkerException) e).keepCallStack;
-			}
-			this.done = true;
-			throw e;
+	private final void doInit() throws Throwable {
+		final DoInitFunctor functor = new DoInitFunctor();
+		work.getInitStates(functor);
+		
+		// Iff one of the init states' checks violates any properties, the
+		// functor will record it.
+		if (functor.e != null) {
+			throw functor.e;
 		}
-		return set;
 	}
 
 	/**
@@ -415,6 +407,8 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 	 * @throws NotBoundException
 	 */
 	protected void modelCheck() throws IOException, InterruptedException, NotBoundException {
+    	final long startTime = System.currentTimeMillis();
+
 		/*
 		 * Before we initialize the server, we check if recovery is requested 
 		 */
@@ -427,68 +421,89 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
                     String.valueOf(stateQueue.size())});
 			recovered = true;
 		}
+
+		// Create the central naming authority that is used by _all_ nodes
+		String hostname = InetAddress.getLocalHost().getHostName();
+		Registry rg = LocateRegistry.createRegistry(Port);
+		rg.rebind(SERVER_NAME, this);
+		
+		// First register TLCSERVER with RMI and only then wait for all FPSets
+		// to become registered. This only waits if we use distributed
+		// fingerprint set (FPSet) servers which have to partition the
+		// distributed hash table (fingerprint space) prior to starting model
+		// checking.
+		waitForFPSetManager();
 		
 		/*
 		 * Start initializing the server by calculating the init state(s)
 		 */
-
-		//TODO if init states is huge, this might go OOM
-		Set<Long> initFPs = new TreeSet<Long>();
 		if (!recovered) {
 			// Initialize with the initial states:
 			try {
                 MP.printMessage(EC.TLC_COMPUTING_INIT);
-                initFPs = doInit();
+                doInit();
 				MP.printMessage(EC.TLC_INIT_GENERATED1,
 						new String[] { String.valueOf(stateQueue.size()), "(s)" });
 			} catch (Throwable e) {
 				// Assert.printStack(e);
 				done = true;
-				// LL modified error message on 7 April 2012
-				MP.printError(EC.GENERAL, "initializing the server", e); // LL changed call 7 April 2012
-				if (errState != null) {
-					MP.printMessage(EC.TLC_INITIAL_STATE, "While working on the initial state: " + errState);
-				}
-				// We redo the work on the error state, recording the call
-				// stack.
-				work.setCallStack();
-				try {
-					initFPs = doInit();
-				} catch (Throwable e1) {
-					MP.printError(EC.GENERAL, "evaluating the nested"   // LL changed call 7 April 2012
-									+ "\nexpressions at the following positions:\n"
-									+ work.printCallStack(), e);
+				
+				// Distributed TLC does not support TLCGet/TLCSet operator. It
+				// would require synchronization among all (distributed)
+				// workers. In distributed mode, it is of limited use anyway. 
+				if (e instanceof EvalException
+						&& ((EvalException) e).getErrorCode() == EC.TLC_MODULE_TLCGET_UNDEFINED
+						&& (((EvalException) e).getMessage().contains("TLCSet")
+								|| ((EvalException) e).getMessage().contains("TLCGet"))
+						|| (e instanceof TLCRuntimeException && ((TLCRuntimeException) e).errorCode == EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE)) {
+					MP.printError(EC.TLC_FEATURE_UNSUPPORTED,
+							"TLCSet & TLCGet operators not supported by distributed TLC.");
+				} else {
+					String msg = e.getMessage();
+					if (msg == null) {
+						msg = e.toString();
+					}
+					if (!this.hasNoErrors()) {
+						MP.printError(EC.TLC_INITIAL_STATE, new String[] { msg, this.errState.toString() });
+					} else {
+						MP.printError(EC.GENERAL, msg);
+					}
+					// We redo the work on the error state, recording the call
+					// stack.
+					work.setCallStack();
+					try {
+						doInit();
+					} catch (Throwable e1) {
+						// Assert.printStack(e);
+						MP.printError(EC.TLC_NESTED_EXPRESSION, work.printCallStack());
+					}
 				}
 			}
 		}
 		if (done) {
+			printSummary(1, 0, stateQueue.size(), fpSetManager.size(), false);
+			MP.printMessage(EC.TLC_FINISHED,
+					TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime));
+			es.shutdown();
 			// clean up before exit:
 			close(false);
 			return;
 		}
-
-		// Create the central naming authority that is used by _all_ nodes
-		String hostname = InetAddress.getLocalHost().getHostName();
-		Registry rg = LocateRegistry.createRegistry(Port);
-		rg.rebind("TLCServer", this);
-		MP.printMessage(EC.TLC_DISTRIBUTED_SERVER_RUNNING, hostname);
-		
-		// First register TLCSERVER with RMI and only then wait for all FPSets
-		// to become registered. This only waits if we use distributed
-		// fingerprint set (FPSet) servers which have to partition the
-		// distributed hash table (fingerprint space) prior to starting model
-		// checking.
-		waitForFPSetManager();
-		
-		// Add the init state(s) to the local FPSet or distributed servers 
-		for (Long fp : initFPs) {
-			fpSetManager.put(fp);
-		}
 		
+		// Init states have been computed successfully which marks the point in
+		// time where workers can start generating and exploring next states.
+		rg.rebind(SERVER_WORKER_NAME, this);
+
 		/*
 		 * This marks the end of the master and FPSet server initialization.
 		 * Model checking can start now.
+		 * Print the startup message now, because the Toolbox is supposed to
+		 * show that it's waiting for workers to connect. If the messaage
+		 * gets printed earlier, it's replaced by EC.TLC_INIT_GENERATED1
+		 * right away and the user can consequently miss that TLCServer is
+		 * running.
 		 */
+		MP.printMessage(EC.TLC_DISTRIBUTED_SERVER_RUNNING, hostname);
 
 		// Model checking results to be collected after model checking has finished
 		long oldNumOfGenStates = 0;
@@ -515,9 +530,13 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 					distinctStatesPerMinute = (long) ((fpSetSize - oldFPSetSize) / factor);
 			        
 					// print to system.out
-					MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { String.valueOf(trace.getLevelForReporting()),
-			                String.valueOf(numOfGenStates), String.valueOf(fpSetSize),
-			                String.valueOf(getNewStates()), String.valueOf(statesPerMinute), String.valueOf(distinctStatesPerMinute) });
+					MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] {
+							String.valueOf(trace.getLevelForReporting()),
+							MP.format(numOfGenStates),
+							MP.format(fpSetSize),
+							MP.format(getNewStates()),
+							MP.format(statesPerMinute),
+							MP.format(distinctStatesPerMinute) });
 					
 					// Make the TLCServer main thread sleep for one report interval
 					wait(REPORT_INTERVAL);
@@ -562,6 +581,14 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 			} catch (NoSuchObjectException e) {
 				// worker might have been lost in the meantime
 				MP.printWarning(EC.GENERAL, "Ignoring attempt to exit dead worker");
+			} catch (ConnectException e) {
+				// worker might have been lost in the meantime
+				MP.printWarning(EC.GENERAL, "Ignoring attempt to exit dead worker");
+			} catch (ServerException e) {
+				// worker might have been lost in the meantime
+				MP.printWarning(EC.GENERAL, "Ignoring attempt to exit dead worker");
+			} finally {
+				threadsToWorkers.remove(thread);
 			}
 		}
 		
@@ -581,9 +608,9 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		// Postprocessing:
 		if (hasNoErrors()) {
 			// We get here because the checking has succeeded.
-			final double actualProb = fpSetManager.checkFPs();
+			final long actualDistance = fpSetManager.checkFPs();
 			final long statesSeen = fpSetManager.getStatesSeen();
-			ModelChecker.reportSuccess(finalNumberOfDistinctStates, actualProb, statesSeen);
+			ModelChecker.reportSuccess(finalNumberOfDistinctStates, actualDistance, statesSeen);
 		} else if (keepCallStack) {
 			// We redo the work on the error state, recording the call stack.
 			work.setCallStack();
@@ -591,14 +618,16 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		
 		// Finally print the results
 		printSummary(level, statesGenerated, statesLeftInQueue, finalNumberOfDistinctStates, hasNoErrors());
-		MP.printMessage(EC.TLC_FINISHED);
+		MP.printMessage(EC.TLC_FINISHED,
+				TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime));
 		MP.flush();
 
 		// Close trace and (distributed) _FPSet_ servers!
 		close(hasNoErrors());
 		
 		// dispose RMI leftovers
-		rg.unbind("TLCServer");
+		rg.unbind(SERVER_WORKER_NAME);
+		rg.unbind(SERVER_NAME);
 		UnicastRemoteObject.unexportObject(this, false);
 	}
 	
@@ -654,9 +683,12 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
     public static final void printSummary(int level, long statesGenerated, long statesLeftInQueue, long distinctStates, boolean success) throws IOException
     {
 		if (TLCGlobals.tool) {
-            MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { String.valueOf(level),
-                    String.valueOf(statesGenerated), String.valueOf(distinctStates),
-                    String.valueOf(statesLeftInQueue), "0", "0" });
+            MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] {
+                    String.valueOf(level),
+                    MP.format(statesGenerated),
+                    MP.format(distinctStates),
+                    MP.format(statesLeftInQueue),
+                    "0", "0" });
         }
 
         MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(statesGenerated),
@@ -666,15 +698,23 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
         }
     }
 
-	public static void main(String argv[]) {
-		MP.printMessage(EC.GENERAL, "TLC Server " + TLCGlobals.versionOfTLC);
+	public static void main(String[] argv) {
+		// Print version before MailSender has been created. Oh well, it misses
+		// the version output.
+		MP.printMessage(EC.TLC_VERSION, "TLC Server " + TLCGlobals.versionOfTLC);
 		TLCStandardMBean tlcServerMXWrapper = TLCStandardMBean.getNullTLCStandardMBean();
 		MailSender mail = null;
 		TLCServer server = null;
+		TLCApp app = null;
 		try {
 			TLCGlobals.setNumWorkers(0);
-			final TLCApp app = TLCApp.create(argv);
-			mail = new MailSender(app.getFileName());
+			// Create MS before TLCApp to capture the parsing output.
+			mail = new MailSender();
+
+			app = TLCApp.create(argv);
+			mail.setModelName(System.getProperty(MailSender.MODEL_NAME, app.getFileName()));
+			mail.setSpecName(System.getProperty(MailSender.SPEC_NAME, app.getFileName()));
+			
 			if (expectedFPSetCount > 0) {
 				server = new DistributedFPSetTLCServer(app, expectedFPSetCount);
 			} else {
@@ -703,18 +743,29 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 				}
 			}
 		} finally {
+			if (!server.es.isShutdown()) {
+				server.es.shutdownNow();
+			}
 			tlcServerMXWrapper.unregister();
-			boolean send = mail.send();
-			// In case sending the mail has failed we treat this as an error.
-			// This is needed when TLC runs on another host and email is
-			// the only means for the user to get access to the model checking
-			// results. 
-			// With distributed TLC and CloudDistributedTLCJob in particular,
-			// the cloud node is immediately turned off once the TLC process has
-			// finished. If we were to shutdown the node even when sending out 
-            // the email has failed, the result would be lost.
-			if (!send) {
-				System.exit(1);
+			// When creation of TLCApp fails, we get here as well.
+			if (mail != null) {
+				List<File> files = new ArrayList<File>();
+				if (app != null) {
+					files = app.getModuleFiles();
+				}
+				boolean send = mail.send(files);
+				// In case sending the mail has failed we treat this as an error.
+				// This is needed when TLC runs on another host and email is
+				// the only means for the user to get access to the model checking
+				// results. 
+				// With distributed TLC and CloudDistributedTLCJob in particular,
+				// the cloud node is immediately turned off once the TLC process has
+				// finished. If we were to shutdown the node even when sending out 
+				// the email has failed, the result would be lost.
+				if (!send) {
+					MP.printMessage(EC.GENERAL, "Sending result mail failed.");
+					System.exit(1);
+				}
 			}
 		}
 	}
@@ -767,7 +818,7 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		String name = new File(file).getName();
 		
 		// Resolve all 
-		File f = new SimpleFilenameToStream().resolve(name);
+		File f = new InJarFilenameToStream(ModelInJar.PATH).resolve(name);
 		return read(f);
 	}
 	
@@ -807,6 +858,41 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		return buffer;
 	}
 	
+	private class DoInitFunctor implements IStateFunctor {
+
+		private Throwable e;
+
+		/* (non-Javadoc)
+		 * @see tlc2.tool.IStateFunctor#addElement(tlc2.tool.TLCState)
+		 */
+		public Object addElement(final TLCState curState) {
+			if (e != null) {
+				return curState;
+			}
+
+			try {
+				final boolean inConstraints = work.isInModel(curState);
+				boolean seen = false;
+				if (inConstraints) {
+					long fp = curState.fingerPrint();
+					seen = fpSetManager.put(fp);
+					if (!seen) {
+						curState.uid = trace.writeState(fp);
+						stateQueue.enqueue(curState);
+					}
+				}
+				if (!inConstraints || !seen) {
+					work.checkState(null, curState);
+				}
+			} catch (Exception e) {
+				if (setErrState(curState, true)) {
+					this.e = e;
+				}
+			}
+			
+			return curState;
+		}
+	}
 
 	/**
 	 * Tries to exit all connected workers
@@ -823,6 +909,24 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI,
 		 * @see java.lang.Runnable#run()
 		 */
 		public void run() {
+			if (server.threadsToWorkers.isEmpty()) {
+				// Nothing to be done here.
+				return;
+			}
+				
+			try {
+				// No need to attempt to exit workers if the server itself
+				// isn't registered any longer. It won't be able to connect to
+				// workers anyway.
+				LocateRegistry.getRegistry(Port).lookup(SERVER_NAME);
+			} catch (AccessException e1) {
+				return;
+			} catch (RemoteException e1) {
+				return;
+			} catch (NotBoundException e1) {
+				return;
+			}
+
 			for (TLCWorkerRMI worker : server.threadsToWorkers.values()) {
 				try {
 					worker.exit();
diff --git a/tlatools/src/tlc2/tool/distributed/TLCServerThread.java b/tlatools/src/tlc2/tool/distributed/TLCServerThread.java
index fa66f1b53e44cfa5decaf5d2f5cdc8a9bbeab0f7..226ab2592043a4240949b3987562ed59fe4bc4ac 100644
--- a/tlatools/src/tlc2/tool/distributed/TLCServerThread.java
+++ b/tlatools/src/tlc2/tool/distributed/TLCServerThread.java
@@ -255,13 +255,14 @@ public class TLCServerThread extends IdThread {
 				state2 = ((WorkerException) e).state2;
 			}
 			if (this.tlcServer.setErrState(state1, true)) {
-				MP.printError(EC.GENERAL, e);
 				if (state1 != null) {
 					try {
 						this.tlcServer.trace.printTrace(state1, state2);
 					} catch (Exception e1) {
 						MP.printError(EC.GENERAL, e1);
 					}
+				} else {
+					MP.printError(EC.GENERAL, e);
 				}
 				stateQueue.finishAll();
 				synchronized (this.tlcServer) {
@@ -318,9 +319,9 @@ public class TLCServerThread extends IdThread {
 		keepAliveTimer.cancel();
 		
 		// This call has to be idempotent, otherwise we see bugs as in 
-		// https://bugzilla.tlaplus.net/show_bug.cgi?id=234
+		// Bug #234 in general/bugzilla/index.html
 		//
-		// Prevent second invocation of worker de-registration which stamps from
+		// Prevent second invocation of worker de-registration which stems from
 		// a race condition between the TimerTask (that periodically checks
 		// server aliveness) and the exception handling that kicks in when the
 		// run() method catches an RMI exception.
diff --git a/tlatools/src/tlc2/tool/distributed/TLCWorker.java b/tlatools/src/tlc2/tool/distributed/TLCWorker.java
index 14d486ffd417a279f279da0bf9d770467c05d3ea..b4cbeca16890a016137f0ce7bf0aab8f446fc9f6 100644
--- a/tlatools/src/tlc2/tool/distributed/TLCWorker.java
+++ b/tlatools/src/tlc2/tool/distributed/TLCWorker.java
@@ -14,6 +14,7 @@ import java.net.UnknownHostException;
 import java.rmi.ConnectException;
 import java.rmi.Naming;
 import java.rmi.NoSuchObjectException;
+import java.rmi.NotBoundException;
 import java.rmi.RemoteException;
 import java.rmi.server.UnicastRemoteObject;
 import java.util.Date;
@@ -305,8 +306,8 @@ public class TLCWorker extends UnicastRemoteObject implements TLCWorkerRMI {
 		cdl = new CountDownLatch(numCores);
 		
 		try {
-			String url = "//" + serverName + ":" + TLCServer.Port
-					+ "/TLCServer";
+			final String url = "//" + serverName + ":" + TLCServer.Port
+					+ "/" + TLCServer.SERVER_WORKER_NAME;
 			
 			// try to repeatedly connect to the server until it becomes available
 			int i = 1;
@@ -331,7 +332,30 @@ public class TLCWorker extends UnicastRemoteObject implements TLCWorkerRMI {
 						// how to handle
 						throw e;
 					}
+				} catch (NotBoundException e) {
+					// Registry is available but no object by "TLCServer". This
+					// happens when TLCServer makes it registry available but
+					// has't registered itself yet.
+					long sleep = (long) Math.sqrt(i);
+					ToolIO.out.println("Server " + serverName + " reachable but not ready yet, sleeping " + sleep
+							+ "s for server to come online...");
+					Thread.sleep(sleep * 1000);
+					i *= 2;
 				}
+				// It is vital to *not* catch NoRouteToHostException or
+				// UnknownHostException. TLCWorker is supposed to terminate when
+				// either of the two is thrown. The rational is that the while
+				// loop could be going while TLCServer is busy generating a huge
+				// set of init states. Close to completion, it finds a violating
+				// state and terminates. In case of cloud distributed TLC 
+				// (see CloudDistributedTLCJob), the host/vm running the master
+				// immediately shuts down. That is when the NoRouteToHostException
+				// will make sure that the set of TLCWorkers will terminate the VM
+				// and shutdown the vm too. There is obviously the chance that
+				// another vm gets the IP of the former master assigned and boots
+				// up during a sleep period above. Even though I do not know what
+				// holding period IP address have across the various cloud providers,
+				// I find this rather unlikely.
 			}
 
 			long irredPoly = server.getIrredPolyForFP();
@@ -348,8 +372,7 @@ public class TLCWorker extends UnicastRemoteObject implements TLCWorkerRMI {
 			fts.setTLCServer(server);
 			
 			DistApp work = new TLCApp(server.getSpecFileName(),
-					server.getConfigFileName(), server.getCheckDeadlock(),
-					server.getPreprocess(), fts);
+					server.getConfigFileName(), server.getCheckDeadlock(), fts);
 
 			final IFPSetManager fpSetManager = server.getFPSetManager();
 			
diff --git a/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java b/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java
index 9017beb6e4ed1f6058deb58c4803838a6346c8d5..4653408b73fa68e3cec3f86a4c1542017fac7774 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java
@@ -115,9 +115,11 @@ public class DistributedFPSet  {
 	
 	private static TLCServerRMI lookupTLCServer(final String serverName) throws MalformedURLException, RemoteException, NotBoundException, InterruptedException {
 		String url = "//" + serverName + ":" + TLCServer.Port
-				+ "/TLCServer";
+				+ "/" + TLCServer.SERVER_NAME;
 
-		// try to repeatedly connect to the server until it becomes available
+		// try to repeatedly connect to the server until it becomes available.
+		// see similar while loop in
+		// tlc2.tool.distributed.TLCWorker.main(String[]) for more comments.
 		int i = 1;
 		while(true) {
 			try {
@@ -138,6 +140,15 @@ public class DistributedFPSet  {
 					// how to handle
 					throw e;
 				}
+			} catch (NotBoundException e) {
+				// Registry is available but no object by "TLCServer". This
+				// happens when TLCServer makes it registry available but
+				// has't registered itself yet.
+				long sleep = (long) Math.sqrt(i);
+				ToolIO.out.println("Server " + serverName + " reachable but not ready yet, sleeping " + sleep
+						+ "s for server to come online...");
+				Thread.sleep(sleep * 1000);
+				i *= 2;
 			}
 		}
 	}
diff --git a/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java
index e37d6334a5c2aa7d5c88f9ef3d7fba8ea2c25ad0..af1f3d10a398a7b798bc1ebe95f33c39fa5fe2a0 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java
@@ -21,6 +21,12 @@ public class DynamicFPSetManager extends FPSetManager implements Serializable {
 
 	public DynamicFPSetManager(int expectedNumOfServers) throws RemoteException {
 		super();
+		// If expectedNumOfServers is no power of two, a subset of FPSet
+		// servers will act as passive backups. Each time a FPSet from
+		// the active set is lost, all workers switch over to the next
+		// available passive one on a list. The list has to be known
+		// prior to when the workers start (it's possible to send out
+		// list updates, but it hasn't been implemented).
 		this.expectedNumOfServers = expectedNumOfServers;
 		
 		// Guard against invalid values
@@ -36,7 +42,7 @@ public class DynamicFPSetManager extends FPSetManager implements Serializable {
 		}
 		
 		// Zero upper bits of mask which won't be used when addressing the
-		// fingerprint servers anyway.
+		// fingerprint servers anyway. See IFPSetManager#getFPSetIndex.
 		this.mask = (1L << log) - 1L;
 	}
 
diff --git a/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java
index 28537d9d0db2386a5f9a91e692032d7478a20eb7..582ff06b056529c0c7ceace497ec4e7eaa80bf83 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java
@@ -29,6 +29,7 @@ import java.util.logging.Logger;
 import tlc2.output.EC;
 import tlc2.tool.distributed.fp.callable.BitVectorWrapper;
 import tlc2.tool.distributed.fp.callable.CheckFPsCallable;
+import tlc2.tool.distributed.fp.callable.CheckInvariantCallable;
 import tlc2.tool.distributed.fp.callable.ContainsBlockCallable;
 import tlc2.tool.distributed.fp.callable.PutBlockCallable;
 import tlc2.util.BitVector;
@@ -206,16 +207,11 @@ public abstract class FPSetManager implements IFPSetManager {
 		return hostname;
 	}
 
-	protected int getIndex(long fp) {
-		long l = fp & mask;
-		return (int) (l % this.fpSets.size());
-	}
-
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.IFPSetManager#put(long)
 	 */
 	public boolean put(long fp) {
-		int fpIdx = getIndex(fp);
+		int fpIdx = getFPSetIndex(fp);
 		while (true) {
 			try {
 				return this.fpSets.get(fpIdx).put(fp);
@@ -236,7 +232,7 @@ public abstract class FPSetManager implements IFPSetManager {
 	 * @see tlc2.tool.distributed.IFPSetManager#contains(long)
 	 */
 	public boolean contains(long fp) {
-		int fpIdx = getIndex(fp);
+		int fpIdx = getFPSetIndex(fp);
 		while (true) {
 			try {
 				return this.fpSets.get(fpIdx).contains(fp);
@@ -425,19 +421,19 @@ public abstract class FPSetManager implements IFPSetManager {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.IFPSetManager#checkFPs()
 	 */
-	public double checkFPs() {
+	public long checkFPs() {
 		final int len = this.fpSets.size();
 		// Instantiation of a thread pool here is fine, as long as checkFPs is only called seldomly.
 		final ExecutorService executorService = Executors.newFixedThreadPool(len);
 		try {
 			// Start checkFP on all FPSets concurrently
 			// (checkFPs scans the full set sequentially!)
-			final CompletionService<Double> ecs = new ExecutorCompletionService<Double>(executorService);
+			final CompletionService<Long> ecs = new ExecutorCompletionService<Long>(executorService);
 			for (int i = 0; i < len; i++) {
 				ecs.submit(new CheckFPsCallable(fpSets.get(i).getFpset()));
 			}
 			// Return minimum value
-			double res = Double.MAX_VALUE;
+			long res = Long.MAX_VALUE;
 			for (int i = 0; i < len; i++) {
 				try {
 					res = Math.min(res, ecs.take().get());
@@ -458,6 +454,43 @@ public abstract class FPSetManager implements IFPSetManager {
 		}
 	}
 
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.fp.IFPSetManager#checkInvariant()
+	 */
+	public boolean checkInvariant() {
+		final int len = this.fpSets.size();
+		// Instantiation of a thread pool here is fine, as long as checkFPs is only called seldomly.
+		final ExecutorService executorService = Executors.newFixedThreadPool(len);
+		try {
+			// Start checkFP on all FPSets concurrently
+			// (checkFPs scans the full set sequentially!)
+			final CompletionService<Boolean> ecs = new ExecutorCompletionService<Boolean>(executorService);
+			for (int i = 0; i < len; i++) {
+				ecs.submit(new CheckInvariantCallable(fpSets.get(i).getFpset()));
+			}
+			// Return minimum value
+			for (int i = 0; i < len; i++) {
+				try {
+					if(!ecs.take().get()) {
+						return false;
+					}
+				} catch (InterruptedException e) {
+					// not expected to happen, could return an approximation
+					// if happens (but fail fast for the moment).
+					e.printStackTrace();
+				} catch (ExecutionException e) {
+					// not expected to happen, could return an approximation
+					// if happens (but fail fast for the moment).
+					e.printStackTrace();
+				}
+			}
+			return true;
+		} finally {
+			// Always shutdown the executor service
+			executorService.shutdown();
+		}
+	}
+
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.IFPSetManager#size()
 	 */
diff --git a/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java b/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java
index bd9ef02490b1b389567f95c4714750c76c8d1b66..b44d0e5200af17117ef6a26ccde5b5081e38590a 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java
@@ -8,6 +8,7 @@ import java.io.IOException;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
 
+import tlc2.tool.TLCTrace;
 import tlc2.tool.fp.FPSet;
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
@@ -32,8 +33,13 @@ public interface FPSetRMI extends Remote {
 	 *         This distance reflects the probability of a fingerprint
 	 *         collision.
 	 */
-	double checkFPs() throws IOException;
-
+	long checkFPs() throws IOException;
+	
+	/**
+	 * @see FPSet#checkInvariant()
+	 */
+	boolean checkInvariant() throws IOException;
+	
 	/**
 	 * Disposes this {@link FPSet}. It cannot be used afterwards anymore.
 	 * 
@@ -97,7 +103,7 @@ public interface FPSetRMI extends Remote {
 	 */
 	BitVector putBlock(LongVec fpv) throws IOException;
 
-	void recover() throws IOException;
+	void recover(TLCTrace trace) throws IOException;
 
 	void recover(String filename) throws IOException;
 
diff --git a/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java
index 480f8cf24a44bc5b5e33a60d5de23907d3b52825..11299ec9f16a4d0d838f8d3a0b6d40adba4f7fd6 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java
@@ -6,6 +6,9 @@ import java.io.Serializable;
 import java.util.concurrent.ExecutorService;
 
 import tlc2.tool.distributed.fp.FPSetManager.FPSets;
+import tlc2.tool.fp.FPSet;
+import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.fp.MSBDiskFPSet;
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
 
@@ -17,8 +20,13 @@ public interface IFPSetManager extends Serializable {
 	/**
 	 * @see FPSetRMI#checkFPs()
 	 */
-	double checkFPs();
+	long checkFPs();
 
+	/**
+	 * @see FPSetRMI#checkInvariant()
+	 */
+	boolean checkInvariant();
+	
 	/**
 	 */
 	void checkpoint(String fname) throws InterruptedException, IOException;
@@ -62,13 +70,27 @@ public interface IFPSetManager extends Serializable {
 	BitVector[] containsBlock(LongVec[] fps, ExecutorService executorService);
 
 	/**
-	 * The index address of the {@link FPSetRMI} corresponding with the given
+	 * The index of the {@link FPSetRMI} corresponding with the given
 	 * fingerprint in this {@link IFPSetManager}. It is used by worker nodes to
 	 * pre-sort the fingerprints in {@link LongVec} according to the index of
-	 * the {@link FPSetRMI} responsible for the partition of the fingerprint
-	 * space.
-	 */
-	int getFPSetIndex(long fp);
+	 * the {@link FPSetRMI} responsible for the subset of the fingerprint space.
+	 * 
+	 * @param fp
+	 *            The fingerprint for which the index should be calculated.
+	 * @return The index of the {@link FPSet} that is assigned this subset of
+	 *         the fingerprint space.
+	 *         <p>
+	 *         Assignment is based on the least significant bits. This selection
+	 *         of fingerprint bits is important. Selecting the most significant
+	 *         bits of a fingerprint to assign an {@link FPSet} would violate
+	 *         the invariant of MSB-based {@link FPSet}s such as
+	 *         {@link MSBDiskFPSet}. They assume that the
+	 *         {@link FPSetConfiguration#getFpBits()} are fixed. FPSetManager
+	 *         may dynamically reassign additional subsets of the fingerprint
+	 *         space to an existing {@link FPSet}, when the previously assigned
+	 *         {@link FPSet} is lost due to a network or hardware failure.
+	 */
+	int getFPSetIndex(final long fp);
 
 	/**
 	 * @see FPSetRMI#getStatesSeen()
diff --git a/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java
index 51f205333f53a1a7fca2354b296b763c16fd9bde..659b064cac2da203497b82283244245adcc7e630 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java
@@ -6,6 +6,7 @@ import java.util.concurrent.ExecutorService;
 
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.TLCTrace;
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
 
@@ -14,11 +15,13 @@ public class NonDistributedFPSetManager implements IFPSetManager {
 
 	private final FPSetRMI fpSet;
 	private final String hostname;
+	private final transient TLCTrace trace; // Do not serialize trace and send it over the wire. Recovery executes on the master, not on the workers.
 
 	public NonDistributedFPSetManager(final FPSetRMI fpSet,
-			final String hostname) throws IOException {
+			final String hostname, TLCTrace trace) throws IOException {
 		this.fpSet = fpSet;
 		this.hostname = hostname;
+		this.trace = trace;
 	}
 
 	/* (non-Javadoc)
@@ -137,7 +140,7 @@ public class NonDistributedFPSetManager implements IFPSetManager {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.fp.FPSetManager#checkFPs()
 	 */
-	public double checkFPs() {
+	public long checkFPs() {
 		try {
 			return this.fpSet.checkFPs();
 		} catch (IOException e) {
@@ -147,6 +150,19 @@ public class NonDistributedFPSetManager implements IFPSetManager {
 		}
 	}
 
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.fp.IFPSetManager#checkInvariant()
+	 */
+	public boolean checkInvariant() {
+		try {
+			return this.fpSet.checkInvariant();
+		} catch (IOException e) {
+			// not expected to happen
+			MP.printError(EC.GENERAL, e);
+			return false;
+		}
+	}
+
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.fp.FPSetManager#size()
 	 */
@@ -198,7 +214,7 @@ public class NonDistributedFPSetManager implements IFPSetManager {
 	 * @see tlc2.tool.distributed.fp.FPSetManager#recover(java.lang.String)
 	 */
 	public void recover(String fname) throws InterruptedException, IOException {
-		this.fpSet.recover();
+		this.fpSet.recover(trace);
 	}
 
 	/* (non-Javadoc)
diff --git a/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java
index 3b9871414e63dc67b1dc8026ca97e2522180c045..c07e2be4aaf8a02c6fd916f2fdf7a1eabf87f2d1 100644
--- a/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java
+++ b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java
@@ -8,7 +8,7 @@ import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.distributed.fp.FPSetRMI;
 
-public class CheckFPsCallable implements Callable<Double> {
+public class CheckFPsCallable implements Callable<Long> {
 	private final FPSetRMI fpSetRMI;
 	
 	public CheckFPsCallable(FPSetRMI fpSetRMI) {
@@ -18,7 +18,7 @@ public class CheckFPsCallable implements Callable<Double> {
 	/* (non-Javadoc)
 	 * @see java.util.concurrent.Callable#call()
 	 */
-	public Double call() throws Exception {
+	public Long call() throws Exception {
 		try {
 			return fpSetRMI.checkFPs();
 		} catch (IOException e) {
@@ -26,7 +26,7 @@ public class CheckFPsCallable implements Callable<Double> {
 			MP.printError(EC.GENERAL, e);
 			// return max value to indicate to caller that the result is
 			// incorrect.
-			return Double.MAX_VALUE;
+			return Long.MAX_VALUE;
 		}
 	}
 }
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/distributed/fp/callable/CheckInvariantCallable.java b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckInvariantCallable.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1f7282b0151ea328115bc3c33bb32d76894acbe
--- /dev/null
+++ b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckInvariantCallable.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved.
+package tlc2.tool.distributed.fp.callable;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.distributed.fp.FPSetRMI;
+
+public class CheckInvariantCallable implements Callable<Boolean> {
+	private final FPSetRMI fpSetRMI;
+	
+	public CheckInvariantCallable(FPSetRMI fpSetRMI) {
+		this.fpSetRMI = fpSetRMI;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.util.concurrent.Callable#call()
+	 */
+	public Boolean call() throws Exception {
+		try {
+			return fpSetRMI.checkInvariant();
+		} catch (IOException e) {
+			// not expected to happen.
+			MP.printError(EC.GENERAL, e);
+			// return max value to indicate to caller that the result is
+			// incorrect.
+			return false;
+		}
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java b/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java
index b91b47ba4a3ed2c95ab158043b25533446b4d189..420999af8669c2202f1ba4adea3cd0469c5573ac 100644
--- a/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java
+++ b/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java
@@ -3,10 +3,12 @@
 package tlc2.tool.distributed.management;
 
 import java.io.IOException;
+import java.rmi.RemoteException;
 
 import javax.management.NotCompliantMBeanException;
 
 import tlc2.TLCGlobals;
+import tlc2.tool.TLCState;
 import tlc2.tool.distributed.TLCServer;
 import tlc2.tool.distributed.fp.IFPSetManager;
 import tlc2.tool.management.TLCStandardMBean;
@@ -105,4 +107,89 @@ public class TLCServerMXWrapper extends TLCStandardMBean implements TLCStatistic
 	public long getAverageBlockCnt() {
 		return tlcServer.getAverageBlockCnt();
 	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getRuntimeRatio()
+	 */
+	public double getRuntimeRatio() {
+		// Distributed TLC does not support liveness checking
+		return 0d;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#liveCheck()
+	 */
+	public void liveCheck() {
+		// Distributed TLC does not support liveness checking
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getCurrentState()
+	 */
+	public String getCurrentState() {
+		final TLCState state = tlcServer.stateQueue.sPeek();
+		if (state != null) {
+			return state.toString();
+		}
+		return "N/A";
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getSpecName()
+	 */
+	public String getSpecName() {
+		if (tlcServer.isRunning()) {
+			try {
+				return tlcServer.getSpecFileName();
+			} catch (RemoteException e) {
+				e.printStackTrace();
+			}
+		}
+		return "N/A";
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getModelName()
+	 */
+	public String getModelName() {
+		if (tlcServer.isRunning()) {
+			try {
+				return tlcServer.getConfigFileName();
+			} catch (RemoteException e) {
+				e.printStackTrace();
+			}
+		}
+		return "N/A";
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#stop()
+	 */
+	public void stop() {
+		synchronized (tlcServer) {
+			tlcServer.setDone();
+			tlcServer.stateQueue.finishAll();
+			tlcServer.notifyAll();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#suspend()
+	 */
+	@Override
+	public void suspend() {
+		synchronized (tlcServer) {
+			tlcServer.stateQueue.suspendAll();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#resume()
+	 */
+	@Override
+	public void resume() {
+		synchronized (tlcServer) {
+			tlcServer.stateQueue.resumeAll();
+		}
+	}
 }
diff --git a/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java b/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java
index ed30f3929999f1e37965cce63f65a8ce8e2867e3..d49f008b71d90a5a857464dd626225a689def856 100644
--- a/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java
+++ b/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java
@@ -2,15 +2,24 @@
 
 package tlc2.tool.distributed.management;
 
+import tlc2.tool.ModelChecker;
+import tlc2.tool.TLCState;
+
 /**
  * @author Markus Alexander Kuppe
  */
-/**
- * @author markus
- *
- */
 public interface TLCStatisticsMXBean {
 
+	/**
+	 * @return The version of TLC.
+	 */
+	String getVersion();
+	
+	/**
+	 * @return The code revision corresponding to this version of TLC.
+	 */
+	String getRevision();
+	
 	/**
 	 * @return The amount of states generated (non-distinct).
 	 *         {@link TLCStatisticsMXBean#getStatesGenerated()} >=
@@ -57,4 +66,46 @@ public interface TLCStatisticsMXBean {
 	 * Creates a checkpoint next time possible
 	 */
 	void checkpoint();
+	
+	/**
+	 * @return The ratio between time dedicated to safety and liveness checking.
+	 */
+	double getRuntimeRatio();
+	
+	/**
+	 * Force new progress interval to check liveness
+	 */
+	void liveCheck();
+	
+	/**
+	 * The string representation of a {@link TLCState} the {@link ModelChecker}
+	 * has recently checked.
+	 */
+	String getCurrentState();
+	
+	/**
+	 * @return The name of the spec currently being checked by TLC.
+	 */
+	String getSpecName();
+	
+	/**
+	 * @return The name of the model curreclty being checked by TLC.
+	 */
+	String getModelName();
+
+	/**
+	 * Force TLC to stop model checking.
+	 */
+	void stop();
+	
+	/**
+	 * Suspend model checking until resume is called.
+	 */
+	void suspend();
+	
+	/**
+	 * Resumes model checking after a suspend. Do not resume an running model checker.
+	 * It could interfere with model checking.
+	 */
+	void resume();
 }
diff --git a/tlatools/src/tlc2/tool/fp/DiskFPSet.java b/tlatools/src/tlc2/tool/fp/DiskFPSet.java
index a6bc57708194b2d172dd0a70812ea52aa31bc24c..f032773b46e25ddf4f5794a4fba8313fd9503d80 100644
--- a/tlatools/src/tlc2/tool/fp/DiskFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/DiskFPSet.java
@@ -9,11 +9,9 @@ import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.net.InetAddress;
 import java.rmi.RemoteException;
-import java.util.Arrays;
+import java.text.DecimalFormat;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Lock;
-import java.util.logging.Level;
+import java.util.concurrent.atomic.LongAdder;
 import java.util.logging.Logger;
 
 import javax.management.NotCompliantMBeanException;
@@ -21,11 +19,11 @@ import javax.management.NotCompliantMBeanException;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.TLCTrace;
+import tlc2.tool.TLCTrace.Enumerator;
 import tlc2.tool.fp.management.DiskFPSetMXWrapper;
 import tlc2.tool.management.TLCStandardMBean;
 import tlc2.util.BufferedRandomAccessFile;
 import tlc2.util.IdThread;
-import tlc2.util.Striped;
 import util.Assert;
 import util.FileUtil;
 
@@ -59,8 +57,11 @@ import util.FileUtil;
 @SuppressWarnings("serial")
 public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 
-	private final static Logger LOGGER = Logger.getLogger(DiskFPSet.class.getName());
+	protected final static Logger LOGGER = Logger.getLogger(DiskFPSet.class.getName());
 
+	protected static final long MARK_FLUSHED = 0x8000000000000000L;
+	protected static final long FLUSHED_MASK = 0x7FFFFFFFFFFFFFFFL;
+	
 	// fields
 	/**
 	 * upper bound on "tblCnt"
@@ -76,24 +77,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 */
 	protected String fpFilename;
 	protected String tmpFilename;
-
-	/**
-	 * Number of locks in the striped lock (#StripeLocks = 2^LogLockCnt).<br>
-	 * Theoretically best performance should be seen with on lock per bucket in
-	 * the primary hash table. An some point though (not yet measured), this
-	 * performance benefit is probably eaten up by the memory consumption of the
-	 * striped lock {@link DiskFPSet#rwLock} itself, which reduces the memory
-	 * available to the hash set.
-	 */
-	protected static final int LogLockCnt = Integer.getInteger(DiskFPSet.class.getName() + ".logLockCnt", 10);
-	/**
-	 * protects n memory buckets
-	 */
-	protected final Striped rwLock;
-	/**
-	 * Is (1 << LogLockCnt) and exposed here for subclasses
-	 */
-	protected final int lockCnt;
 	/**
 	 * Number of entries on disk. This is equivalent to the current number of fingerprints stored on disk.
 	 * @see DiskFPSet#getFileCnt()
@@ -111,13 +94,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * number of entries in "tbl". This is equivalent to the current number of fingerprints stored in in-memory cache/index.
 	 * @see DiskFPSet#getTblCnt()
 	 */
-	protected AtomicLong tblCnt; 
-
+	protected LongAdder tblCnt; 
+	// http://concurrencyfreaks.blogspot.de/2013/09/longadder-is-not-sequentially-consistent.html
 	/**
 	 * Number of used slots in tbl by a bucket
 	 * @see DiskFPSet#getTblLoad()
 	 */
-	protected long tblLoad;
+	protected LongAdder tblLoad;
 	
 	/**
 	 * Number of allocated bucket slots across the complete index table. tblCnt will always <= bucketCnt;
@@ -143,16 +126,16 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	protected long[] index;
 	
 	// statistics
-	private AtomicLong memHitCnt = new AtomicLong(0);
-	private AtomicLong diskLookupCnt = new AtomicLong(0);
-	private AtomicLong diskHitCnt = new AtomicLong(0);
-	private AtomicLong diskWriteCnt = new AtomicLong(0);
-	private AtomicLong diskSeekCnt = new AtomicLong(0);
-	private AtomicLong diskSeekCache = new AtomicLong(0);
+	protected LongAdder memHitCnt = new LongAdder();
+	protected LongAdder diskHitCnt = new LongAdder();
+	private LongAdder diskLookupCnt = new LongAdder();
+	protected LongAdder diskWriteCnt = new LongAdder();
+	private LongAdder diskSeekCnt = new LongAdder();
+	private LongAdder diskSeekCache = new LongAdder();
 	
 	// indicate how many cp or disk grow in put(long) has occurred
 	private int checkPointMark;
-	private int growDiskMark;
+	protected int growDiskMark;
 
 	/**
 	 * The load factor and initial capacity for the hashtable.
@@ -161,7 +144,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	static final int InitialBucketCapacity = (1 << LogMaxLoad);
 
 	/* Number of fingerprints per braf buffer. */
-	public static final int NumEntriesPerPage = 8192 / LongSize;
+	public static final int NumEntriesPerPage = 8192 / (int) LongSize;
 	
 	/**
 	 * This is (assumed to be) the auxiliary storage for a fingerprint that need
@@ -179,7 +162,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * Accumulated wall clock time it has taken to flush this {@link FPSet} to
 	 * disk
 	 */
-	private long flushTime = 0L;
+	protected long flushTime = 0L;
 	
 	/**
 	 * 
@@ -201,15 +184,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 */
 	protected DiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException {
 		super(fpSetConfig);
-		this.lockCnt = 1 << LogLockCnt; //TODO come up with a more dynamic value for stripes that takes tblCapacity into account
-		this.rwLock = Striped.readWriteLock(lockCnt);
 		
 		this.maxTblCnt = fpSetConfig.getMemoryInFingerprintCnt();
 		if (maxTblCnt <= 0) {
 			throw new IllegalArgumentException("Negative or zero upper storage limit");
 		}
 		this.fileCnt = 0;
-		this.tblCnt = new AtomicLong(0);
+		this.tblCnt = new LongAdder();
+		this.tblLoad = new LongAdder();
 		this.flusherChosen = new AtomicBoolean(false);
 		this.index = null;
 		
@@ -229,7 +211,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#init(int, java.lang.String, java.lang.String)
 	 */
-	public void init(int numThreads, String aMetadir, String filename)
+	public FPSet init(int numThreads, String aMetadir, String filename)
 			throws IOException {
 		
 		// Make it possible to pass in alternative location for the .fp and
@@ -284,13 +266,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 					new String[] { this.fpFilename, e.getMessage() });
 			throw new IOException(message);
 		}
+		return this;
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#size()
 	 */
 	public long size() {
-		return this.tblCnt.get() + this.fileCnt;
+		return this.getTblCnt() + this.fileCnt;
 	}
 
 	public abstract long sizeof();
@@ -329,106 +312,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		}
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.FPSet#put(long)
-	 * 
-     * 0 and {@link Long#MIN_VALUE} always return false
-     * 
-     * Locking is as follows:
-     * 
-     * Acquire mem read lock
-     * Acquire disk read lock
-     * Release mem read lock
-     * 
-     * Acquire mem read/write lock
-     * Release disk read lock // interleaved 
-     *  insert into mem
-     * Acquire disk write lock (might cause potential writer to wait() which releases mem read lock (monitor))
-     * 	flushToDisk
-     * Release disk write lock
-     * Release mem read lock
-     * 
-     * asserts:
-     * - Exclusive access to disk and memory for a writer
-     * 
-	 */
-	public final boolean put(long fp) throws IOException {
-		fp = checkValid(fp);
-		// zeros the msb
-		long fp0 = fp & 0x7FFFFFFFFFFFFFFFL;
-		
-		final Lock readLock = rwLock.getAt(getLockIndex(fp0)).readLock();
-		readLock.lock();
-		// First, look in in-memory buffer
-		if (this.memLookup(fp0)) {
-			readLock.unlock();
-			this.memHitCnt.getAndIncrement();
-			return true;
-		}
-		
-		// blocks => wait() if disk is being re-written 
-		// (means the current thread returns rwLock monitor)
-		// Why not return monitor first and then acquire read lock?
-		// => prevent deadlock by acquiring threads in same order? 
-		
-		// next, look on disk
-		boolean diskHit = this.diskLookup(fp0);
-		
-		// In event of disk hit, return
-		if (diskHit) {
-			readLock.unlock();
-			this.diskHitCnt.getAndIncrement();
-			return true;
-		}
-		
-		readLock.unlock();
-		
-		// Another writer could write the same fingerprint here if it gets
-		// interleaved. This is no problem though, because memInsert again
-		// checks existence for fp to be inserted
-		
-		final Lock w = rwLock.getAt(getLockIndex(fp0)).writeLock();
-		w.lock();
-		
-		// if disk lookup failed, add to memory buffer
-		if (this.memInsert(fp0)) {
-			w.unlock();
-			this.memHitCnt.getAndIncrement();
-			return true;
-		}
-		
-		// test if buffer is full && block until there are no more readers 
-		if (needsDiskFlush() && this.flusherChosen.compareAndSet(false, true)) {
-			
-			// statistics
-			growDiskMark++;
-			long timestamp = System.currentTimeMillis();
-			
-			// acquire _all_ write locks
-			rwLock.acquireAllLocks();
-			
-			// flush memory entries to disk
-			flusher.flushTable();
-			
-			// release _all_ write locks
-			rwLock.releaseAllLocks();
-			
-			// reset forceFlush to false
-			forceFlush = false;
-			
-			// finish writing
-			this.flusherChosen.set(false);
-
-			long l = System.currentTimeMillis() - timestamp;
-			flushTime += l;
-			
-			LOGGER.log(Level.FINE, "Flushed disk {0} {1}. tine, in {2} sec", new Object[] {
-					((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l});
-		}
-		w.unlock();
-		return false;
-	}
-
 	/**
 	 * @return true iff the current in-memory buffer has to be flushed to disk
 	 *         to make room.
@@ -439,42 +322,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		// A) the FP distribution causes the index tbl to be unevenly populated.
 		// B) the FP distribution reassembles linear fill-up/down which 
 		// causes tblCnt * buckets with initial load factor to be allocated.
-		return (this.tblCnt.get() >= this.maxTblCnt) || forceFlush ;
+		return (this.getTblCnt() >= this.maxTblCnt) || forceFlush ;
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.FPSet#contains(long)
-	 * 
-     * 0 and {@link Long#MIN_VALUE} always return false
-	 */
-	public final boolean contains(long fp) throws IOException {
-		fp = checkValid(fp);
-		// zeros the msb
-		long fp0 = fp & 0x7FFFFFFFFFFFFFFFL;
-		final Lock readLock = this.rwLock.getAt(getLockIndex(fp0)).readLock();
-		readLock.lock();
-		// First, look in in-memory buffer
-		if (this.memLookup(fp0)) {
-			readLock.unlock();
-			this.memHitCnt.getAndIncrement();
-			return true;
-		}
-
-		// block if disk is being re-written
-		// next, look on disk
-		boolean diskHit = this.diskLookup(fp0);
-		// increment while still locked
-		if(diskHit) {
-			diskHitCnt.getAndIncrement();
-		}
-
-		// end read; add to memory buffer if necessary
-		readLock.unlock();
-		return diskHit;
-	}
-
-	protected abstract int getLockIndex(long fp);
-
 	/**
 	 * Checks if the given fingerprint has a value that can be correctly stored
 	 * by this FPSet
@@ -503,9 +353,16 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	/**
 	 * Return "true" if "fp" is contained in the hash table; otherwise, insert
 	 * it and return "false". Precondition: msb(fp) = 0
+	 * @throws IOException 
 	 */
-	abstract boolean memInsert(long fp);
+	abstract boolean memInsert(long fp) throws IOException;
 
+	/**
+	 * Locks and unlocks tbl
+	 */
+	abstract void acquireTblWriteLock();
+	abstract void releaseTblWriteLock();
+	
 	/**
 	 * Look on disk for the fingerprint "fp". This method requires that
 	 * "this.rwLock" has been acquired for reading by the caller.
@@ -513,11 +370,12 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * @return true iff fp is on disk
 	 */
 	final boolean diskLookup(long fp) throws IOException {
-		if (this.index == null)
+		if (this.index == null) {
 			return false;
+		}
 		
 		// Increment disk lookup counter
-		this.diskLookupCnt.getAndIncrement();
+		this.diskLookupCnt.increment();
 		
 		// search in index for position to seek to
 		// do interpolated binary search
@@ -527,11 +385,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		long hiVal = this.index[hiPage];
 
 		// Test boundary cases (if not inside interval)
-		if (fp < loVal || fp > hiVal)
+		if (fp < loVal || fp > hiVal) {
 			return false;
-		if (fp == hiVal) // why not check loVal? memLookup would have found it already!	
+		}
+		if (fp == hiVal) {// why not check loVal? memLookup would have found it already!	
 			return true;
-		double dfp = (double) fp;
+		}
+		final double dfp = (double) fp;
 
 		// a) find disk page that would potentially contain the fp. this.index contains 
 		// the first fp of each disk page
@@ -543,10 +403,10 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 			 * 
 			 * loVal <= fp < hiVal exists x: loPage < x < hiPage
 			 */
-			double dhi = (double) hiPage;
-			double dlo = (double) loPage;
-			double dhiVal = (double) hiVal;
-			double dloVal = (double) loVal;
+			final double dhi = (double) hiPage;
+			final double dlo = (double) loPage;
+			final double dhiVal = (double) hiVal;
+			final double dloVal = (double) loVal;
 			
 			int midPage = (loPage + 1)
 					+ (int) ((dhi - dlo - 1.0) * (dfp - dloVal) / (dhiVal - dloVal));
@@ -555,7 +415,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 
 			Assert.check(loPage < midPage && midPage < hiPage,
 					EC.SYSTEM_INDEX_ERROR);
-			long v = this.index[midPage];
+			final long v = this.index[midPage];
 			if (fp < v) {
 				hiPage = midPage;
 				hiVal = v;
@@ -567,6 +427,11 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 				return true;
 			}
 		}
+		return diskLookupBinarySearch(fp, indexLength, loPage, hiPage, loVal, hiVal, dfp);
+	}
+
+	private final boolean diskLookupBinarySearch(final long fp, final int indexLength, final int loPage, final int hiPage, long loVal, long hiVal,
+			final double dfp) throws IOException {
 		// no page is in between loPage and hiPage at this point
 		Assert.check(hiPage == loPage + 1, EC.SYSTEM_INDEX_ERROR);
 
@@ -580,18 +445,11 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		try {
 			// b0) open file for reading that is associated with current thread
 			BufferedRandomAccessFile raf;
-			int id = IdThread.GetId(this.braf.length);
+			final int id = IdThread.GetId(this.braf.length);
 			if (id < this.braf.length) {
 				raf = this.braf[id];
 			} else {
-				synchronized (this.brafPool) {
-					if (this.poolIndex < this.brafPool.length) {
-						raf = this.brafPool[this.poolIndex++];
-					} else {
-						raf = new BufferedRandomAccessFile(
-								this.fpFilename, "r");
-					}
-				}
+				raf = poolOpen();
 			}
 			
 			// b1) do interpolated binary search on disk page determined by a)
@@ -609,11 +467,11 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 				// midEntry calculation done on logical indices,
 				// addressing done on bytes, thus convert to long-addressing (* LongSize)
 				if (raf.seeek(midEntry * LongSize)) {
-					diskSeekCnt.getAndIncrement();
+					diskSeekCnt.increment();
 				} else {
-					diskSeekCache.getAndIncrement();
+					diskSeekCache.increment();
 				}
-				long v = raf.readLong();
+				final long v = raf.readLong();
 
 				if (fp < v) {
 					hiEntry = midEntry;
@@ -628,15 +486,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 			}
 			// b2) done doing disk search -> close file (finally candidate? => not really because if we exit with error, TLC exits)
 			if (id >= this.braf.length) {
-				synchronized (this.brafPool) {
-					if (this.poolIndex > 0) {
-						this.brafPool[--this.poolIndex] = raf;
-					} else {
-						raf.close();
-					}
-				}
+				poolClose(raf);
 			}
-		} catch (IOException e) {
+		} catch (final IOException e) {
 			if(midEntry * LongSize < 0) {
 			 // LL modified error message on 7 April 2012
 				MP.printError(EC.GENERAL, new String[]{"looking up a fingerprint, and" + 
@@ -649,6 +501,27 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		return diskHit;
 	}
 
+	private final BufferedRandomAccessFile poolOpen() throws IOException {
+		synchronized (this.brafPool) {
+			if (this.poolIndex < this.brafPool.length) {
+				return this.brafPool[this.poolIndex++];
+			} else {
+				return new BufferedRandomAccessFile(
+						this.fpFilename, "r");
+			}
+		}
+	}
+
+	private final void poolClose(final BufferedRandomAccessFile raf) throws IOException {
+		synchronized (this.brafPool) {
+			if (this.poolIndex > 0) {
+				this.brafPool[--this.poolIndex] = raf;
+			} else {
+				raf.close();
+			}
+		}
+	}
+
 	/**
 	 * Calculates a mid entry where to divide the interval
 	 * 
@@ -683,7 +556,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	protected final void writeFP(RandomAccessFile outRAF, long fp)
 			throws IOException {
 		outRAF.writeLong(fp);
-		diskWriteCnt.getAndIncrement();
+		diskWriteCnt.increment();
 		// update in-memory index file
 		if (this.counter == 0) {
 			this.index[this.currIndex++] = fp;
@@ -697,6 +570,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * @return The new required length for the {@link DiskFPSet#index}
 	 */
 	protected int calculateIndexLen(final long buffLen) {
+		// +2L because we always need the lo and hi bounds for the index.
 		long indexLen = ((this.fileCnt + buffLen - 1L) / (long) NumEntriesPerPage) + 2L;
 
 		//TODO this can cause a NegativeArraySizeException if fileCnt becomes sufficiently large
@@ -730,7 +604,8 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#exit(boolean)
 	 */
-	public final void exit(boolean cleanup) throws IOException {
+	public void exit(boolean cleanup) throws IOException {
+		super.exit(cleanup);
 		if (cleanup) {
 			// Delete the metadata directory:
 			FileUtil.deleteDir(this.metadir, true);
@@ -744,13 +619,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#checkFPs()
 	 */
-	public final double checkFPs() throws IOException {
+	public long checkFPs() throws IOException {
 		// It seems pointless to acquire the locks when checkFPs is only
-		// executed after model checking has finished. Sill lock the disk
+		// executed after model checking has finished. Lock the disk
 		// fingerprint sets though. Acquiring the locks is cheap.
-		rwLock.acquireAllLocks();
+		acquireTblWriteLock();
 		flusher.flushTable();
-		rwLock.releaseAllLocks();
+		releaseTblWriteLock();
 
 		RandomAccessFile braf = new BufferedRandomAccessFile(
 				this.fpFilename, "r");
@@ -768,30 +643,30 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 			}
 		}
 		braf.close();
-		return (1.0 / dis);
+		return dis;
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String)
 	 */
-	public final void beginChkpt(String fname) throws IOException {
+	public void beginChkpt(String fname) throws IOException {
 		
 		this.flusherChosen.set(true);
-		rwLock.acquireAllLocks();
+		acquireTblWriteLock();
 		
 		flusher.flushTable();
 		FileUtil.copyFile(this.fpFilename,
 				this.getChkptName(fname, "tmp"));
 		checkPointMark++;
 
-		rwLock.releaseAllLocks();
+		releaseTblWriteLock();
 		this.flusherChosen.set(false);
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String)
 	 */
-	public final void commitChkpt(String fname) throws IOException {
+	public void commitChkpt(String fname) throws IOException {
 		File oldChkpt = new File(this.getChkptName(fname, "chkpt"));
 		File newChkpt = new File(this.getChkptName(fname, "tmp"));
 		if (!newChkpt.renameTo(oldChkpt)) {
@@ -803,7 +678,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#recover(java.lang.String)
 	 */
-	public final void recover(String fname) throws IOException {
+	public void recover(String fname) throws IOException {
 		RandomAccessFile chkptRAF = new BufferedRandomAccessFile(
 				this.getChkptName(fname, "chkpt"), "r");
 		RandomAccessFile currRAF = new BufferedRandomAccessFile(
@@ -867,79 +742,52 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		// the fingerprints from the TLCTrace file. Not from its own .fp file. 
 	}
 
-	private long[] recoveryBuff = null;
-	private int recoveryIdx = -1;
-
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.FPSet#prepareRecovery()
-	 */
-	public final void prepareRecovery() throws IOException {
-		// First close all "this.braf" and "this.brafPool" objects on currName:
-		for (int i = 0; i < this.braf.length; i++) {
-			this.braf[i].close();
-		}
-		for (int i = 0; i < this.brafPool.length; i++) {
-			this.brafPool[i].close();
-		}
-
-		recoveryBuff = new long[1 << 21];
-		recoveryIdx = 0;
-	}
-
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#recoverFP(long)
 	 */
 	public final void recoverFP(long fp) throws IOException {
-		recoveryBuff[recoveryIdx++] = (fp & 0x7FFFFFFFFFFFFFFFL);
-		if (recoveryIdx == recoveryBuff.length) {
-			Arrays.sort(recoveryBuff, 0, recoveryIdx);
-			flusher.mergeNewEntries(recoveryBuff, recoveryIdx);
-			recoveryIdx = 0;
-		}
-	}
-
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.FPSet#completeRecovery()
-	 */
-	public final void completeRecovery() throws IOException {
-		Arrays.sort(recoveryBuff, 0, recoveryIdx);
-		flusher.mergeNewEntries(recoveryBuff, recoveryIdx);
-		recoveryBuff = null;
-		recoveryIdx = -1;
-
-		// Reopen a BufferedRAF for each thread
-		for (int i = 0; i < this.braf.length; i++) {
-			this.braf[i] = new BufferedRandomAccessFile(this.fpFilename,
-					"r");
-		}
-		for (int i = 0; i < this.brafPool.length; i++) {
-			this.brafPool[i] = new BufferedRandomAccessFile(
-					this.fpFilename, "r");
+		// This implementation used to group n fingerprints into a sorted
+		// in-memory page. Pages were subsequently merged on-disk directly,
+		// creating the on-disk storage file for DiskFPSets.
+		//
+		// The new algorithm simply "replays" the fingerprints found in the
+		// trace file. It's biggest disadvantage is a performance penalty it
+		// pays because it doesn't group fingerprints. On the other hand, it has
+		// advantages over the old algorithm:
+		// 
+		// - Simplified logic/code
+		// - No need for a long[] recovery buffer
+		// - TLC runs with a warm in-memory fingerprint cache
+		// - With large amounts of available fingerprint set memory, the .fp
+		// file might actually never be written. This means that the FPSet never
+		// has to go to disk during contains/put which yields a better overall
+		// runtime performance.
+		// 
+		// TODO Use original on-disk merge if it is known that the fingerprints
+		// won't fit into memory anyway.
+		
+		// The code below is put(long) stripped from synchronization and
+		// statistics code to speed up recovery. Thus, recovery relys on
+		// exclusive access to the fingerprint set, which it has during
+		// recovery.
+		long fp0 = fp & FLUSHED_MASK;
+		boolean unique = !this.memInsert(fp0);
+		Assert.check(unique, EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, "");
+		if (needsDiskFlush()) {
+			this.flusher.flushTable();
 		}
-		this.poolIndex = 0;
 	}
-
 	
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#recover()
 	 */
-	public final void recover() throws IOException {
-		this.prepareRecovery();
-
-		long recoverPtr = TLCTrace.getRecoverPtr();
-		@SuppressWarnings("resource")
-		RandomAccessFile braf = new BufferedRandomAccessFile(
-				TLCTrace.getFilename(), "r");
-		while (braf.getFilePointer() < recoverPtr) {
-			// drop readLongNat
-			if (braf.readInt() < 0)
-				braf.readInt();
-
-			long fp = braf.readLong();
+	public final void recover(TLCTrace trace) throws IOException {
+		final Enumerator elements = trace.elements();
+		while (elements.nextPos() != -1) {
+			long fp = elements.nextFP();
 			this.recoverFP(fp);
 		}
-
-		this.completeRecovery();
+		elements.close();
 	}
 
 	private String getChkptName(String fname, String name) {
@@ -950,7 +798,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * @see tlc2.tool.fp.FPSet#checkInvariant()
 	 */
 	public boolean checkInvariant() throws IOException {
-		rwLock.acquireAllLocks();
+		acquireTblWriteLock();
 		flusher.flushTable(); // No need for any lock here
 		final RandomAccessFile braf = new BufferedRandomAccessFile(
 				this.fpFilename, "r");
@@ -968,7 +816,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 			}
 		} finally {
 			braf.close();
-			rwLock.releaseAllLocks();
+			releaseTblWriteLock();
 		}
 		return true;
 	}
@@ -1017,14 +865,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * {@link DiskFPSet#getTblLoad()} <= {@link DiskFPSet#getTblCnt()}
 	 */
 	public long getTblLoad() {
-		return tblLoad;
+		return tblLoad.sum();
 	}
 	
 	/**
 	 * @return the amount of fingerprints stored in memory. This is less or equal to {@link DiskFPSet#getTblCnt()} depending on if there collision buckets exist. 
 	 */
 	public long getTblCnt() {
-		return tblCnt.get();
+		return tblCnt.sum();
 	}
 	
 	/**
@@ -1045,42 +893,42 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 * @return the diskLookupCnt
 	 */
 	public long getDiskLookupCnt() {
-		return diskLookupCnt.get();
+		return diskLookupCnt.sum();
 	}
 
 	/**
 	 * @return the diskHitCnt
 	 */
 	public long getMemHitCnt() {
-		return memHitCnt.get();
+		return memHitCnt.sum();
 	}
 
 	/**
 	 * @return the diskHitCnt
 	 */
 	public long getDiskHitCnt() {
-		return diskHitCnt.get();
+		return diskHitCnt.sum();
 	}
 
 	/**
 	 * @return the diskWriteCnt
 	 */
 	public long getDiskWriteCnt() {
-		return diskWriteCnt.get();
+		return diskWriteCnt.sum();
 	}
 
 	/**
 	 * @return the diskSeekCnt
 	 */
 	public long getDiskSeekCnt() {
-		return diskSeekCnt.get();
+		return diskSeekCnt.sum();
 	}
 	
 	/**
 	 * @return the diskSeekCache
 	 */
 	public long getDiskSeekCache() {
-		return diskSeekCache.get();
+		return diskSeekCache.sum();
 	}
 
 	/**
@@ -1108,6 +956,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		forceFlush = true;
 	}
 	
+	/**
+	 * @return The (static) number of locks used to guard the set. 
+	 */
+	public int getLockCnt() {
+		return 0;
+	}
+	
 	/**
 	 * @return The technical maximum of readers/writers this {@link DiskFPSet}
 	 *         can handle. It doesn't show the actual numbers of active clients.
@@ -1118,23 +973,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		return this.braf.length + this.brafPool.length;
 	}
 	
-	/**
-	 * @return The amount of elements in the {@link DiskFPSet#collisionBucket}
-	 *         if the {@link DiskFPSet} has a collisionBucket. -1L otherwise.
-	 */
-	public long getCollisionBucketCnt() {
-		return -1L;
-	}
-	
-	/**
-	 * @return The proportional size of the collision bucket compared to the
-	 *         size of the set or <code>-1d</code> if implementation does not
-	 *         use a collision bucket. Domain is [0, 1].
-	 */
-	public double getCollisionRatio() {
-		return -1d;
-	}
-	
 	/**
 	 * The load factor is a measure of how full the (primary) in-memory hash
 	 * table is.
@@ -1144,7 +982,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 	 *         load factor, <code>-1d</code> is returned.
 	 */
 	public double getLoadFactor() {
-		return this.tblCnt.doubleValue() / (double) this.maxTblCnt;
+		return ((double) this.getTblCnt()) / (double) this.maxTblCnt;
 	}
 
 	// /**
@@ -1206,7 +1044,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		 * for writing by the caller, and that the mutex "this.rwLock" is also held.
 		 */
 		void flushTable() throws IOException {
-			if (tblCnt.get() == 0)
+			if (getTblCnt() == 0)
 				return;
 			
 			prepareTable();
@@ -1228,9 +1066,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 				throw new IOException(msg);
 			}
 
-			tblCnt.set(0);
+			tblCnt.reset();
 			bucketsCapacity = 0;
-			tblLoad = 0;
+			tblLoad.reset();
 		}
 
 		/**
@@ -1239,18 +1077,17 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 		 * associated with "this.rwLock" must be held, as must the mutex
 		 * "this.rwLock" itself.
 		 */
-		private final void mergeNewEntries() throws IOException {
+		protected void mergeNewEntries() throws IOException {
 			// Implementation Note: Unfortunately, because the RandomAccessFile
 			// class (and hence, the BufferedRandomAccessFile class) does not
 			// provide a way to re-use an existing RandomAccessFile object on
 			// a different file, this implementation must close all existing
 			// files and re-allocate new BufferedRandomAccessFile objects.
-
-			// close existing files (except brafPool[0])
 			for (int i = 0; i < braf.length; i++) {
-				braf[i].close();
+				// Seek readers to zero position.
+				braf[i].seek(0L);
 			}
-			for (int i = 1; i < brafPool.length; i++) {
+			for (int i = 0; i < brafPool.length; i++) {
 				brafPool[i].close();
 			}
 
@@ -1258,53 +1095,120 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic {
 			File tmpFile = new File(tmpFilename);
 			tmpFile.delete();
 			RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tmpFile, "rw");
-			RandomAccessFile raf = brafPool[0];
-			raf.seek(0);
+			tmpRAF.setLength((getTblCnt() + fileCnt) * FPSet.LongSize);
 
 			// merge
-			mergeNewEntries(raf, tmpRAF);
-
+			mergeNewEntries(braf, tmpRAF);
+			
 			// clean up
-			raf.close();
+			for (int i = 0; i < braf.length; i++) {
+				// close existing files (except brafPool[0])
+				braf[i].close();
+			}
 			tmpRAF.close();
-			String realName = fpFilename;
-			File currFile = new File(realName);
-			currFile.delete();
-			boolean status = tmpFile.renameTo(currFile);
-			Assert.check(status, EC.SYSTEM_UNABLE_NOT_RENAME_FILE);
+			try {
+				FileUtil.replaceFile(tmpFilename, fpFilename);
+			} catch (IOException e) {
+				Assert.fail(EC.SYSTEM_UNABLE_NOT_RENAME_FILE, e);
+			}
 
 			// reopen a BufferedRAF for each thread
 			for (int i = 0; i < braf.length; i++) {
 				// Better way would be to provide method BRAF.open
-				braf[i] = new BufferedRandomAccessFile(realName, "r");
+				braf[i] = new BufferedRandomAccessFile(fpFilename, "r");
 			}
 			for (int i = 0; i < brafPool.length; i++) {
 				// Better way would be to provide method BRAF.open
-				brafPool[i] = new BufferedRandomAccessFile(realName, "r");
+				brafPool[i] = new BufferedRandomAccessFile(fpFilename, "r");
 			}
+			
+			// Verify disk file is sorted.
+			assert checkFile(braf[0], index, fileCnt);
+			
 			poolIndex = 0;
 		}
+		
+		protected abstract void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException;
+		
+	}
+	
+	private static boolean checkFile(BufferedRandomAccessFile braf, long[] index, long elements) throws IOException {
+		final long fileLen = braf.length();
+		if (fileLen / LongSize != elements) {
+			return false;
+		}
+		final long ptr = braf.getFilePointer();
+		long predecessor = Long.MIN_VALUE;
+		if (fileLen > 0) {
+			predecessor = braf.readLong();
+			if (predecessor != index[0]) {
+				return false;
+			}
+			while (braf.getFilePointer() < fileLen) {
+				long l = braf.readLong();
+				if (predecessor >= l) {
+					return false;
+				}
+				predecessor = l;
+			}
+		}
+		braf.seek(ptr);
+		return predecessor == index[index.length - 1];
+	}
+	
+	/*
+	 * Helper to read a fingerprint file (.fp) front to end and check for inconsistencies.   
+	 */
+	
+	@SuppressWarnings("resource")
+	public static void main(String[] args) throws IOException {
+		if (args.length == 1 && !args[0].equals("")) {
+
+			final BufferedRandomAccessFile braf = new BufferedRandomAccessFile(new File(args[0]), "r");
+
+			final long elements = braf.length() / FPSet.LongSize;
+			final DecimalFormat df = new DecimalFormat("###,###.###");
+			System.out.println(String.format("About to scan %s elements.", df.format(elements)));
+
+			long elem = 0L;
+			for (long i = 0; i < elements; i++) {
+				final long l = braf.readLong();
+				if (l < elem) {
+					System.err.println(
+							String.format("Inconsistent elements %s at pos %s < %s at pos %s.", elem, i - 1L, l, i));
+				}
+				elem = l;
+				if (i > 0 && i % 100000000L == 0L) {
+					System.out.println(String.format("Scanned %s elements.", df.format(i)));
+				}
+			}
+		} else if (args.length == 2 && !args[0].equals("") && !args[1].equals("")) {
+			final BufferedRandomAccessFile superset = new BufferedRandomAccessFile(new File(args[0]), "r");
+			final BufferedRandomAccessFile subset = new BufferedRandomAccessFile(new File(args[1]), "r");
+
+			final long elements = subset.length() / FPSet.LongSize;
+			final long fileLen = superset.length();
+
+			OUTER: for (long i = 0; i < elements; i++) {
+				final long l = subset.readLong();
+				while (superset.getFilePointer() < fileLen) {
+					final long m = superset.readLong();
+					if (l == m) {
+						continue OUTER;
+					} else if (m > l) {
+						System.err
+								.println(String.format("Inconsistent element in superset %s not in superset at pos %s.",
+										m, superset.getFilePointer()));
+					}
+				}
+				System.err.println(
+						String.format("Element in subset %s not in superset at pos %s.", l, subset.getFilePointer()));
+			}
 
-		public final void mergeNewEntries(long[] buff, int buffLen)
-				throws IOException {
-			// create temporary file
-			File tmpFile = new File(tmpFilename);
-			tmpFile.delete();
-			RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tmpFile, "rw");
-			File currFile = new File(fpFilename);
-			RandomAccessFile currRAF = new BufferedRandomAccessFile(currFile, "r");
-
-			// merge
-			this.mergeNewEntries(currRAF, tmpRAF);
-
-			// clean up
-			currRAF.close();
-			tmpRAF.close();
-			currFile.delete();
-			boolean status = tmpFile.renameTo(currFile);
-			Assert.check(status, EC.SYSTEM_UNABLE_NOT_RENAME_FILE);
+			System.out.println("Finished scanning files.");
+		} else {
+			System.err.println("Usage: DiskFPSet file.fp OR superset.fp subset.fp");
+			System.exit(1);
 		}
-		
-		protected abstract void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException;
 	}
 }
diff --git a/tlatools/src/tlc2/tool/fp/FPSet.java b/tlatools/src/tlc2/tool/fp/FPSet.java
index 126d46e2a791076171c1f0ee8555eeee682a2a25..8bf0987e10c8b2aa16fb5e5b356f9b7d409f1329 100644
--- a/tlatools/src/tlc2/tool/fp/FPSet.java
+++ b/tlatools/src/tlc2/tool/fp/FPSet.java
@@ -10,6 +10,8 @@ import java.rmi.NoSuchObjectException;
 import java.rmi.RemoteException;
 import java.rmi.server.UnicastRemoteObject;
 
+import tlc2.tool.TLCTrace;
+import tlc2.tool.distributed.fp.DistributedFPSet;
 import tlc2.tool.distributed.fp.FPSetRMI;
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
@@ -26,7 +28,7 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI
 	/**
 	 * Size of a Java long in bytes
 	 */
-	protected static final int LongSize = 8;
+	protected static final long LongSize = 8;
 
 	/**
 	 * Counts the amount of states passed to the containsBlock method
@@ -46,7 +48,11 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI
      * override this method as necessary. This method must be called
      * after the constructor but before any of the other methods below.
      */
-    public abstract void init(int numThreads, String metadir, String filename) throws IOException;
+    public abstract FPSet init(int numThreads, String metadir, String filename) throws IOException;
+    
+    public void incWorkers(int num) {
+    	// subclasses may override
+    }
 
     /* Returns the number of fingerprints in this set. */
     /* (non-Javadoc)
@@ -81,12 +87,21 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI
     /* (non-Javadoc)
      * @see tlc2.tool.distributed.fp.FPSetRMI#exit(boolean)
      */
-    public abstract void exit(boolean cleanup) throws IOException;
+    public void exit(boolean cleanup) throws IOException {
+		// If DistributedFPSet is running, signal termination and wake it up.
+		// This is necessary when a SecurityManager intercepts System.exit(int)
+		// calls which has the side effect that DistributedFPSet's reporting
+		// loop does not terminate and keeps going forever.
+		DistributedFPSet.shutdown();
+		synchronized (this) {
+			this.notify();
+		}
+    }
 
     /* (non-Javadoc)
      * @see tlc2.tool.distributed.fp.FPSetRMI#checkFPs()
      */
-    public abstract double checkFPs() throws IOException;
+    public abstract long checkFPs() throws IOException;
 
     /* (non-Javadoc)
      * @see tlc2.tool.distributed.fp.FPSetRMI#beginChkpt()
@@ -101,14 +116,10 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI
     /* (non-Javadoc)
      * @see tlc2.tool.distributed.fp.FPSetRMI#recover()
      */
-    public abstract void recover() throws IOException;
+    public abstract void recover(TLCTrace trace) throws IOException;
 
     public abstract void recoverFP(long fp) throws IOException;
 
-    public abstract void prepareRecovery() throws IOException;
-
-    public abstract void completeRecovery() throws IOException;
-
     /* The set of checkpoint methods for remote checkpointing. */
     /* (non-Javadoc)
      * @see tlc2.tool.distributed.fp.FPSetRMI#beginChkpt(java.lang.String)
diff --git a/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java b/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java
index a79e8152b97aae155f952b28eba31f54b4022fd7..47473d729533406cf09b211f7b1ff73d387cc0ae 100644
--- a/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java
+++ b/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java
@@ -4,13 +4,18 @@ package tlc2.tool.fp;
 import java.io.Serializable;
 
 import tlc2.output.EC;
+import tlc2.tool.distributed.fp.FPSetManager.FPSets;
 import util.Assert;
 import util.TLCRuntime;
 
 @SuppressWarnings("serial")
 public class FPSetConfiguration implements Serializable {
 	
-	protected int fpBits = 0;
+	/**
+	 * N most significant bits used as address bits by a MultiFPSet. 
+	 */
+	protected int fpBits = 1;
+	
 	protected long memoryInBytes = -1L;
 	protected double ratio;
 	protected String implementation;
@@ -21,22 +26,45 @@ public class FPSetConfiguration implements Serializable {
 	}
 	
 	public FPSetConfiguration(Double aRatio) {
-		this.ratio = aRatio;
 		// Read the implementation class from the System properties instead of
 		// the cmd line. Right now I'm reluctant to expose the impl class as a 
 		// cmd line parameter and carry it forth forever.
-		this.implementation = System.getProperty(FPSetFactory.IMPL_PROPERTY,
-				FPSetFactory.getImplementationDefault());
+		this(aRatio, System.getProperty(FPSetFactory.IMPL_PROPERTY,
+				FPSetFactory.getImplementationDefault()));
+	}
+	
+	public FPSetConfiguration(Double aRatio, String implementation) {
+		this.ratio = aRatio;
+		this.implementation = implementation;
 	}
 
 	public boolean allowsNesting() {
 		return getFpBits() > 0;
 	}
 	
+	/**
+	 * @return The number of most significant bits that must not be used by an
+	 *         FPSet to calculate its index on. The bits are used by a
+	 *         {@link MultiFPSet} to address individual {@link FPSets}.
+	 *         Consequently, for an individual {@link FPSet} these bits are
+	 *         fixed (always either zero or one for all fingerprints that the
+	 *         {@link FPSet} sees).
+	 */
 	public int getFpBits() {
+		if (fpBits == 0 && FPSetFactory.isDiskFPSet(implementation)) {
+			// DiskFPSets always require two instances. A single DiskFPSet
+			// essentially only uses 63. fingerprint bits and thus increases the
+			// likelihood of hash collisions.
+			fpBits = 1;
+		}
 		return fpBits;
 	}
 
+	public void setFpBits(int fpBits) {
+		Assert.check(FPSet.isValid(fpBits), EC.GENERAL);
+		this.fpBits = fpBits;
+	}
+
 	public long getMemoryInBytes() {
 		final TLCRuntime instance = TLCRuntime.getInstance();
 		
@@ -73,10 +101,6 @@ public class FPSetConfiguration implements Serializable {
 		return 1 << getFpBits();
 	}
 	
-	public int getPrefixBits() {
-		return getFpBits();
-	}
-	
 	public void setRatio(double aRatio) {
 		// Allowing aRatio to be 0.0 makes little sense semantically, but we
 		// accept it anyway and let TLCRuntime deal with it.
@@ -84,11 +108,6 @@ public class FPSetConfiguration implements Serializable {
 		this.ratio = aRatio;
 	}
 
-	public void setFpBits(int fpBits) {
-		Assert.check(FPSet.isValid(fpBits), EC.GENERAL);
-		this.fpBits = fpBits;
-	}
-
 	public double getRatio() {
 		return ratio;
 	}
diff --git a/tlatools/src/tlc2/tool/fp/FPSetFactory.java b/tlatools/src/tlc2/tool/fp/FPSetFactory.java
index b1e64c2aa384fb0a86baba0eeab5bf099b3fed26..6dd235f42e29487d6776f818819bd79093c12131 100644
--- a/tlatools/src/tlc2/tool/fp/FPSetFactory.java
+++ b/tlatools/src/tlc2/tool/fp/FPSetFactory.java
@@ -6,9 +6,16 @@ import java.lang.reflect.InvocationTargetException;
 import java.rmi.RemoteException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
+import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import util.TLCRuntime;
+import util.TLCRuntime.ARCH;
 
 public abstract class FPSetFactory {
 
@@ -22,9 +29,10 @@ public abstract class FPSetFactory {
 		return !OffHeapDiskFPSet.class.isAssignableFrom(clazz);
 	}
 
-	static boolean allocatesOnHeap(final String clazz) {
+	public static boolean allocatesOnHeap(final String clazz) {
 		try {
 			final ClassLoader classLoader = FPSet.class.getClassLoader();
+			@SuppressWarnings("unchecked")
 			Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader
 					.loadClass(clazz);
 			return allocatesOnHeap(cls);
@@ -33,6 +41,42 @@ public abstract class FPSetFactory {
 		}
 	}
 
+	private static boolean supportsArchitecture(String clazz) {
+		try {
+			final ClassLoader classLoader = FPSet.class.getClassLoader();
+			@SuppressWarnings("unchecked")
+			Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader
+					.loadClass(clazz);
+			return supports32Bits(cls);
+		} catch (ClassNotFoundException e) {
+			return false;
+		}
+	}
+
+	private static boolean supports32Bits(final Class<? extends FPSet> clazz) {
+		if (TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86
+				&& OffHeapDiskFPSet.class.isAssignableFrom(clazz)) {
+			return false;
+		}
+		return true;
+	}
+	
+	private static boolean isDiskFPSet(Class<? extends FPSet> cls) {
+		return DiskFPSet.class.isAssignableFrom(cls);
+	}
+
+	static boolean isDiskFPSet(final String clazz) {
+		try {
+			final ClassLoader classLoader = FPSet.class.getClassLoader();
+			@SuppressWarnings("unchecked")
+			Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader
+					.loadClass(clazz);
+			return isDiskFPSet(cls);
+		} catch (ClassNotFoundException e) {
+			return false;
+		}
+	}
+
 	/**
 	 * @see getFPSet
 	 * @return
@@ -55,28 +99,49 @@ public abstract class FPSetFactory {
 		
 		// fpBits > 0 indicates that the consumer requires a MultiFPSet
 		if (fpSetConfig.allowsNesting()) {
-			// A MultiFPSet comes in two flavors:
-			// a) For FPSets that sort on LSB
-			// b) For FPSets that use the MSB to sort the internal hash map
-			if (msbBasedFPSet(implClassname)) {
-				// Pass physical memory instead of logical FP count to adhere to
-				// the general FPSet ctor contract.
-				// @see http://bugzilla.tlaplus.net/show_bug.cgi?id=290
-				return new MSBMultiFPSet(fpSetConfig);
-			} else {
-				// Pass physical memory instead of logical FP count to adhere to
-				// the general FPSet ctor contract.
-				// @see http://bugzilla.tlaplus.net/show_bug.cgi?id=290
-				return new MultiFPSet(fpSetConfig);
-			}
+			// Pass physical memory instead of logical FP count to adhere to
+			// the general FPSet ctor contract.
+			// @see http://bugzilla.tlaplus.net/show_bug.cgi?id=290
+			return new MultiFPSet(fpSetConfig);
 		} else {
 			if (implClassname != null) {
-				return loadImplementation(implClassname, fpSetConfig);
-			} else {
-				return new MSBDiskFPSet(fpSetConfig);
+				if (supportsArchitecture(implClassname)) {
+					return loadImplementation(implClassname, fpSetConfig);
+				} else {
+					final ARCH architecture = TLCRuntime.getInstance().getArchitecture();
+					final String msg = String.format(
+							"Selected fingerprint set (set of visited states) %s does not support current architecture %s. "
+									+ "Reverting to default fingerprint set. "
+									+ "Off-heap memory allocated via -XX:MaxDirectMemorySize flag cannot be used by default "
+									+ "fingerprint set and is therefore wasted.",
+							implClassname, architecture);
+					MP.printWarning(EC.TLC_FEATURE_UNSUPPORTED, msg);
+				}
 			}
+			return new MSBDiskFPSet(fpSetConfig);
 		}
 	}
+	
+
+	/**
+	 * Create and *initialize* the set.
+	 */
+	public static Future<FPSet> getFPSetInitialized(final FPSetConfiguration fpSetConfiguration, final String metadir,
+			final String mainFile) {
+		final ExecutorService es = Executors.newSingleThreadExecutor();
+		return es.submit(new Callable<FPSet>() {
+			@Override
+			public FPSet call() throws Exception {
+				try {
+					final FPSet fpSet = FPSetFactory.getFPSet(fpSetConfiguration);
+					fpSet.init(TLCGlobals.getNumWorkers(), metadir, mainFile);
+					return fpSet;
+				} finally {
+					es.shutdown();
+				}
+			}
+		});
+	}
 
 	/**
 	 * @return A list of classes implementing {@link FPSet}. Eventually this
@@ -163,18 +228,4 @@ public abstract class FPSetFactory {
 		MP.printWarning(EC.GENERAL, "unsuccessfully trying to load custom FPSet class: " + clazz, exp);
 		return null;
 	}
-
-	/**
-	 * @param userFpsetImplClassname
-	 * @return true iff the given class uses the MSB to pre-sort its fingerprints
-	 */
-	private static boolean msbBasedFPSet(String userFpsetImplClassname) {
-		if (!allocatesOnHeap(userFpsetImplClassname)) {
-			return true;
-		}
-		if (userFpsetImplClassname.equals(MSBDiskFPSet.class.getName())) {
-			return true;
-		}
-		return false;
-	}
 }
diff --git a/tlatools/src/tlc2/tool/fp/FPSetStatistic.java b/tlatools/src/tlc2/tool/fp/FPSetStatistic.java
index bec95090adc5a9348f2f646c9900bec161cdd8e9..bbc287ea385116a5d1954a14221c8f4e838dffb0 100644
--- a/tlatools/src/tlc2/tool/fp/FPSetStatistic.java
+++ b/tlatools/src/tlc2/tool/fp/FPSetStatistic.java
@@ -97,19 +97,14 @@ public interface FPSetStatistic {
 	public long getFlushTime();
 	
 	/**
-	 * @see DiskFPSet#getReaderWriterCnt()
+	 * @return DiskFPSet#getLockCnt()
 	 */
-	public int getReaderWriterCnt();
+	public int getLockCnt();
 	
 	/**
-	 * @return DiskFPSet#getCollisionBucketCnt()
-	 */
-	public long getCollisionBucketCnt();
-
-	/**
-	 * @return DiskFPSet#getCollisionRatio()
+	 * @see DiskFPSet#getReaderWriterCnt()
 	 */
-	public double getCollisionRatio();
+	public int getReaderWriterCnt();
 	
 	/**
 	 * @return DiskFPSet#getLoadFactor();
diff --git a/tlatools/src/tlc2/tool/fp/FPSetsIdxCalculationBits.jpg b/tlatools/src/tlc2/tool/fp/FPSetsIdxCalculationBits.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..75cb4cdd306cbfc59de294824ffc45ef611dfe36
Binary files /dev/null and b/tlatools/src/tlc2/tool/fp/FPSetsIdxCalculationBits.jpg differ
diff --git a/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java b/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java
index 45e8a195c85ccdf8085fec90c8fc04bd26e4ed12..9a0d165478c60db6d3f562cf341cc1648e1a9e4d 100644
--- a/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java
@@ -1,12 +1,37 @@
 // Copyright (c) 2012 Microsoft Corporation. All rights reserved.
 package tlc2.tool.fp;
 
+import java.io.IOException;
 import java.rmi.RemoteException;
+import java.util.concurrent.locks.Lock;
+import java.util.logging.Level;
 
+import tlc2.TLCGlobals;
+import tlc2.tool.fp.management.DiskFPSetMXWrapper;
+import tlc2.util.Striped;
 import util.Assert;
 
 @SuppressWarnings("serial")
 public abstract class HeapBasedDiskFPSet extends DiskFPSet {
+
+	/**
+	 * Number of locks in the striped lock (#StripeLocks = 2^LogLockCnt).<br>
+	 * Theoretically best performance should be seen with on lock per bucket in
+	 * the primary hash table. An some point though (not yet measured), this
+	 * performance benefit is probably eaten up by the memory consumption of the
+	 * striped lock {@link DiskFPSet#rwLock} itself, which reduces the memory
+	 * available to the hash set.
+	 */
+	protected static final int LogLockCnt = Integer.getInteger(DiskFPSet.class.getName() + ".logLockCnt", (31 - Integer.numberOfLeadingZeros(TLCGlobals.getNumWorkers()) + 8));
+	/**
+	 * protects n memory buckets
+	 */
+	protected final Striped rwLock;
+	/**
+	 * Is (1 << LogLockCnt) and exposed here for subclasses
+	 */
+	protected final int lockCnt;
+	
 	protected final int lockMask;
 	/**
 	 * in-memory buffer of new entries
@@ -19,7 +44,7 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 	protected long mask;
 	
 	/**
-	 * The calculated capacity of tbl
+	 * The calculated capacity of tbl. Will always be a power of two.
 	 */
 	protected final int capacity;
 	
@@ -38,7 +63,19 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 
 	protected HeapBasedDiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException {
 		super(fpSetConfig);
-		
+
+		// Ideally we use one lock per bucket because even with relatively high
+		// lock counts, we fall victim to the birthday paradox. However, locks
+		// ain't cheap, which is why we have to find the sweet spot. We
+		// determined the constant 8 empirically on Intel Xeon CPU E5-2670 v2 @
+		// 2.50GHz. This is based on the number of cores only. We ignore their
+		// speed. MultiThreadedMSBDiskFPSet is the most suitable performance
+		// test available for the job. It just inserts long values into the
+		// set. int is obviously going to be too small once 2^23 cores become
+		// commonplace.
+		this.lockCnt = 1 << LogLockCnt;
+		this.rwLock = Striped.readWriteLock(lockCnt);
+
 		// Reserve a portion of the memory for the auxiliary storage
 		long maxMemCnt = (long) (fpSetConfig.getMemoryInFingerprintCnt() / getAuxiliaryStorageRequirement());
 
@@ -53,24 +90,29 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 		// guard against underflow
 		// LL modified error message on 7 April 2012
 		Assert.check(logMaxMemCnt - LogMaxLoad >= 0, "Underflow when computing HeapBasedDiskFPSet");
-		this.capacity = 1 << (logMaxMemCnt - LogMaxLoad);
 		
-		// instead of changing maxTblCnd to long and pay an extra price when 
-		// comparing int and long every time put(long) is called, we set it to 
-		// Integer.MAX_VALUE instead. capacity can never grow bigger 
-		// (unless java starts supporting 64bit array sizes)
-		//
+		// Guard against a capacity overflow with large amounts (e.g. ~1TB) of
+		// dedicated memory. If cap overflows, the VMs maximum allowed array
+		// size is used.
+		final int cap = 1 << (logMaxMemCnt - LogMaxLoad);
+		if (cap < 0) {
+			// You wonder why 8 and not 42? Ask the VM gods!
+			this.capacity = Integer.MAX_VALUE - 8;
+		} else {
+			this.capacity = cap;
+		}
+		
 		// maxTblCnt mathematically has to be an upper limit for the in-memory storage 
 		// so that a disk flush occurs before an _evenly_ distributed fp distribution fills up 
 		// the collision buckets to a size that exceeds the VM limit (unevenly distributed 
 		// fp distributions can still cause a OutOfMemoryError which this guard).
-		this.maxTblCnt = (logMaxMemCnt >= 31) ? Integer.MAX_VALUE : (1 << logMaxMemCnt); // maxTblCnt := 2^logMaxMemCnt
+		this.maxTblCnt = (1L << logMaxMemCnt); // maxTblCnt := 2^logMaxMemCnt
 
 		Assert.check(maxTblCnt <= fpSetConfig.getMemoryInFingerprintCnt(), "Exceeded upper memory storage limit");
 
 		// guard against negative maxTblCnt
 		// LL modified error message on 7 April 2012
-		Assert.check(maxTblCnt > capacity && capacity > tblCnt.get(),
+		Assert.check(maxTblCnt > capacity && capacity > getTblCnt(),
 				"negative maxTblCnt");
 
 		this.mask = capacity - 1;
@@ -98,6 +140,13 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 		rwLock.releaseAllLocks();
 		return size;
 	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#getLockCnt()
+	 */
+	public int getLockCnt() {
+		return this.rwLock.size();
+	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.DiskFPSet#getTblCapacity()
@@ -126,6 +175,36 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 		return fp & aMask;
 	}
 
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#contains(long)
+	 * 
+     * 0 and {@link Long#MIN_VALUE} always return false
+	 */
+	public final boolean contains(long fp) throws IOException {
+		fp = checkValid(fp);
+		// zeros the msb
+		long fp0 = fp & 0x7FFFFFFFFFFFFFFFL;
+		final Lock readLock = this.rwLock.getAt(getLockIndex(fp0)).readLock();
+		readLock.lock();
+		// First, look in in-memory buffer
+		if (this.memLookup(fp0)) {
+			readLock.unlock();
+			this.memHitCnt.increment();
+			return true;
+		}
+
+		// block if disk is being re-written
+		// next, look on disk
+		boolean diskHit = this.diskLookup(fp0);
+		// increment while still locked
+		if(diskHit) {
+			diskHitCnt.increment();
+		}
+
+		// end read; add to memory buffer if necessary
+		readLock.unlock();
+		return diskHit;
+	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.DiskFPSet#memLookup(long)
@@ -146,6 +225,107 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 		return false;
 	}
 
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#put(long)
+	 * 
+     * 0 and {@link Long#MIN_VALUE} always return false
+     * 
+     * Locking is as follows:
+     * 
+     * Acquire mem read lock
+     * Acquire disk read lock
+     * Release mem read lock
+     * 
+     * Acquire mem read/write lock
+     * Release disk read lock // interleaved 
+     *  insert into mem
+     * Acquire disk write lock (might cause potential writer to wait() which releases mem read lock (monitor))
+     * 	flushToDisk
+     * Release disk write lock
+     * Release mem read lock
+     * 
+     * asserts:
+     * - Exclusive access to disk and memory for a writer
+     * 
+	 */
+	public final boolean put(long fp) throws IOException {
+		fp = checkValid(fp);
+		// zeros the msb
+		long fp0 = fp & 0x7FFFFFFFFFFFFFFFL;
+		
+		final Lock readLock = rwLock.getAt(getLockIndex(fp0)).readLock();
+		readLock.lock();
+		// First, look in in-memory buffer
+		if (this.memLookup(fp0)) {
+			readLock.unlock();
+			this.memHitCnt.increment();
+			return true;
+		}
+		
+		// blocks => wait() if disk is being re-written 
+		// (means the current thread returns rwLock monitor)
+		// Why not return monitor first and then acquire read lock?
+		// => prevent deadlock by acquiring threads in same order? 
+		
+		// next, look on disk
+		boolean diskHit = this.diskLookup(fp0);
+		
+		// In event of disk hit, return
+		if (diskHit) {
+			readLock.unlock();
+			this.diskHitCnt.increment();
+			return true;
+		}
+		
+		readLock.unlock();
+		
+		// Another writer could write the same fingerprint here if it gets
+		// interleaved. This is no problem though, because memInsert again
+		// checks existence for fp to be inserted
+		
+		final Lock w = rwLock.getAt(getLockIndex(fp0)).writeLock();
+		w.lock();
+		
+		// if disk lookup failed, add to memory buffer
+		if (this.memInsert(fp0)) {
+			w.unlock();
+			this.memHitCnt.increment();
+			return true;
+		}
+		
+		// test if buffer is full && block until there are no more readers 
+		if (needsDiskFlush() && this.flusherChosen.compareAndSet(false, true)) {
+			
+			// statistics
+			growDiskMark++;
+			final long timestamp = System.currentTimeMillis();
+			final long insertions = getTblCnt();
+			
+			// acquire _all_ write locks
+			rwLock.acquireAllLocks();
+			
+			// flush memory entries to disk
+			flusher.flushTable();
+			
+			// release _all_ write locks
+			rwLock.releaseAllLocks();
+			
+			// reset forceFlush to false
+			forceFlush = false;
+			
+			// finish writing
+			this.flusherChosen.set(false);
+
+			long l = System.currentTimeMillis() - timestamp;
+			flushTime += l;
+			
+			LOGGER.log(Level.FINE, "Flushed disk {0} {1}. time, in {2} sec after {3} insertions.", new Object[] {
+					((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l, insertions});
+		}
+		w.unlock();
+		return false;
+	}
+
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.DiskFPSet#memInsert(long)
 	 */
@@ -161,7 +341,7 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 			bucket[0] = fp;
 			this.tbl[index] = bucket;
 			this.bucketsCapacity += InitialBucketCapacity; 
-			this.tblLoad++;
+			this.tblLoad.increment();
 		} else {
 			// search for entry in existing bucket
 			int bucketLen = bucket.length;
@@ -199,7 +379,21 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet {
 				bucket[i] = fp;
 			}
 		}
-		this.tblCnt.getAndIncrement();
+		this.tblCnt.increment();
 		return false;
 	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#acquireTblWriteLock()
+	 */
+	void acquireTblWriteLock() {
+		rwLock.acquireAllLocks();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#releaseTblWriteLock()
+	 */
+	void releaseTblWriteLock() {
+		rwLock.releaseAllLocks();
+	}
 }
diff --git a/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java b/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java
index cc84b78fa149cdeee2dfbcd3bc27a3288526aabb..aa126bb88e1d6f150fd9699d0c752648d03ad968 100644
--- a/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java
@@ -9,6 +9,7 @@ import java.rmi.RemoteException;
 import java.util.Arrays;
 
 import tlc2.output.EC;
+import tlc2.util.BufferedRandomAccessFile;
 import util.Assert;
 
 @SuppressWarnings("serial")
@@ -36,7 +37,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet {
 		@Override
 		protected void prepareTable() {
 			// Verify tblCnt is still within positive Integer.MAX_VALUE bounds
-			int cnt = (int) tblCnt.get();
+			int cnt = (int) getTblCnt();
 			Assert.check(cnt > 0, EC.GENERAL);
 			
 			// Why not sort this.tbl in-place rather than doubling memory
@@ -76,7 +77,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet {
 		 * @see tlc2.tool.fp.DiskFPSet.Flusher#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile)
 		 */
 		@Override
-		protected void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException {
+		protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException {
 			final int buffLen = buff.length;
 
 			// Precompute the maximum value of the new file
@@ -97,7 +98,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet {
 			boolean eof = false;
 			if (fileCnt > 0) {
 				try {
-					value = inRAF.readLong();
+					value = inRAFs[0].readLong();
 				} catch (EOFException e) {
 					eof = true;
 				}
@@ -110,7 +111,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet {
 				if (value < buff[i]) {
 					writeFP(outRAF, value);
 					try {
-						value = inRAF.readLong();
+						value = inRAFs[0].readLong();
 					} catch (EOFException e) {
 						eof = true;
 					}
@@ -133,7 +134,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet {
 				do {
 					writeFP(outRAF, value);
 					try {
-						value = inRAF.readLong();
+						value = inRAFs[0].readLong();
 					} catch (EOFException e) {
 						eof = true;
 					}
diff --git a/tlatools/src/tlc2/tool/fp/LongArray.java b/tlatools/src/tlc2/tool/fp/LongArray.java
new file mode 100644
index 0000000000000000000000000000000000000000..82afa7971064cadd96f791558478ada45899c1d3
--- /dev/null
+++ b/tlatools/src/tlc2/tool/fp/LongArray.java
@@ -0,0 +1,312 @@
+// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved.
+package tlc2.tool.fp;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/*
+ * On Java 11 (probably starting with 9) sun.misc.Unsafe is implemented on top
+ * of jdk.internal.misc.Unsafe which provides a similar though not quiet
+ * identical API.  While sun.misc.Unsafe remains available to client code such
+ * as LongArray in Java 11, parts of its API have already been dissolved
+ * (see e.g. https://bugs.openjdk.java.net/browse/JDK-8202999).  This does not
+ * affect LongArray yet, but will probably affect LongArray in the future. 
+ * Refactoring LongArray to replace sun.misc.Unsafe with jdk.internal.misc.Unsafe
+ * requires the following changes:
+ * - Replace sun.misc.Unsafe with jdk.internal.misc.Unsafe
+ * - Change Unsafe#compareAndSwapLong with Unsafe#compareAndSetLong in LongArray#trySet
+ * - Run LongArray on Java 11 with "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED"
+ *   to configure Java's module system (Jigsaw) to expose jdk.internal.misc.Unsafe
+ *   to LongArray.
+ * The last requirement is visible to TLA+ users who run tla2tools.jar on the
+ * command-line.  As we do not want to expose JVM parameters to them, we keep
+ * sun.misc.Unsafe for now.
+ */
+import sun.misc.Unsafe; // jdk.internal.misc.Unsafe;
+import tlc2.output.EC;
+import util.Assert;
+import util.TLCRuntime;
+
+/**
+ * This implementation uses sun.misc.Unsafe instead of a wrapping
+ * java.nio.ByteBuffer due to the fact that the former's allocateMemory
+ * takes a long argument, while the latter is restricted to
+ * Integer.MAX_VALUE as its capacity.<br>
+ * In 2012 this poses a too hard limit on the usable memory, hence we trade
+ * generality for performance.
+ */
+@SuppressWarnings("restriction")
+public final class LongArray {
+
+	private final Unsafe unsafe;
+	
+	/**
+	 * The base address of this direct memory allocated with Unsafe.
+	 */
+	private final long baseAddress;
+
+	/**
+	 * Maximum number of elements that can be contained in this array.
+	 */
+	private final long length;
+	
+	/**
+	 * CHOOSE logAddressSize \in 1..(Long.SIZE / 8): 2^logAddressSize = (Long.SIZE / 8)
+	 */
+	private static final int logAddressSize = 3;
+
+	LongArray(final long positions) {
+		this.length = positions;
+		this.unsafe = getUnsafe();
+		
+		// LongArray is only implemented for 64bit architectures. A 32bit
+		// implementation might be possible. However, implementing CAS (see
+		// trySet) seems impossible when values have to be split in two
+		// parts (low/hi) on a 32 bit architecture.
+		// addressSize(): Report the size in bytes of a native pointer, as
+		// stored via #putAddress . This value will be either 4 or 8. We
+		// expect 8 (Long.SIZE / 8) which is the size of a TLC fingerprint
+		// (see FP64).
+		Assert.check(this.unsafe.addressSize() == (Long.SIZE / 8), EC.GENERAL);
+		baseAddress = this.unsafe.allocateMemory(positions << logAddressSize);
+	}
+	
+	LongArray(final Collection<Long> from) {
+		this(from.size());
+		
+		final Iterator<Long> itr = from.iterator();
+		long i = 0L;
+		while(itr.hasNext()) {
+			Long next = itr.next();
+			set(i++, next);
+		}
+	}
+
+	/**
+	 * @return true iff LongArray can be used on the current JVM. It cannot be used
+	 *         if the architecture is not 64 bit and the sun.misc.Unsafe class
+	 *         cannot be loaded (on some JVM implementations, this isn't possible).
+	 */
+	public static boolean isSupported() {
+		if (TLCRuntime.ARCH.x86_64 != TLCRuntime.getInstance().getArchitecture()) {
+			return false;
+		}
+		try {
+			if (getUnsafe() != null) {
+				return true;
+			}
+			return false;
+		} catch (RuntimeException e) {
+			return false;
+		}
+	}
+	
+	/**
+	 * @return An Unsafe object or a {@link RuntimeException} wrapping any {@link Exception}. 
+	 */
+	private static sun.misc.Unsafe getUnsafe() {
+		// More Details can be found at: http://www.mydailyjava.blogspot.no/2013/12/sunmiscunsafe.html
+		try {
+			// Use reflection API to unhide Unsafe
+			final Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
+			f.setAccessible(true);
+			return (sun.misc.Unsafe) f.get(null);
+		} catch (Exception e) {
+			throw new RuntimeException(
+					"Trying to use Sun VM specific sun.misc.Unsafe implementation but no Sun based VM detected.",
+					e);
+		}
+	}
+
+	/**
+	 * Initializes the memory by overriding each byte with zero starting at
+	 * <code>baseAddress</code> and ending when all positions have been written.
+	 * 
+	 * @throws IOException
+	 */
+	public final void zeroMemory()
+			throws IOException {
+		this.unsafe.setMemory(baseAddress, length * 8L, (byte) 0); // times 8L because it only writes a single byte.
+	}
+	
+	/**
+	 * Initializes the memory by overriding each byte with zero starting at
+	 * <code>baseAddress</code> and ending when all positions have been written.
+	 * <p>
+	 * To speed up the initialization, <code>numThreads</code> allows to set a
+	 * thread count with which zeroing is done in parallel.
+	 * 
+	 * @param numThreads
+	 *            Number of threads used to zero memory
+	 * @throws IOException
+	 */
+	public final void zeroMemory(final int numThreads)
+			throws IOException {
+
+		final long segmentSize = (long) Math.floor(length / numThreads);
+		
+		final ExecutorService es = Executors.newFixedThreadPool(numThreads);
+		try {
+			final Collection<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>(numThreads);
+			for (int i = 0; i < numThreads; i++) {
+				final int offset = i;
+				tasks.add(new Callable<Boolean>() {
+
+					public Boolean call() throws Exception {
+						// Null memory (done in parallel on segments).
+						// This is essential as allocateMemory returns
+						// uninitialized memory and
+						// memInsert/memLockup utilize 0L as a mark for an
+						// unused fingerprint position.
+						// Otherwise memory garbage wouldn't be distinguishable
+						// from a true fp.
+						final long lowerBound = segmentSize * offset;
+						// The last threads zeros up to the end.
+						final long upperBound = offset == numThreads - 1 ? length : (1 + offset) * segmentSize;
+						for (long pos = lowerBound; pos < upperBound; pos++) {
+							set(pos, 0L);
+						}
+						return true;
+					}
+				});
+			}
+			final List<Future<Boolean>> invokeAll = es.invokeAll(tasks);
+			Assert.check(!invokeAll.isEmpty(), EC.GENERAL);
+		} catch (InterruptedException e) {
+			// not expected to happen
+			e.printStackTrace();
+		} finally {
+			es.shutdown();
+		}
+	}
+
+	/**
+	 * Converts from logical positions to 
+	 * physical memory addresses.
+	 * 
+	 * @param logical position (zero indexed)
+	 * @return The physical address of the fp slot
+	 */
+	private final long log2phy(long logicalAddress) {
+		return baseAddress + (logicalAddress << logAddressSize);
+	}
+	
+    private final void rangeCheck(final long position) {
+		assert position >= 0 && position < this.length;
+    }
+	
+	/**
+	 * CAS (compare and swap) variant of {@link LongArray#set(long, long)}.
+	 * 
+	 * @param position
+	 * @param expected
+	 * @param value
+	 * @return true iff successful 
+     * @throws IndexOutOfBoundsException
+	 */
+	public final boolean trySet(final long position, final long expected, final long value) {
+		rangeCheck(position);
+		return this.unsafe.compareAndSwapLong(null, log2phy(position), expected, value);
+	}
+	
+    /**
+     * Inserts the specified element at the specified position in this
+     * array. Overwrites any previous occupant of the specified position.
+     *
+     * @param position position at which the specified element is to be inserted
+     * @param value element to be inserted
+     * @throws IndexOutOfBoundsException
+     */
+	public final void set(final long position, final long value) {
+		rangeCheck(position);
+		this.unsafe.putAddress(log2phy(position), value);
+	}
+
+    /**
+     * Returns the element at the specified position in this array.
+     *
+     * @param  position position of the element to return
+     * @return the element at the specified position in this array
+     * @throws IndexOutOfBoundsException
+     */
+	public final long get(final long position) {
+		rangeCheck(position);
+		return this.unsafe.getAddress(log2phy(position));
+	}
+
+	/**
+	 * Swaps elements at pos1 and pos2. This is not atomic. The element at pos1
+	 * will for a moment not be an element of {@link LongArray}.
+	 */
+	public final void swap(final long position1, final long position2) {
+		rangeCheck(position1);
+		rangeCheck(position2);
+		final long tmp = get(position1);
+		set(position1, get(position2));
+		set(position2, tmp);
+	}
+	
+	/*
+	 * Variant of swap that uses copyMemory. This implementation - suprisingly - is
+	 * *not* faster compared to swap above (see LongArrayBenchmark).
+	 */
+	void swapCopy(final long position1, final long position2) {
+		final long tmp = unsafe.getAddress(log2phy(position1));
+		unsafe.copyMemory(log2phy(position2), log2phy(position1), 8L);
+		unsafe.putAddress(log2phy(position2), tmp);
+	}
+
+	
+    /**
+     * Returns the number of elements in this array.
+     *
+     * @return the number of elements in this array
+     */
+	public final long size() {
+		return length;
+	}
+	
+	/* (non-Javadoc)
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return toString(0L, length - 1L);
+	}
+
+	public String toString(long start, long end) {
+        long iMax = end;
+        if (iMax == -1L) {
+        	return "[]";
+        }
+
+        final StringBuilder b = new StringBuilder();
+        b.append('[');
+        for (long i = start; ; i++) {
+            final long lng = get(i);
+            if (lng == 0L) {
+            	b.append("e");
+            } else {
+            	b.append(lng);
+            }
+            if (i == iMax) {
+            	return b.append(']').toString();
+            }
+            b.append(", ");
+        }
+	}
+	
+	public static void main(final String[] args) throws IOException {
+		final long elements = 1L << Integer.valueOf(args[0]);
+		System.out.format("Allocating LongArray with %,d elements.\n", elements);
+		final LongArray longArray = new LongArray(elements);
+		longArray.zeroMemory();
+	}
+}
diff --git a/tlatools/src/tlc2/tool/fp/LongArrays.java b/tlatools/src/tlc2/tool/fp/LongArrays.java
new file mode 100644
index 0000000000000000000000000000000000000000..179e1a464fdcb007475e913da18fd7d00a64e053
--- /dev/null
+++ b/tlatools/src/tlc2/tool/fp/LongArrays.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved.
+package tlc2.tool.fp;
+
+public class LongArrays {
+
+	/**
+	 * Sorts the specified range [left,right] of the array. In other words,
+	 * sorts the elements from the array index <code>left</code> to index
+	 * <code>right</code>.
+	 */
+	public static void sort(final LongArray a, final long left, final long right, final LongComparator cmp) {
+		final long size = a.size();
+		if (size == 0) {
+			return;
+		}
+
+		//+++++++ Insertions sort +++++++//
+		for (long i = left, j = i; i < right; j = ++i) {
+			final long lo = (i + 1) % size;
+			final long ai = a.get(lo);
+			while (cmp.compare(ai, lo, a.get(j % size), j % size) <= -1) {
+				a.set((j + 1) % size, a.get(j % size));
+				if (j-- == left) {
+					break;
+				}
+			}
+			a.set((j + 1) % size, ai);
+		}
+	}
+	
+	public static class LongComparator {
+		public int compare(long lo, long loPos, long hi, long hiPos) {
+			// The default comparator ignores the positions.
+			return Long.compare(lo, hi);
+		}
+	}
+	
+	public static void sort(final LongArray a, final long left, final long right) {
+		sort(a, left, right, new LongComparator());
+	}
+
+	public static void sort(final LongArray a) {
+		sort(a, 0, a.size() - 1L, new LongComparator());
+	}
+	
+	/**
+	 * DOES NOT HANDLE LONG ARRAYS LARGER THAN INTEGER.MAX_VALUE
+	 */
+	public static long[] toArray(final LongArray array) {
+		final long[] res = new long[(int) array.size()];
+		for (int i = 0; i < array.size(); i++) {
+			long l = array.get(i);
+			res[i] = l;
+		}
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java b/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java
index d5ceafdb440926b49fa45658913781f08fecb4af..d0afaf9b6e713c4d7ded23fb2207a8e82c7b028c 100644
--- a/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java
@@ -10,6 +10,7 @@ import java.util.Arrays;
 import java.util.NoSuchElementException;
 
 import tlc2.output.EC;
+import tlc2.util.BufferedRandomAccessFile;
 import util.Assert;
 
 @SuppressWarnings("serial")
@@ -37,7 +38,8 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 		// index. However, we cannot use the 32st bit, because it is used to
 		// indicate if a fp has been flushed to disk. Hence we use the first n
 		// bits starting from the second most significant bit.
-		this.moveBy = (31 - fpSetConfig.getPrefixBits()) - (logMaxMemCnt - LogMaxLoad);
+		Assert.check(fpSetConfig.getFpBits() > 0, EC.GENERAL);
+		this.moveBy = (32 - fpSetConfig.getFpBits()) - (logMaxMemCnt - LogMaxLoad);
 		this.mask = (capacity - 1) << moveBy;
 		
 		this.flusher = new MSBFlusher();
@@ -61,6 +63,9 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 	protected long index(long fp, long aMask) {
 		// calculate hash value (just n most significant bits of fp) which is
 		// used as an index address
+		// 1) Right shift by 32 bits and cast to int (upper 32 bits are zeroed)
+		// 2) Zero the lower n bits according to mask
+		// 3) Right shift by moveBy (the bits which have previously been zeroed because of mask.
 		return ((int) (fp >>> 32) & aMask) >> moveBy;
 	}
 	
@@ -96,8 +101,8 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 		/* (non-Javadoc)
 		 * @see tlc2.tool.fp.DiskFPSet#mergeNewEntries(long[], int, java.io.RandomAccessFile, java.io.RandomAccessFile)
 		 */
-		protected void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException {
-			final long buffLen = tblCnt.get();
+		protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException {
+			final long buffLen = getTblCnt();
 			final TLCIterator itr = new TLCIterator(tbl);
 
 			// Precompute the maximum value of the new file.
@@ -105,13 +110,9 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 			// the last element of the disk file. In that case
 			// the new maxVal is the larger element of the two
 			// in-memory and on-disk elements.
-			// The largest on-disk element is passed to the
-			// iterator as a lower bound.
-			long maxVal;
+			long maxVal = itr.getLast();
 			if (index != null) {
-				maxVal = itr.getLast(index[index.length - 1]);
-			} else {
-				maxVal = itr.getLast();
+				maxVal = Math.max(maxVal, index[index.length - 1]);
 			}
 	
 			int indexLen = calculateIndexLen(buffLen);
@@ -125,7 +126,7 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 			boolean eof = false;
 			if (fileCnt > 0) {
 				try {
-					value = inRAF.readLong();
+					value = inRAFs[0].readLong();
 				} catch (EOFException e) {
 					eof = true;
 				}
@@ -140,7 +141,7 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 				if ((value < fp || eol) && !eof) {
 					writeFP(outRAF, value);
 					try {
-						value = inRAF.readLong();
+						value = inRAFs[0].readLong();
 					} catch (EOFException e) {
 						eof = true;
 					}
@@ -176,7 +177,10 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 	}
 
 	/**
-	 *
+	 * A TLCIterator wraps the buff given to it from its outer MSBDiskFPSet and
+	 * provides convenience methods to traverse the elements in buff, namely:
+	 * Check if there is a next element and get it as well as obtaining the
+	 * largest element.
 	 */
 	public static class TLCIterator {
 		/**
@@ -286,75 +290,15 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet {
 		}
 
 		/**
-		 * @param lowBound
-		 *            Stop searching for the last element if no element is
-		 *            larger than lowBound
-		 * @return The last element in the iteration that is larger than lowBound
+		 * @return The last (largest) element in the iteration (aka the
+		 *         underlying buff of buckets).
 		 * @exception NoSuchElementException
 		 *                if iteration is empty.
-		 *                <p>
+		 * 
 		 *                Pre-condition: Each bucket is sorted in ascending
-		 *                order. on-disk fingerprints don't adhere to the
-		 *                ascending order though!
-		 */
-		public long getLast(final long lowBound) {
-			int len = buff.length;
-
-			// Walk from the end of buff to the beginning. Each bucket that is
-			// found and non-null (null if no fingerprint for such an index has
-			// been added to the DiskFPSet) is checked for a valid fingerprint.
-			// A fp is valid iff it is larger zero. A zero fingerprint slot
-			// indicates an unoccupied slot, while a negative one corresponds to
-			// a fp that has already been flushed to disk.
-			while (len > 0) {
-				long[] bucket = buff[--len];
-
-				// Find last element > 0 in bucket (negative elements have already
-				// been flushed to disk, zero indicates an unoccupied slot).
-				if (bucket != null) {
-					for (int i = bucket.length - 1; i >= 0; i--) {
-						final long fp = bucket[i];
-						// unused slot
-						if (fp == 0) {
-							continue;
-						}
-						// in-memory fingerprint
-						if (fp > 0) {
-							if (fp < lowBound) {
-								// smaller lowBound
-								return lowBound;
-							} else {
-								// larger or equal lowBound
-								return fp;
-							}
-						}
-						// Cannot take on-disk fingerprints (negative ones) into
-						// account. They don't adhere to the precondition that
-						// the bucket is sorted. The bucket is logically sorted
-						// only for in-memory fingerprints.
-					}
-				}
-			}
-			// At this point we have scanned the complete buff, but haven't
-			// found a single fingerprint that hasn't been flushed to disk
-			// already.
-			throw new NoSuchElementException();
-		}
-
-		/**
-		 * @return The last element in the iteration.
-	     * @exception NoSuchElementException if iteration is empty.
-	     * 
-	     * Pre-condition: Each bucket is sorted in ascending order!
+		 *                order!
 		 */
 		public long getLast() {
-			/*
-			 * Tempting to delegate to getLast(Long.MAX_VALUE). However,
-			 * getLast() could not throw a NoSuchElementException when the
-			 * value returned is Long.MAX_VALUE. It could be a valid value, but
-			 * it might as well be the cutOff.
-			 */
-			
 			int len = buff.length;
 
 			// Walk from the end of buff to the beginning. Each bucket that is
diff --git a/tlatools/src/tlc2/tool/fp/MSBMultiFPSet.java b/tlatools/src/tlc2/tool/fp/MSBMultiFPSet.java
deleted file mode 100644
index f95177f2ca8f1210aab75f7b78fc1a10276ee558..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/tool/fp/MSBMultiFPSet.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
-package tlc2.tool.fp;
-
-import java.rmi.RemoteException;
-
-@SuppressWarnings("serial")
-public class MSBMultiFPSet extends MultiFPSet {
-
-	private final int moveBy;
-
-	public MSBMultiFPSet(final FPSetConfiguration fpSetConfiguration) throws RemoteException {
-		super(fpSetConfiguration);
-		
-		this.moveBy = 63 - fpSetConfiguration.getFpBits();
-	}
-
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.MultiFPSet#getNestedFPSets(int, long, int)
-	 */
-	protected FPSet[] getNestedFPSets(final FPSetConfiguration fpSetConfiguration) throws RemoteException {
-		int len = fpSetConfiguration.getMultiFPSetCnt();
-		final FPSet[] s = new FPSet[len];
-		for (int i = 0; i < len; i++) {
-			s[i] = FPSetFactory.getFPSet(new MultiFPSetConfiguration(fpSetConfiguration));
-		}
-		return s;
-	}
-
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.MultiFPSet#getFPSet(long)
-	 */
-	protected FPSet getFPSet(long fp) {
-		long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; // really zero msb?
-		// determine corresponding fpset based on the msb bits
-		final int idx = (int) (fp0 >> moveBy);
-		return this.sets[idx];
-	}
-}
diff --git a/tlatools/src/tlc2/tool/fp/MemFPSet.java b/tlatools/src/tlc2/tool/fp/MemFPSet.java
index 4bae4b362032a16646cbf9ff50216488e440cf97..366e2ae5f3db79e77566ac4cd78352a686ebf5cb 100644
--- a/tlatools/src/tlc2/tool/fp/MemFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/MemFPSet.java
@@ -10,6 +10,7 @@ import java.rmi.RemoteException;
 
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.TLCTrace;
 import util.Assert;
 import util.BufferedDataInputStream;
 import util.BufferedDataOutputStream;
@@ -63,9 +64,10 @@ public class MemFPSet extends FPSet {
     this.mask = initialCapacity - 1;
   }
 
-  public final void init(int numThreads, String metadir, String filename) {
+  public final FPSet init(int numThreads, String metadir, String filename) {
     this.metadir = metadir;
     this.filename = filename;
+	return this;
   }
     
   /**
@@ -202,6 +204,7 @@ public class MemFPSet extends FPSet {
   }
 
   public final void exit(boolean cleanup) throws IOException {
+	super.exit(cleanup);
     if (cleanup) {
       // Delete the metadata directory:
       File file = new File(this.metadir);
@@ -212,7 +215,7 @@ public class MemFPSet extends FPSet {
     System.exit(0);    
   }
 
-  public final double checkFPs() {
+  public final long checkFPs() {
     long dis = Long.MAX_VALUE;
     for (int i = 0; i < this.table.length; i++) {
       long[] bucket = this.table[i];
@@ -237,7 +240,7 @@ public class MemFPSet extends FPSet {
 	}
       }
     }
-    return (1.0/dis);
+    return dis;
   }
 
   // Checkpoint.
@@ -286,18 +289,14 @@ public class MemFPSet extends FPSet {
     this.commitChkpt(this.filename);
   }
     
-  final public void recover() throws IOException {
+  final public void recover(TLCTrace trace) throws IOException {
     this.recover(this.filename);
   }
 
-  public final void prepareRecovery() throws IOException { /*SKIP*/ }
-
   public final void recoverFP(long fp) throws IOException {
     Assert.check(!this.put(fp), EC.TLC_FP_NOT_IN_SET);
   }
   
-  public final void completeRecovery() throws IOException { /*SKIP*/ }
-    
   final private String chkptName(String fname, String ext) {
     return this.metadir + FileUtil.separator + fname + ".fp." + ext;
   }
diff --git a/tlatools/src/tlc2/tool/fp/MemFPSet1.java b/tlatools/src/tlc2/tool/fp/MemFPSet1.java
index 858a142be6fa90335e6f4a1a3528057be1567cc0..dd6c5386aba1e5d0637c62f6f4966a63e4a384f3 100644
--- a/tlatools/src/tlc2/tool/fp/MemFPSet1.java
+++ b/tlatools/src/tlc2/tool/fp/MemFPSet1.java
@@ -14,6 +14,7 @@ import java.rmi.RemoteException;
 
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.TLCTrace;
 import tlc2.util.SetOfLong;
 import util.Assert;
 import util.FileUtil;
@@ -33,9 +34,10 @@ public final class MemFPSet1 extends FPSet {
     this.set = new SetOfLong(10001, 0.75f);
   }
 
-  public final void init(int numThreads, String metadir, String filename) {
+  public final FPSet init(int numThreads, String metadir, String filename) {
     this.metadir = metadir;
     this.filename = filename;
+	return this;
   }
 
   public final long size() { return this.set.size(); }
@@ -51,6 +53,7 @@ public final class MemFPSet1 extends FPSet {
   }
 
   public final void exit(boolean cleanup) throws IOException {
+    super.exit(cleanup);
     if (cleanup) {
       // Delete the metadata directory:
       File file = new File(this.metadir);
@@ -61,7 +64,7 @@ public final class MemFPSet1 extends FPSet {
     System.exit(0);    
   }
 
-  public final double checkFPs() { return this.set.checkFPs(); }
+  public final long checkFPs() { return this.set.checkFPs(); }
 
   /* Checkpoint. */
   public final void beginChkpt(String fname) throws IOException {
@@ -95,18 +98,14 @@ public final class MemFPSet1 extends FPSet {
     this.commitChkpt(this.filename);
   }
   
-  public final void recover() throws IOException {
+  public final void recover(TLCTrace trace) throws IOException {
     this.recover(this.filename);
   }
 
-  public final void prepareRecovery() throws IOException { /*SKIP*/ }
-
   public final void recoverFP(long fp) throws IOException {
     Assert.check(!this.set.put(fp), EC.TLC_FP_NOT_IN_SET);
   }
   
-  public final void completeRecovery() throws IOException { /*SKIP*/ }
-
   private final String chkptName(String fname, String ext) {
     return this.metadir + FileUtil.separator + fname + ".fp." + ext;
   }
diff --git a/tlatools/src/tlc2/tool/fp/MemFPSet2.java b/tlatools/src/tlc2/tool/fp/MemFPSet2.java
index 4e97b12e8890a171ac2c1c89e13c0fe632a81e09..413bd337c69593eb1318d1ec133cab255b6fb11a 100644
--- a/tlatools/src/tlc2/tool/fp/MemFPSet2.java
+++ b/tlatools/src/tlc2/tool/fp/MemFPSet2.java
@@ -10,6 +10,7 @@ import java.rmi.RemoteException;
 
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.TLCTrace;
 import util.Assert;
 import util.BufferedDataInputStream;
 import util.BufferedDataOutputStream;
@@ -72,9 +73,10 @@ public final class MemFPSet2 extends FPSet {
     this.mask = spineSize - 1;
   }
   
-  public void init(int numThreads, String metadir, String fname) {
+  public FPSet init(int numThreads, String metadir, String fname) {
     this.metadir = metadir;
     this.filename = metadir + FileUtil.separator + fname;
+	return this;
   }
 
   public synchronized final long size() { return this.count; }
@@ -130,6 +132,7 @@ public final class MemFPSet2 extends FPSet {
   }
 
   public final void exit(boolean cleanup) throws IOException {
+	super.exit(cleanup);
     if (cleanup) {
       // Delete the metadata directory:
       FileUtil.deleteDir(this.metadir, true);
@@ -139,7 +142,7 @@ public final class MemFPSet2 extends FPSet {
     System.exit(0);    
   }
 
-  public final double checkFPs() {
+  public final long checkFPs() {
     long dis = Long.MAX_VALUE;
     for (int i = 0; i < this.table.length; i++) {
       long low = i & 0xffffffL;	
@@ -188,7 +191,7 @@ public final class MemFPSet2 extends FPSet {
 	}
       }
     }
-    return (1.0/dis);
+    return dis;
   }
 
   public final void beginChkpt(String fname) throws IOException {
@@ -246,18 +249,14 @@ public final class MemFPSet2 extends FPSet {
     this.commitChkpt(this.filename);
   }
 
-  public final void recover() throws IOException {
+  public final void recover(TLCTrace trace) throws IOException {
     this.recover(this.filename);
   }
 
-  public final void prepareRecovery() throws IOException { /*SKIP*/ }
-
   public final void recoverFP(long fp) throws IOException {
     Assert.check(!this.put(fp), EC.TLC_FP_NOT_IN_SET);
   }
   
-  public final void completeRecovery() throws IOException { /*SKIP*/ }
-
   final private String chkptName(String fname, String ext) {
     return this.metadir + FileUtil.separator + fname + ".fp." + ext;
   }
diff --git a/tlatools/src/tlc2/tool/fp/MultiFPSet.java b/tlatools/src/tlc2/tool/fp/MultiFPSet.java
index d3e12673796e773d4741b13215861211808cbda3..61a379c43011e1702051cf554bda1dc8ea41bf16 100644
--- a/tlatools/src/tlc2/tool/fp/MultiFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/MultiFPSet.java
@@ -8,10 +8,13 @@ import java.io.IOException;
 import java.rmi.NoSuchObjectException;
 import java.rmi.RemoteException;
 import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.IntStream;
 
 import tlc2.output.EC;
 import tlc2.tool.TLCTrace;
-import tlc2.util.BufferedRandomAccessFile;
+import tlc2.tool.TLCTrace.Enumerator;
 import util.Assert;
 
 /**
@@ -31,7 +34,7 @@ public class MultiFPSet extends FPSet {
 	/**
 	 * Contains all nested {@link FPSet}s 
 	 */
-	protected FPSet[] sets;
+	protected final List<FPSet> sets;
 
 	/**
 	 * Amount of leftmost bits used to determine nested {@link FPSet}
@@ -59,11 +62,10 @@ public class MultiFPSet extends FPSet {
 		this.fpbits = 64 - bits;
 	}
 
-	protected FPSet[] getNestedFPSets(final FPSetConfiguration fpSetConfiguration) throws RemoteException {
-		int len = fpSetConfiguration.getMultiFPSetCnt();
-		final FPSet[] s = new FPSet[len];
-		for (int i = 0; i < len; i++) {
-			s[i] = FPSetFactory.getFPSet(new MultiFPSetConfiguration(fpSetConfiguration));
+	protected List<FPSet> getNestedFPSets(final FPSetConfiguration fpSetConfiguration) throws RemoteException {
+		final List<FPSet> s = new ArrayList<>(fpSetConfiguration.getMultiFPSetCnt());
+		for (int i = 0; i < fpSetConfiguration.getMultiFPSetCnt(); i++) {
+			s.add(FPSetFactory.getFPSet(new MultiFPSetConfiguration(fpSetConfiguration)));
 		}
 		return s;
 	}
@@ -71,10 +73,21 @@ public class MultiFPSet extends FPSet {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#init(int, java.lang.String, java.lang.String)
 	 */
-	public final void init(int numThreads, String metadir, String filename) throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].init(numThreads, metadir, filename + "_" + i);
-		}
+	public final FPSet init(final int numThreads, final String metadir, final String filename) throws IOException {
+		IntStream.range(0, this.sets.size()).parallel().forEach(i -> {
+			try {
+				sets.get(i).init(numThreads, metadir, filename + "_" + i);
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
+		});
+		return this;
+	}
+
+	
+	@Override
+	public void incWorkers(final int num) {
+		sets.stream().forEach(s -> s.incWorkers(num) );
 	}
 
 	/* (non-Javadoc)
@@ -82,11 +95,7 @@ public class MultiFPSet extends FPSet {
 	 */
 	public final long size() {
 		/* Returns the number of fingerprints in this set. */
-		long sum = 0;
-		for (int i = 0; i < this.sets.length; i++) {
-			sum += this.sets[i].size();
-		}
-		return sum;
+		return sets.parallelStream().mapToLong(FPSet::size).sum();
 	}
 	
 	/**
@@ -98,7 +107,7 @@ public class MultiFPSet extends FPSet {
 		// shifts a zero into the leftmost (msb) position of the first operand for right operand times
 		// and cast it to int loosing the leftmost 32 bit
 		final int idx = (int) (fp >>> this.fpbits);
-		return this.sets[idx];
+		return this.sets.get(idx);
 	}
 
 	/**
@@ -126,8 +135,8 @@ public class MultiFPSet extends FPSet {
 	 * @see tlc2.tool.fp.FPSet#close()
 	 */
 	public final void close() {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].close();
+		for (FPSet fpSet : sets) {
+			fpSet.close();
 		}
 	}
 	
@@ -135,8 +144,8 @@ public class MultiFPSet extends FPSet {
 	 * @see tlc2.tool.fp.FPSet#unexportObject(boolean)
 	 */
 	public void unexportObject(boolean force) throws NoSuchObjectException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].unexportObject(force);
+		for (FPSet fpSet : sets) {
+			fpSet.unexportObject(force);
 		}
 		UnicastRemoteObject.unexportObject(this, force);
 	}
@@ -144,21 +153,36 @@ public class MultiFPSet extends FPSet {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#checkFPs()
 	 */
-	public final double checkFPs() throws IOException {
-		/* This is not quite correct. */
-		double res = Double.NEGATIVE_INFINITY;
-		for (int i = 0; i < this.sets.length; i++) {
-			res = Math.max(res, this.sets[i].checkFPs());
-		}
-		return res;
+	public final long checkFPs() throws IOException {
+		return sets.parallelStream().mapToLong(s -> {
+			try {
+				return s.checkFPs();
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
+		}).min().orElse(Long.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#checkInvariant()
+	 */
+	public boolean checkInvariant() throws IOException {
+		return sets.parallelStream().allMatch(s -> {
+			try {
+				return s.checkInvariant();
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
+		});
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#exit(boolean)
 	 */
 	public final void exit(boolean cleanup) throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].exit(cleanup);
+	    super.exit(cleanup);
+		for (FPSet fpSet : sets) {
+			fpSet.exit(cleanup);
 		}
 	}
 
@@ -166,8 +190,8 @@ public class MultiFPSet extends FPSet {
 	 * @see tlc2.tool.fp.FPSet#beginChkpt()
 	 */
 	public final void beginChkpt() throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].beginChkpt();
+		for (FPSet fpSet : sets) {
+			fpSet.beginChkpt();
 		}
 	}
 
@@ -175,40 +199,21 @@ public class MultiFPSet extends FPSet {
 	 * @see tlc2.tool.fp.FPSet#commitChkpt()
 	 */
 	public final void commitChkpt() throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].commitChkpt();
+		for (FPSet fpSet : sets) {
+			fpSet.commitChkpt();
 		}
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#recover()
 	 */
-	public final void recover() throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].prepareRecovery();
-		}
-
-		long recoverPtr = TLCTrace.getRecoverPtr();
-		BufferedRandomAccessFile braf = new BufferedRandomAccessFile(TLCTrace.getFilename(), "r");
-		while (braf.getFilePointer() < recoverPtr) {
-			braf.readLongNat(); /* drop */
-			long fp = braf.readLong();
+	public final void recover(TLCTrace trace) throws IOException {
+		final Enumerator elements = trace.elements();
+		while (elements.nextPos() != -1) {
+			long fp = elements.nextFP();
 			getFPSet(fp).recoverFP(fp);
 		}
-		braf.close();
-
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].completeRecovery();
-		}
-	}
-
-	/* (non-Javadoc)
-	 * 
-	 * NOOP!
-	 * 
-	 * @see tlc2.tool.fp.FPSet#prepareRecovery()
-	 */
-	public final void prepareRecovery() throws IOException { /* SKIP */
+		elements.close();
 	}
 
 	/* (non-Javadoc)
@@ -218,43 +223,46 @@ public class MultiFPSet extends FPSet {
 		Assert.check(!this.put(fp), EC.TLC_FP_NOT_IN_SET);
 	}
 
-	/* (non-Javadoc)
-	 * 
-	 * NOOP!
-	 * 
-	 * @see tlc2.tool.fp.FPSet#completeRecovery()
-	 */
-	public final void completeRecovery() throws IOException { /* SKIP */
-	}
-
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String)
 	 */
 	public final void beginChkpt(String filename) throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].beginChkpt(filename + "_" + i);
-		}
+		IntStream.range(0, this.sets.size()).parallel().forEach(i -> {
+			try {
+				sets.get(i).beginChkpt(filename + "_" + i);
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
+		});
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String)
 	 */
 	public final void commitChkpt(String filename) throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].commitChkpt(filename + "_" + i);
-		}
+		IntStream.range(0, this.sets.size()).parallel().forEach(i -> {
+			try {
+				sets.get(i).commitChkpt(filename + "_" + i);
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
+		});
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.FPSet#recover(java.lang.String)
 	 */
 	public final void recover(String filename) throws IOException {
-		for (int i = 0; i < this.sets.length; i++) {
-			this.sets[i].recover(filename + "_" + i);
-		}
+		IntStream.range(0, this.sets.size()).parallel().forEach(i -> {
+			try {
+				sets.get(i).recover(filename + "_" + i);
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
+		});
 	}
 
 	public FPSet[] getFPSets() {
-		return sets;
+		return sets.toArray(new FPSet[sets.size()]);
 	}
 }
diff --git a/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java b/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java
index 8d26cd5c1d4ec2552855f9389739b47cedc702a5..7bd34763870cbf8e558f15d145b7bb136b2b8bd7 100644
--- a/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java
+++ b/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java
@@ -11,7 +11,7 @@ class MultiFPSetConfiguration extends FPSetConfiguration {
 		// Set wrapper to values of config to be wrapped
 		this.fpBits = fpSetConfig.getFpBits();
 		this.ratio = fpSetConfig.getRatio();
-		
+		this.implementation = fpSetConfig.getImplementation();		
 		// Sanity check configuration right away
 		if (getMemoryInFingerprintCnt() <= 0) {
 			throw new IllegalArgumentException(
@@ -37,6 +37,10 @@ class MultiFPSetConfiguration extends FPSetConfiguration {
 	 * @see tlc2.tool.fp.FPSetConfiguration#getMemoryInFingerprintCnt()
 	 */
 	public long getMemoryInFingerprintCnt() {
-		return super.getMemoryInFingerprintCnt() / getMultiFPSetCnt();
+		// No need to divide by getMultiFPSetCnt because calls to
+		// super.getMemoryInFingerprintCnt eventually calls getMemoryInBytes above.
+		// Dividing by getMultiFPSetCnt here again will waste half the
+		// available memory.
+		return super.getMemoryInFingerprintCnt();
 	}
 }
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java b/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e8e7680af32e2d63f499c447e20efde415a8cdb
--- /dev/null
+++ b/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+
+@SuppressWarnings("serial")
+public abstract class NonCheckpointableDiskFPSet extends DiskFPSet {
+
+	protected NonCheckpointableDiskFPSet(FPSetConfiguration fpSetConfig) throws RemoteException {
+		super(fpSetConfig);
+	}
+
+	public void beginChkpt(String fname) throws IOException {
+		MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName());
+	}
+
+	public void commitChkpt(String fname) throws IOException {
+		MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName());
+	}
+
+	public void recover(String fname) throws IOException {
+		MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName());
+	}
+}
diff --git a/tlatools/src/tlc2/tool/fp/NoopFPSet.java b/tlatools/src/tlc2/tool/fp/NoopFPSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..d35e1e00005c0726f273ee20ccd48d285eea44cc
--- /dev/null
+++ b/tlatools/src/tlc2/tool/fp/NoopFPSet.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+
+import tlc2.tool.TLCTrace;
+
+/**
+ * An FPSet whose methods are all no-ops.
+ */
+@SuppressWarnings("serial")
+public class NoopFPSet extends FPSet {
+
+	protected NoopFPSet(FPSetConfiguration fpSetConfig) throws RemoteException {
+		super(fpSetConfig);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#init(int, java.lang.String, java.lang.String)
+	 */
+	@Override
+	public FPSet init(int numThreads, String metadir, String filename) throws IOException {
+		return this;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#size()
+	 */
+	@Override
+	public long size() {
+		return 0;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#put(long)
+	 */
+	@Override
+	public boolean put(long fp) throws IOException {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#contains(long)
+	 */
+	@Override
+	public boolean contains(long fp) throws IOException {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#checkFPs()
+	 */
+	@Override
+	public long checkFPs() throws IOException {
+		return 0;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#beginChkpt()
+	 */
+	@Override
+	public void beginChkpt() throws IOException {
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#commitChkpt()
+	 */
+	@Override
+	public void commitChkpt() throws IOException {
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#recover()
+	 */
+	@Override
+	public void recover(TLCTrace trace) throws IOException {
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#recoverFP(long)
+	 */
+	@Override
+	public void recoverFP(long fp) throws IOException {
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String)
+	 */
+	@Override
+	public void beginChkpt(String filename) throws IOException {
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String)
+	 */
+	@Override
+	public void commitChkpt(String filename) throws IOException {
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#recover(java.lang.String)
+	 */
+	@Override
+	public void recover(String filename) throws IOException {
+	}
+}
diff --git a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java
index d3322f5332e07e3ed97c8098f76d0e77e0906f40..954b7f72a6d2b1831f1235bafbfc92420e8deba1 100644
--- a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java
+++ b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java
@@ -1,154 +1,237 @@
 // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
 package tlc2.tool.fp;
 
-import java.io.EOFException;
+import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
-import java.nio.LongBuffer;
 import java.rmi.RemoteException;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.TreeSet;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.ToLongFunction;
+import java.util.logging.Level;
 
-import sun.misc.Unsafe;
+import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.fp.LongArrays.LongComparator;
+import tlc2.tool.fp.management.DiskFPSetMXWrapper;
+import tlc2.util.BufferedRandomAccessFile;
 import util.Assert;
 
-@SuppressWarnings({ "serial", "restriction" })
-public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic {
+/**
+ * see OpenAddressing.tla
+ */
+@SuppressWarnings({ "serial" })
+public final class OffHeapDiskFPSet extends NonCheckpointableDiskFPSet implements FPSetStatistic {
 	
-	protected static final double COLLISION_BUCKET_RATIO = .025d;
-	
-	protected final int bucketCapacity;
+	private static class OffHeapSynchronizer {
+		
+		private final Set<OffHeapDiskFPSet> sets = new HashSet<OffHeapDiskFPSet>();
+		
+		private final AtomicBoolean flusherChosen = new AtomicBoolean();
+		
+		// This barrier gets run after one thread signals the need to suspend
+		// put and contains operations to evict to secondary. Signaling is done
+		// via the flusherChoosen AtomicBoolean. All threads (numThreads) will
+		// then await on the barrier and the Runnable be executed when the
+		// last of numThreads arrives.
+		// Compared to an AtomicBoolean, the barrier operation use locks and
+		// are thus comparably expensive.
+		private final Phaser phaser = new Phaser(1) {
 
-	/**
-	 * This implementation uses sun.misc.Unsafe instead of a wrapping
-	 * java.nio.ByteBuffer due to the fact that the former's allocateMemory
-	 * takes a long argument, while the latter is restricted to
-	 * Integer.MAX_VALUE as its capacity.<br>
-	 * In 2012 this poses a too hard limit on the usable memory, hence we trade
-	 * generality for performance.
-	 */
-	private final Unsafe u;
+			@Override
+			protected boolean onAdvance(int phase, int registeredParties) {
+				// Atomically evict and reset flusherChosen to make sure no
+				// thread re-read flusherChosen=true after an eviction and
+				// waits again.
+				for (OffHeapDiskFPSet set : sets) {
+					set.evict();
+				}
+
+				// Release exclusive access. It has to be done by the runnable
+				// before workers waiting on the barrier wake up again.
+				Assert.check(flusherChosen.compareAndSet(true, false), EC.GENERAL);
+				
+				return super.onAdvance(phase, registeredParties);
+			}
+		};
+		
+		private OffHeapSynchronizer() {
+			// Don't want any copies.
+		}
+		
+		public final void add(final OffHeapDiskFPSet aSet) {
+			this.sets.add(aSet);
+		}
+		
+		public final void incWorkers(final int numWorkers) {
+			final int parties = phaser.getRegisteredParties();
+			if (parties < numWorkers) {
+				phaser.bulkRegister(numWorkers - parties);
+			}
+		}
+		
+		public final boolean evictPending() {
+			return flusherChosen.get();
+		}
+		
+		public final void evict() {
+			flusherChosen.compareAndSet(false, true);
+		}
+		
+		public final void awaitEviction() {
+			phaser.arriveAndAwaitAdvance();
+		}
+		
+		public AtomicBoolean getFlusherChosen() {
+			return flusherChosen;
+		}
+	}
+
+	// We require a singleton here, because if TLC is run with multiple instances
+	// of FPSets - the default - workers will call evict and awaitEvict on 
+	// all FPSet instances. Thus, an individual synchronization internal to each
+	// FPSet would never see the complete set of waiting workers. TLC would thus
+	// deadlock. This is why SYNC is a singleton and shared by all FPSet instances.
+	private static final OffHeapSynchronizer SYNC = new OffHeapSynchronizer();
 	
-	/**
-	 * The base address allocated for fingerprints
-	 */
-	private final long baseAddress;
+	private static final int PROBE_LIMIT = Integer.getInteger(OffHeapDiskFPSet.class.getName() + ".probeLimit", 1024);
+	static final long EMPTY = 0L;
 	
-	/**
-	 * Address size (either 4 or 8 bytes) depending on current architecture
-	 */
-	private final int logAddressSize;
 
 	/**
-	 * A bucket containing collision elements which is used as a fall-back if a
-	 * bucket is fully used up. Buckets cannot grow as the whole in-memory
-	 * data-structure is static and not designed to be resized.
-	 * 
-	 * <p>
-	 * Open addressing - contrary to separate chaining - is not an option for an
-	 * {@link OffHeapDiskFPSet}, because it does not support the invariant of
-	 * monotonic increasing buckets required by the {@link Indexer}. Adhering to
-	 * this invariant has the benefit, that only the elements in a bucket have
-	 * to be sorted, but they don't change buckets during sort. Thus, a
-	 * temporary sort array as in {@link LSBDiskFPSet.LSBFlusher#prepareTable()} is
-	 * obsolete halving the memory footprint.
-	 * </p>
+	 * @see LongArray#isSupported()
 	 */
-	protected CollisionBucket collisionBucket;
+	public static boolean isSupported() {
+		return LongArray.isSupported();
+	}
+
+	private final transient LongArray array;
 	
 	/**
 	 * The indexer maps a fingerprint to a in-memory bucket and the associated lock
 	 */
-	private final Indexer indexer;
-	
-	private final ReadWriteLock csRWLock = new ReentrantReadWriteLock();
+	private final transient Indexer indexer;
+
+	private int numThreads;
 
 	protected OffHeapDiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException {
 		super(fpSetConfig);
 		
-		final long memoryInFingerprintCnt = fpSetConfig.getMemoryInFingerprintCnt();
+		final long positions = fpSetConfig.getMemoryInFingerprintCnt();
 		
 		// Determine base address which varies depending on machine architecture.
-		u = OffHeapDiskFPSetHelper.getUnsafe();
-		int addressSize = u.addressSize();
-		int cnt = -1;
-		while (addressSize > 0) {
-			cnt++;
-			addressSize = addressSize >>> 1; // == (n/2)
-		}
-		logAddressSize = cnt;
-
-		// Allocate non-heap memory for maxInMemoryCapacity fingerprints
-		long bytes = memoryInFingerprintCnt << logAddressSize;
-		
-		baseAddress = u.allocateMemory(bytes);
-
-		final int csCapacity = (int) (maxTblCnt * COLLISION_BUCKET_RATIO);
-		this.collisionBucket = new TreeSetCollisionBucket(csCapacity);
-		
-		this.flusher = new OffHeapMSBFlusher();
-		
-		// Move n as many times to the right to calculate moveBy. moveBy is the
-		// number of bits the (fp & mask) has to be right shifted to make the
-		// logical bucket index.
-		long n = (Long.MAX_VALUE >>> fpSetConfig.getPrefixBits()) - (memoryInFingerprintCnt - 1);
-		int moveBy = 0;
-		while (n >= memoryInFingerprintCnt) {
-			moveBy++;
-			n = n >>> 1; // == (n/2)
-		}
-		
-		// Calculate Hamming weight of maxTblCnt
-		final int bitCount = Long.bitCount(memoryInFingerprintCnt);
+		this.array = new LongArray(positions);
 		
 		// If Hamming weight is 1, the logical index address can be calculated
 		// significantly faster by bit-shifting. However, with large memory
 		// sizes, only supporting increments of 2^n sizes would waste memory
 		// (e.g. either 32GiB or 64Gib). Hence, we check if the bitCount allows
 		// us to use bit-shifting. If not, we fall back to less efficient
-		// calculations. Additionally we increase the bucket capacity to make
-		// use of extra memory. The down side is, that larger buckets mean
-		// increased linear search. But linear search on maximally 31 elements
-		// still outperforms disk I/0.
-		if (bitCount == 1) {
-			bucketCapacity = InitialBucketCapacity;
-			this.indexer = new BitshiftingIndexer(moveBy, fpSetConfig.getPrefixBits());
+		// calculations.
+		if (Long.bitCount(positions) == 1) {
+			this.indexer = new BitshiftingIndexer(positions, fpSetConfig.getFpBits());
 		} else {
-			// Round maxInMemoryCapacity to next lower 2^n power
-			cnt = -1;
-			while (bytes > 0) {
-				cnt++;
-				bytes = bytes >>> 1;
-			}
-			
-			// Extra memory that cannot be addressed by BitshiftingIndexer
-			final long extraMem = (memoryInFingerprintCnt * LongSize) - (long) Math.pow(2, cnt);
-			
-			// Divide extra memory across addressable buckets
-			int x = (int) (extraMem / ((n + 1) / InitialBucketCapacity));
-			bucketCapacity = InitialBucketCapacity + (x / LongSize) ;
-			// Twice InitialBucketCapacity would mean we could have used one
-			// more bit for addressing.
-			Assert.check(bucketCapacity < (2 * InitialBucketCapacity), EC.GENERAL);
-
 			// non 2^n buckets cannot use a bit shifting indexer
-			this.indexer = new Indexer(moveBy, fpSetConfig.getPrefixBits());
+			this.indexer = new Indexer(positions, fpSetConfig.getFpBits());
 		}
+		
+		// Use the non-concurrent flusher as the default. Will be replaced by
+		// the CyclicBarrier-Runnable later. Just set to prevent NPEs when
+		// eviction/flush is called before init.
+		this.flusher = new OffHeapMSBFlusher(array);
+		
+		this.flusherChosen = SYNC.getFlusherChosen();
+		SYNC.add(this);
 	}
 	
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.DiskFPSet#init(int, java.lang.String, java.lang.String)
 	 */
-	public void init(int numThreads, String aMetadir, String filename)
+	@Override
+	public FPSet init(final int numThreads, String aMetadir, String filename)
 			throws IOException {
 		super.init(numThreads, aMetadir, filename);
+		this.numThreads = numThreads;
+		
+		array.zeroMemory(numThreads);
+		return this;
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#incWorkers(int)
+	 */
+	public void incWorkers(int numWorkers) {
+		assert numWorkers == this.numThreads;
+		SYNC.incWorkers(numWorkers);
+	}
+
+	public void evict() {
+		// statistics
+		growDiskMark++;
+		final long timestamp = System.currentTimeMillis();
+		final long insertions = tblCnt.longValue();
+		final double lf = tblCnt.doubleValue() / (double) maxTblCnt;
 		
-		OffHeapDiskFPSetHelper.zeroMemory(u, baseAddress, numThreads, fpSetConfig.getMemoryInFingerprintCnt());
+		LOGGER.log(Level.FINE,
+				"Started eviction of disk {0} the {1}. time at {2} after {3} insertions, load factor {4} and reprobe of {5}.",
+				new Object[] { ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(),
+						timestamp, insertions, lf, PROBE_LIMIT });
+		
+		// Check that the table adheres to our invariant. Otherwise, we
+		// can't hope to successfully evict it.
+		assert checkInput(array, indexer, PROBE_LIMIT) : "Table violates invariants prior to eviction: "
+				+ array.toString();
+		
+		// Only pay the price of creating threads when array is
+		// sufficiently large and the array size is large enough to
+		// partition it for multiple threads.
+		flusher = getFlusher(numThreads, insertions);
+		
+		try {
+			flusher.flushTable(); // Evict()
+		} catch (IOException e) {
+			// wrap in unchecked.
+			throw new OffHeapRuntimeException(e);
+		}
+
+		// statistics and logging again.
+		final long l = System.currentTimeMillis() - timestamp;
+		flushTime += l;
+		LOGGER.log(Level.FINE,
+				"Finished eviction of disk {0} the {1}. time at {2}, in {3} sec after {4} insertions, load factor {5} and reprobe of {6}.",
+				new Object[] { ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l,
+						System.currentTimeMillis(), insertions, lf, PROBE_LIMIT });
+	}
+
+	private Flusher getFlusher(final int numThreads, final long insertions) {
+		if (array.size() >= 8192 && Math.floor(array.size() / (double) numThreads) > 2 * PROBE_LIMIT) {
+			return new ConcurrentOffHeapMSBFlusher(array, PROBE_LIMIT, numThreads, insertions);
+		} else {
+			return this.flusher;
+		}
+	}
+
+	private boolean checkEvictPending() {
+		if (SYNC.evictPending()) {
+			SYNC.awaitEviction();
+			return true;
+		}
+		return false;
 	}
 
 	/* (non-Javadoc)
@@ -156,21 +239,17 @@ public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic {
 	 */
 	public long sizeof() {
 		long size = 44; // approx size of this DiskFPSet object
-		size += maxTblCnt * (long) LongSize;
+		size += maxTblCnt * LongSize;
 		size += getIndexCapacity() * 4;
-		size += getCollisionBucketCnt() * (long) LongSize; // ignoring the internal TreeSet overhead here
 		return size;
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.DiskFPSet#needsDiskFlush()
 	 */
-	protected boolean needsDiskFlush() {
-		// Only flush due to collision ratio when primary hash table is at least
-		// 25% full. Otherwise a second flush potentially immediately follows a
-		// first one, when both values for tblCnt and collision size can be small.
-		return (collisionRatioExceeds(COLLISION_BUCKET_RATIO) && loadFactorExceeds(.25d)) 
-				|| loadFactorExceeds(1d) || forceFlush;
+	@Override
+	protected final boolean needsDiskFlush() {
+		return loadFactorExceeds(1d) || forceFlush;
 	}
 	
 	/**
@@ -182,148 +261,163 @@ public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic {
 	 *            from growing past it.
 	 * @return true iff the current hash table load exceeds the given limit
 	 */
-	private boolean loadFactorExceeds(final double limit) {
-		// Base this one the primary hash table only and exclude the
-		// collision bucket
-		final double d = (this.tblCnt.doubleValue() - collisionBucket.size()) / (double) this.maxTblCnt;
+	private final boolean loadFactorExceeds(final double limit) {
+		final double d = (this.tblCnt.doubleValue()) / (double) this.maxTblCnt;
 		return d >= limit;
 	}
-
-	/**
-	 * @param limit A limit the collsionBucket is not allowed to exceed
-	 * @return The proportional size of the collision bucket compared to the
-	 *         size of the set.
-	 */
-	private boolean collisionRatioExceeds(final double limit) {
-		// Do not use the thread safe getCollisionRatio here to avoid
-		// unnecessary locking. put() calls us holding a memory write locking
-		// which also blocks writers to collisionBucket.
-		final long size = collisionBucket.size();
-		// Subtract size from overall tblCnt as it includes the cs size
-		// @see put(long)
-		final double d = (double) size / (tblCnt.doubleValue() - size);
-		return d >= limit;
-	}
-	
+    
+	private static final int FOUND = -1;
+    
 	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.DiskFPSet#getLockIndex(long)
+	 * @see tlc2.tool.fp.DiskFPSet#memLookup(long)
 	 */
-	@Override
-	protected int getLockIndex(long fp) {
-		return this.indexer.getLockIndex(fp);
+	final boolean memLookup(final long fp0) {
+		return memLookup0(fp0) == FOUND;
+	}
+
+	final int memLookup0(final long fp0) {
+		int free = PROBE_LIMIT;
+		for (int i = 0; i <= PROBE_LIMIT; i++) {
+			final long position = indexer.getIdx(fp0, i);
+			final long l = array.get(position);
+			if (fp0 == (l & FLUSHED_MASK)) {
+				// zero the long msb (which is 1 if fp has been flushed to disk)
+				return FOUND;
+			} else if (l == EMPTY) {
+				return Math.min(i, free);
+			} else if (l < EMPTY && free == PROBE_LIMIT) {
+				free = i;
+			}
+		}
+
+		return free;
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.DiskFPSet#memLookup(long)
+	 * @see tlc2.tool.fp.DiskFPSet#memInsert(long)
 	 */
-	boolean memLookup(long fp) {
-		final long position = indexer.getLogicalPosition(fp);
-		
-		// Linearly search the logical bucket; 0L is an invalid fp and marks the
-		// end of the allocated bucket
-		long l = -1L;
-		for (int i = 0; i < bucketCapacity && l != 0L; i++) {
-			l = u.getAddress(log2phy(position, i));
-			// zero the long msb (which is 1 if fp has been flushed to disk)
-			if (fp == (l & 0x7FFFFFFFFFFFFFFFL)) {
+	final boolean memInsert(final long fp0) throws IOException {
+		return memInsert0(fp0, 0);
+	}
+
+	final boolean memInsert0(final long fp0, final int start) throws IOException {
+		// See OffHeapDiskFPSetJPFTest for a (verbatim) version that has
+		// additionally been verified with JPF.
+		for (int i = start; i < PROBE_LIMIT; i++) {
+			final long position = indexer.getIdx(fp0, i);
+			final long expected = array.get(position);
+			if (expected == EMPTY || (expected < 0 && fp0 != (expected & FLUSHED_MASK))) {
+				// Try to CAS the new fingerprint.
+				if (array.trySet(position, expected, fp0)) {
+					this.tblCnt.increment();
+					return false;
+				} else {
+					// Retry at current position because another thread wrote a
+					// value concurrently (possibly the same one this thread is
+					// trying to write).
+					i = i - 1;
+					continue;
+				}
+			}
+			
+			// Expected is the fingerprint to be inserted.
+			if ((expected & FLUSHED_MASK) == fp0) {
 				return true;
 			}
 		}
+
 		
-		return csLookup(fp);
+		// We failed to insert into primary. Consequently, lets try and make
+		// some room by signaling all threads to wait for eviction.
+		forceFlush();
+		// We've signaled for eviction to start or failed because some other
+		// thread beat us to it. Actual eviction and setting flusherChosen back
+		// to false is done by the Barrier's Runnable. We cannot set
+		// flusherChosen back to false after barrier.awaits returns because it
+		// leaves a window during which other threads read the old true value of
+		// flusherChosen a second time and immediately wait again.
+		
+		return put(fp0);
 	}
-	
-	/**
-	 * Probes {@link OffHeapDiskFPSet#collisionBucket} for the given fingerprint.
-	 * @param fp
-	 * @return true iff fp is in the collision bucket
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.FPSet#put(long)
 	 */
-	protected boolean csLookup(long fp) {
-		try {
-			csRWLock.readLock().lock();
-			return collisionBucket.contains(fp);
-		} finally {
-			csRWLock.readLock().unlock();
+	public final boolean put(final long fp) throws IOException {
+		if (checkEvictPending()) {
+			return put(fp);
 		}
+
+		// zeros the msb
+		final long fp0 = fp & FLUSHED_MASK;
+
+		// Only check primary and disk iff there exists a disk file. index is
+		// created when we wait and thus cannot race.
+		int start = 0;
+		if (index != null) {
+			// Lookup primary memory
+			if ((start = memLookup0(fp0)) == FOUND) {
+				this.memHitCnt.increment();
+				return true;
+			}
+			
+			// Lookup on disk
+			if (this.diskLookup(fp0)) {
+				this.diskHitCnt.increment();
+				return true;
+			}
+		}
+		
+		// Lastly, try to insert into memory.
+		return memInsert0(fp0, start);
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.DiskFPSet#memInsert(long)
+	 * @see tlc2.tool.fp.FPSet#contains(long)
 	 */
-	boolean memInsert(long fp) {
-		final long position = indexer.getLogicalPosition(fp);
-
-		long l = -1;
-		long freePosition = -1L;
-		for (int i = 0; i < bucketCapacity && l != 0L; i++) {
-			l = u.getAddress(log2phy(position, i));
-			// zero the long msb (which is 1 if fp has been flushed to disk)
-			if (fp == (l & 0x7FFFFFFFFFFFFFFFL)) {
-				return true;
-			} else if (l == 0L && freePosition == -1) {
-				if (i == 0) {
-					tblLoad++;
-				}
-				// empty or disk written slot found, simply insert at _current_ position
-				u.putAddress(log2phy(position, i), fp);
-				this.tblCnt.getAndIncrement();
-				return false;
-			} else if (l < 0L && freePosition == -1) {
-				// record free (disk written fp) slot
-				freePosition = log2phy(position, i);
-			}
+	public final boolean contains(final long fp) throws IOException {
+		// maintains happen-before with regards to successful put
+		
+		if (checkEvictPending()) {
+			return contains(fp);
 		}
 
-		// index slot overflow, thus add to collisionBucket of write to free
-		// position.
-		if (freePosition > -1 && !csLookup(fp)) {
-			u.putAddress(freePosition, fp);
-			this.tblCnt.getAndIncrement();
-			return false;
-		} else {
-			boolean success = csInsert(fp);
-			if (success) {
-				this.tblCnt.getAndIncrement();
-			}
-			return !success;
+		// zeros the msb
+		final long fp0 = fp & FLUSHED_MASK;
+		
+		// Lookup in primary
+		if (memLookup(fp0)) {
+			return true;
+		}
+		
+		// Lookup on secondary/disk
+		if (this.diskLookup(fp0)) {
+			diskHitCnt.increment();
+			return true;
 		}
+		
+		return false;
 	}
 	
-	/**
-	 * Inserts the given fingerprint into the {@link OffHeapDiskFPSet#collisionBucket}.
-	 * @param fp
-	 * @return true iff fp has been added to the collision bucket
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#forceFlush()
 	 */
-	protected boolean csInsert(long fp) {
-		try {
-			csRWLock.writeLock().lock();
-			return collisionBucket.add(fp);
-		} finally {
-			csRWLock.writeLock().unlock();
-		}
+	public void forceFlush() {
+		SYNC.evict();
 	}
 
-	/**
-	 * Converts from logical bucket index numbers and in-bucket position to a
-	 * physical memory address.
-	 * 
-	 * @param bucketNumber
-	 * @param inBucketPosition
-	 * @return The physical address of the fp slot
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#acquireTblWriteLock()
 	 */
-	private long log2phy(long bucketNumber, long inBucketPosition) {
-		return log2phy(bucketNumber + inBucketPosition);
+	void acquireTblWriteLock() {
+		// no-op for now
 	}
-	
-	/**
-	 * Converts from logical addresses to 
-	 * physical memory addresses.
-	 * 
-	 * @param logicalAddress
-	 * @return The physical address of the fp slot
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#releaseTblWriteLock()
 	 */
-	private long log2phy(long logicalAddress) {
-		return baseAddress + (logicalAddress << logAddressSize);
+	void releaseTblWriteLock() {
+		// no-op for now
 	}
 
 	/* (non-Javadoc)
@@ -332,560 +426,1003 @@ public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic {
 	public long getTblCapacity() {
 		return maxTblCnt;
 	}
-
+	
 	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.DiskFPSet#getCollisionBucketCnt()
+	 * @see tlc2.tool.fp.DiskFPSet#getTblLoad()
 	 */
-	public long getCollisionBucketCnt() {
-		try {
-			this.csRWLock.readLock().lock();
-			return collisionBucket.size();
-		} finally {
-			this.csRWLock.readLock().unlock();
-		}
+	public long getTblLoad() {
+		return getTblCnt();
 	}
-
+	
 	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.DiskFPSet#getCollisionRatio()
+	 * @see tlc2.tool.fp.DiskFPSet#getOverallCapacity()
 	 */
-	public double getCollisionRatio() {
-		return (double) getCollisionBucketCnt() / tblCnt.doubleValue();
+	public long getOverallCapacity() {
+		return array.size();
 	}
-
-	public class Indexer {
-		protected final long prefixMask;
-		/**
-		 * Number of bits to right shift bits during index calculation
-		 * @see MSBDiskFPSet#moveBy
-		 */
-		protected final int moveBy;
-		/**
-		 * Number of bits to right shift bits during index calculation of
-		 * striped lock.
-		 */
-		protected final int lockMoveBy;
-
-		public Indexer(final int moveBy, int prefixBits) {
-			// same for lockCnt
-			this.prefixMask = 0x7FFFFFFFFFFFFFFFL >>> prefixBits;
-			this.moveBy = moveBy;
-			this.lockMoveBy = 63 - prefixBits - LogLockCnt;
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#getBucketCapacity()
+	 */
+	public long getBucketCapacity() {
+		// A misnomer, but bucketCapacity is obviously not applicable with open
+		// addressing.
+		return PROBE_LIMIT;
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#checkFPs()
+	 */
+	@Override
+	public long checkFPs() throws IOException {
+		if (getTblCnt() <= 0) {
+			return Long.MAX_VALUE;
 		}
+		// The superclass implementation provides insufficient performance and
+		// scalability. These problems get more pronounced with large arrays.
+		// In order to provide a faster solution to the closest pair problem in
+		// one-dimensional space, we cheat and determine the minimum distance
+		// between fingerprints only on a subset of fingerprints. The subset are
+		// those fingerprints which happen to be in main memory and have not yet
+		// been evicted to disk. We believe this to be a sufficient approximation.
+		// The algorithm to find the closest pair in one-dimensional space is:
+		// 1) Sort all fingerprints.
+		// 2) Perform a linear scan/search
+		// => O(n log n)
+		// Scalability is achieved by partitioning the array for 1) and 2),
+		// which is possible due to the bounded disorder in array.
 		
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.DiskFPSet#getLockIndex(long)
-		 */
-		protected int getLockIndex(long fp) {
-			// calculate hash value (just n most significant bits of fp) which is
-			// used as an index address
-			final long idx = (fp & prefixMask) >> lockMoveBy;
-			Assert.check(0 <= idx && idx < lockCnt, EC.GENERAL);
-			return (int) idx;
+		// One can think of two shortcuts to approximate of the closest pair:
+		// a) Track the minimum distance during the 1) step in prepareTable. A possible
+		// implementation can pass something similar to a BinaryOpeartor to the
+		// LongComparator. Due to the bounded disorder, this generally works except for
+		// the corner case where the closest pair are two fingerprints in two
+		// non-adjacent partitions, i.e. [,,,fp23],[],...,[],[,,fp42,,,]. This is
+		// frequently the case for very sparsely populated arrays. 
+		// b) For low load factors (thus low collision rates) simply skip step 1) and do
+		// not sort the array at all. A zero collision rate implies that the array
+		// is sorted (except for first PROBE_LIMIT slots) if fingerprints wrapped around.
+		// c) Select just 1..N partitions out of the set of all array partitions
+		// for which the closest pair gets calculated. This is a more extreme variant
+		// of the current approximation which skips fingerprints on disk.
+		
+		// 1) Sort all fingerprints, either with a sequential or concurrent
+		// flusher depending on the size of the array.
+		final int numThreads = TLCGlobals.getNumWorkers();
+		this.flusher = getFlusher(numThreads, getTblCnt());
+		this.flusher.prepareTable();
+		
+		// 2) A task finds the pair with the minimum distance in an ordered
+		// subrange of array. Each task is assigned an id. This id determines
+		// which subrange of array (partition) it searches.
+		final Collection<Callable<Long>> tasks = new ArrayList<Callable<Long>>(numThreads);
+		final long length = (long) Math.floor(array.size() / (double) numThreads);
+		for (int i = 0; i < numThreads; i++) {
+			final int id = i;
+			tasks.add(new Callable<Long>() {
+				@Override
+				public Long call() throws Exception {
+					final boolean isLast = id == numThreads - 1;
+					final long start = id * length;
+					// Partitions overlap in case the two fingerprints with
+					// minimum distance are in two adjacent partitions.
+					final long end = (isLast ? array.size() - 1L : start + length) + 1L;
+					
+					long distance = Long.MAX_VALUE;
+					
+					// Reuse Iterator implementation which already handles the
+					// various cases related to the cyclic table layout. Pass it
+					// getTblCnt to make sure next does not keep going forever
+					// if the array is empty. Instead, check the itr's position.
+					final Iterator itr = new Iterator(array, getTblCnt(), start, indexer,
+							isLast && id != 0 ? Iterator.WRAP.FORBIDDEN : Iterator.WRAP.ALLOWED);
+					try {
+						// If the partition is empty, next throws a NSEE.
+						long x = itr.next();
+						long y = 0;
+						while ((y = itr.next(end)) != EMPTY) {
+							long d = y - x;
+							// d < 0 indicates that the array is not sorted.
+							// d == 0 indicates two identical elements in the
+							// fingerprint set. Iff this is the last partition,
+							// itr.next might have returned the first element of the
+							// first partition.
+							assert d > 0 ^ (d < 0 && itr.getPos() > end && isLast);
+							distance = Math.min(distance, d);
+							x = y;
+						}
+						return distance;
+					} catch (NoSuchElementException e) {
+						return distance;
+					}
+				}
+			});
+		}
+		
+		// Start as many tasks as we have workers. Afterwards, collect the
+		// minimum distance out of the set of results provided by the workers.
+		final ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
+		try {
+			final long distance = executorService.invokeAll(tasks).stream().min(new Comparator<Future<Long>>() {
+				public int compare(Future<Long> o1, Future<Long> o2) {
+					try {
+						return Long.compare(o1.get(), o2.get());
+					} catch (InterruptedException e) {
+						Thread.currentThread().interrupt();
+						throw new OffHeapRuntimeException(e);
+					} catch (ExecutionException e) {
+						throw new OffHeapRuntimeException(e);
+					}
+				}
+			}).get().get();
+			return distance;
+		} catch (InterruptedException ie) {
+			Thread.currentThread().interrupt();
+			throw new OffHeapRuntimeException(ie);
+		} catch (ExecutionException e) {
+			throw new OffHeapRuntimeException(e);
+		} finally {
+			executorService.shutdown();
 		}
+		
+		
+		// Dynamic variant to maintain closest pair:
+		// The dynamic (during insertions and not on static data) variant of the closest
+		// pair problem also provides an approximation of the actual closest pair. It
+		// can be maintained with little overhead in runtime and space and returned in
+		// constant time O(1). Contrary to the current implementation, it does not skip
+		// fingerprints on disk but observes all fingerprints.
+		// For that, we observe the minimum distance OD of the fingerprint to be inserted
+		// and all fingerprints it collides with in the method memInsert0. Before
+		// returning from memInsert0, the global minimum distance GD gets updated to the
+		// observed distance OD iff OD < GD (for scalability reasons we want to use a
+		// java.util.concurrent.atomic.LongAccumulator).
+		// This provides a sufficient approximation iff the collision rate is
+		// sufficiently high. Under a low collision rate, the number of comparisons in
+		// memInsert0 will be low. But even under a high collision rate, the
+		// approximation GD does not necessarily converge with the real minimum distance:
+		// Let f1 differs from f2 in just the lowest significant bit (LSB) and let f1 <
+		// f2. Depending on the size of the array, it is possible that f1 and f2 are
+		// indexed to two adjacent positions. If both positions for f1 and f2 are empty
+		// by the time f1 and f2 get inserted, no distance for f1 and f2 will be
+		// calculated.
+		// For low hash table/array collision rates (small load factors), we could
+		// simply report "insufficient data". Users will still have the optimistic FP64
+		// collision probability, which will also be very small.
+	}
 
-		/**
-		 * @param fp
-		 * @return The logical bucket position in the table for the given fingerprint.
-		 */
-		protected long getLogicalPosition(final long fp) {
-			// push MSBs for moveBy positions to the right and align with a bucket address
-			long position = (fp & prefixMask) >> moveBy;
-			position = floorToBucket(position);
-			Assert.check(0 <= position && position < maxTblCnt, EC.GENERAL);
-			return position;
+	//**************************** Indexer ****************************//
+	
+	public static class Indexer {
+
+		private static final long minFingerprint = 1L; //Minimum possible fingerprint (0L marks an empty position)
+
+		private final double tblScalingFactor;
+		private final long maxFingerprint;
+		protected final long positions;
+		
+		public Indexer(final long positions, final int fpBits) {
+			this(positions, fpBits, 0xFFFFFFFFFFFFFFFFL >>> fpBits);
+			assert fpBits > 0;
 		}
 
-		public long getNextBucketBasePosition(long logicalPosition) {
-			return floorToBucket(logicalPosition + bucketCapacity);
+		public Indexer(final long positions, final int fpBits, final long maxValue) {
+			this.positions = positions;
+			this.maxFingerprint = maxValue;
+			// (position-1L) because array is zero indexed.
+			this.tblScalingFactor = (positions - 1L) / ((maxFingerprint - minFingerprint) * 1f);
 		}
 		
-		/**
-		 * Returns the largest position that
-		 * is less than or equal to the argument and is equal to bucket base address.
-		 * 
-		 * @param logicalPosition
-		 * @return
-		 */
-		private long floorToBucket(long logicalPosition) {
-			long d = (long) Math.floor(logicalPosition / bucketCapacity);
-			return bucketCapacity * d;
+		protected long getIdx(final long fp) {
+			return getIdx(fp, 0);
 		}
-
-		/**
-		 * @param logicalPosition
-		 * @return true iff logicalPosition is a multiple of bucketCapacity
-		 */
-		public boolean isBucketBasePosition(long logicalPosition) {
-			return logicalPosition % bucketCapacity == 0;
+		
+		protected long getIdx(final long fp, final int probe) {
+			long idx = Math.round(tblScalingFactor * (fp - minFingerprint)) + probe;
+			return idx % positions;
 		}
 	}
 	
-	/**
-	 * A {@link BitshiftingIndexer} uses the more efficient AND operation
-	 * compared to MODULO and DIV used by {@link Indexer}. Since indexing is
-	 * executed on every {@link FPSet#put(long)} or {@link FPSet#contains(long)}
-	 * , it is worthwhile to minimize is execution overhead.
-	 */
-	public class BitshiftingIndexer extends Indexer {
-		
-		/**
-		 * Mask used to round of to a bucket address which is a power of 2.
-		 */
-		protected final long bucketBaseIdx;
+	public static class BitshiftingIndexer extends Indexer {
+		private final long prefixMask;
+		private final int rShift;
 
-		public BitshiftingIndexer(final int moveBy, final int prefixBits) throws RemoteException {
-			super(moveBy, prefixBits);
-			this.bucketBaseIdx = 0x7FFFFFFFFFFFFFFFL - (bucketCapacity - 1);
+		public BitshiftingIndexer(final long positions, final int fpBits) throws RemoteException {
+			super(positions, fpBits);
+			
+			this.prefixMask = 0xFFFFFFFFFFFFFFFFL >>> fpBits;
+			
+			long n = (0xFFFFFFFFFFFFFFFFL >>> fpBits) - (positions - 1);
+			int moveBy = 0;
+			while (n >= positions) {
+				moveBy++;
+				n = n >>> 1; // == (n/2)
+			}
+			this.rShift = moveBy;
 		}
 		
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#getLogicalPosition(long)
-		 */
 		@Override
-		protected long getLogicalPosition(final long fp) {
-			// push MSBs for moveBy positions to the right and align with a bucket address
-			long position = ((fp & prefixMask) >> moveBy)  & bucketBaseIdx; 
-			//Assert.check(0 <= position && position < maxTblCnt, EC.GENERAL);
-			return position;
+		protected long getIdx(final long fp) {
+			return (fp & prefixMask) >>> rShift;
 		}
-
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#getNextBucketPosition(long)
-		 */
+		
 		@Override
-		public long getNextBucketBasePosition(long logicalPosition) {
-			return (logicalPosition + bucketCapacity) & bucketBaseIdx;
+		protected long getIdx(final long fp, int probe) {
+			// Have to mod positions because probe might cause us to overshoot.
+			return (((fp & prefixMask) >>> rShift) + probe) % positions; 
 		}
+	}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#isBucketBase(long)
-		 */
-		@Override
-		public boolean isBucketBasePosition(long logicalPosition) {
-			return (logicalPosition & (InitialBucketCapacity - 1)) == 0;
+	//**************************** Flusher ****************************//
+	
+	private LongComparator getLongComparator() {
+		return new LongComparator() {
+			@Override
+			public int compare(long fpA, long posA, long fpB, long posB) {
+				
+				// Elements not in Nat \ {0} remain at their current
+				// position.
+				if (fpA <= EMPTY || fpB <= EMPTY) {
+					return 0;
+				}
+				
+				final boolean wrappedA = indexer.getIdx(fpA) > posA;
+				final boolean wrappedB = indexer.getIdx(fpB) > posB;
+				
+				if (wrappedA == wrappedB && posA > posB) {
+					return fpA < fpB ? -1 : 1;
+				} else if (wrappedA ^ wrappedB) {
+					if (posA < posB && fpA < fpB) {
+						return -1;
+					}
+					if (posA > posB && fpA > fpB) {
+						return -1;
+					}
+				}
+				return 0;
+			}
+		};
+	}
+
+	/**
+	 * Returns the number of fingerprints stored in table/array in the range
+	 * [start,limit].
+	 */
+	private long getTableOffset(final LongArray a, final long reprobe, final Indexer indexer, final long start,
+			final long limit) {
+		long occupied = 0L;
+		for (long pos = start; pos < limit; pos++) {
+			final long fp = a.get(pos % a.size());
+			if (fp <= EMPTY) {
+				continue;
+			}
+			final long idx = indexer.getIdx(fp);
+			if (idx > pos) {
+				// Ignore the elements that wrapped around the
+				// end when scanning the first partition.
+				continue;
+			}
+			if (idx + reprobe < pos) {
+				// Ignore the elements of the first partition
+				// when wrapping around for the last partition.
+				continue;
+			}
+			occupied = occupied + 1L;
 		}
+		return occupied;
 	}
 	
-	public class OffHeapMSBFlusher extends Flusher {
-		
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.DiskFPSet.Flusher#flushTable()
-		 */
-		@Override
-		void flushTable() throws IOException {
-			super.flushTable();
-			
-			// garbage old values in collision bucket
-			collisionBucket.clear();
+	private long getNextLower(long idx) {
+		// Reverse to the next non-evicted/empty fp that belongs to this partition.
+		long fp = array.get(idx);
+		while (fp <= EMPTY || indexer.getIdx(fp) > idx) {
+			fp = array.get(--idx);
+		}
+		return fp;
+	}
+	
+	/**
+	 * The number of fingerprints stored on disk smaller than fp.
+	 */
+	private long getDiskOffset(final int id, final long fp) throws IOException {
+		if (this.index == null) {
+			return 0L;
 		}
 		
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.MSBDiskFPSet#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile)
-		 */
-		@Override
-		protected void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException {
-			final long buffLen = tblCnt.get();
-			ByteBufferIterator itr = new ByteBufferIterator(u, baseAddress, collisionBucket, buffLen);
+		final int indexLength = this.index.length;
+		int loPage = 0;
+		int hiPage = indexLength - 1;
+		long loVal = this.index[loPage];
+		long hiVal = this.index[hiPage];
 
-			// Precompute the maximum value of the new file
-			long maxVal = itr.getLast();
-			if (index != null) {
-				maxVal = Math.max(maxVal, index[index.length - 1]);
+		if (fp <= loVal) {
+			return 0L;
+		}
+		if (fp >= hiVal) {
+			return this.braf[id].length() / FPSet.LongSize;
+		}
+		// See DiskFPSet#diskLookup for comments.
+		
+		// Lookup the corresponding disk page in index.
+		final double dfp = (double) fp;
+		while (loPage < hiPage - 1) {
+			final double dhi = (double) hiPage;
+			final double dlo = (double) loPage;
+			final double dhiVal = (double) hiVal;
+			final double dloVal = (double) loVal;
+			int midPage = (loPage + 1) + (int) ((dhi - dlo - 1.0) * (dfp - dloVal) / (dhiVal - dloVal));
+			if (midPage == hiPage) {
+				midPage--;
 			}
-
-			int indexLen = calculateIndexLen(buffLen);
-			index = new long[indexLen];
-			index[indexLen - 1] = maxVal;
-			currIndex = 0;
-			counter = 0;
-
-			// initialize positions in "buff" and "inRAF"
-			long value = 0L; // initialize only to make compiler happy
-			boolean eof = false;
-			if (fileCnt > 0) {
-				try {
-					value = inRAF.readLong();
-				} catch (EOFException e) {
-					eof = true;
-				}
+			final long v = this.index[midPage];
+			if (fp < v) {
+				hiPage = midPage;
+				hiVal = v;
+			} else if (fp > v) {
+				loPage = midPage;
+				loVal = v;
 			} else {
-				eof = true;
+				return (midPage * 1L) * (NumEntriesPerPage * 1L);
 			}
+		}
+		// no page is in between loPage and hiPage at this point
+		Assert.check(hiPage == loPage + 1, EC.SYSTEM_INDEX_ERROR);
+		assert this.index[loPage] < fp && fp < this.index[hiPage];
+		
+		// Read the disk page and try to find the given fingerprint or the next
+		// smaller one. Calculate its offset in file.
+		long midEntry = -1L;
+		long loEntry = ((long) loPage) * NumEntriesPerPage;
+		long hiEntry = ((loPage == indexLength - 2) ? this.fileCnt - 1 : ((long) hiPage) * NumEntriesPerPage);
+		final BufferedRandomAccessFile raf = this.braf[id];
+		while (loEntry < hiEntry) {
+			midEntry = calculateMidEntry(loVal, hiVal, dfp, loEntry, hiEntry);
+			raf.seek(midEntry * LongSize);
+			final long v = raf.readLong();
 
-			// merge while both lists still have elements remaining
-			boolean eol = false;
-			long fp = itr.next();
-			while (!eof || !eol) {
-				if ((value < fp || eol) && !eof) {
-					writeFP(outRAF, value);
-					try {
-						value = inRAF.readLong();
-					} catch (EOFException e) {
-						eof = true;
-					}
-				} else {
-					// prevent converting every long to String when assertion holds (this is expensive)
-					if (value == fp) {
-						//MAK: Commented cause a duplicate does not pose a risk for correctness.
-						// It merely indicates a bug somewhere.
-						//Assert.check(false, EC.TLC_FP_VALUE_ALREADY_ON_DISK,
-						//		String.valueOf(value));
-						MP.printWarning(EC.TLC_FP_VALUE_ALREADY_ON_DISK, String.valueOf(value));
-					}
-					writeFP(outRAF, fp);
-					// we used one fp up, thus move to next one
-					try {
-						fp = itr.next();
-					} catch (NoSuchElementException e) {
-						// has read all elements?
-						Assert.check(!itr.hasNext(), EC.GENERAL);
-						eol = true;
-					}
-				}
+			if (fp < v) {
+				hiEntry = midEntry;
+				hiVal = v;
+			} else if (fp > v) {
+				loEntry = midEntry + 1;
+				loVal = v;
+				midEntry = loEntry;
+			} else {
+				break;
 			}
-			
-			// both sets used up completely
-			Assert.check(eof && eol, EC.GENERAL);
-
-			// currIndex is amount of disk writes
-			Assert.check(currIndex == indexLen - 1, EC.SYSTEM_INDEX_ERROR);
+		}
+		
+		assert isHigher(midEntry, fp, raf);
+		return midEntry;
+	}
 
-			// maintain object invariants
-			fileCnt += buffLen;
+	public boolean isHigher(long midEntry, long fp, BufferedRandomAccessFile raf) throws IOException {
+		raf.seek((midEntry - 1L) * LongSize);
+		final long low = raf.readLong();
+		final long high = raf.readLong();
+		if (low < fp && fp < high) {
+			return true;
 		}
+		return false;
 	}
-	
-	/**
-	 * A non-thread safe Iterator 
-	 */
-	public class ByteBufferIterator {
 
-		private final CollisionBucket cs;
-		/**
-		 * Number of elements in the buffer
-		 */
-		private long bufferElements;
-		/**
-		 * Total amount of elements in both the buffer as well as the collisionBucket. 
-		 */
-		private final long totalElements;
-		/**
-		 * The logical position is the position inside the {@link LongBuffer} and
-		 * thus reflects a fingerprints
-		 */
-		private long logicalPosition = 0;
+	public class ConcurrentOffHeapMSBFlusher extends OffHeapMSBFlusher {
+		
+		private final int numThreads;
+		private final ExecutorService executorService;
+		private final int r; 
+		private final long insertions;
 		/**
-		 * Used to verify that the elements we hand out are strictly monotonic
-		 * increasing.
+		 * The length of a single partition.
 		 */
-		private long previous = -1l;
-		/**
-		 * Number of elements read with next()
+		private final long length;
+		private List<Result> offsets; 
+
+		public ConcurrentOffHeapMSBFlusher(final LongArray array, final int r, final int numThreads,
+				final long insertions) {
+			super(array);
+			this.r = r;
+			this.numThreads = numThreads;
+			this.insertions = insertions;
+
+			this.length = (long) Math.floor(a.size() / (double) numThreads);
+			this.executorService = Executors.newFixedThreadPool(numThreads);
+		}
+		
+		/* (non-Javadoc)
+		 * @see tlc2.tool.fp.DiskFPSet.Flusher#prepareTable()
 		 */
-		private long readElements = 0L;
+		@Override
+		protected void prepareTable() {
+			final long now = System.currentTimeMillis();
+			final CyclicBarrier phase = new CyclicBarrier(this.numThreads);
+			final Collection<Callable<Result>> tasks = new ArrayList<Callable<Result>>(numThreads);
+			for (int i = 0; i < numThreads; i++) {
+				final int id = i;
+				tasks.add(new Callable<Result>() {
+					@Override
+					public Result call() throws Exception {
+						final boolean isFirst = id == 0;
+						final boolean isLast = id == numThreads - 1;
+						final long start = id * length;
+						final long end = isLast ? a.size() - 1L : start + length;
+						
+						// Sort partition p_n. We have exclusive access.
+						LongArrays.sort(a, isFirst ? 0L : start + 1L, end, getLongComparator());
+						assert checkSorted(a, indexer, r, isFirst ? 0L : start + 1L, end) == -1L : String.format(
+								"Array %s not fully sorted at index %s and reprobe %s in range [%s,%s].", a.toString(),
+								checkSorted(array, indexer, r, start, end), r, start, end);
+						
+						// Wait for the other threads sorting p_n+1 to be done
+						// before we stitch together p_n and p_n+1.
+						phase.await();
 
-		private long cache = -1L;
-		private final Unsafe unsafe;
+						// Sort the range between partition p_n and
+						// p_n+1 bounded by reprobe.
+						LongArrays.sort(a, end - r+1L, end + r+1L, getLongComparator());
+						
+						// Wait for table to be fully sorted before we calculate
+						// the offsets. Offsets can only be correctly calculated
+						// on a sorted table.
+						phase.await();
+						
+						// Count the occupied positions for this
+						// partition. Occupied positions are those which
+						// get evicted (written to disk).
+						// This could be done as part of (insertion) sort
+						// above at the price of higher complexity. Thus,
+						// it's done here until it becomes a bottleneck.
+						final long limit = isLast ? a.size() + r : end;
+						final long occupied = getTableOffset(a, r, indexer, start, limit);
+						assert occupied <= limit - start;
+						
+						if (index == null) {
+							// No index, no need to calculate a disk offset.
+							return new Result(occupied, 0L);
+						}
+						
+						// Determine number of elements in the old/current file.
+						if (isFirst && isLast) {
+							return new Result(occupied, fileCnt);
+						} else if (isFirst) {
+							return new Result(occupied, getDiskOffset(id, getNextLower(end)));
+						} else if (isLast) {
+							return new Result(occupied, fileCnt - getDiskOffset(id, getNextLower(start)));
+						} else {
+							return new Result(occupied,
+									getDiskOffset(id, getNextLower(end)) - getDiskOffset(id, getNextLower(start)));
+						}
+					}
+				});
+			}
+			try {
+				offsets = futuresToResults(executorService.invokeAll(tasks));
+			} catch (InterruptedException ie) {
+				Thread.currentThread().interrupt();
+				throw new OffHeapRuntimeException(ie);
+			} catch (ExecutionException notExpectedToHappen) {
+				throw new OffHeapRuntimeException(notExpectedToHappen);
+			}
 
-		public ByteBufferIterator(Unsafe u, long baseAddress, CollisionBucket collisionBucket, long expectedElements) {
-			this.unsafe = u;
-			this.logicalPosition = 0L;
-			this.totalElements = expectedElements;
-			
-			// Do calculation before prepareForFlush() potentially empties the cs causing size() to return 0 
-			this.bufferElements = expectedElements - collisionBucket.size();
+			assert checkSorted(a, indexer, r) == -1L : String.format(
+					"Array %s not fully sorted at index %s and reprobe %s.", a.toString(),
+					checkSorted(array, indexer, r), r);
 			
-			this.cs = collisionBucket;
-			this.cs.prepareForFlush();
+			LOGGER.log(Level.FINE, "Sorted in-memory table with {0} workers and reprobe {1} in {2} ms.",
+					new Object[] { numThreads, r, System.currentTimeMillis() - now });
+		}
+		
+		private List<Result> futuresToResults(List<Future<Result>> futures) throws InterruptedException, ExecutionException {
+			final List<Result> res = new ArrayList<Result>(futures.size());
+			for (Future<Result> future : futures) {
+				res.add(future.get());
+			}
+			return res;
 		}
 
-	    /**
-	     * Returns the next element in the iteration.
-	     *
-	     * @return the next element in the iteration.
-	     * @exception NoSuchElementException iteration has no more elements.
-	     */
-		public long next() {
-			long result = -1l;
+		@Override
+		protected void mergeNewEntries(final BufferedRandomAccessFile[] inRAFs, final RandomAccessFile outRAF, final Iterator ignored) throws IOException {
+			final long now = System.currentTimeMillis();
+			assert offsets.stream().mapToLong(new ToLongFunction<Result>() {
+				public long applyAsLong(Result result) {
+					return result.getTable();
+				}
+			}).sum() == insertions : "Missing inserted elements during eviction.";
+			assert offsets.stream().mapToLong(new ToLongFunction<Result>() {
+				public long applyAsLong(Result result) {
+					return result.getDisk();
+				}
+			}).sum() == fileCnt : "Missing disk elements during eviction.";
 
-			if (cache < 0L && bufferElements > 0) {
-				result = getNextFromBuffer();
-				bufferElements--;
-			} else {
-				result = cache;
-				cache = -1L;
+			// Calculate offsets for in and the out file, i.e. sum up the
+			// combined number of elements in lower partitions.
+			for (int id = 1; id < numThreads; id++) {
+				final Result prev = offsets.get(id - 1);
+				final Result result = offsets.get(id);
+				result.setInOffset(prev.getInOffset() + prev.getDisk());
+				result.setOutOffSet(prev.getOutOffset() + prev.getTotal());
 			}
 
-			if (!cs.isEmpty()) {
-				long first = cs.first();
-				if (result > first || result == -1L) {
-					cs.remove(first);
-					cache = result;
-					result = first;
-				}
+			final long outLength = outRAF.length();
+			final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>(numThreads);
+			final BufferedRandomAccessFile[] tmpRAFs = new BufferedRandomAccessFile[numThreads];
+			for (int i = 0; i < numThreads; i++) {
+				final int id = i;
+				
+				// Create a new RAF instance. The outRAF instance is
+				// otherwise shared by multiple writers leading to race
+				// conditions and inconsistent fingerprint set files.
+				tmpRAFs[id] = new BufferedRandomAccessFile(new File(tmpFilename), "rw");
+				tmpRAFs[id].setLength(outLength);
+
+				// Set offsets into the out (tmp) file.
+				final Result result = offsets.get(id);
+				tmpRAFs[id].seekAndMark(result.getOutOffset() * FPSet.LongSize);
+
+				// Set offset and the number of elements the
+				// iterator is supposed to return.
+				final Iterator itr = new Iterator(a, result.getTable(), id * length, indexer,
+						id == 0 ? Iterator.WRAP.ALLOWED : Iterator.WRAP.FORBIDDEN);
+				
+				final BufferedRandomAccessFile inRAF = inRAFs[id];
+				assert (result.getInOffset() + result.getDisk()) * FPSet.LongSize <= inRAF.length();
+				inRAF.seekAndMark(result.getInOffset() * FPSet.LongSize);
+
+				// Stop reading after diskReads elements (after
+				// which the next thread continues) except for the
+				// last thread which reads until EOF. Pass 0 when
+				// nothing can be read from disk.
+				final long diskReads = id == numThreads - 1 ? fileCnt - result.getInOffset() : result.getDisk();
+				
+				tasks.add(new Callable<Void>() {
+					public Void call() throws Exception {
+						ConcurrentOffHeapMSBFlusher.super.mergeNewEntries(inRAF, tmpRAFs[id], itr, diskReads);
+						assert tmpRAFs[id].getFilePointer() == (result.getOutOffset() + result.getTotal()) * FPSet.LongSize : id
+								+ " writer did not write expected amount of fingerprints to disk.";
+						return null;
+					}
+				});
 			}
-			
-			// adhere to the general Iterator contract to fail fast and not hand out
-			// meaningless values
-			if (result == -1L) {
-				throw new NoSuchElementException();
+			// Combine the callable results.
+			try {
+				executorService.invokeAll(tasks);
+				executorService.shutdown();
+				executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+				assert checkRAFs(tmpRAFs);
+			} catch (InterruptedException ie) {
+				Thread.currentThread().interrupt();
+				throw new OffHeapRuntimeException(ie);
+			} finally {
+				executorService.shutdown();
 			}
 			
-			// hand out strictly monotonic increasing elements
-			Assert.check(previous < result, EC.GENERAL);
-			previous = result;
-			
-			// maintain read statistics
-			readElements++;
+			// Finally close the out rafs after all tasks have finished. On
+			// Linux, closing an instance of the tmpRAFs appears to be racy when
+			// other tasks still execute.
+			for (int i = 0; i < tmpRAFs.length; i++) {
+				tmpRAFs[i].close();
+			}
+
+			assert checkRAFs(inRAFs);
+			assert checkTable(a) : "Missed element during eviction.";
 			
-			return result;
+			LOGGER.log(Level.FINE, "Wrote table to disk with {0} workers in {1} ms.",
+					new Object[] { numThreads, (System.currentTimeMillis() - now) });
 		}
 
-		private long getNextFromBuffer() {
-			sortNextBucket();
+		private class Result {
+			private final long occupiedTable;
+			private final long occupiedDisk;
+			private long outOffset;
+			private long inOffset;
 			
-			long l = unsafe.getAddress(log2phy(logicalPosition));
-			if (l > 0L) {
-				unsafe.putAddress(log2phy(logicalPosition++), l | 0x8000000000000000L);
-				return l;
+			public Result(long occupiedTable, long occupiedDisk) {
+				this.occupiedTable = occupiedTable;
+				this.occupiedDisk = occupiedDisk;
 			}
-			
-			while ((l = unsafe.getAddress(log2phy(logicalPosition))) <= 0L && logicalPosition < maxTblCnt) {
-				// increment position to next bucket
-				logicalPosition = indexer.getNextBucketBasePosition(logicalPosition);
-				sortNextBucket();
+			public long getDisk() {
+				return occupiedDisk;
 			}
-			
-			if (l > 0L) {
-				unsafe.putAddress(log2phy(logicalPosition++), l | 0x8000000000000000L);
-				return l;
+			public long getTable() {
+				return occupiedTable;
+			}
+			public long getTotal() {
+				return occupiedDisk + occupiedTable;
+			}
+			public void setOutOffSet(long offset) {
+				this.outOffset = offset;
+			}
+			public void setInOffset(long offset) {
+				this.inOffset = offset;
+			}
+			public long getInOffset() {
+				return this.inOffset;
+			}
+			public long getOutOffset() {
+				return this.outOffset;
 			}
-			throw new NoSuchElementException();
 		}
+	}
+	
+	public class OffHeapMSBFlusher extends Flusher {
+		
+		protected final LongArray a;
 
-		// sort the current logical bucket if we reach the first slot of the
-		// bucket
-		private void sortNextBucket() {
-			if (indexer.isBucketBasePosition(logicalPosition)) {
-				long[] longBuffer = new long[bucketCapacity];
-				int i = 0;
-				for (; i < bucketCapacity; i++) {
-					long l = unsafe.getAddress(log2phy(logicalPosition + i));
-					if (l <= 0L) {
-						break;
-					} else {
-						longBuffer[i] = l;
-					}
-				}
-				if (i > 0) {
-					Arrays.sort(longBuffer, 0, i);
-					for (int j = 0; j < i; j++) {
-						unsafe.putAddress(log2phy(logicalPosition, j),
-								longBuffer[j]);
-					}
-				}
-			}
+		public OffHeapMSBFlusher(LongArray array) {
+			a = array;
 		}
 
-	    /**
-	     * Returns <tt>true</tt> if the iteration has more elements. (In other
-	     * words, returns <tt>true</tt> if <tt>next</tt> would return an element
-	     * rather than throwing an exception.)
-	     *
-	     * @return <tt>true</tt> if the iterator has more elements.
-	     */
-		public boolean hasNext() {
-			// hasNext does not move the indices at all!
-			return readElements < totalElements;
+		/* (non-Javadoc)
+		 * @see tlc2.tool.fp.DiskFPSet.Flusher#prepareTable()
+		 */
+		@Override
+		protected void prepareTable() {
+			super.prepareTable();
+			final int r = PROBE_LIMIT;
+			
+			assert checkInput(array, indexer, r) : "Table violates invariants prior to eviction";
+			
+			// Sort with a single thread.
+			LongArrays.sort(a, 0, a.size() - 1L + r, getLongComparator());
+
+			assert checkSorted(a, indexer, r) == -1L : String.format(
+					"Array %s not fully sorted at index %s and reprobe %s.", a.toString(),
+					checkSorted(array, indexer, r), r);
 		}
-		
-		/**
-		 * @return The last element in the iteration.
-	     * @exception NoSuchElementException if iteration is empty.
+
+		/* (non-Javadoc)
+		 * @see tlc2.tool.fp.MSBDiskFPSet#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile)
 		 */
-		public long getLast() {
-			// Remember current position
-			final long tmpLogicalPosition = logicalPosition;
+		@Override
+		protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException {
+			final long buffLen = tblCnt.sum();
+			final Iterator itr = new Iterator(array, buffLen, indexer);
 
-			// Calculate last bucket position and have it sorted 
-			logicalPosition = maxTblCnt - bucketCapacity;
-			sortNextBucket();
+			final int indexLen = calculateIndexLen(buffLen);
+			index = new long[indexLen];
+			mergeNewEntries(inRAFs, outRAF, itr);
 
-			// Reverse the current bucket to obtain last element (More elegantly
-			// this could be achieved recursively, but this can cause a
-			// stack overflow).
-			long l = 1L;
-			while ((l = unsafe.getAddress(log2phy(logicalPosition-- + bucketCapacity - 1))) <= 0L) {
-				sortNextBucket();
-			}
+			final long length = (outRAF.length() / LongSize) - 1L;
+			writeIndex(index, outRAF, length);
+			assert checkIndex(index) : "Broken disk index.";
+			assert checkIndex(index, outRAF, length) : "Misaligned disk index.";
 			
-			// Done searching in-memory storage backwards, reset position to
-			// original value.
-			logicalPosition = tmpLogicalPosition;
+			// maintain object invariants
+			fileCnt += buffLen;
+		}
+
+		protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF, Iterator itr)
+				throws IOException {
+			inRAFs[0].seek(0);
+			mergeNewEntries(inRAFs[0], outRAF, itr, inRAFs[0].length() / FPSet.LongSize);
+		}
+
+		/*
+		 * See PlusCal spec OpenAddressing.ConcurrentFlusher.tla which has been checked for Nat == 0..6.
+		 */
+		protected void mergeNewEntries(BufferedRandomAccessFile inRAF, RandomAccessFile outRAF, final Iterator itr,
+				long diskReads) throws IOException {
 			
-			// Compare max element found in main in-memory buffer to man
-			// element in collisionBucket. Return max of the two.
-			if (!cs.isEmpty()) {
-				l = Math.max(cs.last(), l);
+			// Disk might be empty.
+			long value = 0L;
+			if (diskReads > 0) {
+				value = inRAF.readLong();
+			} else {
+				assert fileCnt == 0L;
 			}
 			
-			// Either return the maximum element or fail fast.
-			if (l > 0L) {
-				return l;
-			}
-			throw new NoSuchElementException();
+			long tableReads = itr.elements;
+			long fp = itr.markNext();
+			
+			do {
+				if (value == fp) {
+					MP.printWarning(EC.TLC_FP_VALUE_ALREADY_ON_DISK, String.valueOf(value));
+				}
+				assert fp > EMPTY : "Wrote an invalid fingerprint to disk.";
+				
+				// From memory/table
+		        if (tableReads > 0 && (fp < value || diskReads == 0)) {
+					outRAF.writeLong(fp);
+					tableReads--;
+					diskWriteCnt.increment();
+					// Read next value if any.
+		            if (tableReads > 0) {
+						final long nextFP = itr.markNext();
+						assert nextFP > fp : nextFP + " > " + fp + " from table at pos " + itr.pos + " "
+								+ a.toString(itr.pos - 10L, itr.pos + 10L);
+						fp = nextFP;
+		            }
+		         }
+		         
+		         // From file/disk
+				if (diskReads > 0 && (value < fp || tableReads == 0)) {
+					outRAF.writeLong(value);
+					diskReads--;
+					diskWriteCnt.increment();
+					// Read next value if any.
+					if (diskReads > 0) {
+						final long nextValue = inRAF.readLong();
+						assert value < nextValue;
+						value = nextValue;
+					}
+				}
+
+			} while (diskReads > 0 || tableReads > 0);
+			
+			// both sets used up completely
+			Assert.check(diskReads == 0L && tableReads == 0L, EC.GENERAL);
+			assert !itr.hasNext();
 		}
-		
-		// prevent synthetic methods
-		private long log2phy(long logicalAddress) {
-			return OffHeapDiskFPSet.this.log2phy(logicalAddress);
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.DiskFPSet#calculateIndexLen(long)
+	 */
+	@Override
+	protected int calculateIndexLen(final long tblcnt) {
+		int indexLen = super.calculateIndexLen(tblcnt);
+		if ((tblcnt + fileCnt - 1L) % NumEntriesPerPage == 0L) {
+			// This is the special case where the largest fingerprint
+			// happened is going to end up on the last entry of the previous
+			// page. Thus, we won't need the last extra index cell.
+			indexLen--;
 		}
-		private long log2phy(long bucketAddress, long inBucketAddress) {
-			return OffHeapDiskFPSet.this.log2phy(bucketAddress, inBucketAddress);
+		return indexLen;
+	}
+
+	protected void writeIndex(long[] index, final RandomAccessFile raf, long length) throws IOException {
+		for (int i = 0; i < index.length; i++) {
+			long pos = Math.min(((long) i) * NumEntriesPerPage, length);
+			raf.seek(pos * LongSize);
+			final long value = raf.readLong();
+			index[i] = value;
 		}
 	}
 	
-	public interface CollisionBucket {
-		void clear();
+	/**
+	 * A non-thread safe Iterator whose next method returns the next largest
+	 * element.
+	 */
+	public static class Iterator {
 
-		void prepareForFlush();
+		private enum WRAP {
+			ALLOWED, FORBIDDEN;
+		}
 
-		void remove(long first);
+		private final long elements;
+		private final LongArray array;
+		private final Indexer indexer;
+		private final WRAP canWrap;
 
-		long first();
+		private long pos = 0;
+		private long elementsRead = 0L;
 		
-		long last();
-
-		boolean isEmpty();
-
-		/**
-		 * @param fp
-	     * @return {@code true} if this set did not already contain the specified
-	     *         fingerprint
-		 */
-		boolean add(long fp);
-
-		boolean contains(long fp);
+		public Iterator(final LongArray array, final long elements, final Indexer indexer) {
+			this(array, elements, 0L, indexer, WRAP.ALLOWED);
+		}
 
-		long size();
-	}
-	
-	public class TreeSetCollisionBucket implements CollisionBucket {
-		private final TreeSet<Long> set;
+		public Iterator(final LongArray array, final long elements, final long start, final Indexer indexer) {
+			this(array, elements, start, indexer, WRAP.FORBIDDEN);
+		}
 
-		public TreeSetCollisionBucket(int initialCapacity) {
-			this.set = new TreeSet<Long>();
+		public Iterator(final LongArray array, final long elements, final long start, final Indexer indexer,
+				final WRAP canWrap) {
+			this.array = array;
+			this.elements = elements;
+			this.indexer = indexer;
+			this.pos = start;
+			this.canWrap = canWrap;
 		}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#clear()
-		 */
-		public void clear() {
-			set.clear();
+		public long getPos() {
+			return pos;
 		}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#prepareForFlush()
+		/**
+		 * Returns the next element in the iteration that is not EMPTY nor
+		 * marked evicted.
+		 *
+		 * @return the next element in the iteration that is not EMPTY nor
+		 *         marked evicted.
+		 * @exception NoSuchElementException
+		 *                iteration has no more elements.
 		 */
-		public void prepareForFlush() {
-			// no-op
+		public long next() {
+			return next0(false, Long.MAX_VALUE);
 		}
-
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#remove(long)
+		
+		long next(long maxPos) {
+			if (pos >= maxPos) {
+				return EMPTY;
+			}
+			return next0(false, maxPos);
+		}
+		
+		/**
+		 * Returns the next element in the iteration that is not EMPTY nor
+		 * marked evicted and marks it to be evicted.
+		 * <p>
+		 * THIS IS NOT SIDEEFFECT FREE. AFTERWARDS, THE ELEMENT WILL BE MARKED
+		 * EVICTED.
+		 *
+		 * @return the next element in the iteration that is not EMPTY nor
+		 *         marked evicted.
+		 * @exception NoSuchElementException
+		 *                iteration has no more elements.
 		 */
-		public void remove(long first) {
-			set.remove(first);
+		public long markNext() {
+			return next0(true, Long.MAX_VALUE);
 		}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#first()
-		 */
-		public long first() {
-			return set.first();
+		private long next0(final boolean mark, final long maxPos) {
+			long elem;
+			do {
+				final long position = pos % array.size();
+				elem = array.get(position);
+				if (elem == EMPTY) {
+					pos = pos + 1L;
+					continue;
+				}
+				if (elem < EMPTY) {
+					pos = pos + 1L;
+					continue;
+				}
+				final long baseIdx = indexer.getIdx(elem);
+				if (baseIdx > pos) {
+					// This branch should only be active for thread with id 0.
+					assert canWrap == WRAP.ALLOWED;
+					pos = pos + 1L;
+					continue;
+				}
+				pos = pos + 1L;
+				// mark elem in array as being evicted.
+				if (mark) {array.set(position, elem | MARK_FLUSHED);}
+				elementsRead = elementsRead + 1L;
+				return elem;
+			} while (hasNext() && pos < maxPos);
+			if (pos >= maxPos) {
+				return EMPTY;
+			}
+			throw new NoSuchElementException();
 		}
 		
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#last()
-		 */
-		public long last() {
-			return set.last();
+	    /**
+	     * Returns <tt>true</tt> if the iteration has more elements. (In other
+	     * words, returns <tt>true</tt> if <tt>next</tt> would return an element
+	     * rather than throwing an exception.)
+	     *
+	     * @return <tt>true</tt> if the iterator has more elements.
+	     */
+		public boolean hasNext() {
+			return elementsRead < elements;
 		}
+	}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#isEmpty()
-		 */
-		public boolean isEmpty() {
-			return set.isEmpty();
+	public static class OffHeapRuntimeException extends RuntimeException {
+
+		public OffHeapRuntimeException(Exception ie) {
+			super(ie);
 		}
+	}
+	
+	// ***************** assertion helpers *******************//
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#add(long)
-		 * 
-		 * If this set already contains the element, the call leaves the set
-		 * unchanged and returns false.
-		 */
-		public boolean add(long fp) {
-			return set.add(fp);
+	private static boolean checkInput(final LongArray array, final Indexer indexer, final int reprobe) {
+		for (long pos = 0; pos <= array.size() + reprobe - 1L; pos++) {
+			long tmp = array.get(pos % array.size());
+			if (tmp == EMPTY) {
+				continue;
+			}
+			if (tmp < EMPTY) {
+				// Convert evicted fps back.
+				tmp = tmp & FLUSHED_MASK;
+			}
+			final long idx = indexer.getIdx(tmp);
+			// Accept wrapped elements.
+			if (pos < reprobe && idx > (array.size() - 1L - pos - reprobe)) {
+				continue;
+			}
+			// Accept non-wrapped elements when pos > array.size
+			if (pos > array.size() - 1L && idx + reprobe < pos) {
+				continue;
+			}
+			// Accept any other valid elements where pos is within the range
+			// given by [idx,idx + reprobe].
+			if (idx <= pos && pos <= idx + reprobe) {
+				continue;
+			}
+			System.err.println(String.format("%s with idx %s at pos %s (reprobe: %s).", tmp, idx, pos, reprobe));
+			return false;
 		}
+		return true;
+	}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#contains(long)
-		 */
-		public boolean contains(long fp) {
-			return set.contains(fp);
+	/**
+	 * @return -1L iff array is sorted, index/position of the element that violates otherwise in the range [start, end].
+	 */
+	private static long checkSorted(final LongArray array, final Indexer indexer, int reprobe, long start, long end) {
+		if (reprobe >= array.size()) {
+			// When the array is smaller than the number of positions/slots, the
+			// loop below will run past its bounds. If this happens, it compares
+			// elements the element at the start with the element at the end.
+			reprobe = (int) (array.size() - 1);
+		}
+		long e = 0L;
+		for (long pos = start; pos <= end; pos++) {
+			final long tmp = array.get(pos % array.size());
+			if (tmp <= EMPTY) {
+				continue;
+			}
+			final long idx = indexer.getIdx(tmp);
+			if (idx > pos) {
+				continue;
+			}
+			if (idx + reprobe < pos) {
+				continue;
+			}
+			if (e == 0L) {
+				// Initialize e with the first element that is not <=EMPTY
+				// or has wrapped.
+				e = tmp;
+				continue;
+			}
+			if (e >= tmp) {
+				System.err.println(String.format("%s >= %s at pos %s.", e, tmp, pos));
+				return pos;
+			}
+			e = tmp;
 		}
+		return -1L;
+	}
 
-		/* (non-Javadoc)
-		 * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#size()
-		 */
-		public long size() {
-			return set.size();
+	/**
+	 * @return -1L iff array is sorted, index/position of the element that violates otherwise.
+	 */
+	private static long checkSorted(final LongArray array, final Indexer indexer, final int reprobe) {
+		return checkSorted(array, indexer, reprobe, 0, array.size() - 1L + reprobe);
+	}
+
+	private static boolean checkRAFs(final BufferedRandomAccessFile[] rafs) throws IOException {
+		for (int i = 0; i < rafs.length - 1; i++) {
+			final long end = rafs[i].getFilePointer();
+			final long start = rafs[i + 1].getMark();
+			if (end != start) {
+				return false;
+			}
 		}
+		return true;
 	}
 	
-	public class PrettyPrinter {
-		/**
-		 * Print the current in-memory hash table to System.out with increments
-		 */
-		public void printDistribution(final int increments) {
-			final int mask = increments - 1;
-			int cnt = 0;
-			int min = Integer.MAX_VALUE;
-			int max = 0;
-			for (long i = maxTblCnt - 1; i >= 0; i--) {
-				if ((i & mask) == 0) {
-					if (cnt > max) {
-						max = cnt;
-					}
-					if (cnt < min) {
-						min = cnt;
-					}
-					System.out.println(i + " " + cnt);
-					cnt = 0;
-				}
-				if (u.getAddress(log2phy(i)) > 0L) {
-					cnt++;
-				}
+	private static boolean checkTable(LongArray array) {
+		for (long i = 0L; i < array.size(); i++) {
+			long elem = array.get(i);
+			if (elem > EMPTY) {
+				System.err.println(String.format("%s elem at pos %s.", elem, i));
+				return false;
 			}
-			System.out.println("max: " + max + " min: " + min + " avg:" + (tblLoad / tblCnt.doubleValue()));
 		}
-		
-		public void printBuckets() {
-			printBuckets(0, maxTblCnt);
+		return true;
+	}
+
+	private static boolean checkIndex(final long[] idx) {
+		for (int i = 1; i < idx.length; i++) {
+			if (idx[i - 1] >= idx[i]) {
+				return false;
+			}
 		}
-		
-		/**
-		 * @param from inclusive lower bound
-		 * @param to exclusive upper bound
-		 */
-		public void printBuckets(int from, long to) {
-			for (long i = from; i < maxTblCnt && i < to; i++) {
-				if (i % bucketCapacity == 0) {
-					System.out.println("Bucket idx: " + i);
-				}
-				System.out.println(u.getAddress(log2phy(i)));
+	    return true;
+    } 
+	
+	private static boolean checkIndex(final long[] idx, final RandomAccessFile raf, final long length) throws IOException {
+		for (long i = 0L; i < idx.length; i++) {
+			final long pos = Math.min(i * NumEntriesPerPage, length);
+			raf.seek(pos * LongSize);
+			final long value = raf.readLong();
+			final long index = idx[(int) i];
+			if (value != index) {
+				return false;
 			}
 		}
+		return true;
 	}
 }
diff --git a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSetHelper.java b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSetHelper.java
deleted file mode 100644
index 9a71228dac904480f07b887080d785082d1ec8e1..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSetHelper.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
-package tlc2.tool.fp;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-
-import tlc2.output.EC;
-import util.Assert;
-
-import sun.misc.Unsafe;
-
-@SuppressWarnings("restriction")
-public final class OffHeapDiskFPSetHelper {
-
-	private OffHeapDiskFPSetHelper() {
-		// no instantiation!
-	}
-
-	/**
-	 * @return An Unsafe object or a {@link RuntimeException} wrapping any {@link Exception}. 
-	 */
-	public static sun.misc.Unsafe getUnsafe() {
-		// More Details can be found at: http://www.mydailyjava.blogspot.no/2013/12/sunmiscunsafe.html
-		try {
-			// Use reflection API to unhide Unsafe
-			final Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
-			f.setAccessible(true);
-			return (sun.misc.Unsafe) f.get(null);
-		} catch (Exception e) {
-			throw new RuntimeException(
-					"Trying to use Sun VM specific sun.misc.Unsafe implementation but no Sun based VM detected.",
-					e);
-		}
-	}
-	
-	/**
-	 * Initializes the memory by overriding each byte with zero starting at
-	 * <code>baseAddress</code> and ending when <code>fingerprintCount</code>
-	 * bytes have been written.
-	 * <p>
-	 * To speed up the initialization, <code>numThreads</code> allows to set a
-	 * thread count with which zeroing is then done in parallel.
-	 * 
-	 * @param u
-	 * @param baseAddress Base address of the (previously) allocated memory 
-	 * @param numThreads Number of threads used to zero memory
-	 * @param fingerprintCount Number of fingerprint for which the memory should be initialized
-	 * @throws IOException
-	 */
-	public static void zeroMemory(final Unsafe u, final long baseAddress, int numThreads, final long fingerprintCount)
-			throws IOException {
-		
-		final int addressSize = u.addressSize();
-		final long segmentSize = fingerprintCount / numThreads;
-
-		final ExecutorService es = Executors.newFixedThreadPool(numThreads);
-		try {
-			final Collection<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>(numThreads);
-			for (int i = 0; i < numThreads; i++) {
-				final int offset = i;
-				tasks.add(new Callable<Boolean>() {
-
-					public Boolean call() throws Exception {
-						// Null memory (done in parallel on segments).
-						// This is essential as allocateMemory returns
-						// uninitialized memory and
-						// memInsert/memLockup utilize 0L as a mark for an
-						// unused fingerprint slot.
-						// Otherwise memory garbage wouldn't be distinguishable
-						// from a true fp.
-						final long lowerBound = segmentSize * offset;
-						final long upperBound = (1 + offset) * segmentSize;
-						for (long i = lowerBound; i < upperBound; i++) {
-							final long address = baseAddress + (i * addressSize);
-							u.putAddress(address, 0L);
-						}
-						return true;
-					}
-				});
-			}
-			final List<Future<Boolean>> invokeAll = es.invokeAll(tasks);
-			Assert.check(!invokeAll.isEmpty(), EC.GENERAL);
-		} catch (InterruptedException e) {
-			// not expected to happen
-			e.printStackTrace();
-		} finally {
-			es.shutdown();
-		}
-	}
-}
diff --git a/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla b/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla
new file mode 100644
index 0000000000000000000000000000000000000000..53eafd5599f16d9f73070695b96a256869a38a82
--- /dev/null
+++ b/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla
@@ -0,0 +1,225 @@
+------------------------------- MODULE Merge -------------------------------
+EXTENDS Integers, TLC, Sequences, FiniteSets
+
+(**********************************)
+(* The larger of the two elements *)
+(**********************************)
+Max(a,b) == IF a > b THEN a ELSE b
+
+(***************************************************************************)
+(* The image of the function F.                                            *)
+(***************************************************************************)
+Image(F) == { F[x] : x \in DOMAIN F }
+
+(***************************************************************************)
+(* Convertes the given Sequence seq into a Set of all the                  *) 
+(* Sequence's elements. In other words, the image of the function          *)
+(* that seq is.                                                            *)
+(***************************************************************************)
+SeqToSet(seq) == {seq[i] : i \in 1..Len(seq)} 
+
+(***************************************************************************)
+(* Returns a Set of all possible permutations with distinct elemenents     *)
+(* created out of the elements of Set set. All elements of set occur in    *)
+(* in the sequence.                                                        *)
+(***************************************************************************)
+IsOrdered(seq) == IF seq = <<>> \/ Len(seq) = 1 THEN TRUE ELSE
+                     \A i \in 1..(Len(seq) - 1): seq[i] < seq[i+1]
+OrderedSequences(set) == UNION {{perm \in [1..Cardinality(set) -> set]: 
+                                   IsOrdered(perm)}}
+
+(***
+--fair algorithm Merge {
+
+  variables history = {},
+            a = 0,
+            aLength = 0,
+            A = <<>>,
+            b = 0,
+            bLength = 0,
+            B = <<>>,
+            O = <<>>; 
+            
+    define {
+       Inv == /\ Image(O) \subseteq history
+              /\ IsOrdered(O)
+    }
+    
+    macro Consume(var, seq) {
+         var := Head(seq);
+         seq := Tail(seq);
+    }
+  {
+
+  (* Initialize a and b with all possible combination of sorted sequences. *)
+  init:           
+    with (r \in SUBSET Nat \ {{}}) {
+       with (s \in ((SUBSET (Nat \ r)) \ {{}})) {
+           with (t \in OrderedSequences(s) \cup {<<>>}) {
+               B := t;
+               bLength := Len(B);
+               with (u \in OrderedSequences(r) \cup {<<>>}) {
+                    A := u;
+                    aLength := Len(A);
+               }
+           };
+       };  
+    };
+  
+  (* Keep history. *)
+  hstry:
+     history := SeqToSet(A) \cup SeqToSet(B);
+     if (Cardinality(history) = 0) {
+        goto "Done";
+     };
+       
+  (* Merge sorted A and B to obtain sorted O. *)
+  mrg1: 
+     if (aLength > 0) {
+         Consume(a, A);
+     };
+     if (bLength > 0) {
+         Consume(b, B);
+     };
+     
+  mrg2:
+        if (aLength > 0 /\ (a < b \/ bLength = 0)) {
+           O := Append(O, a);
+           aLength := aLength - 1;
+           if (aLength > 0) {
+             Consume(a, A);
+           };
+        };
+  mrg2b:     
+        if (bLength > 0 /\ (b < a \/ aLength = 0)) {
+           O := Append(O, b);
+           bLength := bLength - 1;
+           if (bLength > 0) {
+             Consume(b, B);
+           };
+        };
+   mrg3:     
+        if (bLength > 0 \/ aLength > 0) {
+           goto mrg2;
+        };
+   mrg4:
+       assert bLength = 0 /\ aLength = 0;
+       assert Len(O) = Cardinality(history);
+       assert Image(O) = history;
+   }
+}
+
+***     this ends the comment containg the pluscal code      **********)
+\* BEGIN TRANSLATION
+VARIABLES history, a, aLength, A, b, bLength, B, O, pc
+
+(* define statement *)
+Inv == /\ Image(O) \subseteq history
+       /\ IsOrdered(O)
+
+
+vars == << history, a, aLength, A, b, bLength, B, O, pc >>
+
+Init == (* Global variables *)
+        /\ history = {}
+        /\ a = 0
+        /\ aLength = 0
+        /\ A = <<>>
+        /\ b = 0
+        /\ bLength = 0
+        /\ B = <<>>
+        /\ O = <<>>
+        /\ pc = "init"
+
+init == /\ pc = "init"
+        /\ \E r \in SUBSET Nat \ {{}}:
+             \E s \in ((SUBSET (Nat \ r)) \ {{}}):
+               \E t \in OrderedSequences(s) \cup {<<>>}:
+                 /\ B' = t
+                 /\ bLength' = Len(B')
+                 /\ \E u \in OrderedSequences(r) \cup {<<>>}:
+                      /\ A' = u
+                      /\ aLength' = Len(A')
+        /\ pc' = "hstry"
+        /\ UNCHANGED << history, a, b, O >>
+
+hstry == /\ pc = "hstry"
+         /\ history' = (SeqToSet(A) \cup SeqToSet(B))
+         /\ IF Cardinality(history') = 0
+               THEN /\ pc' = "Done"
+               ELSE /\ pc' = "mrg1"
+         /\ UNCHANGED << a, aLength, A, b, bLength, B, O >>
+
+mrg1 == /\ pc = "mrg1"
+        /\ IF aLength > 0
+              THEN /\ a' = Head(A)
+                   /\ A' = Tail(A)
+              ELSE /\ TRUE
+                   /\ UNCHANGED << a, A >>
+        /\ IF bLength > 0
+              THEN /\ b' = Head(B)
+                   /\ B' = Tail(B)
+              ELSE /\ TRUE
+                   /\ UNCHANGED << b, B >>
+        /\ pc' = "mrg2"
+        /\ UNCHANGED << history, aLength, bLength, O >>
+
+mrg2 == /\ pc = "mrg2"
+        /\ IF aLength > 0 /\ (a < b \/ bLength = 0)
+              THEN /\ O' = Append(O, a)
+                   /\ aLength' = aLength - 1
+                   /\ IF aLength' > 0
+                         THEN /\ a' = Head(A)
+                              /\ A' = Tail(A)
+                         ELSE /\ TRUE
+                              /\ UNCHANGED << a, A >>
+              ELSE /\ TRUE
+                   /\ UNCHANGED << a, aLength, A, O >>
+        /\ pc' = "mrg2b"
+        /\ UNCHANGED << history, b, bLength, B >>
+
+mrg2b == /\ pc = "mrg2b"
+         /\ IF bLength > 0 /\ (b < a \/ aLength = 0)
+               THEN /\ O' = Append(O, b)
+                    /\ bLength' = bLength - 1
+                    /\ IF bLength' > 0
+                          THEN /\ b' = Head(B)
+                               /\ B' = Tail(B)
+                          ELSE /\ TRUE
+                               /\ UNCHANGED << b, B >>
+               ELSE /\ TRUE
+                    /\ UNCHANGED << b, bLength, B, O >>
+         /\ pc' = "mrg3"
+         /\ UNCHANGED << history, a, aLength, A >>
+
+mrg3 == /\ pc = "mrg3"
+        /\ IF bLength > 0 \/ aLength > 0
+              THEN /\ pc' = "mrg2"
+              ELSE /\ pc' = "mrg4"
+        /\ UNCHANGED << history, a, aLength, A, b, bLength, B, O >>
+
+mrg4 == /\ pc = "mrg4"
+        /\ Assert(bLength = 0 /\ aLength = 0, 
+                  "Failure of assertion at line 106, column 8.")
+        /\ Assert(Len(O) = Cardinality(history), 
+                  "Failure of assertion at line 107, column 8.")
+        /\ Assert(Image(O) = history, 
+                  "Failure of assertion at line 108, column 8.")
+        /\ pc' = "Done"
+        /\ UNCHANGED << history, a, aLength, A, b, bLength, B, O >>
+
+Next == init \/ hstry \/ mrg1 \/ mrg2 \/ mrg2b \/ mrg3 \/ mrg4
+           \/ (* Disjunct to prevent deadlock on termination *)
+              (pc = "Done" /\ UNCHANGED vars)
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION
+
+=============================================================================
+\* Modification History
+\* Last modified Tue Jun 06 20:39:04 CEST 2017 by markus
+\* Created Mon Jun 05 23:14:05 CEST 2017 by markus
diff --git a/tlatools/src/tlc2/tool/fp/OpenAddressing.tla b/tlatools/src/tlc2/tool/fp/OpenAddressing.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9505db8a30cc7e216caa18fc2e17e737534e4f14
--- /dev/null
+++ b/tlatools/src/tlc2/tool/fp/OpenAddressing.tla
@@ -0,0 +1,866 @@
+\begin{ppcal}
+-------------------------- MODULE OpenAddressing --------------------------
+EXTENDS Sequences, FiniteSets, Integers, TLC
+
+(***************************************************************************)
+(* K: The overall number of fingerprints that fit into the table.          *)
+(* fps: The set of fingerprints to be inserted into the hash table.        *)
+(* empty: An empty (model) value. Used to mark an unoccupied table element.*)
+(* Writer: The set of processes/threads which insert fingerprints. Reader: *)
+(* The set of processes which check fingerprints with contains. L: The     *)
+(* probing limit.                                                          *)
+(***************************************************************************)
+CONSTANT K, fps, empty, Writer, Reader, L
+
+(***************************************************************************)
+(* K is a positive natural.  emtpy is disjunct to all elements in fps.     *)
+(* fingerprints are natural numbers and can be well-ordered.               *)
+(***************************************************************************)
+ASSUME /\ K \in (Nat \ {0})
+       /\ \A fp \in fps: fp \in (Nat \ {0})
+       /\ empty \notin fps
+       /\ (2*L) <= K 
+
+----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* The element of position Len(seq) of a sequence seq.                     *)
+(***************************************************************************)
+last(seq) == seq[Len(seq)]                     
+
+(***************************************************************************)
+(* The largest element in the sequence, assuming sequence to be sorted in  *) 
+(* ascending order.                                                        *)
+(***************************************************************************)
+largestElem(sortedSeq) == IF sortedSeq = <<>> THEN 0 ELSE last(sortedSeq)
+ 
+(***************************************************************************)
+(* All elements of seq1 smaller than elem and the largest element in seq2. *)
+(***************************************************************************)
+subSetSmaller(seq1, seq2, elem) == SelectSeq(seq1, LAMBDA p:
+                                     p < elem /\ p > largestElem(seq2))               
+
+(***************************************************************************)
+(* All elements of seq1 larger than the largest element in seq2.           *)
+(***************************************************************************)
+subSetLarger(seq1, seq2) == IF seq2 = <<>> 
+                            THEN seq1 
+                            ELSE SelectSeq(seq1, LAMBDA p:
+                                                    p > largestElem(seq2))               
+
+(***************************************************************************)
+(* TRUE iff the sequence seq contains the element elem.                    *)
+(***************************************************************************)
+containsElem(seq, elem) == \E i \in 1..Len(seq): seq[i] = elem
+                    
+(***************************************************************************)
+(* The minimum and maximum element in set s.                               *)
+(***************************************************************************)
+min(S) == CHOOSE s \in S: \A a \in S: s <= a 
+max(S) == CHOOSE s \in S: \A a \in S: s >= a 
+                     
+(***************************************************************************)
+(* The smaller of the two values.                                          *)
+(***************************************************************************)
+minimum(a, b) == IF a < b THEN a ELSE b
+                     
+(***************************************************************************)
+(* The given index i modulo the sequences' length.                         *)
+(***************************************************************************)
+mod(i,len) == IF i % len = 0 THEN len ELSE (i % len)
+ 
+(***************************************************************************)
+(* Logical bit-shifting to the right (shifts in zeros from the left/MSB).  *)
+(* TLC's standard division does not round towards zero, thus this is       *)
+(* specified recursively, manually taking care of rounding.                *)
+(***************************************************************************)
+RECURSIVE shiftR(_,_)
+shiftR(n,pos) == IF pos = 0 THEN n
+                 ELSE LET odd(z) == z % 2 = 1
+                          m == IF odd(n) THEN (n-1) \div 2 ELSE n \div 2
+                      IN shiftR(m, pos - 1)
+
+(***************************************************************************)
+(* Bitshifting (faster for any real implementation).                       *)
+(***************************************************************************)
+bitshift(fp, p) == LET k == CHOOSE k \in 1..K: 2^k = K
+                   IN mod(shiftR(fp, k - 1) + 1 + p, K)
+
+(***************************************************************************)
+(* Re-scale.                                                               *)
+(***************************************************************************) 
+rescale(k,maxF,minF,fp,p) == LET f == (k - 1) \div (maxF - minF)
+                             IN mod((f * (fp - minF + 1)) + p, k)
+
+(***************************************************************************)
+(* Calculates an fp's index where fp \in fps. p is an alternative address, *)
+(* such that: p \in 0..P. Uses bitshifting iff K is power of two.          *)
+(***************************************************************************)
+idx(fp, p) == rescale(K, max(fps), min(fps), fp, p)
+
+(***************************************************************************)
+(* TRUE iff the fingerprint at table position index is equal to fp or its  *)
+(* corresponding negative fp value (marked as to be copied to secondary).  *)
+(***************************************************************************)
+isMatch(fp, index, table) == \/ table[index] = fp
+                             \/ table[index] = (-1*fp)
+
+(***************************************************************************)
+(* TRUE iff the table at position index is empty.                          *)
+(***************************************************************************)
+isEmpty(index, table) == table[index] = empty
+
+(***************************************************************************)
+(* TRUE iff the table at position index is marked evicted.                 *)
+(***************************************************************************)
+isMarked(index, table) == table[index] < 0
+
+(***************************************************************************)
+(* t is sorted iff its empty-filtered sub-sequence is sorted. An empty     *)
+(* sequence is defined to be sorted.                                       *)
+(***************************************************************************)
+isSorted(seq) == LET sub == SelectSeq(seq, LAMBDA e: e # empty)
+                 IN IF sub = <<>> THEN TRUE
+                    ELSE \A i \in 1..(Len(sub) - 1):
+                         sub[mod(i, Len(sub))] < sub[mod(i+1, Len(sub))]
+
+(***************************************************************************)
+(* TRUE iff fingerprint fp is either found within table according to       *)
+(* isMatch or in seq.                                                      *)
+(***************************************************************************)
+contains(f,t,seq,Q) == \/ \E i \in 0..Q: isMatch(f,idx(f,i),t)
+                       \/ \E i \in 1..Len(seq): seq[i] = f 
+                       
+----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* A fp wrapped around if its alternate indices are beyond K and its       *)
+(* actual index is lower than its primary idx. Another mental picture for  *)
+(* table is a circular list and wrapped means that a fingerprint crossed   *)
+(* the logically first position of table.                                  *) 
+(***************************************************************************)
+wrapped(fp, pos) == idx(fp, 0) > mod(pos, K) 
+
+(***************************************************************************)
+(* TRUE iff the given two fingerprints have to be swapped to satisfy the   *)
+(* expected order.                                                         *)
+(***************************************************************************)
+compare(fp1,i1,fp2,i2) == IF fp1 # empty /\ fp2 # empty
+                          THEN IF wrapped(fp1, i1) = wrapped(fp2, i2) /\ i1 > i2
+                               THEN IF fp1 < fp2 THEN -1 ELSE 1
+                               ELSE IF ~(wrapped(fp1, i1) <=> wrapped(fp2, i2))
+                                    THEN IF i1 < i2 /\ fp1 < fp2
+                                         THEN -1
+                                         ELSE IF i1 > i2 /\ fp1 > fp2
+                                              THEN -1
+                                              ELSE 0
+                                    ELSE 0
+                          ELSE 0            
+                             
+----------------------------------------------------------------------------
+
+(***       this is a comment containing the PlusCal code *
+--fair algorithm OpenAddressing 
+(***************************************************************************)
+(* table: The actual hash table specified as a TLA+ sequence. history: An  *)
+(* auxiliary (history) variable unrelated to the actual hash table         *)
+(* specification. It just records the inserted fingerprints to be verified *)
+(* by Inv. An implementation won't need history. secondary: The secondary  *)
+(* storage where fingerprints are eventually evicted to. outer/inner:      *)
+(* Index variables local to the sort action. P: The number of times an     *) 
+(* alterante index is to be tried.                                         *)          
+(***************************************************************************)
+{ variable table = [i \in 1..K |-> empty], 
+           secondary = <<>>,
+           newsecondary = <<>>,
+           P = 0,         \* LongAccumulator in Java
+           evict = FALSE, \* AtomicBoolean in Java
+           waitCnt = 0,   \* CyclicBarrier in Java
+           history = {};
+    
+   (************************************************************************)
+   (* Compare-and-swap is linearizable (appears atomic from the POV of     *)
+   (* observers such as other threads) and disjoint-access-parallel (two   *)
+   (* CAS operations on disjoint memory locations do not synchronize).     *)
+   (*                                                                      *)
+   (* Variants of CAS (only basic CAS supported by Java. No HW support     *)
+   (* for DCAS, CASN):                                                     *)       
+   (* CAS: Basic compare and swap of a 64bit memory location.              *)
+   (* DWCAS: CAS of two adjacent/contiguous 64bit memory locations.        *)
+   (*        (CMPXCHG16) (to swap to adjacent positions in e.g table)      *)
+   (* DCAS: CAS of two arbitrary 64bit memory locations (swap of arbitrary *)
+   (*       locations).                                                    *)
+   (* CASN: CAS N arbitrary 64bit memory locations.                        *)
+   (*                                                                      *)
+   (* http://liblfds.org/mediawiki/index.php?title=Article:CAS_and_LL/     *)
+   (* SC_Implementation_Details_by_Processor_family                        *)       
+   (************************************************************************)
+
+  (* Atomically compare and swap. *)      
+  macro CAS(result, pos, expected, new) {
+     if (table[pos] = expected) {
+         table[pos] := new;
+         result := TRUE
+     } else { 
+         result := FALSE
+     }
+  }
+
+  procedure Evict()
+     variables ei = 1, ej = 1, lo = 0; {
+                (* Insertion sort. *)
+     strIns:    while (ei <= K+P) {
+                  lo := table[mod(ei + 1, K)];
+     nestedIns:   while (compare(lo, mod(ei + 1, K), 
+                            table[mod(ej, K)], mod(ej, K)) <= -1) {
+                    table[mod(ej + 1, K)] := table[mod(ej, K)];
+                    if (ej = 0) {
+                       ej := ej - 1;
+                       goto set;
+                    } else {
+                       ej := ej - 1;
+                    };
+                  };
+     set:         table[mod(ej + 1, K)] := lo;
+                  ej := ei + 1;
+                  ei := ei + 1;
+                };
+                ei := 1;
+                
+                (* Write to external storage. *)
+     flush:     while (ei <= K+P) {
+                     lo := table[mod(ei, K)];
+                     if (lo # empty /\
+                         lo > largestElem(newsecondary) /\
+                         ((ei <= K /\ ~wrapped(lo,ei)) \/ 
+                          (ei > K /\ wrapped(lo,ei)))) {
+                        (* Copy all smaller fps than lo from   *)
+                        (* secondary to newsecondary.          *)
+                        newsecondary := Append(newsecondary \o 
+                               subSetSmaller(secondary, newsecondary, lo), lo);
+                        (* Mark table[mod(cpy,table)] as being *)
+                        (* written to secondary.               *)
+                        table[mod(ei, K)] := lo * (-1);
+                     };
+                     ei := ei + 1;
+                };
+                (* Append remainder of secondary to newsecondary and *) 
+                (* assign newsecondary to secondary. *)
+                secondary := newsecondary \o 
+                             subSetLarger(secondary, newsecondary);
+                newsecondary := <<>>;
+                (* Setting P to 0 means a larger portion of cntns queries   *)
+                (* go to secondary. Leaving P unchanged results in a longer *) 
+                (* lookup chain. However neither violates any invariants.   *)
+                (* An implementation is likely to set P to the              *)
+                (* mean/median/some other fancy statistics or treats P as   *)
+                (* as an optimization to speedup insertions prior to the    *)
+                (* first eviction.                                          *)
+                with (p \in 0..P) {
+                   P := p;
+                };
+     rtrn:      return;
+  }
+  
+  process (q \in Reader) 
+    variables rfp = 0, rindex = 0, checked = {}; {
+     rwait:   await history # {};
+     rpick:   while (TRUE) {
+                  if (checked = history /\ history = fps) {
+                     goto Done;
+                  } else {
+                     with (f \in history) { 
+                        rfp := f;
+                        checked := checked \cup {f};
+                     };
+                  };
+                   
+     rcntns:      rindex := 0;
+                  if (evict) {
+                     waitCnt := waitCnt + 1;
+     rwaitEv:        await evict = FALSE;
+     rendWEv:        waitCnt := waitCnt - 1;
+                     goto rcntns
+                  };
+                 
+     ronPrm:      while (rindex <= P) {
+                     if (isMatch(rfp, idx(rfp, rindex), table)) {
+                        goto rpick
+                     } else {
+                        if (isEmpty(idx(rfp, rindex), table) 
+                         \/ isMarked(idx(rfp, rindex), table)) {
+                           goto ronSnc;
+                        } else {
+                          rindex := rindex + 1
+                        }
+                     }
+                  };
+     ronSnc:      if (containsElem(secondary,rfp)) {
+                      goto rpick
+                  } else {
+                      (* Since we picked a fp from history, it always *) 
+                      (* either has to be in table or secondary.      *)
+                      assert(FALSE);
+                  };
+             }
+  }
+       
+  (* A weak fair process. *)        
+  fair process (p \in Writer)
+    variables fp = 0, index = 0, result = FALSE, expected = -1; {
+    pick:  while (TRUE) {
+              (* No deadlock once all fingerprints have been inserted. *)
+              if ((fps \ history) = {}) {
+                goto Done;
+              } else {
+                (* Select some fp to be inserted *)
+                with (f \in (fps \ history)) { fp := f; };
+              };
+
+     put:     index := 0;
+              result := FALSE;
+              (* Set expected to infinity. expected is reused when  *)
+              (* the algorithm runs a primary lookup and finds a    *)
+              (* position which is either EMPTY or isMarked(...).   *)
+              (* expected stores the (open) position for later use  *)
+              (* where the fp is inserted. Maximally, a position    *)
+              (* can be K + L, thus expected is set to K + L + 1;   *)
+              (* as an approximation of infinity.                   *)
+              expected := K + L + 1;
+              (* Wait for eviction thread to do its work. *)
+              if (evict) {
+                 waitCnt := waitCnt + 1;
+     waitEv:     await evict = FALSE;
+     endWEv:     waitCnt := waitCnt - 1;
+                 goto put
+              };
+              
+              (* Check secondary unless empty. First though, we do   *)
+              (* a primary lookup in case the fp in question has not *)
+              (* been evicted to secondary yet.                      *)
+     chkSnc:  if (secondary # <<>>) {
+                 (* Primary lookup. *)
+     cntns:       while (index < P) {
+                    if (isMatch(fp, idx(fp, index), table)) {
+                       goto pick
+                    } else {
+                        if (isEmpty(idx(fp, index), table)) {
+                            (* Found an EMPTY position which proves *) 
+                            (* that fp cannot be found at higher    *)
+                            (* positions. Thus, no need to continue.*)
+                            expected := minimum(expected, index);
+                            goto onSnc;
+                        } else {
+                            if (isMarked(idx(fp, index), table)) {
+                                (* None of the lower positions has    *)
+                                (* fp, thus keep the lowest position  *)
+                                (* for the second loop as the start   *)
+                                (* index. No point in checking known  *)
+                                (* lower positions in the loop again. *)                                
+                                expected := minimum(expected, index);
+                                index := index + 1;
+                            } else {
+                                index := index + 1
+                            }
+                        }
+                    }
+                  };
+              
+              
+                  (* Secondary lookup. *)
+     onSnc:       if (containsElem(secondary,fp)) {
+                     goto pick
+                  } else {
+                     (* Have next loop start at expected determined *)
+                     (* by previous loop.                           *)
+                     index := expected;
+                     (* Re-init expected to be used for its alternate purpose. *)
+                     expected := -1;
+                  };
+              };
+              
+              (* Put inserts the given fp into the hash table by sequentially *)
+              (* trying the primary to the P's alternate position.            *)
+     insrt:   while (index < L) {
+                 expected := table[idx(fp,index)];
+                 if (expected = empty \/ 
+                     (expected < 0 /\ expected # (-1) * fp))  {
+     incP:           if (index > P) {P := index};
+     cas:            CAS(result, idx(fp,index), expected, fp);
+                     if (result) {
+                        history := history \cup {fp};
+                        goto pick
+                     } else {
+                        (* Has been occupied in the meantime, *)
+                        (* try to find another position.      *)
+                        goto insrt
+                      }
+                 };
+                
+                 (* Has fp been inserted by another process? Check isMatch *)
+                 (* AFTER empty and on-secondary because of two reasons:   *)
+                 (* a) Thread A finds table[pos] to be empty but fails     *)
+                 (*    to CAS fpX. Thread B concurrently also finds        *)
+                 (*    table[pos] to be empty and succeeds to CAS fpX.     *)
+                 (* b) Iff table[pos] is empty or -1, higher positions     *)
+                 (*    cannot be a match.                                  *)
+     isMth:      if (isMatch(fp,idx(fp,index),table)) {
+                    goto pick
+                 } else {
+                    index := index + 1;
+                 };
+              }; \* end of while/insrt
+
+
+              (* We failed to insert fp into a full table, thus try *)
+              (* to become the thread that evicts to secondary.     *)
+     tryEv:   if (evict = FALSE) {
+                 evict := TRUE;
+                 (* Wait for all other insertion threads and  *) 
+                 (* the one reader to park.                   *)
+     waitIns:    await waitCnt = Cardinality(Writer) - 1 + Cardinality(Reader);
+                 call Evict();
+     endEv:      evict := FALSE;
+                 goto put;                   
+              } else {
+                 goto put
+              }
+            } 
+         } \* end while/pick
+    }
+}
+***     this ends the comment containg the pluscal code      **********)
+----------------------------------------------------------------------------
+\* BEGIN TRANSLATION
+VARIABLES table, secondary, newsecondary, P, evict, waitCnt, history, pc, 
+          stack, ei, ej, lo, rfp, rindex, checked, fp, index, result, 
+          expected
+
+vars == << table, secondary, newsecondary, P, evict, waitCnt, history, pc, 
+           stack, ei, ej, lo, rfp, rindex, checked, fp, index, result, 
+           expected >>
+
+ProcSet == (Reader) \cup (Writer)
+
+Init == (* Global variables *)
+        /\ table = [i \in 1..K |-> empty]
+        /\ secondary = <<>>
+        /\ newsecondary = <<>>
+        /\ P = 0
+        /\ evict = FALSE
+        /\ waitCnt = 0
+        /\ history = {}
+        (* Procedure Evict *)
+        /\ ei = [ self \in ProcSet |-> 1]
+        /\ ej = [ self \in ProcSet |-> 1]
+        /\ lo = [ self \in ProcSet |-> 0]
+        (* Process q *)
+        /\ rfp = [self \in Reader |-> 0]
+        /\ rindex = [self \in Reader |-> 0]
+        /\ checked = [self \in Reader |-> {}]
+        (* Process p *)
+        /\ fp = [self \in Writer |-> 0]
+        /\ index = [self \in Writer |-> 0]
+        /\ result = [self \in Writer |-> FALSE]
+        /\ expected = [self \in Writer |-> -1]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self \in Reader -> "rwait"
+                                        [] self \in Writer -> "pick"]
+
+strEv(self) == /\ pc[self] = "strEv"
+               /\ IF ei[self] <= K+P
+                     THEN /\ lo' = [lo EXCEPT ![self] = table[mod(ei[self] + 1, K)]]
+                          /\ pc' = [pc EXCEPT ![self] = "fdsaf"]
+                          /\ ei' = ei
+                     ELSE /\ ei' = [ei EXCEPT ![self] = 1]
+                          /\ pc' = [pc EXCEPT ![self] = "flush"]
+                          /\ lo' = lo
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ej, rfp, rindex, 
+                               checked, fp, index, result, expected >>
+
+fdsaf(self) == /\ pc[self] = "fdsaf"
+               /\ IF compare(lo[self], mod(ei[self] + 1, K), table[mod(ej[self], K)], mod(ej[self], K)) <= -1
+                     THEN /\ table' = [table EXCEPT ![mod(ej[self] + 1, K)] = table[mod(ej[self], K)]]
+                          /\ IF ej[self] = 0
+                                THEN /\ ej' = [ej EXCEPT ![self] = ej[self] - 1]
+                                     /\ pc' = [pc EXCEPT ![self] = "set"]
+                                ELSE /\ ej' = [ej EXCEPT ![self] = ej[self] - 1]
+                                     /\ pc' = [pc EXCEPT ![self] = "fdsaf"]
+                     ELSE /\ pc' = [pc EXCEPT ![self] = "set"]
+                          /\ UNCHANGED << table, ej >>
+               /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, 
+                               history, stack, ei, lo, rfp, rindex, checked, 
+                               fp, index, result, expected >>
+
+set(self) == /\ pc[self] = "set"
+             /\ table' = [table EXCEPT ![mod(ej[self] + 1, K)] = lo[self]]
+             /\ ej' = [ej EXCEPT ![self] = ei[self] + 1]
+             /\ ei' = [ei EXCEPT ![self] = ei[self] + 1]
+             /\ pc' = [pc EXCEPT ![self] = "strEv"]
+             /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, 
+                             history, stack, lo, rfp, rindex, checked, fp, 
+                             index, result, expected >>
+
+flush(self) == /\ pc[self] = "flush"
+               /\ IF ei[self] <= K+P
+                     THEN /\ lo' = [lo EXCEPT ![self] = table[mod(ei[self], K)]]
+                          /\ IF lo'[self] # empty /\
+                                lo'[self] > largestElem(newsecondary) /\
+                                ((ei[self] <= K /\ ~wrapped(lo'[self],ei[self])) \/
+                                 (ei[self] > K /\ wrapped(lo'[self],ei[self])))
+                                THEN /\ newsecondary' = Append(newsecondary \o subSetSmaller(secondary, newsecondary, lo'[self]), lo'[self])
+                                     /\ table' = [table EXCEPT ![mod(ei[self], K)] = lo'[self] * (-1)]
+                                ELSE /\ TRUE
+                                     /\ UNCHANGED << table, newsecondary >>
+                          /\ ei' = [ei EXCEPT ![self] = ei[self] + 1]
+                          /\ pc' = [pc EXCEPT ![self] = "flush"]
+                          /\ UNCHANGED << secondary, P >>
+                     ELSE /\ secondary' = newsecondary \o subSetLarger(secondary, newsecondary)
+                          /\ newsecondary' = <<>>
+                          /\ P' = 0
+                          /\ pc' = [pc EXCEPT ![self] = "rtrn"]
+                          /\ UNCHANGED << table, ei, lo >>
+               /\ UNCHANGED << evict, waitCnt, history, stack, ej, rfp, rindex, 
+                               checked, fp, index, result, expected >>
+
+rtrn(self) == /\ pc[self] = "rtrn"
+              /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+              /\ ei' = [ei EXCEPT ![self] = Head(stack[self]).ei]
+              /\ ej' = [ej EXCEPT ![self] = Head(stack[self]).ej]
+              /\ lo' = [lo EXCEPT ![self] = Head(stack[self]).lo]
+              /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+              /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                              waitCnt, history, rfp, rindex, checked, fp, 
+                              index, result, expected >>
+
+Evict(self) == strEv(self) \/ fdsaf(self) \/ set(self) \/ flush(self)
+                  \/ rtrn(self)
+
+rwait(self) == /\ pc[self] = "rwait"
+               /\ history # {}
+               /\ pc' = [pc EXCEPT ![self] = "rpick"]
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ei, ej, lo, rfp, 
+                               rindex, checked, fp, index, result, expected >>
+
+rpick(self) == /\ pc[self] = "rpick"
+               /\ IF checked[self] = history /\ history = fps
+                     THEN /\ pc' = [pc EXCEPT ![self] = "Done"]
+                          /\ UNCHANGED << rfp, checked >>
+                     ELSE /\ \E f \in history:
+                               /\ rfp' = [rfp EXCEPT ![self] = f]
+                               /\ checked' = [checked EXCEPT ![self] = checked[self] \cup {f}]
+                          /\ pc' = [pc EXCEPT ![self] = "rcntns"]
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ei, ej, lo, rindex, fp, 
+                               index, result, expected >>
+
+rcntns(self) == /\ pc[self] = "rcntns"
+                /\ rindex' = [rindex EXCEPT ![self] = 0]
+                /\ IF evict
+                      THEN /\ waitCnt' = waitCnt + 1
+                           /\ pc' = [pc EXCEPT ![self] = "rwaitEv"]
+                      ELSE /\ pc' = [pc EXCEPT ![self] = "ronPrm"]
+                           /\ UNCHANGED waitCnt
+                /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                history, stack, ei, ej, lo, rfp, checked, fp, 
+                                index, result, expected >>
+
+rwaitEv(self) == /\ pc[self] = "rwaitEv"
+                 /\ evict = FALSE
+                 /\ pc' = [pc EXCEPT ![self] = "rendWEv"]
+                 /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                 waitCnt, history, stack, ei, ej, lo, rfp, 
+                                 rindex, checked, fp, index, result, expected >>
+
+rendWEv(self) == /\ pc[self] = "rendWEv"
+                 /\ waitCnt' = waitCnt - 1
+                 /\ pc' = [pc EXCEPT ![self] = "rcntns"]
+                 /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                 history, stack, ei, ej, lo, rfp, rindex, 
+                                 checked, fp, index, result, expected >>
+
+ronPrm(self) == /\ pc[self] = "ronPrm"
+                /\ IF rindex[self] <= P
+                      THEN /\ IF isMatch(rfp[self], idx(rfp[self], rindex[self]), table)
+                                 THEN /\ pc' = [pc EXCEPT ![self] = "rpick"]
+                                      /\ UNCHANGED rindex
+                                 ELSE /\ IF    isEmpty(idx(rfp[self], rindex[self]), table)
+                                            \/ isMarked(idx(rfp[self], rindex[self]), table)
+                                            THEN /\ pc' = [pc EXCEPT ![self] = "ronSnc"]
+                                                 /\ UNCHANGED rindex
+                                            ELSE /\ rindex' = [rindex EXCEPT ![self] = rindex[self] + 1]
+                                                 /\ pc' = [pc EXCEPT ![self] = "ronPrm"]
+                      ELSE /\ pc' = [pc EXCEPT ![self] = "ronSnc"]
+                           /\ UNCHANGED rindex
+                /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                waitCnt, history, stack, ei, ej, lo, rfp, 
+                                checked, fp, index, result, expected >>
+
+ronSnc(self) == /\ pc[self] = "ronSnc"
+                /\ IF containsElem(secondary,rfp[self])
+                      THEN /\ pc' = [pc EXCEPT ![self] = "rpick"]
+                      ELSE /\ Assert((FALSE), 
+                                     "Failure of assertion at line 291, column 23.")
+                           /\ pc' = [pc EXCEPT ![self] = "rpick"]
+                /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                waitCnt, history, stack, ei, ej, lo, rfp, 
+                                rindex, checked, fp, index, result, expected >>
+
+q(self) == rwait(self) \/ rpick(self) \/ rcntns(self) \/ rwaitEv(self)
+              \/ rendWEv(self) \/ ronPrm(self) \/ ronSnc(self)
+
+pick(self) == /\ pc[self] = "pick"
+              /\ IF (fps \ history) = {}
+                    THEN /\ pc' = [pc EXCEPT ![self] = "Done"]
+                         /\ fp' = fp
+                    ELSE /\ \E f \in (fps \ history):
+                              fp' = [fp EXCEPT ![self] = f]
+                         /\ pc' = [pc EXCEPT ![self] = "put"]
+              /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                              waitCnt, history, stack, ei, ej, lo, rfp, rindex, 
+                              checked, index, result, expected >>
+
+put(self) == /\ pc[self] = "put"
+             /\ index' = [index EXCEPT ![self] = 0]
+             /\ result' = [result EXCEPT ![self] = FALSE]
+             /\ expected' = [expected EXCEPT ![self] = -1]
+             /\ IF evict
+                   THEN /\ waitCnt' = waitCnt + 1
+                        /\ pc' = [pc EXCEPT ![self] = "waitEv"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "chkSnc"]
+                        /\ UNCHANGED waitCnt
+             /\ UNCHANGED << table, secondary, newsecondary, P, evict, history, 
+                             stack, ei, ej, lo, rfp, rindex, checked, fp >>
+
+waitEv(self) == /\ pc[self] = "waitEv"
+                /\ evict = FALSE
+                /\ pc' = [pc EXCEPT ![self] = "endWEv"]
+                /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                waitCnt, history, stack, ei, ej, lo, rfp, 
+                                rindex, checked, fp, index, result, expected >>
+
+endWEv(self) == /\ pc[self] = "endWEv"
+                /\ waitCnt' = waitCnt - 1
+                /\ pc' = [pc EXCEPT ![self] = "put"]
+                /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                history, stack, ei, ej, lo, rfp, rindex, 
+                                checked, fp, index, result, expected >>
+
+chkSnc(self) == /\ pc[self] = "chkSnc"
+                /\ IF secondary # <<>>
+                      THEN /\ pc' = [pc EXCEPT ![self] = "cntns"]
+                      ELSE /\ pc' = [pc EXCEPT ![self] = "insrt"]
+                /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                waitCnt, history, stack, ei, ej, lo, rfp, 
+                                rindex, checked, fp, index, result, expected >>
+
+cntns(self) == /\ pc[self] = "cntns"
+               /\ IF index[self] < P
+                     THEN /\ IF isMatch(fp[self], idx(fp[self], index[self]), table)
+                                THEN /\ pc' = [pc EXCEPT ![self] = "pick"]
+                                     /\ index' = index
+                                ELSE /\ IF    isEmpty(idx(fp[self], index[self]), table)
+                                           \/ isMarked(idx(fp[self], index[self]), table)
+                                           THEN /\ pc' = [pc EXCEPT ![self] = "onSnc"]
+                                                /\ index' = index
+                                           ELSE /\ index' = [index EXCEPT ![self] = index[self] + 1]
+                                                /\ pc' = [pc EXCEPT ![self] = "cntns"]
+                     ELSE /\ pc' = [pc EXCEPT ![self] = "onSnc"]
+                          /\ index' = index
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ei, ej, lo, rfp, 
+                               rindex, checked, fp, result, expected >>
+
+onSnc(self) == /\ pc[self] = "onSnc"
+               /\ IF containsElem(secondary,fp[self])
+                     THEN /\ pc' = [pc EXCEPT ![self] = "pick"]
+                          /\ index' = index
+                     ELSE /\ index' = [index EXCEPT ![self] = 0]
+                          /\ pc' = [pc EXCEPT ![self] = "insrt"]
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ei, ej, lo, rfp, 
+                               rindex, checked, fp, result, expected >>
+
+insrt(self) == /\ pc[self] = "insrt"
+               /\ IF index[self] < L
+                     THEN /\ expected' = [expected EXCEPT ![self] = table[idx(fp[self],index[self])]]
+                          /\ IF expected'[self] = empty \/ (expected'[self] < 0 /\ expected'[self] # (-1) * fp[self])
+                                THEN /\ pc' = [pc EXCEPT ![self] = "incP"]
+                                ELSE /\ pc' = [pc EXCEPT ![self] = "isMth"]
+                     ELSE /\ pc' = [pc EXCEPT ![self] = "tryEv"]
+                          /\ UNCHANGED expected
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ei, ej, lo, rfp, 
+                               rindex, checked, fp, index, result >>
+
+isMth(self) == /\ pc[self] = "isMth"
+               /\ IF isMatch(fp[self],idx(fp[self],index[self]),table)
+                     THEN /\ pc' = [pc EXCEPT ![self] = "pick"]
+                          /\ index' = index
+                     ELSE /\ index' = [index EXCEPT ![self] = index[self] + 1]
+                          /\ pc' = [pc EXCEPT ![self] = "insrt"]
+               /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                               waitCnt, history, stack, ei, ej, lo, rfp, 
+                               rindex, checked, fp, result, expected >>
+
+incP(self) == /\ pc[self] = "incP"
+              /\ IF index[self] > P
+                    THEN /\ P' = index[self]
+                    ELSE /\ TRUE
+                         /\ P' = P
+              /\ pc' = [pc EXCEPT ![self] = "cas"]
+              /\ UNCHANGED << table, secondary, newsecondary, evict, waitCnt, 
+                              history, stack, ei, ej, lo, rfp, rindex, checked, 
+                              fp, index, result, expected >>
+
+cas(self) == /\ pc[self] = "cas"
+             /\ IF table[(idx(fp[self],index[self]))] = expected[self]
+                   THEN /\ table' = [table EXCEPT ![(idx(fp[self],index[self]))] = fp[self]]
+                        /\ result' = [result EXCEPT ![self] = TRUE]
+                   ELSE /\ result' = [result EXCEPT ![self] = FALSE]
+                        /\ table' = table
+             /\ IF result'[self]
+                   THEN /\ history' = (history \cup {fp[self]})
+                        /\ pc' = [pc EXCEPT ![self] = "pick"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "insrt"]
+                        /\ UNCHANGED history
+             /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, stack, 
+                             ei, ej, lo, rfp, rindex, checked, fp, index, 
+                             expected >>
+
+tryEv(self) == /\ pc[self] = "tryEv"
+               /\ IF evict = FALSE
+                     THEN /\ evict' = TRUE
+                          /\ pc' = [pc EXCEPT ![self] = "waitIns"]
+                     ELSE /\ pc' = [pc EXCEPT ![self] = "put"]
+                          /\ evict' = evict
+               /\ UNCHANGED << table, secondary, newsecondary, P, waitCnt, 
+                               history, stack, ei, ej, lo, rfp, rindex, 
+                               checked, fp, index, result, expected >>
+
+waitIns(self) == /\ pc[self] = "waitIns"
+                 /\ waitCnt = Cardinality(Writer) - 1 + Cardinality(Reader)
+                 /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "Evict",
+                                                          pc        |->  "endEv",
+                                                          ei        |->  ei[self],
+                                                          ej        |->  ej[self],
+                                                          lo        |->  lo[self] ] >>
+                                                      \o stack[self]]
+                 /\ ei' = [ei EXCEPT ![self] = 1]
+                 /\ ej' = [ej EXCEPT ![self] = 1]
+                 /\ lo' = [lo EXCEPT ![self] = 0]
+                 /\ pc' = [pc EXCEPT ![self] = "strEv"]
+                 /\ UNCHANGED << table, secondary, newsecondary, P, evict, 
+                                 waitCnt, history, rfp, rindex, checked, fp, 
+                                 index, result, expected >>
+
+endEv(self) == /\ pc[self] = "endEv"
+               /\ evict' = FALSE
+               /\ pc' = [pc EXCEPT ![self] = "put"]
+               /\ UNCHANGED << table, secondary, newsecondary, P, waitCnt, 
+                               history, stack, ei, ej, lo, rfp, rindex, 
+                               checked, fp, index, result, expected >>
+
+p(self) == pick(self) \/ put(self) \/ waitEv(self) \/ endWEv(self)
+              \/ chkSnc(self) \/ cntns(self) \/ onSnc(self) \/ insrt(self)
+              \/ isMth(self) \/ incP(self) \/ cas(self) \/ tryEv(self)
+              \/ waitIns(self) \/ endEv(self)
+
+Next == (\E self \in ProcSet: Evict(self))
+           \/ (\E self \in Reader: q(self))
+           \/ (\E self \in Writer: p(self))
+           \/ (* Disjunct to prevent deadlock on termination *)
+              ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+        /\ \A self \in Writer : WF_vars(p(self)) /\ WF_vars(Evict(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION
+
+(***************************************************************************)
+(* A type correctness invariant for outer, inner and P.                    *)
+(***************************************************************************)
+TypeOK == /\ P \in Nat
+          /\ P <= L
+                        
+(***************************************************************************)
+(* All fingerprint in history are (always) hash table members,             *)
+(* all fps \ history never are.                                            *)
+(***************************************************************************)
+Inv == evict = FALSE => /\ \A seen \in history: 
+                                       contains(seen,table,secondary,P+1)
+                        /\ \A unseen \in (fps \ history):
+                                      ~contains(unseen,table,secondary,P+1)
+       
+(***************************************************************************)
+(* FALSE iff table contains duplicate elements (excluding empty).          *)
+(***************************************************************************)
+Duplicates == evict = FALSE => LET sub == SelectSeq(table, LAMBDA e: e # empty)
+                               IN IF Len(sub) < 2 THEN TRUE
+                                  ELSE \A i \in 1..(Len(sub) - 1):
+                                           /\ sub[i] # sub[i+1]
+                                           /\ sub[i] # (-1) * sub[i+1]
+                        
+(***************************************************************************)
+(* Secondary storage is always sorted in ascending order.                  *)
+(***************************************************************************) 
+Sorted == isSorted(secondary) /\ isSorted(newsecondary)
+
+(***************************************************************************)
+(* TRUE iff f is found in table within idx(f,0)..id(f,r).                  *)
+(***************************************************************************)
+containedInTable(f) == \E r \in 0..P+1: IF f < 0
+                                        THEN table[idx((-1)*f, r)] = f
+                                        ELSE table[idx(f, r)] = f
+
+(***************************************************************************)
+(* TRUE iff f is found in secondary.                                       *)
+(***************************************************************************)
+containedInSecondary(f) == containsElem(secondary,f)
+
+(***************************************************************************)
+(* TRUE iff f is found in newsecondary.                                    *)
+(***************************************************************************)
+containedInNewSecondary(f) == containsElem(newsecondary,f)
+
+(***************************************************************************)
+(* TRUE iff fingerprints correctly transition from table to newsecondary   *)
+(* to secondary.                                                           *)
+(***************************************************************************)
+Consistent == evict = FALSE => \A seen \in history:
+        /\ (containedInTable(seen) => ~containedInSecondary(seen))
+        /\ (containedInTable(seen) => ~containedInNewSecondary(seen))
+        /\ (~containedInTable(seen) => (containedInSecondary(seen)) \/ 
+                                        containedInNewSecondary(seen))
+        /\ (containedInTable(seen * (-1)) => (containedInSecondary(seen)) \/ 
+                                              containedInNewSecondary(seen))
+\* Universal quantification and leads-to not supported by TLC
+\*      /\ (containedInTable((-1)*seen) ~> containedInSecondary(seen))
+
+(***************************************************************************)
+(* TRUE iff the maximum displacement of all elements (without empty) is    *)
+(* equal to P. In other words, P is always minimal.                        *)
+(***************************************************************************)
+Minimal == \A i \in 1..Len(table): IF table[i] = empty 
+                                     THEN TRUE
+                                     ELSE LET f == IF table[i] < 0 
+                                                   THEN -1 * table[i]
+                                                   ELSE table[i]
+                                          IN (i - idx(f, 0)) <= P
+
+(***************************************************************************)
+(* P has to reach the limit L in some behaviors.                           *)
+(***************************************************************************)
+Maxed == <>(P = L)
+
+(***************************************************************************)
+(* Under all behaviors, the algorithm makes progress.                      *)
+(***************************************************************************)
+Prop == <>[](history = fps)
+=============================================================================
+\end{ppcal}
diff --git a/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java b/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java
index d7e2663497f429db2332d720d3c27b4f988382c3..3a4edd2305a0b0919ec1804656d758c07281081d 100644
--- a/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java
+++ b/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java
@@ -115,7 +115,7 @@ public abstract class FPIntSet
   
   public abstract void exit(boolean cleanup) throws IOException;
 
-  public abstract double checkFPs() throws IOException;
+  public abstract long checkFPs() throws IOException;
 
   public abstract void beginChkpt() throws IOException;
   public abstract void commitChkpt() throws IOException;
diff --git a/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java b/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java
index 20e07455f5d212827f5bb80756e308084e53c2d2..b54df811813cb3e6742274df171f502833c1a3e3 100644
--- a/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java
+++ b/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java
@@ -262,7 +262,7 @@ public class MemFPIntSet extends FPIntSet {
     System.exit(0);    
   }
 
-  public final double checkFPs() {
+  public final long checkFPs() {
     long dis = Long.MAX_VALUE;
     for (int i = 0; i < this.table.length; i++) {
       int[] bucket = this.table[i];
@@ -289,7 +289,7 @@ public class MemFPIntSet extends FPIntSet {
 	}
       }
     }
-    return (1.0/dis);
+    return dis;
   }
 
   // Checkpoint.
diff --git a/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java b/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java
index 32c5dfc47eeb3b0838d39b1949dd91dff58d16e0..533c3d545fd67557f8a6557dac238211d9ef295f 100644
--- a/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java
+++ b/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java
@@ -73,8 +73,8 @@ public class MultiFPIntSet extends FPIntSet {
   }
 
   /* This is not quite correct. */
-  public final double checkFPs() throws IOException {
-    double res = Double.NEGATIVE_INFINITY;
+  public final long checkFPs() throws IOException {
+    long res = Long.MIN_VALUE;
     for (int i = 0; i < this.sets.length; i++) {
       res = Math.max(res, this.sets[i].checkFPs());
     }
diff --git a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java
index 6ca77dfc1cfce40e641db52692cd7bb50893ccd9..573c2674c74a782205844bf14f8e3ea132e68540 100644
--- a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java
+++ b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java
@@ -8,6 +8,16 @@ import tlc2.tool.fp.FPSet;
 
 public interface DiskFPSetMXBean {
 
+	/**
+	 * @return The version of TLC.
+	 */
+	String getVersion();
+	
+	/**
+	 * @return The code revision corresponding to this version of TLC.
+	 */
+	String getRevision();
+	
 	/**
 	 * @see DiskFPSet#getDiskHitCnt()
 	 */
@@ -90,19 +100,14 @@ public interface DiskFPSetMXBean {
 	long getFlushTime();
 	
 	/**
-	 * @see DiskFPSet#getReaderWriterCnt()
+	 * @see DiskFPSet#getLockCnt()
 	 */
-	int getReaderWriterCnt();
-	
-	/**
-	 * @see DiskFPSet#getCollisionBucketCnt()
-	 */
-	long getCollisionBucketCnt();
+	int getLockCnt();
 	
 	/**
-	 * @see DiskFPSet#getCollisionRatio()
+	 * @see DiskFPSet#getReaderWriterCnt()
 	 */
-	double getCollisionRatio();
+	int getReaderWriterCnt();
 	
 	/**
 	 * @see DiskFPSet#getLoadFactor()
diff --git a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java
index 74d097f8b843f17229672f6ef8cd2bf250457dcc..6da1f5e7c868ba587576bdb0a5df74c19690d217 100644
--- a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java
+++ b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java
@@ -21,8 +21,11 @@ public class DiskFPSetMXWrapper extends TLCStandardMBean implements DiskFPSetMXB
 		super(DiskFPSetMXBean.class);
 		fpset = diskFPSet;
 		
+		// Append ",name=COUNT" suffix to objectname to expose all DiskFPSet instances
+		// as children of type DiskFPSet. However, jfr2jmx does not support it, nor does
+		// jmx2munin used by cloud based distributed TLC.
 		objectName = "DiskFPSet" + COUNT++;
-		registerMBean("tlc2.tool.fp:type=" + objectName);
+		registerMBean("tlc2.tool.fp:type=" + objectName/* + ",name=" + COUNT++*/);
 	}
 	
 	public String getObjectName() {
@@ -164,20 +167,6 @@ public class DiskFPSetMXWrapper extends TLCStandardMBean implements DiskFPSetMXB
 		return fpset.getReaderWriterCnt();
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.management.DiskFPSetMXBean#getCollisionBucketCnt()
-	 */
-	public long getCollisionBucketCnt() {
-		return fpset.getCollisionBucketCnt();
-	}
-	
-	/* (non-Javadoc)
-	 * @see tlc2.tool.fp.management.DiskFPSetMXBean#getCollisionRatio()
-	 */
-	public double getCollisionRatio() {
-		return fpset.getCollisionRatio();
-	}
-
 	/* (non-Javadoc)
 	 * @see tlc2.tool.fp.management.DiskFPSetMXBean#getLoadFactor()
 	 */
@@ -198,4 +187,11 @@ public class DiskFPSetMXWrapper extends TLCStandardMBean implements DiskFPSetMXB
 	public boolean checkInvariant() throws IOException {
 		return fpset.checkInvariant();
 	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.management.DiskFPSetMXBean#getLockCnt()
+	 */
+	public int getLockCnt() {
+		return fpset.getLockCnt();
+	}
 }
diff --git a/tlatools/src/tlc2/tool/ActionItemList.java b/tlatools/src/tlc2/tool/impl/ActionItemList.java
similarity index 58%
rename from tlatools/src/tlc2/tool/ActionItemList.java
rename to tlatools/src/tlc2/tool/impl/ActionItemList.java
index 2a1cfe8f53ecca727ea14ca73deac08e4b39ce78..f4a9df648c812e95d054cdb8ada0f7f6234835cd 100644
--- a/tlatools/src/tlc2/tool/ActionItemList.java
+++ b/tlatools/src/tlc2/tool/impl/ActionItemList.java
@@ -1,48 +1,61 @@
 // Copyright (c) 2003 Compaq Corporation.  All rights reserved.
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
 
-package tlc2.tool;
+package tlc2.tool.impl;
 
 import tla2sany.semantic.SemanticNode;
+import tlc2.TLCGlobals;
+import tlc2.tool.Action;
+import tlc2.tool.IActionItemList;
+import tlc2.tool.coverage.CostModel;
 import tlc2.util.Context;
 
-public class ActionItemList {
-  /**
+class ActionItemList implements IActionItemList {
+	private static final boolean coverage = TLCGlobals.isCoverageEnabled();
+	/**
    * We assume that this.pred is null iff the list is empty.
-   * The meaning of this.kind is given as follows:
-   *    kind > 0:  pred of a conjunction
-   *    kind = -1: pred
-   *    kind = -2: UNCHANGED pred
-   *    kind = -3: pred' # pred
    */
   public final SemanticNode pred;     // Expression of the action
   public final Context con;           // Context of the action
-  public final int kind;  
+  private final int kind;  
   public final ActionItemList next;
+  public final CostModel cm;
 
   public final static ActionItemList
-    Empty = new ActionItemList(null, null, 0, null);
+    Empty = new ActionItemList(null, null, 0, null, null);
   
   /* Constructors */
   private ActionItemList(SemanticNode pred, Context con,
-			 int kind, ActionItemList next) {
+			 int kind, ActionItemList next, CostModel cm) {
     this.pred = pred;
     this.con = con;
     this.kind = kind;
     this.next = next;
+    this.cm = cm;
   }
 
   public final SemanticNode carPred() { return this.pred; }
 
   public final Context carContext() { return this.con; }
 
+  /**
+   * The meaning of this.kind is given as follows:
+   *    kind > 0:  pred of a conjunction
+   *    kind = -1: pred
+   *    kind = -2: UNCHANGED pred
+   *    kind = -3: pred' # pred
+   */
   public final int carKind() { return this.kind; }
 
   public final ActionItemList cdr() { return this.next; }
 
-  public final ActionItemList cons(SemanticNode pred,
-				   Context con, int kind) {
-    return new ActionItemList(pred, con, kind, this);
+  public final IActionItemList cons(SemanticNode pred,
+				   Context con, CostModel cm, int kind) {
+    return new ActionItemList(pred, con, kind, this, coverage ? cm.get(pred) : cm);
+  }
+
+  public ActionItemList cons(final Action act, final int kind) {
+	return new ActionItemList(act.pred, act.con, kind, this, coverage ? act.cm.get(pred) : act.cm);
   }
 
   public final boolean isEmpty() { return this == Empty; }
diff --git a/tlatools/src/tlc2/tool/impl/CallStackTool.java b/tlatools/src/tlc2/tool/impl/CallStackTool.java
new file mode 100644
index 0000000000000000000000000000000000000000..14ad1102dec57d93a2e857617cddab8cf638ec0f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/CallStackTool.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.impl;
+
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.SemanticNode;
+import tlc2.tool.Action;
+import tlc2.tool.CallStack;
+import tlc2.tool.EvalException;
+import tlc2.tool.IActionItemList;
+import tlc2.tool.INextStateFunctor;
+import tlc2.tool.IStateFunctor;
+import tlc2.tool.ITool;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.impl.FcnLambdaValue;
+import tlc2.value.impl.OpLambdaValue;
+import tlc2.value.impl.SetPredValue;
+import tlc2.value.impl.Value;
+import util.Assert.TLCRuntimeException;
+
+public final class CallStackTool extends Tool {
+
+	private final CallStack callStack = new CallStack();
+
+	public CallStackTool(ITool other) {
+		super((Tool) other);
+	}
+
+	@Override
+	public final String toString() {
+		return this.callStack.toString();
+	}
+
+	@Override
+	protected final void getInitStates(final SemanticNode init, final ActionItemList acts, final Context c,
+			final TLCState ps, final IStateFunctor states, final CostModel cm) {
+		this.callStack.push(init);
+		try {
+			super.getInitStates(init, acts, c, ps, states, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// Freeze the callStack to ignore subsequent pop operations. This is
+			// necessary to ignore the callStack#pop calls in the finally blocks when the
+			// Java call stack gets unwounded.
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	protected void getInitStatesAppl(final OpApplNode init, final ActionItemList acts, final Context c,
+			final TLCState ps, final IStateFunctor states, final CostModel cm) {
+		this.callStack.push(init);
+		try {
+			super.getInitStatesAppl(init, acts, c, ps, states, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final TLCState getNextStates(final Action action, final SemanticNode pred, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) {
+		this.callStack.push(pred);
+		try {
+			return getNextStatesImpl(action, pred, acts, c, s0, s1, nss, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final TLCState getNextStatesAppl(final Action action, final OpApplNode pred, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) {
+		this.callStack.push(pred);
+		try {
+			return getNextStatesApplImpl(action, pred, acts, c, s0, s1, nss, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	public final Value eval(final SemanticNode expr, final Context c, final TLCState s0, final TLCState s1,
+			final int control, final CostModel cm) {
+		this.callStack.push(expr);
+		try {
+			// Replace stale ITool instances with this instance.
+			final Value value = evalImpl(expr, c, s0, s1, control, cm);
+			if (value instanceof SetPredValue) {
+				return new SetPredValue((SetPredValue) value, this);
+			} else if (value instanceof FcnLambdaValue) {
+				return new FcnLambdaValue((FcnLambdaValue) value, this);
+			} else if (value instanceof OpLambdaValue) {
+				return new OpLambdaValue((OpLambdaValue) value, this);
+			}
+			return value;
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final Value evalAppl(final OpApplNode expr, final Context c, final TLCState s0, final TLCState s1,
+			final int control, final CostModel cm) {
+		this.callStack.push(expr);
+		try {
+			// Replace stale ITool instances with this instance.
+			final Value value = evalApplImpl(expr, c, s0, s1, control, cm);
+			if (value instanceof SetPredValue) {
+				return new SetPredValue((SetPredValue) value, this);
+			} else if (value instanceof FcnLambdaValue) {
+				return new FcnLambdaValue((FcnLambdaValue) value, this);
+			} else if (value instanceof OpLambdaValue) {
+				return new OpLambdaValue((OpLambdaValue) value, this);
+			}
+			return value;
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	public final TLCState enabled(final SemanticNode pred, final IActionItemList acts, final Context c,
+			final TLCState s0, final TLCState s1, final CostModel cm) {
+		this.callStack.push(pred);
+		try {
+			return enabledImpl(pred, (ActionItemList) acts, c, s0, s1, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final TLCState enabledAppl(final OpApplNode pred, final ActionItemList acts, final Context c,
+			final TLCState s0, final TLCState s1, CostModel cm) {
+		this.callStack.push(pred);
+		try {
+			return enabledApplImpl(pred, acts, c, s0, s1, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final TLCState processUnchanged(final Action action, final SemanticNode expr, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, CostModel cm) {
+		this.callStack.push(expr);
+		try {
+			return processUnchangedImpl(action, expr, acts, c, s0, s1, nss, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final TLCState enabledUnchanged(final SemanticNode expr, final ActionItemList acts, final Context c,
+			final TLCState s0, final TLCState s1, final CostModel cm) {
+		this.callStack.push(expr);
+		try {
+			return enabledUnchangedImpl(expr, acts, c, s0, s1, cm);
+		} catch (TLCRuntimeException | EvalException e) {
+			// see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context,
+			// TLCState, IStateFunctor)
+			this.callStack.freeze();
+			throw e;
+		} finally {
+			this.callStack.pop();
+		}
+	}
+
+	@Override
+	protected final Value setSource(final SemanticNode expr, final Value value) {
+		value.setSource(expr);
+		return value;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/ContextEnumerator.java b/tlatools/src/tlc2/tool/impl/ContextEnumerator.java
similarity index 90%
rename from tlatools/src/tlc2/tool/ContextEnumerator.java
rename to tlatools/src/tlc2/tool/impl/ContextEnumerator.java
index 2ed8032915a7840b39006f6d7d36d7b93be4ef22..516282c030f4ec4f1aefb776f182c5d1b9b6192f 100644
--- a/tlatools/src/tlc2/tool/ContextEnumerator.java
+++ b/tlatools/src/tlc2/tool/impl/ContextEnumerator.java
@@ -3,17 +3,18 @@
 // Last modified on Mon 30 Apr 2007 at 15:29:55 PST by lamport
 //      modified on Tue Nov  9 11:06:41 PST 1999 by yuanyu
 
-package tlc2.tool;
+package tlc2.tool.impl;
 
 import tla2sany.semantic.SymbolNode;
 import tlc2.output.EC;
+import tlc2.tool.IContextEnumerator;
 import tlc2.util.Context;
-import tlc2.value.TupleValue;
-import tlc2.value.Value;
-import tlc2.value.ValueEnumeration;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueEnumeration;
 import util.Assert;
 
-public final class ContextEnumerator {
+public final class ContextEnumerator implements IContextEnumerator {
   private Context con;
   private Object[] vars;
   private ValueEnumeration[] enums;
@@ -35,6 +36,7 @@ public final class ContextEnumerator {
     }
   }
   
+  @Override
   public final Context nextElement() {
       Context con1 = this.con;
       if (this.isDone) return null;
diff --git a/tlatools/src/tlc2/tool/impl/FastTool.java b/tlatools/src/tlc2/tool/impl/FastTool.java
new file mode 100644
index 0000000000000000000000000000000000000000..89b71cf2b584b808e2d0e393b0ea0a5d9fc4c5f7
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/FastTool.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.impl;
+
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.SemanticNode;
+import tlc2.tool.Action;
+import tlc2.tool.IActionItemList;
+import tlc2.tool.INextStateFunctor;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.util.ExpectInlined;
+import tlc2.value.impl.Value;
+import util.FilenameToStream;
+
+public final class FastTool extends Tool {
+
+	public FastTool(String mainFile, String configFile) {
+		super(mainFile, configFile);
+	}
+
+	public FastTool(String mainFile, String configFile, FilenameToStream resolver) {
+		super(mainFile, configFile, resolver);
+	}
+
+	public FastTool(String specDir, String specFile, String configFile, FilenameToStream fts) {
+		super(specDir, specFile, configFile, fts);
+	}
+
+	// The methods below are supposed to be inlined during execution for performance
+	// reasons, collapsing this class effectively into Tool. Later and in case of a
+	// violation, the FastTool instance will be exchanged for the CallStackTool
+	// instance that properly records error for the purpose of error reporting.
+	@ExpectInlined
+	@Override
+	protected final TLCState getNextStates(final Action action, final SemanticNode pred, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) {
+		return getNextStatesImpl(action, pred, acts, c, s0, s1, nss, cm);
+	}
+
+	@ExpectInlined
+	@Override
+	protected final TLCState getNextStatesAppl(final Action action, final OpApplNode pred, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) {
+		return getNextStatesApplImpl(action, pred, acts, c, s0, s1, nss, cm);
+	}
+
+	@ExpectInlined
+	@Override
+	protected final TLCState processUnchanged(final Action action, final SemanticNode expr, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) {
+		return processUnchangedImpl(action, expr, acts, c, s0, s1, nss, cm);
+	}
+
+	@ExpectInlined
+	@Override
+	public final Value eval(final SemanticNode expr, final Context c, final TLCState s0, final TLCState s1,
+			final int control, final CostModel cm) {
+		return evalImpl(expr, c, s0, s1, control, cm);
+	}
+
+	@ExpectInlined
+	@Override
+	protected final Value evalAppl(final OpApplNode expr, final Context c, final TLCState s0, final TLCState s1,
+			final int control, final CostModel cm) {
+		return evalApplImpl(expr, c, s0, s1, control, cm);
+	}
+
+	@ExpectInlined
+	@Override
+	protected final Value setSource(final SemanticNode expr, final Value value) {
+		return value;
+	}
+
+	@ExpectInlined
+	@Override
+	public final TLCState enabled(final SemanticNode pred, final IActionItemList acts, final Context c,
+			final TLCState s0, final TLCState s1, final CostModel cm) {
+		return enabledImpl(pred, (ActionItemList) acts, c, s0, s1, cm); // TODO This cast sucks performance-wise.
+	}
+
+	@ExpectInlined
+	@Override
+	protected final TLCState enabledAppl(final OpApplNode pred, final ActionItemList acts, final Context c,
+			final TLCState s0, final TLCState s1, final CostModel cm) {
+		return enabledApplImpl(pred, acts, c, s0, s1, cm);
+	}
+
+	@ExpectInlined
+	@Override
+	protected final TLCState enabledUnchanged(final SemanticNode expr, final ActionItemList acts, final Context c,
+			final TLCState s0, final TLCState s1, final CostModel cm) {
+		return enabledUnchangedImpl(expr, acts, c, s0, s1, cm);
+	}
+}
diff --git a/tlatools/src/tlc2/tool/ModelConfig.java b/tlatools/src/tlc2/tool/impl/ModelConfig.java
similarity index 82%
rename from tlatools/src/tlc2/tool/ModelConfig.java
rename to tlatools/src/tlc2/tool/impl/ModelConfig.java
index a106dea9ef888c9c385898cd04724f032ffe8f47..7f8af9803409c7a2b49c601dea874a17f53d41a6 100644
--- a/tlatools/src/tlc2/tool/ModelConfig.java
+++ b/tlatools/src/tlc2/tool/impl/ModelConfig.java
@@ -3,7 +3,7 @@
 // Last modified on Mon 30 Apr 2007 at 15:29:56 PST by lamport
 //      modified on Thu Aug 23 17:46:39 PDT 2001 by yuanyu
 
-package tlc2.tool;
+package tlc2.tool.impl;
 
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -16,53 +16,67 @@ import tla2sany.parser.TLAplusParserTokenManager;
 import tla2sany.parser.Token;
 import tla2sany.parser.TokenMgrError;
 import tlc2.output.EC;
+import tlc2.tool.ConfigFileException;
 import tlc2.util.Vect;
-import tlc2.value.IntValue;
-import tlc2.value.ModelValue;
-import tlc2.value.SetEnumValue;
-import tlc2.value.StringValue;
-import tlc2.value.Value;
+import tlc2.value.IValue;
 import tlc2.value.ValueConstants;
-import tlc2.value.ValueVec;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueVec;
 import util.FileUtil;
 import util.FilenameToStream;
 import util.SimpleFilenameToStream;
+import util.TLAConstants;
 
 /** 
  * Stores information from user's model configuration file.
+ * 
+ * TODO we should move from Hashtable to HashMap (we should probably also stop using our own collection implmentations
+ * 			like {@link Vect}.)
+ * TODO we're storing a heterogeneous mishmash in the values of configTbl - sometimes a Vect, sometimes a String, sometime
+ * 			that Vect has only String instances, sometimes is has a String instance and Value subclasses, ... it would
+ * 			be nice were the design cleaner.
+ * 
  * @author Yuan Yu, Leslie Lamport
- * @version $Id$
  */
-public class ModelConfig implements ValueConstants, Serializable
-{
-    // keywords of the configuration file
-    private static final String Constant = "CONSTANT";
-    private static final String Constants = "CONSTANTS";
+public class ModelConfig implements ValueConstants, Serializable {
+    // keywords of the configuration file.
+	// CAREFUL: HAVE TO BE IN CONFIGTBL FOR PARSING TO WORK!
+    private static final String Constant = TLAConstants.KeyWords.CONSTANT;
+    private static final String Constants = TLAConstants.KeyWords.CONSTANTS;
     private static final String Constraint = "CONSTRAINT";
     private static final String Constraints = "CONSTRAINTS";
-    private static final String ActionConstraint = "ACTION_CONSTRAINT";
-    private static final String ActionConstraints = "ACTION_CONSTRAINTS";
-    private static final String Invariant = "INVARIANT";
-    private static final String Invariants = "INVARIANTS";
-    private static final String Init = "INIT";
-    private static final String Next = "NEXT";
+    private static final String ActionConstraint = TLAConstants.KeyWords.ACTION_CONSTRAINT;
+    private static final String ActionConstraints = ActionConstraint + 'S';
+    private static final String Invariant = TLAConstants.KeyWords.INVARIANT;
+    private static final String Invariants = Invariant + 'S';
+    private static final String Init = TLAConstants.KeyWords.INIT;
+    private static final String Next = TLAConstants.KeyWords.NEXT;
     private static final String View = "VIEW";
-    private static final String Symmetry = "SYMMETRY";
-    private static final String Spec = "SPECIFICATION";
-    private static final String Prop = "PROPERTY";
+    private static final String Symmetry = TLAConstants.KeyWords.SYMMETRY;
+    private static final String Spec = TLAConstants.KeyWords.SPECIFICATION;
+    private static final String Prop = TLAConstants.KeyWords.PROPERTY;
     private static final String Props = "PROPERTIES";
     private static final String Type = "TYPE";
     private static final String TypeConstraint = "TYPE_CONSTRAINT";
+    private static final String CheckDeadlock = "CHECK_DEADLOCK";
+
+    private static final long serialVersionUID = 1L;
 
     /**
      * All keywords used in the configuration file
      */
     public final static String[] ALL_KEYWORDS = { Constant, Constants, Constraint, Constraints, ActionConstraint,
             ActionConstraints, Invariant, Invariants, Init, Next, View, Symmetry, Spec, Prop, Props, Type,
-            TypeConstraint };
+            TypeConstraint, CheckDeadlock };
 
     private Hashtable configTbl;
-    private Hashtable overrides;
+    private Hashtable<String, String> overrides;
+    private Hashtable<String, String> overridesReverseMap;
     private Hashtable modConstants;
     private Hashtable modOverrides;
     private String configFileName;
@@ -89,17 +103,17 @@ public class ModelConfig implements ValueConstants, Serializable
         ModelValue.init();
 
         this.configFileName = configFileName;
-        this.configTbl = new Hashtable();
-        Vect temp = new Vect();
+        this.configTbl = new Hashtable<>();
+        Vect temp = new Vect<>();
         this.configTbl.put(Constant, temp);
         this.configTbl.put(Constants, temp);
-        temp = new Vect();
+        temp = new Vect<>();
         this.configTbl.put(Constraint, temp);
         this.configTbl.put(Constraints, temp);
-        temp = new Vect();
+        temp = new Vect<>();
         this.configTbl.put(ActionConstraint, temp);
         this.configTbl.put(ActionConstraints, temp);
-        temp = new Vect();
+        temp = new Vect<>();
         this.configTbl.put(Invariant, temp);
         this.configTbl.put(Invariants, temp);
         this.configTbl.put(Init, "");
@@ -107,15 +121,17 @@ public class ModelConfig implements ValueConstants, Serializable
         this.configTbl.put(View, "");
         this.configTbl.put(Symmetry, "");
         this.configTbl.put(Spec, "");
-        temp = new Vect();
+        temp = new Vect<>();
         this.configTbl.put(Prop, temp);
         this.configTbl.put(Props, temp);
         this.configTbl.put(Type, "");
         this.configTbl.put(TypeConstraint, "");
-
-        this.modConstants = new Hashtable();
-        this.modOverrides = new Hashtable();
-        this.overrides = new Hashtable();
+        this.configTbl.put(CheckDeadlock, "undef");
+        
+        this.modConstants = new Hashtable<>();
+        this.modOverrides = new Hashtable<>();
+        this.overrides = new Hashtable<>();
+        this.overridesReverseMap = new Hashtable<>();
     }
 
     /**
@@ -155,7 +171,7 @@ public class ModelConfig implements ValueConstants, Serializable
                     String old = (String) this.configTbl.put(Init, tt.image);
                     if (old.length() != 0)
                     {
-                        throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), Spec });
+                        throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), Init });
                     }
                     tt = getNextToken(tmgr);
                 } else if (tval.equals(Next))
@@ -306,7 +322,9 @@ public class ModelConfig implements ValueConstants, Serializable
                                     throw new ConfigFileException(EC.CFG_EXPECT_ID, new String[] {
                                             String.valueOf(scs.getBeginLine()), "<-" });
                                 }
-                                this.overrides.put(line.elementAt(0), tt.image);
+                                final String string = (String)line.elementAt(0);
+                                this.overrides.put(string, tt.image);
+                                this.overridesReverseMap.put(tt.image, string);
                             }
                         } else
                         {
@@ -315,7 +333,7 @@ public class ModelConfig implements ValueConstants, Serializable
                                 while (true)
                                 {
                                     tt = getNextToken(tmgr);
-                                    Value arg = this.parseValue(tt, scs, tmgr);
+                                    IValue arg = this.parseValue(tt, scs, tmgr);
                                     line.addElement(arg);
                                     tt = getNextToken(tmgr);
                                     if (!tt.image.equals(","))
@@ -398,6 +416,26 @@ public class ModelConfig implements ValueConstants, Serializable
                             break;
                         actionConstraints.addElement(tt.image);
                     }
+                } else if (tval.equals(CheckDeadlock)) {
+                    tt = getNextToken(tmgr);
+                    if (tt.kind == TLAplusParserConstants.EOF)
+                    {
+                        throw new ConfigFileException(EC.CFG_MISSING_ID, new String[] { String.valueOf(loc), CheckDeadlock });
+                    }
+                    Object previous;
+                    if (tt.image.equals("TRUE")) {
+                        previous = this.configTbl.put(CheckDeadlock, true);
+                    } else if (tt.image.equals("FALSE")) {
+                        previous = this.configTbl.put(CheckDeadlock, false);
+                    } else {
+                        throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] {
+                            String.valueOf(scs.getBeginLine()), "TRUE or FALSE" });
+                    }
+                    if (previous != "undef")
+                    {
+                        throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), CheckDeadlock });
+                    }
+                    tt = getNextToken(tmgr);
                 } else
                 {
                     throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] {
@@ -426,10 +464,10 @@ public class ModelConfig implements ValueConstants, Serializable
             return new StringValue(tval.substring(1, tval.length() - 1));
         } else if (tt.image.equals("TRUE"))
         {
-            return ValTrue;
+            return BoolValue.ValTrue;
         } else if (tt.image.equals("FALSE"))
         {
-            return ValFalse;
+            return BoolValue.ValFalse;
         } else if (tt.image.equals("{"))
         {
             ValueVec elems = new ValueVec();
@@ -438,7 +476,7 @@ public class ModelConfig implements ValueConstants, Serializable
             {
                 while (true)
                 {
-                    Value elem = this.parseValue(tt, scs, tmgr);
+                	Value elem = this.parseValue(tt, scs, tmgr);
                     elems.addElement(elem);
                     tt = getNextToken(tmgr);
                     if (!tt.image.equals(","))
@@ -488,10 +526,14 @@ public class ModelConfig implements ValueConstants, Serializable
         return this.modConstants;
     }
 
-    public synchronized final Hashtable getOverrides()
+    public synchronized final Hashtable<String, String> getOverrides()
     {
         return this.overrides;
     }
+    
+    public synchronized final String getOverridenSpecNameForConfigName(final String configName) {
+    	return this.overridesReverseMap.get(configName);
+    }
 
     public synchronized final Hashtable getModOverrides()
     {
@@ -522,6 +564,12 @@ public class ModelConfig implements ValueConstants, Serializable
     {
         return (String) this.configTbl.get(View);
     }
+    
+    public synchronized final boolean configDefinesSpecification() {
+    	final String spec = getSpec();
+    	
+    	return ((spec != null) && (spec.trim().length() > 0));
+    }
 
     public synchronized final String getSymmetry()
     {
@@ -553,6 +601,15 @@ public class ModelConfig implements ValueConstants, Serializable
         return (String) this.configTbl.get(TypeConstraint);
     }
 
+    public synchronized final boolean getCheckDeadlock()
+    {
+    	Object object = this.configTbl.get(CheckDeadlock);
+    	if (object instanceof Boolean) {
+    		return (boolean) object;
+    	}
+    	return true;
+    }
+
     /**
      * Testing method of the parser
      * @param args
diff --git a/tlatools/src/tlc2/tool/impl/OpDefEvaluator.java b/tlatools/src/tlc2/tool/impl/OpDefEvaluator.java
new file mode 100644
index 0000000000000000000000000000000000000000..38d56f884fe143ca604322d6f3ff849528e84009
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/OpDefEvaluator.java
@@ -0,0 +1,11 @@
+package tlc2.tool.impl;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.IValue;
+
+public interface OpDefEvaluator {
+	IValue eval(SemanticNode body, Context empty, TLCState empty2, CostModel doNotRecord);
+}
diff --git a/tlatools/src/tlc2/tool/impl/Spec.java b/tlatools/src/tlc2/tool/impl/Spec.java
new file mode 100644
index 0000000000000000000000000000000000000000..69558c2a53c220631d5ca78a51c39e9860f11373
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/Spec.java
@@ -0,0 +1,644 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 19 May 2008 at  1:13:48 PST by lamport
+//      modified on Fri Aug 24 14:43:24 PDT 2001 by yuanyu
+
+package tlc2.tool.impl;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import tla2sany.modanalyzer.ParseUnit;
+import tla2sany.semantic.APSubstInNode;
+import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.ExprOrOpArgNode;
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.FrontEnd;
+import tla2sany.semantic.LabelNode;
+import tla2sany.semantic.LetInNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.Subst;
+import tla2sany.semantic.SubstInNode;
+import tla2sany.semantic.SymbolNode;
+import tla2sany.semantic.ThmOrAssumpDefNode;
+import tlc2.TLCGlobals;
+import tlc2.output.EC;
+import tlc2.tool.Action;
+import tlc2.tool.BuiltInOPs;
+import tlc2.tool.Defns;
+import tlc2.tool.TLCState;
+import tlc2.tool.ToolGlobals;
+import tlc2.util.Context;
+import tlc2.util.ObjLongTable;
+import tlc2.util.Vect;
+import tlc2.value.ValueConstants;
+import tlc2.value.impl.LazyValue;
+import tlc2.value.impl.ModelValue;
+import util.Assert;
+import util.FilenameToStream;
+import util.TLAConstants;
+import util.UniqueString;
+
+// Note that we use all of the {@code default} defined functionality in our
+//		implemented interface {@link SymbolNodeValueLookupProvider} (and our
+//		lack of implementation of {@link OpDefEvaluator} is why this class
+//		is marked {@code abstract}.)
+abstract class Spec
+		implements ValueConstants, ToolGlobals, Serializable, OpDefEvaluator, SymbolNodeValueLookupProvider {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * @see See note on performance in CostModelCreator.
+	 */
+	protected static final boolean coverage = TLCGlobals.isCoverageEnabled();
+
+	protected static final int toolId = FrontEnd.getToolId();
+	
+	protected final String specDir; // The spec directory.
+    protected final String rootFile; // The root file of this spec.
+    protected final String configFile; // The model config file.
+    protected final Set<OpDefNode> processedDefs ; 
+      // The set of OpDefNodes on which processSpec has been called.
+      // Added by LL & YY on 25 June 2014 to eliminate infinite
+      // loop when a recursively defined operator is used as an
+      // operator argument in its own definition.
+    protected final Defns defns; // Global definitions reachable from root
+	protected final Defns unprocessedDefns;
+    protected final TLAClass tlaClass; // TLA built-in classes.
+    private final FilenameToStream resolver; // takes care of path to stream resolution
+
+    protected final ModelConfig config; // The model configuration.
+    private final SpecProcessor specProcessor;
+
+    // SZ Feb 20, 2009: added support to name resolver, to be able to run outside of the tool
+	public Spec(final String specDir, final String specFile, final String configFile, final FilenameToStream resolver) {
+        this.specDir = specDir;
+        this.rootFile = specFile;
+        this.defns = new Defns();
+        this.tlaClass = new TLAClass("tlc2.module", resolver);
+        this.processedDefs = new HashSet<OpDefNode>();
+        this.resolver = resolver;
+        
+        // SZ Mar 9, 2009: added initialization of the modelValue class
+        ModelValue.init();
+        this.configFile = configFile;
+        this.config = new ModelConfig(configFile + TLAConstants.Files.CONFIG_EXTENSION, resolver);
+        this.config.parse();
+        ModelValue.setValues(); // called after seeing all model values
+
+        specProcessor = new SpecProcessor(getRootName(), resolver, toolId, defns, config, this, this, tlaClass);
+        
+        this.unprocessedDefns = specProcessor.getUnprocessedDefns();
+    }
+    
+    protected Spec(final Spec other) {
+    	this.specDir = other.specDir;
+    	this.rootFile = other.rootFile;
+    	this.configFile = other.configFile;
+    	this.processedDefs = other.processedDefs;
+    	this.defns = other.defns;
+    	this.tlaClass = other.tlaClass;
+        this.resolver = other.resolver;
+        this.unprocessedDefns = other.unprocessedDefns;
+    	this.config = other.config;
+        this.specProcessor = other.specProcessor;
+    }
+    
+    public ModelConfig getModelConfig() {
+    	return config;
+    }
+    
+    public SpecProcessor getSpecProcessor() {
+    	return specProcessor;
+    }
+
+    /* Return the variable if expr is a primed state variable. Otherwise, null. */
+    public final SymbolNode getPrimedVar(SemanticNode expr, Context c, boolean cutoff)
+    {
+        if (expr instanceof OpApplNode)
+        {
+            OpApplNode expr1 = (OpApplNode) expr;
+            SymbolNode opNode = expr1.getOperator();
+
+            if (BuiltInOPs.getOpCode(opNode.getName()) == OPCODE_prime)
+            {
+                return this.getVar(expr1.getArgs()[0], c, cutoff, toolId);
+            }
+
+            if (opNode.getArity() == 0)
+            {
+                boolean isVarDecl = (opNode.getKind() == VariableDeclKind);
+                Object val = this.lookup(opNode, c, cutoff && isVarDecl, toolId);
+
+                if (val instanceof LazyValue)
+                {
+                    LazyValue lval = (LazyValue) val;
+                    return this.getPrimedVar(lval.expr, lval.con, cutoff);
+                }
+                if (val instanceof OpDefNode)
+                {
+                    return this.getPrimedVar(((OpDefNode) val).getBody(), c, cutoff);
+                }
+            }
+        }
+        return null;
+    }
+
+    /** 
+     * Get model constraints.  
+     */
+	public final ExprNode[] getModelConstraints() {
+		return specProcessor.getModelConstraints();
+	}
+
+    /**
+     * Get action constraints.  
+     */
+	public final ExprNode[] getActionConstraints() {
+		return specProcessor.getActionConstraints();
+	}
+
+    /* Get the initial state predicate of the specification.  */
+	public final Vect<Action> getInitStateSpec() {
+		return specProcessor.getInitPred();
+	}
+
+    /* Get the action (next state) predicate of the specification. */
+	public final Action getNextStateSpec() {
+		return specProcessor.getNextPred();
+	}
+
+    /** 
+     * Get the view mapping for the specification. 
+     */
+    public final SemanticNode getViewSpec()
+    {
+        String name = this.config.getView();
+        if (name.length() == 0)
+            return null;
+
+        Object view = this.defns.get(name);
+        if (view == null)
+        {
+            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "view function", name });
+        }
+        if (!(view instanceof OpDefNode))
+        {
+            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "view function", name });
+        }
+        OpDefNode def = (OpDefNode) view;
+        if (def.getArity() != 0)
+        {
+            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "view function", name });
+        }
+        return def.getBody();
+    }
+
+    /* Get the type declaration for the state variables. */
+    public final SemanticNode getTypeSpec()
+    {
+        String name = this.config.getType();
+        if (name.length() == 0)
+        {
+            Assert.fail(EC.TLC_CONFIG_NO_STATE_TYPE);
+        }
+
+        Object type = this.defns.get(name);
+        if (type == null)
+        {
+            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type", name });
+        }
+        if (!(type instanceof OpDefNode))
+        {
+            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type", name });
+        }
+        OpDefNode def = (OpDefNode) type;
+        if (def.getArity() != 0)
+        {
+            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type", name });
+        }
+        return def.getBody();
+    }
+
+    /* Get the type declaration for the state variables. */
+    public final SemanticNode getTypeConstraintSpec()
+    {
+        String name = this.config.getTypeConstraint();
+        if (name.length() == 0)
+        {
+            return null;
+        }
+
+        Object type = this.defns.get(name);
+        if (type == null)
+        {
+            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type constraint", name });
+        }
+        if (!(type instanceof OpDefNode))
+        {
+            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type constraint", name });
+        }
+        OpDefNode def = (OpDefNode) type;
+        if (def.getArity() != 0)
+        {
+            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type constraint", name });
+
+        }
+        return def.getBody();
+    }
+
+	public final boolean livenessIsTrue() {
+		return getImpliedTemporals().length == 0;
+	}
+
+    /* Get the fairness condition of the specification.  */
+    public final Action[] getTemporals()
+    {
+        return specProcessor.getTemporal();
+    }
+
+    public final String[] getTemporalNames()
+    {
+        return specProcessor.getTemporalNames();
+    }
+
+    /* Get the liveness checks of the specification.  */
+	public final Action[] getImpliedTemporals() {
+		return specProcessor.getImpliedTemporals();
+	}
+
+	public final String[] getImpliedTemporalNames() {
+		return specProcessor.getImpliedTemporalNames();
+	}
+
+    /* Get the invariants of the specification. */
+	public final Action[] getInvariants() {
+		return specProcessor.getInvariants();
+	}
+
+	public final String[] getInvNames() {
+		return specProcessor.getInvariantsNames();
+	}
+
+    /* Get the implied-inits of the specification. */
+	public final Action[] getImpliedInits() {
+		return specProcessor.getImpliedInits();
+	}
+
+	public final String[] getImpliedInitNames() {
+		return specProcessor.getImpliedInitNames();
+	}
+
+    /* Get the implied-actions of the specification. */
+	public final Action[] getImpliedActions() {
+		return specProcessor.getImpliedActions();
+	}
+
+	public final String[] getImpliedActNames() {
+		return specProcessor.getImpliedActionNames();
+	}
+
+    /* Get the assumptions of the specification. */
+	public final ExprNode[] getAssumptions() {
+		return specProcessor.getAssumptions();
+	}
+    
+    /* Get the assumptionIsAxiom field */
+    public final boolean[] getAssumptionIsAxiom() {
+        return specProcessor.getAssumptionIsAxiom();
+    }
+    
+    /**
+     * This method gets the value of a symbol from the environment. We
+     * look up in the context c, its tool object, and the state s.
+     * 
+     * It and the lookup method that follows it were modified by LL
+     * on 10 April 2011 to fix the following bug.  When a constant definition
+     *    Foo == ...
+     * is overridden to substitute Bar for Foo, the TLC tool object for
+     * the body of Foo's OpDef node is set to the OpDefNode for Bar.
+     * When evaluating a use of Foo, the lookup method is apparently
+     * supposed to return the OpDefNode for Bar.  (I don't understand
+     * how the callers make use of the returned value.) That's what it
+     * does for uses of Foo in the module in which Foo is defined.
+     * However, if Foo is imported by instantiation with renaming as 
+     * X!Foo, then it appears that looking up X!Foo should also return 
+     * the OpDefNode for Bar.  If the instantiated module had no
+     * parameters, then that's what happened because the body of the
+     * OpDefNode for X!Foo is the same (contains a pointer to the
+     * same object) as the body of Foo's OpDefNode.  However, that
+     * wasn't the case if the instantiated module had parameters,
+     * because then X!Foo's OpDefNode consists of a sequence of
+     * nested SubstInNode objects, the last of which points to
+     * the body of Foo's OpDefNode.  So, LL modified the lookup
+     * methods so they follow the sequence of SubstInNode bodies
+     * down to the body of Foo's OpDefNode when looking up the result.  
+     * (If a SubstInNode has a non-null TLC tool object for a
+     * SubstInNode, then it returns that object.  I don't think this 
+     * should ever be the case, and if it is, I have no idea what the
+     * lookup method should do.)
+     * 
+     */
+    public final Object lookup(SymbolNode opNode, Context c, TLCState s, boolean cutoff)
+    {
+        Object result = lookup(opNode, c, cutoff, toolId);
+        if (result != opNode) {
+            return result;
+        }
+
+		// CalvinL/LL/MAK 02/2021: Added conditional as part of Github issue #362 Name
+		// clash between variable in refined spec and operator in instantiated spec. See
+		// releated test in Github362.java.
+        if (opNode.getKind() != UserDefinedOpKind) {
+			result = s.lookup(opNode.getName());
+			if (result != null) {
+				return result;
+			}
+		}
+
+        return opNode;
+    }
+
+    public final Object lookup(final SymbolNode opNode)
+    {
+        return lookup(opNode, Context.Empty, false, toolId);
+    }
+
+    /**
+     * The following added by LL on 23 October 2012 to fix bug in evaluation of names of theorems and 
+     * assumptions imported by parameterized instantiation.
+     *  
+     * @param opDef
+     * @param args
+     * @param c
+     * @param cachable
+     * @return
+     */
+    public final Context getOpContext(ThmOrAssumpDefNode opDef, ExprOrOpArgNode[] args, Context c, boolean cachable)
+    {
+        FormalParamNode[] formals = opDef.getParams();
+        int alen = args.length;
+        Context c1 = c;
+        for (int i = 0; i < alen; i++)
+        {
+            Object aval = this.getVal(args[i], c, cachable, toolId);
+            c1 = c1.cons(formals[i], aval);
+        }
+        return c1;
+    }
+    
+    /**
+     * Return a table containing the locations of subexpression in the
+     * spec of forms x' = e and x' \in e. Warning: Current implementation
+     * may not be able to find all such locations.
+     */
+    public final ObjLongTable<SemanticNode> getPrimedLocs()
+    {
+        final ObjLongTable<SemanticNode> tbl = new ObjLongTable<SemanticNode>(10);
+        final Action act = this.getNextStateSpec();
+		if (act == null) {
+			// MAK 10/17/2018: If spec defines no next-state action (see e.g.
+			// tlc2.tool.ASTest) and this method is called before ModelChecker checks
+			// actions (search for tlc2.output.EC.TLC_STATES_AND_NO_NEXT_ACTION) this will
+			// NPE.
+			return tbl;
+		}
+        this.collectPrimedLocs(act.pred, act.con, tbl);
+        return tbl;
+    }
+
+    public final void collectPrimedLocs(SemanticNode pred, Context c, ObjLongTable<SemanticNode> tbl)
+    {
+        switch (pred.getKind()) {
+        case OpApplKind: {
+            OpApplNode pred1 = (OpApplNode) pred;
+            this.collectPrimedLocsAppl(pred1, c, tbl);
+            return;
+        }
+        case LetInKind: {
+            LetInNode pred1 = (LetInNode) pred;
+            this.collectPrimedLocs(pred1.getBody(), c, tbl);
+            return;
+        }
+        case SubstInKind: {
+            SubstInNode pred1 = (SubstInNode) pred;
+            Subst[] subs = pred1.getSubsts();
+            Context c1 = c;
+            for (int i = 0; i < subs.length; i++)
+            {
+                Subst sub = subs[i];
+                c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, toolId));
+            }
+            this.collectPrimedLocs(pred1.getBody(), c, tbl);
+            return;
+        }
+
+        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+        case APSubstInKind: {
+            APSubstInNode pred1 = (APSubstInNode) pred;
+            Subst[] subs = pred1.getSubsts();
+            Context c1 = c;
+            for (int i = 0; i < subs.length; i++)
+            {
+                Subst sub = subs[i];
+                c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, toolId));
+            }
+            this.collectPrimedLocs(pred1.getBody(), c, tbl);
+            return;
+        }
+
+
+            /***********************************************************************
+            * LabelKind case added by LL on 13 Jun 2007.                           *
+            ***********************************************************************/
+        case LabelKind: {
+            LabelNode pred1 = (LabelNode) pred;
+            this.collectPrimedLocs(pred1.getBody(), c, tbl);
+            return;
+        }
+        }
+    }
+
+    private final void collectPrimedLocsAppl(OpApplNode pred, Context c, ObjLongTable<SemanticNode> tbl)
+    {
+        ExprOrOpArgNode[] args = pred.getArgs();
+        SymbolNode opNode = pred.getOperator();
+        int opcode = BuiltInOPs.getOpCode(opNode.getName());
+
+        switch (opcode) {
+        case OPCODE_fa: // FcnApply
+        {
+            this.collectPrimedLocs(args[0], c, tbl);
+            break;
+        }
+        case OPCODE_ite: // IfThenElse
+        {
+            this.collectPrimedLocs(args[1], c, tbl);
+            this.collectPrimedLocs(args[2], c, tbl);
+            break;
+        }
+        case OPCODE_case: // Case
+        {
+            for (int i = 0; i < args.length; i++)
+            {
+                OpApplNode pair = (OpApplNode) args[i];
+                this.collectPrimedLocs(pair.getArgs()[1], c, tbl);
+            }
+            break;
+        }
+        case OPCODE_eq:   // x' = 42
+        case OPCODE_in: { // x' \in S (eq case "falls through")
+            SymbolNode var = this.getPrimedVar(args[0], c, false);
+            if (var != null && var.getName().getVarLoc() != -1)
+            {
+                tbl.put(pred, 0);
+            }
+            break;
+        }
+        case OPCODE_cl: // ConjList
+        case OPCODE_dl: // DisjList
+        case OPCODE_be: // BoundedExists
+        case OPCODE_bf: // BoundedForall
+        case OPCODE_land:
+        case OPCODE_lor:
+        case OPCODE_implies:
+        case OPCODE_nop: // This case added 13 Nov 2009 by LL to handle subexpression names.
+          {
+            for (int i = 0; i < args.length; i++)
+            {
+                this.collectPrimedLocs(args[i], c, tbl);
+            }
+            break;
+        }
+        case OPCODE_unchanged: {
+            this.collectUnchangedLocs(args[0], c, tbl);
+            break;
+        }
+        case OPCODE_aa: // AngleAct <A>_e
+        {
+            this.collectPrimedLocs(args[0], c, tbl);
+            break;
+        }
+        case OPCODE_sa: // [A]_e
+        {
+            this.collectPrimedLocs(args[0], c, tbl);
+            tbl.put(args[1], 0);
+            break;
+        }
+        default: {
+            if (opcode == 0)
+            {
+                Object val = this.lookup(opNode, c, false, toolId);
+
+                if (val instanceof OpDefNode)
+                {
+                    OpDefNode opDef = (OpDefNode) val;
+                    // Following added by LL on 10 Apr 2010 to avoid infinite 
+                    // recursion for recursive operator definitions
+                    if (opDef.getInRecursive()) {
+                        return ;
+                    }
+                    Context c1 = this.getOpContext(opDef, args, c, true, toolId);
+                    this.collectPrimedLocs(opDef.getBody(), c1, tbl);
+                } else if (val instanceof LazyValue)
+                {
+                    LazyValue lv = (LazyValue) val;
+                    this.collectPrimedLocs(lv.expr, lv.con, tbl);
+                }
+            }
+        }
+        }
+    }
+
+	private final void collectUnchangedLocs(final SemanticNode expr, final Context c,
+			final ObjLongTable<SemanticNode> tbl) {
+        if (expr instanceof OpApplNode)
+        {
+            OpApplNode expr1 = (OpApplNode) expr;
+            SymbolNode opNode = expr1.getOperator();
+            UniqueString opName = opNode.getName();
+            int opcode = BuiltInOPs.getOpCode(opName);
+
+            if (opName.getVarLoc() >= 0)
+            {
+                // a state variable:
+                tbl.put(expr, 0);
+                return;
+            }
+
+            ExprOrOpArgNode[] args = expr1.getArgs();
+            if (opcode == OPCODE_tup)
+            {
+				// a tuple, might be:
+            	// UNCHANGED <<x,y,z>>
+            	// or:
+            	// vars == <<x,y,z>>
+            	// ...
+            	// UNCHANGED vars
+				// For the latter, we don't want vars == <<x,y,z>> to show up, but the vars in
+				// UNCHANGED vars (see CoverageStatisticsTest).
+                for (int i = 0; i < args.length; i++)
+                {
+               		this.collectUnchangedLocs(args[i], c, tbl);
+                }
+                return;
+            }
+
+            if (opcode == 0 && args.length == 0)
+            {
+                // a 0-arity operator:
+                Object val = this.lookup(opNode, c, false, toolId);
+                if (val instanceof OpDefNode)
+                {
+                    this.collectUnchangedLocs(((OpDefNode) val).getBody(), c, tbl);
+                    return;
+                }
+            }
+        }
+        return;
+    }
+
+    public FilenameToStream getResolver()
+    {
+        return resolver;
+    }
+    
+    public String getRootName() {
+    	return new File(this.rootFile).getName();
+    }
+    
+    public String getRootFile() {
+    	return this.rootFile;
+    }
+
+    public String getConfigFile() {
+    	return this.configFile;
+    }
+    
+    public String getSpecDir() {
+    	return this.specDir;
+    }
+    
+    public int getId() {
+    	return toolId;
+    }
+
+	public List<File> getModuleFiles(final FilenameToStream resolver) {
+		final List<File> result = new ArrayList<File>();
+	
+		final Enumeration<ParseUnit> parseUnitContext = specProcessor.getSpecObj().parseUnitContext.elements();
+		while (parseUnitContext.hasMoreElements()) {
+			ParseUnit pu = (ParseUnit) parseUnitContext.nextElement();
+			File resolve = resolver.resolve(pu.getFileName(), false);
+			result.add(resolve);
+		}
+		return result;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/Spec.java b/tlatools/src/tlc2/tool/impl/SpecProcessor.java
similarity index 53%
rename from tlatools/src/tlc2/tool/Spec.java
rename to tlatools/src/tlc2/tool/impl/SpecProcessor.java
index 1094f2586c269d9947ab6c304b36071706ae412d..172150f14d0aec289b870a10d5dfc69ec3a1d1ec 100644
--- a/tlatools/src/tlc2/tool/Spec.java
+++ b/tlatools/src/tlc2/tool/impl/SpecProcessor.java
@@ -1,30 +1,53 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 19 May 2008 at  1:13:48 PST by lamport
-//      modified on Fri Aug 24 14:43:24 PDT 2001 by yuanyu
-
-package tlc2.tool;
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.impl;
 
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Arrays;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 
 import tla2sany.drivers.FrontEndException;
 import tla2sany.drivers.SANY;
 import tla2sany.modanalyzer.SpecObj;
+import tla2sany.semantic.APSubstInNode;
 import tla2sany.semantic.AssumeNode;
 import tla2sany.semantic.DecimalNode;
 import tla2sany.semantic.ExprNode;
 import tla2sany.semantic.ExprOrOpArgNode;
 import tla2sany.semantic.ExternalModuleTable;
-import tla2sany.semantic.FormalParamNode;
 import tla2sany.semantic.LabelNode;
 import tla2sany.semantic.LetInNode;
-import tla2sany.semantic.LevelNode;
 import tla2sany.semantic.ModuleNode;
 import tla2sany.semantic.NumeralNode;
 import tla2sany.semantic.OpApplNode;
@@ -35,168 +58,310 @@ import tla2sany.semantic.SemanticNode;
 import tla2sany.semantic.StringNode;
 import tla2sany.semantic.Subst;
 import tla2sany.semantic.SubstInNode;
-import tla2sany.semantic.APSubstInNode;
 import tla2sany.semantic.SymbolNode;
 import tla2sany.semantic.TheoremNode;
-import tla2sany.semantic.ThmOrAssumpDefNode;
 import tlc2.TLCGlobals;
+import tlc2.module.BuiltInModuleHelper;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.overrides.Evaluation;
+import tlc2.overrides.ITLCOverrides;
+import tlc2.overrides.TLAPlusCallable;
+import tlc2.overrides.TLAPlusOperator;
+import tlc2.tool.Action;
+import tlc2.tool.BuiltInOPs;
+import tlc2.tool.Defns;
+import tlc2.tool.EvalException;
+import tlc2.tool.Specs;
+import tlc2.tool.TLAPlusExecutorState;
+import tlc2.tool.TLCStateMut;
+import tlc2.tool.ToolGlobals;
 import tlc2.util.Context;
 import tlc2.util.List;
-import tlc2.util.ObjLongTable;
 import tlc2.util.Vect;
-import tlc2.value.BoolValue;
-import tlc2.value.IntValue;
-import tlc2.value.LazyValue;
-import tlc2.value.MethodValue;
-import tlc2.value.ModelValue;
-import tlc2.value.OpRcdValue;
-import tlc2.value.SetEnumValue;
-import tlc2.value.StringValue;
-import tlc2.value.Value;
+import tlc2.value.IBoolValue;
+import tlc2.value.IValue;
 import tlc2.value.ValueConstants;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.CallableValue;
+import tlc2.value.impl.EvaluatingValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.LazyValue;
+import tlc2.value.impl.MethodValue;
+import tlc2.value.impl.OpRcdValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.Value;
 import util.Assert;
 import util.FilenameToStream;
 import util.ToolIO;
 import util.UniqueString;
 
-public class Spec implements ValueConstants, ToolGlobals, Serializable
-{
-
-    public String specDir; // The spec directory.
-    public String rootFile; // The root file of this spec.
-    protected String configFile; // The model config file.
-    protected ModelConfig config; // The model configuration.
-    protected ExternalModuleTable moduleTbl; // The external modules reachable from root
-    protected ModuleNode rootModule; // The root module.
-    protected HashSet processedDefs ; 
-      // The set of OpDefNodes on which processSpec has been called.
-      // Added by LL & YY on 25 June 2014 to eliminate infinite
-      // loop when a recursively defined operator is used as an
-      // operator argument in its own definition.
-    protected Defns defns; // Global definitions reachable from root
-    // SZ 10.04.2009: changed the name of the variable to reflect its nature
-    public OpDeclNode[] variablesNodes; // The state variables.
-    protected TLAClass tlaClass; // TLA built-in classes.
-    protected Vect initPredVec; // The initial state predicate.
-    protected Action nextPred; // The next state predicate.
-    protected Action[] temporals; // Fairness specifications...
-    protected String[] temporalNames; // ... and their names
-    protected Action[] impliedTemporals; // Liveness conds to check...
-    protected String[] impliedTemporalNames; // ... and their names
-    protected Action[] invariants; // Invariants to be checked...
-    protected String[] invNames; // ... and their names
-    protected Action[] impliedInits; // Implied-inits to be checked...
-    protected String[] impliedInitNames; // ... and their names
-    protected Action[] impliedActions; // Implied-actions to be checked...
-    protected String[] impliedActNames; // ... and their names
-    protected ExprNode[] modelConstraints; // Model constraints
-    protected ExprNode[] actionConstraints; // Action constraints
-    protected ExprNode[] assumptions; // Assumptions
-    protected boolean[] assumptionIsAxiom; // assumptionIsAxiom[i] is true iff assumptions[i]
+public class SpecProcessor implements ValueConstants, ToolGlobals {
+	
+    private final String rootFile; // The root file of this spec.
+    private final FilenameToStream resolver; // takes care of path to stream resolution
+    private final int toolId;
+    private final Defns defns; // Global definitions reachable from root
+    private final ModelConfig config; // The model configuration.
+    private final OpDefEvaluator opDefEvaluator;
+    private final SymbolNodeValueLookupProvider symbolNodeValueLookupProvider;
+    private final TLAClass tlaClass;
+
+    private OpDeclNode[] variablesNodes; // The state variables.
+    private ExternalModuleTable moduleTbl; // The external modules reachable from root
+    private ModuleNode rootModule; // The root module.
+    private Set<OpDefNode> processedDefs;
+    private SpecObj specObj;
+    private Defns snapshot;
+
+    private Vect<Action> initPredVec; // The initial state predicate.
+    private Action nextPred; // The next state predicate.
+    private Action[] temporals; // Fairness specifications...
+    private String[] temporalNames; // ... and their names
+    private Action[] impliedTemporals; // Liveness conds to check...
+    private String[] impliedTemporalNames; // ... and their names
+    private Action[] invariants; // Invariants to be checked...
+    private String[] invNames; // ... and their names
+    private Action[] impliedInits; // Implied-inits to be checked...
+    private String[] impliedInitNames; // ... and their names
+    private Action[] impliedActions; // Implied-actions to be checked...
+    private String[] impliedActNames; // ... and their names
+    private ExprNode[] modelConstraints; // Model constraints
+    private ExprNode[] actionConstraints; // Action constraints
+    private ExprNode[] assumptions; // Assumpt	ions
+    private boolean[] assumptionIsAxiom; // assumptionIsAxiom[i] is true iff assumptions[i]
                                            // is an AXIOM.  Added 26 May 2010 by LL
-    private FilenameToStream resolver; // takes car of path to stream resoltion
+    
+    private Vect<Action> invVec = new Vect<>();
+    private Vect<String> invNameVec = new Vect<>();
+    private Vect<Action> impliedInitVec = new Vect<>();
+    private Vect<String> impliedInitNameVec = new Vect<>();
+    private Vect<Action> impliedActionVec = new Vect<>();
+    private Vect<String> impliedActNameVec = new Vect<>();
+    private Vect<Action> temporalVec = new Vect<>();
+    private Vect<String> temporalNameVec = new Vect<>();
+    private Vect<Action> impliedTemporalVec = new Vect<>();
+    private Vect<String> impliedTemporalNameVec = new Vect<>();
+    
+	public SpecProcessor(final String rootFile, final FilenameToStream resolver, final int toolId, final Defns defns,
+			final ModelConfig config, final SymbolNodeValueLookupProvider snvlp, final OpDefEvaluator ode,
+			final TLAClass tlaClass) {
+		super();
+		this.rootFile = rootFile;
+		this.resolver = resolver;
+		this.toolId = toolId;
+		this.defns = defns;
+		this.config = config;
+		this.tlaClass = tlaClass;
+		this.processedDefs = new HashSet<OpDefNode>();
+        this.initPredVec = new Vect<>(5);
+        
+        opDefEvaluator = ode;
+        symbolNodeValueLookupProvider = snvlp;
 
-    public Spec(String specDir, String file, FilenameToStream resolver)
-    {
-        this.processedDefs = new HashSet();
-        this.specDir = specDir;
-        this.rootFile = file;
-        this.rootModule = null;
-        this.config = null;
-        this.moduleTbl = null;
-        this.variablesNodes = null;
-        this.defns = new Defns();
-        this.tlaClass = new TLAClass("tlc2.module");
-        this.initPredVec = new Vect(5);
-        this.nextPred = null;
-        this.temporals = null;
-        this.temporalNames = null;
-        this.impliedTemporals = null;
-        this.impliedTemporalNames = null;
-        this.invariants = null;
-        this.invNames = null;
-        this.impliedInits = null;
-        this.impliedInitNames = null;
-        this.impliedActions = null;
-        this.impliedActNames = null;
-        this.modelConstraints = null;
-        this.actionConstraints = null;
-        this.assumptions = null;
-        this.assumptionIsAxiom = null;  // added 26 May 2010 by LL
-        this.resolver = resolver;
+		// Parse and process this spec.
+		// It takes care of all overrides.
+		processSpec();
+
+		snapshot = defns.snapshot();
+
+		if (opDefEvaluator != null) {
+			// Pre-evaluate all the definitions in the spec that are constants.
+			processConstantDefns();
+		}
+
+	      // Finally, process the config file.
+		processConfig();
+	}
+
+    /**
+     * This method converts every definition that is constant into TLC
+     * value. By doing this, TLC avoids evaluating the same expression
+     * multiple times.
+     *
+     * The method runs for every module in the module tables.
+     *
+     * Modified by LL on 23 July 2013 so it is not run for modules that are
+     * instantiated and have parameters (CONSTANT or VARIABLE declarations)
+     */
+    private void processConstantDefns() {
+        ModuleNode[] mods = this.moduleTbl.getModuleNodes();
+        for (int i = 0; i < mods.length; i++) {
+          if (   (! mods[i].isInstantiated())
+            || (   (mods[i].getConstantDecls().length == 0)
+              && (mods[i].getVariableDecls().length == 0) ) ) {
+              this.processConstantDefns(mods[i]);
+          }
+        }
     }
 
-    // SZ Feb 20, 2009: added support to name resolver, to be able to run outside of the tool
-    public Spec(String specDir, String specFile, String configFile, FilenameToStream resolver)
-    {
-        this(specDir, specFile, resolver);
-        // SZ Mar 9, 2009: added initialization of the modelValue class
-        ModelValue.init();
-        this.configFile = configFile;
-        this.config = new ModelConfig(configFile + ".cfg", resolver);
-        this.config.parse();
-        ModelValue.setValues(); // called after seeing all model values
+    /**
+     * Converts the constant definitions in the corresponding value for the
+     * module -- that is, it "converts" (which seems to mean calling deepNormalize)
+     * the values substituted for the declared constants.  On 17 Mar 2012 it was
+     * modified by LL to evaluate the OpDefNode when a defined operator is substituted
+     * for an ordinary declared constant (not a declared operator constant).  Without this
+     * evaluation, the definition gets re-evaluated every time TLC evaluates the declared
+     * constant.  LL also added a check that an operator substituted for the declared
+     * constant also has the correct arity.
+     *
+     * @param mod the module to run on
+     */
+    private void processConstantDefns(ModuleNode mod) {
+
+      // run for constant definitions
+      OpDeclNode[] consts = mod.getConstantDecls();
+      for (int i = 0; i < consts.length; i++) {
+        Object val = consts[i].getToolObject(toolId);
+        if (val != null && val instanceof IValue) {
+		  // We do not wrap this value in a WorkerValue, because we assume that explicit
+		  // initialization does not pose a problem here. This is based on the observation,
+          // that val is either an atom (IValue#isAtom) or a set (of sets) of atoms (primarily
+          // ModelValues).
+	      ((IValue)val).initialize();
+          // System.err.println(consts[i].getName() + ": " + val);
+        } // The following else clause was added by LL on 17 March 2012.
+        else if (val != null && val instanceof OpDefNode) {
+          OpDefNode opDef = (OpDefNode) val;
+          // The following check logically belongs in Spec.processSpec, but it's not there.
+          // So, LL just added it here.  This error cannot occur when running TLC from
+          // the Toolbox.
+          Assert.check(opDef.getArity() == consts[i].getArity(),
+                       EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS,
+                       new String[] {consts[i].getName().toString(), opDef.getName().toString()});
+
+          if (opDef.getArity() == 0) {
+            try {
+            	Object defVal = WorkerValue.demux(opDefEvaluator, consts[i], opDef);
+                opDef.setToolObject(toolId, defVal);
+            } catch (Assert.TLCRuntimeException | EvalException e) {
+              final String addendum = (e instanceof EvalException) ? "" : (" - specifically: " + e.getMessage());
+              Assert.fail(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT,
+                  new String[] { consts[i].getName().toString(), opDef.getName().toString(), addendum });
+            }
+          }
+        }
+      }
+
+      // run for constant operator definitions
+      OpDefNode[] opDefs = mod.getOpDefs();
+      DEFS: for (int i = 0; i < opDefs.length; i++) {
+        OpDefNode opDef = opDefs[i];
+
+        // The following variable evaluate and its value added by LL on 24 July 2013
+        // to prevent pre-evaluation of a definition from an EXTENDS of a module that
+        // is also instantiated.
+        ModuleNode moduleNode = opDef.getOriginallyDefinedInModuleNode() ;
+        boolean evaluate =    (moduleNode == null)
+                       || (! moduleNode.isInstantiated())
+                       || (   (moduleNode.getConstantDecls().length == 0)
+                           && (moduleNode.getVariableDecls().length == 0) ) ;
+
+        if (evaluate && opDef.getArity() == 0) {
+          Object realDef = symbolNodeValueLookupProvider.lookup(opDef, Context.Empty, false, toolId);
+          if (realDef instanceof OpDefNode) {
+            opDef = (OpDefNode)realDef;
+            if (symbolNodeValueLookupProvider.getLevelBound(opDef.getBody(), Context.Empty, toolId) == 0) {
+              try {
+                UniqueString opName = opDef.getName();
+                if (isVetoed(opName)) {
+                	continue DEFS;
+                }
+                // System.err.println(opName);
+                final Object val = WorkerValue.demux(opDefEvaluator, opDef);
+                // System.err.println(opName + ": " + val);
+                opDef.setToolObject(toolId, val);
+                Object def = this.defns.get(opName);
+                if (def == opDef) {
+                  this.defns.put(opName, val);
+                }
+              }
+              catch (Throwable swallow) {
+				// We get here when Op fails to evaluate. e is swallowed because Op might e.g. be 
+            	// Reals!Infinity from the standard module that has to be redefined iff it appears
+              	// in the actual spec. Another example is TLC!TLCGet(42) that the code above 
+              	// attempts to evaluate that fails with an EvalException. By definition, TLCGet
+              	// is not constant. 
+			  }
+            }
+          }
+        }
+      }
+
+      // run for all inner modules
+      ModuleNode[] imods = mod.getInnerModules();
+      for (int i = 0; i < imods.length; i++) {
+        this.processConstantDefns(imods[i]);
+      }
     }
 
+	public static final String LAZY_CONSTANT_OPERATORS = SpecProcessor.class.getName() + ".vetoed";
+
+	private static final Set<String> vetos = new HashSet<String>(
+			Arrays.asList(System.getProperty(LAZY_CONSTANT_OPERATORS, "")));
+
+	private boolean isVetoed(final UniqueString us) {
+		return vetos.contains(us.toString());
+	}
+
     /**
      * Processes the specification and collects information to be used
      * by tools. The processing tries to use any customized module (Java
      * class) to override the corresponding TLA+ module.
      */
     // SZ Feb 20, 2009: added support for existing specObj
-    protected final SpecObj processSpec(SpecObj spec)
+    private final void processSpec()
     {
 
-        if (spec == null)
-        {
-            // construct new specification object, if the
-            // passed one was null
-            spec = new SpecObj(this.rootFile, resolver);
-
-            // We first call the SANY front-end to parse and semantic-analyze
-            // the complete TLA+ spec starting with the main module rootFile.
-            if (TLCGlobals.tool)
-            {
-                MP.printMessage(EC.TLC_SANY_START);
-            }
-            try
-            {
-                // SZ Feb 20, 2009:
-                // call SANY to parse the module
-                // this method will not throw any exceptions on
-                // checked errors (init, parse, semantic).
-                // Only if something unexpected happens the
-                // exception is thrown
-                SANY.frontEndMain(spec, this.rootFile, ToolIO.out);
-            } catch (FrontEndException e)
-            {
-                Assert.fail(EC.TLC_PARSING_FAILED2, e);
-            }
+        // construct new specification object, if the
+        // passed one was null
+        specObj = new SpecObj(this.rootFile, resolver);
 
-            if (TLCGlobals.tool)
-            {
-                MP.printMessage(EC.TLC_SANY_END);
-            }
-            // The following statement moved here by LL on 11 March 2011
-            MP.printMessage(EC.TLC_STARTING);
+        // We first call the SANY front-end to parse and semantic-analyze
+        // the complete TLA+ spec starting with the main module rootFile.
+        if (TLCGlobals.tool)
+        {
+            MP.printMessage(EC.TLC_SANY_START);
+        }
+        try
+        {
+            // SZ Feb 20, 2009:
+            // call SANY to parse the module
+            // this method will not throw any exceptions on
+            // checked errors (init, parse, semantic).
+            // Only if something unexpected happens the
+            // exception is thrown
+            SANY.frontEndMain(specObj, this.rootFile, ToolIO.out);
+        } catch (FrontEndException e)
+        {
+            Assert.fail(EC.TLC_PARSING_FAILED2, e);
+        }
 
+        if (TLCGlobals.tool)
+        {
+            MP.printMessage(EC.TLC_SANY_END);
         }
+        // The following statement moved here by LL on 11 March 2011
+        MP.printMessage(EC.TLC_STARTING);
 
         // SZ Feb 20, 2009:
         // since failed parsing is not marked by an exception,
         // check the status of the spec
         // check if the specification has been successfully created
-        if (!spec.initErrors.isSuccess() || !spec.parseErrors.isSuccess() || !spec.semanticErrors.isSuccess())
+        if (!specObj.initErrors.isSuccess() || !specObj.parseErrors.isSuccess() || !specObj.semanticErrors.isSuccess())
         {
             Assert.fail(EC.TLC_PARSING_FAILED);
         }
 
         // Set the rootModule:
-        this.moduleTbl = spec.getExternalModuleTable();
+        this.moduleTbl = specObj.getExternalModuleTable();
         UniqueString rootName = UniqueString.uniqueStringOf(this.rootFile);
         this.rootModule = this.moduleTbl.getModuleNode(rootName);
+        
+		Assert.check(this.rootModule != null, EC.TLC_PARSING_FAILED2,
+				String.format(" Module-Table lookup failure for module name %s derived from %s file name.",
+						rootName.toString(), this.rootFile));
 
         // Get all the state variables in the spec:
         OpDeclNode[] varDecls = this.rootModule.getVariableDecls();
@@ -211,9 +376,6 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             varNames[i].setLoc(i);
         }
 
-        // set variables to the static filed in the state
-        TLCState.setVariables(this.variablesNodes);
-
         // SZ 11.04.2009: set the number of variables
         UniqueString.setVariableCount(varDecls.length);
 
@@ -226,14 +388,14 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
         this.defns.setDefnCount(varDecls.length);
 
         // Add predefined (Boolean and String) in defns.
-        this.defns.put("TRUE", ValTrue);
-        this.defns.put("FALSE", ValFalse);
+        this.defns.put("TRUE", BoolValue.ValTrue);
+        this.defns.put("FALSE", BoolValue.ValFalse);
         Value[] elems = new Value[2];
-        elems[0] = ValFalse;
-        elems[1] = ValTrue;
+        elems[0] = BoolValue.ValFalse;
+        elems[1] = BoolValue.ValTrue;
         this.defns.put("BOOLEAN", new SetEnumValue(elems, true));
 
-        Class stringModule = this.tlaClass.loadClass("Strings");
+        Class<?> stringModule = this.tlaClass.loadClass("Strings");
         if (stringModule == null)
         {
 
@@ -253,9 +415,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
 				// should not be accessed by application code. Thus, exclude
 				// synthetic members from being processed.
                 if (!ms[i].isSynthetic()) {
-                	MethodValue mv = new MethodValue(ms[i]);
-                	Value val = (acnt == 0) ? mv.apply(EmptyArgs, EvalControl.Clear) : mv;
-                	this.defns.put(name, val);
+                	this.defns.put(name, MethodValue.get(ms[i]));
                 }
             }
         }
@@ -264,11 +424,11 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
         // here since we use defns. Things added into defns later will make it
         // wrong to use it in the method processConstants.
         ModuleNode[] mods = this.moduleTbl.getModuleNodes();
-        HashSet modSet = new HashSet();
+        final Map<String, ModuleNode> modSet = new HashMap<String, ModuleNode>();
         for (int i = 0; i < mods.length; i++)
         {
             this.processConstants(mods[i]);
-            modSet.add(mods[i].getName().toString());
+            modSet.put(mods[i].getName().toString(), mods[i]);
         }
 
         // Collect all the assumptions.
@@ -296,7 +456,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             {
                 Assert.fail(EC.TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM, name.toString());
             }
-            rootConsts[i].setToolObject(TLCGlobals.ToolId, val);
+            rootConsts[i].setToolObject(toolId, val);
             this.defns.put(name, val);
         }
 
@@ -311,18 +471,18 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                 this.defns.put(name, rootOpDefs[i]);
             } else
             {
-                rootOpDefs[i].setToolObject(TLCGlobals.ToolId, val);
+                rootOpDefs[i].setToolObject(toolId, val);
                 this.defns.put(name, val);
             }
         }
 
         // Apply config file module specific constants to operator defns.
         // We do not allow this kind of replacement for constant decls.
-        Hashtable modConstants = this.initializeModConstants();
+        Hashtable<String, Hashtable> modConstants = this.initializeModConstants();
         for (int i = 0; i < mods.length; i++)
         {
             UniqueString modName = mods[i].getName();
-            Hashtable mConsts = (Hashtable) modConstants.get(modName.toString());
+            Hashtable mConsts = modConstants.get(modName.toString());
             if (mConsts != null)
             {
                 OpDefNode[] opDefs = mods[i].getOpDefs();
@@ -332,7 +492,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                     Object val = mConsts.get(name.toString());
                     if (val != null)
                     {
-                        opDefs[j].getBody().setToolObject(TLCGlobals.ToolId, val);
+                        opDefs[j].getBody().setToolObject(toolId, val);
                     }
                 }
             }
@@ -341,26 +501,55 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
         // Apply module overrides:
         for (int i = 0; i < mods.length; i++)
         {
-            UniqueString modName = mods[i].getName();
-            Class userModule = this.tlaClass.loadClass(modName.toString());
+        	
+        	final UniqueString modName = mods[i].getName();
+            final Class<?> userModule = this.tlaClass.loadClass(modName.toString());
             if (userModule != null)
             {
+            	final Map<UniqueString, Integer> opname2arity = new HashMap<>();
+            	if (!BuiltInModuleHelper.isBuiltInModule(userModule)) {
+					// Remember arity for non built-in overrides to later match with java override
+					// when loading.
+            		for (OpDefNode opDefNode : rootOpDefs) {
+            			if (opDefNode.getOriginallyDefinedInModuleNode().getName().equals(modName)) {
+            				opname2arity.put(opDefNode.getName(), opDefNode.getArity());
+            			}
+            		}
+            	}
                 // Override with a user defined Java class for the TLA+ module.
                 // Collects new definitions:
-                Hashtable javaDefs = new Hashtable();
-                Method[] mds = userModule.getDeclaredMethods();
+                final Hashtable<UniqueString, IValue> javaDefs = new Hashtable<UniqueString, IValue>();
+                final Method[] mds = userModule.getDeclaredMethods();
                 for (int j = 0; j < mds.length; j++)
                 {
-                    int mdf = mds[j].getModifiers();
+                	final Method method = mds[j];
+                    int mdf = method.getModifiers();
                     if (Modifier.isPublic(mdf) && Modifier.isStatic(mdf))
                     {
-                        String name = TLARegistry.mapName(mds[j].getName());
+                        String name = TLARegistry.mapName(method.getName());
                         UniqueString uname = UniqueString.uniqueStringOf(name);
-                        int acnt = mds[j].getParameterTypes().length;
-                        MethodValue mv = new MethodValue(mds[j]);
-                        boolean isConstant = (acnt == 0) && Modifier.isFinal(mdf);
-                        Value val = isConstant ? mv.apply(EmptyArgs, EvalControl.Clear) : mv;
-                        javaDefs.put(uname, val);
+                        if (method.getAnnotation(TLAPlusOperator.class) != null) {
+                        	// Skip, handled below with annotation based mechanism.
+                        	continue;
+                        }
+                    	final int acnt = method.getParameterCount();
+                    	final Value val = MethodValue.get(method);
+                        
+                        if (!BuiltInModuleHelper.isBuiltInModule(userModule)) {
+                    		final URL resource = userModule.getResource(userModule.getSimpleName() + ".class");
+                    		// Print success or failure of loading the module override (arity mismatch).
+							final Integer arity = opname2arity.get(uname);
+							if (arity == null || arity != acnt) {
+								MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH, uname.toString(),
+										resource.toExternalForm(), val.toString());
+							} else {
+		                        javaDefs.put(uname, val);
+								MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED, uname.toString(),
+										resource.toExternalForm(), val.toString());
+							}
+                        } else {
+                            javaDefs.put(uname, val);
+                        }
                     }
                 }
                 // Adds/overrides new definitions:
@@ -371,14 +560,133 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                     Object val = javaDefs.get(uname);
                     if (val != null)
                     {
-                        opDefs[j].getBody().setToolObject(TLCGlobals.ToolId, val);
+                        opDefs[j].getBody().setToolObject(toolId, val);
                         this.defns.put(uname, val);
                     }
                 }
             }
         }
+		// Load override definitions through user-provided index class. In other words,
+		// a user creates a class that implements the interface ITLCOverrides.
+		// ITLCOverride defines a single method that returns an array of classes which
+		// define Java overrides (this approach is simpler and faster than scanning
+		// the complete classpath). The convention is to name the index class
+		// "tlc2.overrides.TLCOverrides":
+        //TODO: Support multiple loader classes (MyTLCOverrides, AnotherTLCOverrides, ...).
+        boolean hasCallableValue = false;
+		final Class<?> idx = this.tlaClass.loadClass("tlc2.overrides.TLCOverrides");
+		if (idx != null && ITLCOverrides.class.isAssignableFrom(idx)) {
+			try {
+				final ITLCOverrides index = (ITLCOverrides) idx.newInstance();
+				final Class<?>[] candidateClasses = index.get();
+				for (Class<?> c : candidateClasses) {
+					final Method[] candidateMethods = c.getDeclaredMethods();
+					LOOP: for (Method m : candidateMethods) {
+						
+						
+						final Evaluation evaluation = m.getAnnotation(Evaluation.class);
+						if (evaluation != null) {
+							final Value val = new EvaluatingValue(m, evaluation.minLevel());
+							
+							final ModuleNode moduleNode = modSet.get(evaluation.module());
+							if (moduleNode == null) {
+								if (evaluation.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH,
+										evaluation.module() + "!" + evaluation.definition(),
+										c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString());
+								continue LOOP;
+							}
+							final OpDefNode opDef = moduleNode.getOpDef(evaluation.definition());
+							if (opDef == null) {
+								if (evaluation.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH,
+										evaluation.module() + "!" + evaluation.definition(),
+										c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString());
+								continue LOOP;
+							}
+							
+							opDef.getBody().setToolObject(toolId, val);
+		                    this.defns.put(evaluation.definition(), val);
+		                    
+							// Print success of loading the module override.
+							MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED,
+									evaluation.module() + "!" + evaluation.definition(),
+									c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString());
+		                    
+		                    // continue with next method (don't try to also load Execution annotation below).
+		                    continue LOOP;
+						}
+						
+						final TLAPlusCallable jev = m.getAnnotation(TLAPlusCallable.class);
+						if (jev != null) {
+							final Value val = new CallableValue(m, jev.minLevel());
+							
+							final ModuleNode moduleNode = modSet.get(jev.module());
+							if (moduleNode == null) {
+								if (jev.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH,
+										jev.module() + "!" + jev.definition(),
+										c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString());
+								continue LOOP;
+							}
+							final OpDefNode opDef = moduleNode.getOpDef(jev.definition());
+							if (opDef == null) {
+								if (jev.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH,
+										jev.module() + "!" + jev.definition(),
+										c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString());
+								continue LOOP;
+							}
+							
+							opDef.getBody().setToolObject(toolId, val);
+		                    this.defns.put(jev.definition(), val);
+		                    hasCallableValue = true;
+		                    
+							// Print success of loading the module override.
+							MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED,
+									jev.module() + "!" + jev.definition(),
+									c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString());
+		                    
+		                    // continue with next method (don't try to also load Execution annotation below).
+		                    continue LOOP;
+						}
+						
+						final TLAPlusOperator opOverrideCandidate = m.getAnnotation(TLAPlusOperator.class);
+						if (opOverrideCandidate != null) {
+							final ModuleNode moduleNode = modSet.get(opOverrideCandidate.module());
+							if (moduleNode == null) {
+								if (opOverrideCandidate.warn()) MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH,
+										opOverrideCandidate.identifier(), opOverrideCandidate.module(), m.toString());
+								continue LOOP;
+							}
+							final OpDefNode opDef = moduleNode.getOpDef(opOverrideCandidate.identifier());
+							if (opDef == null) {
+								if (opOverrideCandidate.warn()) MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH,
+										opOverrideCandidate.identifier(), opOverrideCandidate.module(), m.toString());
+								continue LOOP;
+							}
+
+							final Value val = MethodValue.get(m, opOverrideCandidate.minLevel());
+							if (opDef.getArity() != m.getParameterCount()) {
+								if (opOverrideCandidate.warn()) MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH,
+										opDef.getName().toString(), c.getName(), val.toString());
+								continue LOOP;
+							} else {
+								if (opOverrideCandidate.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED,
+										opDef.getName().toString(), c.getName(),
+										val instanceof MethodValue ? val.toString() : val.getClass().getName()); // toString of non-MethodValue instances can be expensive.
+							}
+
+							opDef.getBody().setToolObject(toolId, val);
+							this.defns.put(opOverrideCandidate.identifier(), val);
+						}
+					}
+				}
+			} catch (InstantiationException | IllegalAccessException e) {
+				// TODO Specific error code.
+				Assert.fail(EC.GENERAL);
+				return;
+			}
+        }
+        
 
-        HashSet overriden = new HashSet();
+        Set<String> overriden = new HashSet<String>();
         // Apply config file overrides to constants:
         for (int i = 0; i < rootConsts.length; i++)
         {
@@ -395,12 +703,19 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                 {
                     Assert.fail(EC.TLC_CONFIG_WRONG_SUBSTITUTION, new String[] { lhs.toString(), rhs });
                 }
-                rootConsts[i].setToolObject(TLCGlobals.ToolId, myVal);
+                rootConsts[i].setToolObject(toolId, myVal);
                 this.defns.put(lhs, myVal);
                 overriden.add(lhs.toString());
             }
         }
 
+        // set variables to the static filed in the state
+        if (hasCallableValue) {
+        	TLAPlusExecutorState.setVariables(this.variablesNodes);
+        } else {
+            TLCStateMut.setVariables(this.variablesNodes);
+        }
+
         // Apply config file overrides to operator definitions:
         for (int i = 0; i < rootOpDefs.length; i++)
         {
@@ -422,7 +737,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                 {
                     Assert.fail(EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, new String[] { lhs.toString(), rhs });
                 }
-                rootOpDefs[i].setToolObject(TLCGlobals.ToolId, myVal);
+                rootOpDefs[i].setToolObject(toolId, myVal);
                 this.defns.put(lhs, myVal);
                 overriden.add(lhs.toString());
             }
@@ -440,12 +755,12 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
 
         // Apply config file module specific overrides to operator defns.
         // We do not allow this kind of replacement for constant decls.
-        Hashtable modOverrides = this.config.getModOverrides();
+        Hashtable<String, Hashtable> modOverrides = this.config.getModOverrides();
         for (int i = 0; i < mods.length; i++)
         {
             UniqueString modName = mods[i].getName();
-            Hashtable mDefs = (Hashtable) modOverrides.get(modName.toString());
-            HashSet modOverriden = new HashSet();
+            Hashtable mDefs = modOverrides.get(modName.toString());
+            HashSet<String> modOverriden = new HashSet<>();
             if (mDefs != null)
             {
                 // the operator definitions:
@@ -472,7 +787,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                             Assert.fail(EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, new String[] { lhs.toString(),
                                     rhs });
                         }
-                        opDefs[j].getBody().setToolObject(TLCGlobals.ToolId, myVal);
+                        opDefs[j].getBody().setToolObject(toolId, myVal);
                         modOverriden.add(lhs.toString());
                     }
                 }
@@ -494,367 +809,120 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
         while (modKeys.hasMoreElements())
         {
             Object modName = modKeys.nextElement();
-            if (!modSet.contains(modName))
+            if (!modSet.keySet().contains(modName))
             {
                 Assert.fail(EC.TLC_NO_MODULES, modName.toString());
             }
         }
-        
-        return spec;
     }
 
-    /*************************************************************************
-     * The following method goes through all the nodes to set their           *
-     * tool-specific fields.  It was modified on 1 May 2007 so it would find  *
-     * the nodes in the body of a Lambda expression.  Obviously, if new       *
-     * semantic node types are added, this method will have to be modified.   *
-     * Less obviously, if a tool wants to call TLC on a specification that    *
-     * was not all created inside a module, then this method may need to be   *
-     * modified so TLC finds those nodes not part of the module.              *
-     *                                                                        *
-     * Yuan claims that this is the only method in TLC that has to find all   *
-     * the nodes in such a way.                                               *
-     *************************************************************************/
-    private final void processConstants(SemanticNode expr)
+    /** 
+     * Process the configuration file. 
+     */
+    private final void processConfig()
     {
-        switch (expr.getKind()) {
-        case ModuleKind: {
-            ModuleNode expr1 = (ModuleNode) expr;
-            // Process operator definitions:
-            OpDefNode[] opDefs = expr1.getOpDefs();
-            for (int i = 0; i < opDefs.length; i++)
+        // Process the invariants:
+        this.processConfigInvariants();
+
+        // Process specification:
+        String specName = this.config.getSpec();
+        if (specName.length() == 0)
+        {
+            this.processConfigInitAndNext();
+        } else
+        {
+            if (this.config.getInit().length() != 0 || this.config.getNext().length() != 0)
             {
-                Object def = opDefs[i].getToolObject(TLCGlobals.ToolId);
-                if (def instanceof OpDefNode)
+                Assert.fail(EC.TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT);
+            }
+            Object spec = this.defns.get(specName);
+            if (spec instanceof OpDefNode)
+            {
+                OpDefNode opDef = (OpDefNode) spec;
+                if (opDef.getArity() != 0)
                 {
-                	this.processedDefs.add(def);
-                    this.processConstants(((OpDefNode) def).getBody());
+                    Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { specName });
                 }
-                this.processConstants(opDefs[i].getBody());
-            }
-            // Process all the inner modules:
-            ModuleNode[] imods = expr1.getInnerModules();
-            for (int i = 0; i < imods.length; i++)
+                this.processConfigSpec(opDef.getBody(), Context.Empty, List.Empty);
+            } else if (spec == null)
             {
-                this.processConstants(imods[i]);
-            }
-            // Process all the assumptions:
-            AssumeNode[] assumps = expr1.getAssumptions();
-            for (int i = 0; i < assumps.length; i++)
+                Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "name", specName });
+            } else
             {
-                this.processConstants(assumps[i]);
-            }
-            // On 13 Nov 2009, Yuan Yu added the following
-            // processing of all TheoremNodes, which was needed to
-            // allow Theorem and Assumption names to be used as expressions.
-            //
-            // Process all the theorems:
-            TheoremNode[] thms = expr1.getTheorems();
-            for (int i = 0; i < thms.length; i++) {
-              this.processConstants(thms[i]);
+                Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "value", specName, spec.toString() });
             }
-
-            return;
         }
-        case OpApplKind: {
-            OpApplNode expr1 = (OpApplNode) expr;
-            SymbolNode opNode = expr1.getOperator();
-            Object val = this.defns.get(opNode.getName());
-            if (val != null)
-            {
-                opNode.setToolObject(TLCGlobals.ToolId, val);
-            } else
+
+        // Process the properties:
+        Vect<String> propNames = this.config.getProperties();
+        for (int i = 0; i < propNames.size(); i++)
+        {
+            String propName = (String) propNames.elementAt(i);
+            Object prop = this.defns.get(propName);
+            if (prop instanceof OpDefNode)
             {
-                SemanticNode[] args = expr1.getArgs();
-                for (int i = 0; i < args.length; i++)
-                {
-                    if (args[i] != null)
-                    {
-                        this.processConstants(args[i]);
-                    }
-                }
-                ExprNode[] bnds = expr1.getBdedQuantBounds();
-                for (int i = 0; i < bnds.length; i++)
+                OpDefNode opDef = (OpDefNode) prop;
+                if (opDef.getArity() != 0)
                 {
-                    this.processConstants(bnds[i]);
+                    Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { propName });
                 }
-            }
-            return;
-        }
-        case LetInKind: {
-            LetInNode expr1 = (LetInNode) expr;
-            OpDefNode[] letDefs = expr1.getLets();
-            for (int i = 0; i < letDefs.length; i++)
+                this.processConfigProps(propName, opDef.getBody(), Context.Empty, List.Empty);
+            } else if (prop == null)
             {
-                this.processConstants(letDefs[i].getBody());
-            }
-            this.processConstants(expr1.getBody());
-            return;
-        }
-        case SubstInKind: {
-            SubstInNode expr1 = (SubstInNode) expr;
-            Subst[] subs = expr1.getSubsts();
-            for (int i = 0; i < subs.length; i++)
+                Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "property", propName });
+            } else if (!(prop instanceof IBoolValue) || !(((BoolValue) prop).val))
             {
-                this.processConstants(subs[i].getExpr());
+                Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "property", propName, prop.toString() });
             }
-            this.processConstants(expr1.getBody());
-            return;
         }
 
-        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-        case APSubstInKind: {
-            APSubstInNode expr1 = (APSubstInNode) expr;
-            Subst[] subs = expr1.getSubsts();
-            for (int i = 0; i < subs.length; i++)
-            {
-                this.processConstants(subs[i].getExpr());
-            }
-            this.processConstants(expr1.getBody());
-            return;
+        // Postprocess:
+        this.invariants = new Action[this.invVec.size()];
+        this.invNames = new String[this.invVec.size()];
+        for (int i = 0; i < this.invariants.length; i++)
+        {
+            this.invariants[i] = (Action) this.invVec.elementAt(i);
+            this.invNames[i] = (String) this.invNameVec.elementAt(i);
         }
+        this.invVec = null;
+        this.invNameVec = null;
 
+        this.impliedInits = new Action[this.impliedInitVec.size()];
+        this.impliedInitNames = new String[this.impliedInitVec.size()];
+        for (int i = 0; i < this.impliedInits.length; i++)
+        {
+            this.impliedInits[i] = (Action) this.impliedInitVec.elementAt(i);
+            this.impliedInitNames[i] = (String) this.impliedInitNameVec.elementAt(i);
+        }
+        this.impliedInitVec = null;
+        this.impliedInitNameVec = null;
 
-        case NumeralKind: {
-            NumeralNode expr1 = (NumeralNode) expr;
-            IntValue val = IntValue.gen(expr1.val());
-            // LL added this test on 20 Jul 2011; otherwise
-            // TLC treats a number bigger than MAX_VALUE 
-            // (2^31-1 or 2,147,483,647) as if it equals 0.
-            if (expr1.bigVal() != null) {
-            	Assert.fail(EC.TLC_INTEGER_TOO_BIG, expr1.toString());
-                return;
-            }
-            expr1.setToolObject(TLCGlobals.ToolId, val);
-            return;
+        this.impliedActions = new Action[this.impliedActionVec.size()];
+        this.impliedActNames = new String[this.impliedActionVec.size()];
+        for (int i = 0; i < this.impliedActions.length; i++)
+        {
+            this.impliedActions[i] = (Action) this.impliedActionVec.elementAt(i);
+            this.impliedActNames[i] = (String) this.impliedActNameVec.elementAt(i);
         }
-        case DecimalKind: {
-            DecimalNode expr1 = (DecimalNode) expr; // SZ: using typed variable
-            Assert.fail(EC.TLC_CANT_HANDLE_REAL_NUMBERS, expr1.toString());
-            return;
+        this.impliedActionVec = null;
+        this.impliedActNameVec = null;
+
+        this.temporals = new Action[this.temporalVec.size()];
+        this.temporalNames = new String[this.temporalNameVec.size()];
+        for (int i = 0; i < this.temporals.length; i++)
+        {
+            this.temporals[i] = (Action) this.temporalVec.elementAt(i);
+            this.temporalNames[i] = (String) this.temporalNameVec.elementAt(i);
         }
-        case StringKind: {
-            StringNode expr1 = (StringNode) expr;
-            StringValue val = new StringValue(expr1.getRep());
-            expr1.setToolObject(TLCGlobals.ToolId, val);
-            return;
-        }
-        case AssumeKind: {
-            AssumeNode expr1 = (AssumeNode) expr;
-            this.processConstants(expr1.getAssume());
-            return;
-        }
-        // On 13 Nov 2009, Yuan Yu added the following case, which was
-        // needed to allow Theorem and Assumption names to be used as 
-        // expressions.
-        //
-        case TheoremKind:
-          {
-        TheoremNode expr1 = (TheoremNode)expr;
-        this.processConstants(expr1.getTheorem());
-        return;
-          }
-        case OpArgKind: {
-            SymbolNode opArgNode = ((OpArgNode) expr).getOp();
-            if (opArgNode.getKind() == UserDefinedOpKind)
-            {   OpDefNode opdef = (OpDefNode) opArgNode ;
-                if (! processedDefs.contains(opdef)) {
-                	processedDefs.add(opdef) ;
-                	this.processConstants(opdef.getBody());
-                }
-            }
-            return;
-        }
-            /***********************************************************************
-             * LabelKind case added by LL on 13 Jun 2007.                           *
-             ***********************************************************************/
-        case LabelKind: {
-            LabelNode expr1 = (LabelNode) expr;
-            this.processConstants(expr1.getBody());
-        }
-        }
-    }
-
-    /* Return the variable if expr is a state variable. Otherwise, null. */
-    public final SymbolNode getVar(SemanticNode expr, Context c, boolean cutoff)
-    {
-        if (expr instanceof OpApplNode)
-        {
-            SymbolNode opNode = ((OpApplNode) expr).getOperator();
-
-            if (opNode.getArity() == 0)
-            {
-                boolean isVarDecl = (opNode.getKind() == VariableDeclKind);
-                Object val = this.lookup(opNode, c, cutoff && isVarDecl);
-
-                if (val instanceof LazyValue)
-                {
-                    LazyValue lval = (LazyValue) val;
-                    return this.getVar(lval.expr, lval.con, cutoff);
-                }
-                if (val instanceof OpDefNode)
-                {
-                    return this.getVar(((OpDefNode) val).getBody(), c, cutoff);
-                }
-                if (isVarDecl)
-                {
-                    return opNode;
-                }
-            }
-        }
-        return null;
-    }
-
-    /* Return the variable if expr is a primed state variable. Otherwise, null. */
-    public final SymbolNode getPrimedVar(SemanticNode expr, Context c, boolean cutoff)
-    {
-        if (expr instanceof OpApplNode)
-        {
-            OpApplNode expr1 = (OpApplNode) expr;
-            SymbolNode opNode = expr1.getOperator();
-
-            if (BuiltInOPs.getOpCode(opNode.getName()) == OPCODE_prime)
-            {
-                return this.getVar(expr1.getArgs()[0], c, cutoff);
-            }
-
-            if (opNode.getArity() == 0)
-            {
-                boolean isVarDecl = (opNode.getKind() == VariableDeclKind);
-                Object val = this.lookup(opNode, c, cutoff && isVarDecl);
-
-                if (val instanceof LazyValue)
-                {
-                    LazyValue lval = (LazyValue) val;
-                    return this.getPrimedVar(lval.expr, lval.con, cutoff);
-                }
-                if (val instanceof OpDefNode)
-                {
-                    return this.getPrimedVar(((OpDefNode) val).getBody(), c, cutoff);
-                }
-            }
-        }
-        return null;
-    }
-
-    private Vect invVec = new Vect();
-    private Vect invNameVec = new Vect();
-    private Vect impliedInitVec = new Vect();
-    private Vect impliedInitNameVec = new Vect();
-    private Vect impliedActionVec = new Vect();
-    private Vect impliedActNameVec = new Vect();
-    private Vect temporalVec = new Vect();
-    private Vect temporalNameVec = new Vect();
-    private Vect impliedTemporalVec = new Vect();
-    private Vect impliedTemporalNameVec = new Vect();
-
-    /** 
-     * Process the configuration file. 
-     */
-    public final void processConfig()
-    {
-        // Process the invariants:
-        this.processConfigInvariants();
-
-        // Process specification:
-        String specName = this.config.getSpec();
-        if (specName.length() == 0)
-        {
-            this.processConfigInitAndNext();
-        } else
-        {
-            if (this.config.getInit().length() != 0 || this.config.getNext().length() != 0)
-            {
-                Assert.fail(EC.TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT);
-            }
-            Object spec = this.defns.get(specName);
-            if (spec instanceof OpDefNode)
-            {
-                OpDefNode opDef = (OpDefNode) spec;
-                if (opDef.getArity() != 0)
-                {
-                    Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { specName });
-                }
-                this.processConfigSpec(opDef.getBody(), Context.Empty, List.Empty);
-            } else if (spec == null)
-            {
-                Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "name", specName });
-            } else
-            {
-                Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "value", specName, spec.toString() });
-            }
-        }
-
-        // Process the properties:
-        Vect propNames = this.config.getProperties();
-        for (int i = 0; i < propNames.size(); i++)
-        {
-            String propName = (String) propNames.elementAt(i);
-            Object prop = this.defns.get(propName);
-            if (prop instanceof OpDefNode)
-            {
-                OpDefNode opDef = (OpDefNode) prop;
-                if (opDef.getArity() != 0)
-                {
-                    Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { propName });
-                }
-                this.processConfigProps(propName, opDef.getBody(), Context.Empty, List.Empty);
-            } else if (prop == null)
-            {
-                Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "property", propName });
-            } else if (!(prop instanceof BoolValue) || !(((BoolValue) prop).val))
-            {
-                Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "property", propName, prop.toString() });
-            }
-        }
-
-        // Postprocess:
-        this.invariants = new Action[this.invVec.size()];
-        this.invNames = new String[this.invVec.size()];
-        for (int i = 0; i < this.invariants.length; i++)
-        {
-            this.invariants[i] = (Action) this.invVec.elementAt(i);
-            this.invNames[i] = (String) this.invNameVec.elementAt(i);
-        }
-        this.invVec = null;
-        this.invNameVec = null;
-
-        this.impliedInits = new Action[this.impliedInitVec.size()];
-        this.impliedInitNames = new String[this.impliedInitVec.size()];
-        for (int i = 0; i < this.impliedInits.length; i++)
-        {
-            this.impliedInits[i] = (Action) this.impliedInitVec.elementAt(i);
-            this.impliedInitNames[i] = (String) this.impliedInitNameVec.elementAt(i);
-        }
-        this.impliedInitVec = null;
-        this.impliedInitNameVec = null;
-
-        this.impliedActions = new Action[this.impliedActionVec.size()];
-        this.impliedActNames = new String[this.impliedActionVec.size()];
-        for (int i = 0; i < this.impliedActions.length; i++)
-        {
-            this.impliedActions[i] = (Action) this.impliedActionVec.elementAt(i);
-            this.impliedActNames[i] = (String) this.impliedActNameVec.elementAt(i);
-        }
-        this.impliedActionVec = null;
-        this.impliedActNameVec = null;
-
-        this.temporals = new Action[this.temporalVec.size()];
-        this.temporalNames = new String[this.temporalNameVec.size()];
-        for (int i = 0; i < this.temporals.length; i++)
-        {
-            this.temporals[i] = (Action) this.temporalVec.elementAt(i);
-            this.temporalNames[i] = (String) this.temporalNameVec.elementAt(i);
-        }
-        this.temporalVec = null;
-        this.temporalNameVec = null;
-
-        this.impliedTemporals = new Action[this.impliedTemporalVec.size()];
-        this.impliedTemporalNames = new String[this.impliedTemporalNameVec.size()];
-        for (int i = 0; i < this.impliedTemporals.length; i++)
-        {
-            this.impliedTemporals[i] = (Action) this.impliedTemporalVec.elementAt(i);
-            this.impliedTemporalNames[i] = (String) this.impliedTemporalNameVec.elementAt(i);
+        this.temporalVec = null;
+        this.temporalNameVec = null;
+
+        this.impliedTemporals = new Action[this.impliedTemporalVec.size()];
+        this.impliedTemporalNames = new String[this.impliedTemporalNameVec.size()];
+        for (int i = 0; i < this.impliedTemporals.length; i++)
+        {
+            this.impliedTemporals[i] = (Action) this.impliedTemporalVec.elementAt(i);
+            this.impliedTemporalNames[i] = (String) this.impliedTemporalNameVec.elementAt(i);
         }
         this.impliedTemporalVec = null;
         this.impliedTemporalNameVec = null;
@@ -870,6 +938,15 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
         {
             Assert.fail(EC.TLC_CONFIG_MISSING_NEXT);
         }
+        
+		// Process the model constraints in the config. It's done after all
+		// other config processing because it used to be processed lazy upon the
+		// first invocation of getModelConstraints. However, this caused a NPE
+		// in distributed mode due to concurrency issues and there was no
+		// apparent reason to process the model constraints lazily.
+        processModelConstraints();
+        
+        processActionConstraints();
     }
 
     /** 
@@ -894,7 +971,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             {
                 Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "initial predicate", name });
             }
-            this.initPredVec.addElement(new Action(def.getBody(), Context.Empty));
+            this.initPredVec.addElement(new Action(def.getBody(), Context.Empty, def));
         }
 
         name = this.config.getNext();
@@ -914,7 +991,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             {
                 Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "next state action", name });
             }
-            this.nextPred = new Action(def.getBody(), Context.Empty);
+            this.nextPred = new Action(def.getBody(), Context.Empty, def);
         }
     }
 
@@ -934,13 +1011,27 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                 if (def.getArity() != 0)
                 {
                     Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "invariant", name });
+                }
+				// MK 07/25/2017: Check if the invariant is a valid state predicate and produce
+				// a meaningful warning otherwise. With this enhancement, a rare bug in TLC's
+				// level-checking surfaced for which we don't have a fix right now. Fortunately,
+				// the bug is rather unlikely which is why TLC simply produces a warning for now
+				// if it "thinks" a user might be affected by the bug.
+                // see LevelNode.java line 590ff, Test52, TestInvalidInvariant, and related files
+                // for more context.
+                if (def.getLevel() >= 2)
+                {
+               		if (!def.getBody().levelParams.isEmpty()) {
+                        Assert.fail(EC.TLC_INVARIANT_VIOLATED_LEVEL, new String[] { def.getName().toString(), "includeWarning" });
+               		}
+                	Assert.fail(EC.TLC_INVARIANT_VIOLATED_LEVEL, def.getName().toString());
                 }
                 this.invNameVec.addElement(name);
-                this.invVec.addElement(new Action(def.getBody(), Context.Empty));
+                this.invVec.addElement(new Action(def.getBody(), Context.Empty, def));
             } else if (inv == null)
             {
                 Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "invariant", name });
-            } else if (!(inv instanceof BoolValue) || !(((BoolValue) inv).val))
+            } else if (!(inv instanceof IBoolValue) || !(((BoolValue) inv).val))
             {
                 Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "invariant", name, inv.toString() });
             }
@@ -965,7 +1056,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             if (args.length == 0)
             {
                 SymbolNode opNode = pred1.getOperator();
-                Object val = this.lookup(opNode, c, false);
+                Object val = symbolNodeValueLookupProvider.lookup(opNode, c, false, toolId);
                 if (val instanceof OpDefNode)
                 {
                     if (((OpDefNode) val).getArity() != 0)
@@ -973,9 +1064,9 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                         Assert.fail(EC.TLC_CONFIG_OP_NO_ARGS, new String[] { opNode.getName().toString() });
                     }
                     ExprNode body = ((OpDefNode) val).getBody();
-                    if (this.getLevelBound(body, c) == 1)
+                    if (symbolNodeValueLookupProvider.getLevelBound(body, c, toolId) == 1)
                     {
-                        this.initPredVec.addElement(new Action(Spec.addSubsts(body, subs), c));
+                        this.initPredVec.addElement(new Action(Specs.addSubsts(body, subs), c, ((OpDefNode) val)));
                     } else
                     {
                         this.processConfigSpec(body, c, subs);
@@ -983,7 +1074,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                 } else if (val == null)
                 {
                     Assert.fail(EC.TLC_CONFIG_OP_NOT_IN_SPEC, new String[] { opNode.getName().toString() });
-                } else if (val instanceof BoolValue)
+                } else if (val instanceof IBoolValue)
                 {
                     if (!((BoolValue) val).val)
                     {
@@ -999,6 +1090,10 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             }
 
             int opcode = BuiltInOPs.getOpCode(pred1.getOperator().getName());
+            if ((opcode == OPCODE_te) || (opcode == OPCODE_tf))
+            {
+            	Assert.fail(EC.TLC_SPECIFICATION_FEATURES_TEMPORAL_QUANTIFIER);
+            }
             if (opcode == OPCODE_cl || opcode == OPCODE_land)
             {
                 for (int i = 0; i < args.length; i++)
@@ -1032,17 +1127,17 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                              * of this specification object -- otherwise TLC's
                              * analysis is incorrect.
                              **/
-                            Vect components;
+                            Vect<SymbolNode> components;
 
                             SubscriptCollector()
                             {
-                                this.components = new Vect();
+                                this.components = new Vect<>();
                             }
 
                             void enter(ExprNode subscript, Context c)
                             {
                                 // if it's a variable, add it to the vector and return
-                                SymbolNode var = getVar(subscript, c, false);
+                                SymbolNode var = symbolNodeValueLookupProvider.getVar(subscript, c, false, toolId);
                                 if (var != null)
                                 {
                                     components.addElement(var);
@@ -1070,11 +1165,11 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                                         return;
                                     }
                                     // user-defined operator: look up its definition
-                                    Object opDef = lookup(opNode, c, false);
+                                    Object opDef = symbolNodeValueLookupProvider.lookup(opNode, c, false, toolId);
                                     if (opDef instanceof OpDefNode)
                                     {
                                         OpDefNode opDef1 = (OpDefNode) opDef;
-                                        this.enter(opDef1.getBody(), getOpContext(opDef1, args, c, false));
+                                        this.enter(opDef1.getBody(), symbolNodeValueLookupProvider.getOpContext(opDef1, args, c, false, toolId));
                                         return;
                                     }
                                     if (opDef instanceof LazyValue)
@@ -1092,7 +1187,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                                     Context c1 = c;
                                     for (int i = 0; i < subs.length; i++)
                                     {
-                                        c1 = c1.cons(subs[i].getOp(), getVal(subs[i].getExpr(), c, false));
+                                        c1 = c1.cons(subs[i].getOp(), symbolNodeValueLookupProvider.getVal(subs[i].getExpr(), c, false, toolId));
                                     }
                                     this.enter(subscript1.getBody(), c1);
                                     return;
@@ -1122,7 +1217,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                                 }
                             }
 
-                            Vect getComponents()
+                            Vect<SymbolNode> getComponents()
                             {
                                 return components;
                             }
@@ -1137,7 +1232,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                             Subst[] snsubs = sn.getSubsts();
                             for (int i = 0; i < snsubs.length; i++)
                             {
-                                c1 = c1.cons(snsubs[i].getOp(), getVal(snsubs[i].getExpr(), c, false));
+                                c1 = c1.cons(snsubs[i].getOp(), symbolNodeValueLookupProvider.getVal(snsubs[i].getExpr(), c, false, toolId));
                             }
                             subs1 = subs1.cdr();
                         }
@@ -1165,7 +1260,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                     }
                     if (this.nextPred == null)
                     {
-                        this.nextPred = new Action(Spec.addSubsts(arg, subs), c);
+                        this.nextPred = new Action(Specs.addSubsts(arg, subs), c);
                     } else
                     {
                         Assert.fail(EC.TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS);
@@ -1173,7 +1268,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                     // ---sm 09/06/04 >>>
                 } else
                 {
-                    this.temporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c));
+                    this.temporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c));
                     this.temporalNameVec.addElement(pred.toString());
                 }
                 return;
@@ -1186,13 +1281,13 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
            }
         }
 
-        int level = this.getLevelBound(pred, c);
+        int level = symbolNodeValueLookupProvider.getLevelBound(pred, c, toolId);
         if (level <= 1)
         {
-            this.initPredVec.addElement(new Action(Spec.addSubsts(pred, subs), c));
+            this.initPredVec.addElement(new Action(Specs.addSubsts(pred, subs), c));
         } else if (level == 3)
         {
-            this.temporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c));
+            this.temporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c));
             this.temporalNameVec.addElement(pred.toString());
         } else
         {
@@ -1216,7 +1311,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
             if (args.length == 0)
             {
                 SymbolNode opNode = pred1.getOperator();
-                Object val = this.lookup(opNode, c, false);
+                Object val = symbolNodeValueLookupProvider.lookup(opNode, c, false, toolId);
                 if (val instanceof OpDefNode)
                 {
                     if (((OpDefNode) val).getArity() != 0)
@@ -1227,7 +1322,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                 } else if (val == null)
                 {
                     Assert.fail(EC.TLC_CONFIG_OP_NOT_IN_SPEC, opNode.getName().toString());
-                } else if (val instanceof BoolValue)
+                } else if (val instanceof IBoolValue)
                 {
                     if (!((BoolValue) val).val)
                     {
@@ -1263,10 +1358,10 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                         name = boxArg1.getOperator().getName().toString();
                     }
                     this.impliedActNameVec.addElement(name);
-                    this.impliedActionVec.addElement(new Action(Spec.addSubsts(boxArg, subs), c));
-                } else if (this.getLevelBound(boxArg, c) < 2)
+                    this.impliedActionVec.addElement(new Action(Specs.addSubsts(boxArg, subs), c));
+                } else if (symbolNodeValueLookupProvider.getLevelBound(boxArg, c, toolId) < 2)
                 {
-                    this.invVec.addElement(new Action(Spec.addSubsts(boxArg, subs), c));
+                    this.invVec.addElement(new Action(Specs.addSubsts(boxArg, subs), c));
                     if ((boxArg instanceof OpApplNode) && (((OpApplNode) boxArg).getArgs().length == 0))
                     {
                         name = ((OpApplNode) boxArg).getOperator().getName().toString();
@@ -1274,7 +1369,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                     this.invNameVec.addElement(name);
                 } else
                 {
-                    this.impliedTemporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c));
+                    this.impliedTemporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c));
                     this.impliedTemporalNameVec.addElement(name);
                 }
                 return;
@@ -1286,874 +1381,414 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable
                return;
            }
         }
-        int level = this.getLevelBound(pred, c);
+        int level = symbolNodeValueLookupProvider.getLevelBound(pred, c, toolId);
         if (level <= 1)
         {
-            this.impliedInitVec.addElement(new Action(Spec.addSubsts(pred, subs), c));
+            this.impliedInitVec.addElement(new Action(Specs.addSubsts(pred, subs), c));
             this.impliedInitNameVec.addElement(name);
         } else if (level == 3)
         {
-            this.impliedTemporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c));
+            this.impliedTemporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c));
             this.impliedTemporalNameVec.addElement(name);
         } else
         {
             Assert.fail(EC.TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED, name);
         }
     }
-
-    private final Hashtable makeConstantTable(Vect consts)
+    
+	private void processActionConstraints() {
+	    Vect names = this.config.getActionConstraints();
+	    this.actionConstraints = new ExprNode[names.size()];
+	    int idx = 0;
+	    for (int i = 0; i < names.size(); i++)
+	    {
+	        String name = (String) names.elementAt(i);
+	        Object constr = this.defns.get(name);
+	        if (constr instanceof OpDefNode)
+	        {
+	            OpDefNode def = (OpDefNode) constr;
+	            if (def.getArity() != 0)
+	            {
+	                Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "action constraint", name });
+	            }
+	            this.actionConstraints[idx++] = def.getBody();
+	        } else if (constr != null)
+	        {
+	            if (!(constr instanceof IBoolValue) || !((BoolValue) constr).val)
+	            {
+	                Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE,
+	                        new String[] { "action constraint", name, constr.toString() });
+	            }
+	        } else
+	        {
+	            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "action constraint", name });
+	        }
+	    }
+	    // Shrink in case array has been overallocated
+	    if (idx < this.actionConstraints.length)
+	    {
+	        ExprNode[] constrs = new ExprNode[idx];
+	        for (int i = 0; i < idx; i++)
+	        {
+	            constrs[i] = this.actionConstraints[i];
+	        }
+	        this.actionConstraints = constrs;
+	    }
+	}
+    
+	private final void processModelConstraints() {
+	    Vect names = this.config.getConstraints();
+	    this.modelConstraints = new ExprNode[names.size()];
+	    int idx = 0;
+	    for (int i = 0; i < names.size(); i++)
+	    {
+	        String name = (String) names.elementAt(i);
+	        Object constr = this.defns.get(name);
+	        if (constr instanceof OpDefNode)
+	        {
+	            OpDefNode def = (OpDefNode) constr;
+	            if (def.getArity() != 0)
+	            {
+	                Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "constraint", name });
+	            }
+	            this.modelConstraints[idx++] = def.getBody();
+	        } else if (constr != null)
+	        {
+	            if (!(constr instanceof IBoolValue) || !((BoolValue) constr).val)
+	            {
+	                Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "constraint", name, constr.toString() });
+	            }
+	        } else
+	        {
+	            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "constraint", name });
+	        }
+	    }
+		// Shrink modelContraints in case we allocated a too large array. See
+		// nested if block above for why some constraints don't get instantiated.
+	    if (idx < this.modelConstraints.length)
+	    {
+	        ExprNode[] constrs = new ExprNode[idx];
+	        for (int i = 0; i < idx; i++)
+	        {
+	            constrs[i] = this.modelConstraints[i];
+	        }
+	        this.modelConstraints = constrs;
+	    }
+	}
+	 
+    /*************************************************************************
+     * The following method goes through all the nodes to set their           *
+     * tool-specific fields.  It was modified on 1 May 2007 so it would find  *
+     * the nodes in the body of a Lambda expression.  Obviously, if new       *
+     * semantic node types are added, this method will have to be modified.   *
+     * Less obviously, if a tool wants to call TLC on a specification that    *
+     * was not all created inside a module, then this method may need to be   *
+     * modified so TLC finds those nodes not part of the module.              *
+     *                                                                        *
+     * Yuan claims that this is the only method in TLC that has to find all   *
+     * the nodes in such a way.                                               *
+     *************************************************************************/
+    private final void processConstants(SemanticNode expr)
     {
-        Hashtable constTbl = new Hashtable();
-        for (int i = 0; i < consts.size(); i++)
-        {
-            Vect line = (Vect) consts.elementAt(i);
-            int len = line.size();
-            String name = (String) line.elementAt(0);
-            if (len <= 2)
-            {
-                constTbl.put(name, line.elementAt(1));
-            } else
+        switch (expr.getKind()) {
+        case ModuleKind: {
+            ModuleNode expr1 = (ModuleNode) expr;
+            // Process operator definitions:
+            OpDefNode[] opDefs = expr1.getOpDefs();
+            for (int i = 0; i < opDefs.length; i++)
             {
-                Object val = constTbl.get(name);
-                if (val == null)
-                {
-                    OpRcdValue opVal = new OpRcdValue();
-                    opVal.addLine(line);
-                    constTbl.put(name, opVal);
-                } else
+                Object def = opDefs[i].getToolObject(toolId);
+                if (def instanceof OpDefNode)
                 {
-                    OpRcdValue opVal = (OpRcdValue) val;
-                    int arity = ((Value[]) opVal.domain.elementAt(0)).length;
-                    if (len != arity + 2)
-                    {
-                        Assert.fail(EC.TLC_CONFIG_OP_ARITY_INCONSISTENT, name);
-                    }
-                    opVal.addLine(line);
+                	this.processedDefs.add((OpDefNode) def);
+                    this.processConstants(((OpDefNode) def).getBody());
                 }
+                this.processConstants(opDefs[i].getBody());
             }
-        }
-        return constTbl;
-    }
-
-    /** 
-     * Initialize the spec constants using the config file.  
-     */
-    public final Hashtable initializeConstants()
-    {
-        Vect consts = this.config.getConstants();
-        if (consts == null)
-        {
-            return new Hashtable();
-        }
-        return this.makeConstantTable(consts);
-    }
-
-    public final Hashtable initializeModConstants()
-    {
-        Hashtable modConstants = this.config.getModConstants();
-        Hashtable constants = new Hashtable();
-        Enumeration mods = modConstants.keys();
-        while (mods.hasMoreElements())
-        {
-            String modName = (String) mods.nextElement();
-            constants.put(modName, this.makeConstantTable((Vect) modConstants.get(modName)));
-        }
-        return constants;
-    }
-
-    /** 
-     * Get model constraints.  
-     */
-    public final ExprNode[] getModelConstraints()
-    {
-        if (this.modelConstraints != null)
-        {
-            return this.modelConstraints;
-        }
-        Vect names = this.config.getConstraints();
-        this.modelConstraints = new ExprNode[names.size()];
-        int idx = 0;
-        for (int i = 0; i < names.size(); i++)
-        {
-            String name = (String) names.elementAt(i);
-            Object constr = this.defns.get(name);
-            if (constr instanceof OpDefNode)
+            // Process all the inner modules:
+            ModuleNode[] imods = expr1.getInnerModules();
+            for (int i = 0; i < imods.length; i++)
             {
-                OpDefNode def = (OpDefNode) constr;
-                if (def.getArity() != 0)
-                {
-                    Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "constraint", name });
-                }
-                this.modelConstraints[idx++] = def.getBody();
-            } else if (constr != null)
-            {
-                if (!(constr instanceof BoolValue) || !((BoolValue) constr).val)
-                {
-                    Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "constraint", name, constr.toString() });
-                }
-            } else
-            {
-                Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "constraint", name });
-            }
-        }
-        if (idx < this.modelConstraints.length)
-        {
-            ExprNode[] constrs = new ExprNode[idx];
-            for (int i = 0; i < idx; i++)
-            {
-                constrs[i] = this.modelConstraints[i];
-            }
-            this.modelConstraints = constrs;
-        }
-        return this.modelConstraints;
-    }
-
-    /**
-     * Get action constraints.  
-     */
-    public final ExprNode[] getActionConstraints()
-    {
-        if (this.actionConstraints != null)
-        {
-            return this.actionConstraints;
-        }
-        Vect names = this.config.getActionConstraints();
-        this.actionConstraints = new ExprNode[names.size()];
-        int idx = 0;
-        for (int i = 0; i < names.size(); i++)
-        {
-            String name = (String) names.elementAt(i);
-            Object constr = this.defns.get(name);
-            if (constr instanceof OpDefNode)
-            {
-                OpDefNode def = (OpDefNode) constr;
-                if (def.getArity() != 0)
-                {
-                    Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "action constraint", name });
-                }
-                this.actionConstraints[idx++] = def.getBody();
-            } else if (constr != null)
-            {
-                if (!(constr instanceof BoolValue) || !((BoolValue) constr).val)
-                {
-                    Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE,
-                            new String[] { "action constraint", name, constr.toString() });
-                }
-            } else
-            {
-                Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "action constraint", name });
+                this.processConstants(imods[i]);
             }
-        }
-        if (idx < this.actionConstraints.length)
-        {
-            ExprNode[] constrs = new ExprNode[idx];
-            for (int i = 0; i < idx; i++)
+            // Process all the assumptions:
+            AssumeNode[] assumps = expr1.getAssumptions();
+            for (int i = 0; i < assumps.length; i++)
             {
-                constrs[i] = this.actionConstraints[i];
-            }
-            this.actionConstraints = constrs;
-        }
-        return this.actionConstraints;
-    }
-
-    /* Get the initial state predicate of the specification.  */
-    public final Vect getInitStateSpec()
-    {
-        return this.initPredVec;
-    }
-
-    /* Get the action (next state) predicate of the specification. */
-    public final Action getNextStateSpec()
-    {
-        return this.nextPred;
-    }
-
-    /** 
-     * Get the view mapping for the specification. 
-     */
-    public final SemanticNode getViewSpec()
-    {
-        String name = this.config.getView();
-        if (name.length() == 0)
-            return null;
-
-        Object view = this.defns.get(name);
-        if (view == null)
-        {
-            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "view function", name });
-        }
-        if (!(view instanceof OpDefNode))
-        {
-            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "view function", name });
-        }
-        OpDefNode def = (OpDefNode) view;
-        if (def.getArity() != 0)
-        {
-            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "view function", name });
-        }
-        return def.getBody();
-    }
-
-    /* Get the type declaration for the state variables. */
-    public final SemanticNode getTypeSpec()
-    {
-        String name = this.config.getType();
-        if (name.length() == 0)
-        {
-            Assert.fail(EC.TLC_CONFIG_NO_STATE_TYPE);
-        }
-
-        Object type = this.defns.get(name);
-        if (type == null)
-        {
-            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type", name });
-        }
-        if (!(type instanceof OpDefNode))
-        {
-            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type", name });
-        }
-        OpDefNode def = (OpDefNode) type;
-        if (def.getArity() != 0)
-        {
-            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type", name });
-        }
-        return def.getBody();
-    }
-
-    /* Get the type declaration for the state variables. */
-    public final SemanticNode getTypeConstraintSpec()
-    {
-        String name = this.config.getTypeConstraint();
-        if (name.length() == 0)
-        {
-            return null;
-        }
-
-        Object type = this.defns.get(name);
-        if (type == null)
-        {
-            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type constraint", name });
-        }
-        if (!(type instanceof OpDefNode))
-        {
-            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type constraint", name });
-        }
-        OpDefNode def = (OpDefNode) type;
-        if (def.getArity() != 0)
-        {
-            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type cinstraint", name });
-
-        }
-        return def.getBody();
-    }
-
-    public final boolean livenessIsTrue()
-    {
-        return this.impliedTemporals.length == 0;
-    }
-
-    /* Get the fairness condition of the specification.  */
-    public final Action[] getTemporals()
-    {
-        return this.temporals;
-    }
-
-    public final String[] getTemporalNames()
-    {
-        return this.temporalNames;
-    }
-
-    /* Get the liveness checks of the specification.  */
-    public final Action[] getImpliedTemporals()
-    {
-        return this.impliedTemporals;
-    }
-
-    public final String[] getImpliedTemporalNames()
-    {
-        return this.impliedTemporalNames;
-    }
-
-    /* Get the invariants of the specification. */
-    public final Action[] getInvariants()
-    {
-        return this.invariants;
-    }
-
-    public final String[] getInvNames()
-    {
-        return this.invNames;
-    }
-
-    /* Get the implied-inits of the specification. */
-    public final Action[] getImpliedInits()
-    {
-        return this.impliedInits;
-    }
-
-    public final String[] getImpliedInitNames()
-    {
-        return this.impliedInitNames;
-    }
-
-    /* Get the implied-actions of the specification. */
-    public final Action[] getImpliedActions()
-    {
-        return this.impliedActions;
-    }
-
-    public final String[] getImpliedActNames()
-    {
-        return this.impliedActNames;
-    }
-
-    /* Get the assumptions of the specification. */
-    public final ExprNode[] getAssumptions()
-    {
-        return this.assumptions;
-    }
-    
-    /* Get the assumptionIsAxiom field */
-    public final boolean[] getAssumptionIsAxiom() {
-        return this.assumptionIsAxiom;
-    }
-    /**
-     * This method gets the value of a symbol from the enviroment. We
-     * look up in the context c, its tool object, and the state s.
-     * 
-     * It and the lookup method that follows it were modified by LL
-     * on 10 April 2011 to fix the following bug.  When a constant definition
-     *    Foo == ...
-     * is overridden to substitute Bar for Foo, the TLC tool object for
-     * the body of Foo's OpDef node is set to the OpDefNode for Bar.
-     * When evaluating a use of Foo, the lookup method is apparently
-     * supposed to return the OpDefNode for Bar.  (I don't understand
-     * how the callers make use of the returned value.) That's what it
-     * does for uses of Foo in the module in which Foo is defined.
-     * However, if Foo is imported by instantiation with renaming as 
-     * X!Foo, then it appears that looking up X!Foo should also return 
-     * the OpDefNode for Bar.  If the instantiated module had no
-     * parameters, then that's what happened because the body of the
-     * OpDefNode for X!Foo is the same (contains a pointer to the
-     * same object) as the body of Foo's OpDefNode.  However, that
-     * wasn't the case if the instantiated module had parameters,
-     * because then X!Foo's OpDefNode consists of a sequence of
-     * nested SubstInNode objects, the last of which points to
-     * the body of Foo's OpDefNode.  So, LL modified the lookup
-     * methods so they follow the sequence of SubstInNode bodies
-     * down to the body of Foo's OpDefNode when looking up the result.  
-     * (If a SubstInNode has a non-null TLC tool object for a
-     * SubstInNode, then it returns that object.  I don't think this 
-     * should ever be the case, and if it is, I have no idea what the
-     * lookup method should do.)
-     * 
-     */
-    public final Object lookup(SymbolNode opNode, Context c, TLCState s, boolean cutoff)
-    {
-        boolean isVarDecl = (opNode.getKind() == VariableDeclKind);
-        Object result = c.lookup(opNode, cutoff && isVarDecl);
-        if (result != null)
-            return result;
-
-        result = opNode.getToolObject(TLCGlobals.ToolId);
-        if (result != null)
-            return result;
-
-        if (opNode.getKind() == UserDefinedOpKind)
-        {
-            // Changed by LL on 10 Apr 2011 from
-            //
-            //    result = ((OpDefNode) opNode).getBody().getToolObject(TLCGlobals.ToolId);
-            //
-            // to the following
-            ExprNode body = ((OpDefNode) opNode).getBody();
-            result = body.getToolObject(TLCGlobals.ToolId);
-            while ((result == null) && (body.getKind() == SubstInKind)) {
-                body = ((SubstInNode) body).getBody();
-                result = body.getToolObject(TLCGlobals.ToolId);
+                this.processConstants(assumps[i]);
             }
-            // end change
-
-            if (result != null)
-                return result;
-        }
-
-        result = s.lookup(opNode.getName());
-        if (result != null)
-            return result;
-        return opNode;
-    }
-
-    public final Object lookup(SymbolNode opNode, Context c, boolean cutoff)
-    {
-        boolean isVarDecl = (opNode.getKind() == VariableDeclKind);
-        Object result = c.lookup(opNode, cutoff && isVarDecl);
-        if (result != null)
-            return result;
-
-        result = opNode.getToolObject(TLCGlobals.ToolId);
-        if (result != null)
-            return result;
-
-        if (opNode.getKind() == UserDefinedOpKind)
-        {
-            // Changed by LL on 10 Apr 2011 from
-            //
-            //    result = ((OpDefNode) opNode).getBody().getToolObject(TLCGlobals.ToolId);
+            // On 13 Nov 2009, Yuan Yu added the following
+            // processing of all TheoremNodes, which was needed to
+            // allow Theorem and Assumption names to be used as expressions.
             //
-            // to the following
-            ExprNode body = ((OpDefNode) opNode).getBody();
-            result = body.getToolObject(TLCGlobals.ToolId);
-            while ((result == null) && (body.getKind() == SubstInKind)) {
-                body = ((SubstInNode) body).getBody();
-                result = body.getToolObject(TLCGlobals.ToolId);
-            }
-            // end change
-            if (result != null)
-                return result;
-        }
-        return opNode;
-    }
-
-    public final Object getVal(ExprOrOpArgNode expr, Context c, boolean cachable)
-    {
-        if (expr instanceof ExprNode)
-        {
-            LazyValue lval = new LazyValue(expr, c);
-            if (!cachable)
-            {
-                lval.setUncachable();
-            }
-            return lval;
-        }
-        SymbolNode opNode = ((OpArgNode) expr).getOp();
-        return this.lookup(opNode, c, false);
-    }
-
-    public final Context getOpContext(OpDefNode opDef, ExprOrOpArgNode[] args, Context c, boolean cachable)
-    {
-        FormalParamNode[] formals = opDef.getParams();
-        int alen = args.length;
-        Context c1 = c;
-        for (int i = 0; i < alen; i++)
-        {
-            Object aval = this.getVal(args[i], c, cachable);
-            c1 = c1.cons(formals[i], aval);
-        }
-        return c1;
-    }
-
-    /**
-     * The following added by LL on 23 October 2012 to fix bug in evaluation of names of theorems and 
-     * assumptions imported by parameterized instantiation.
-     *  
-     * @param opDef
-     * @param args
-     * @param c
-     * @param cachable
-     * @return
-     */
-    public final Context getOpContext(ThmOrAssumpDefNode opDef, ExprOrOpArgNode[] args, Context c, boolean cachable)
-    {
-        FormalParamNode[] formals = opDef.getParams();
-        int alen = args.length;
-        Context c1 = c;
-        for (int i = 0; i < alen; i++)
-        {
-            Object aval = this.getVal(args[i], c, cachable);
-            c1 = c1.cons(formals[i], aval);
-        }
-        return c1;
-    }
-    /**
-     * Return a table containing the locations of subexpression in the
-     * spec of forms x' = e and x' \in e. Warning: Current implementation
-     * may not be able to find all such locations.
-     */
-    public final ObjLongTable getPrimedLocs()
-    {
-        ObjLongTable tbl = new ObjLongTable(10);
-        Action act = this.getNextStateSpec();
-        this.collectPrimedLocs(act.pred, act.con, tbl);
-        return tbl;
-    }
-
-    public final void collectPrimedLocs(SemanticNode pred, Context c, ObjLongTable tbl)
-    {
-        switch (pred.getKind()) {
-        case OpApplKind: {
-            OpApplNode pred1 = (OpApplNode) pred;
-            this.collectPrimedLocsAppl(pred1, c, tbl);
-            return;
-        }
-        case LetInKind: {
-            LetInNode pred1 = (LetInNode) pred;
-            this.collectPrimedLocs(pred1.getBody(), c, tbl);
-            return;
-        }
-        case SubstInKind: {
-            SubstInNode pred1 = (SubstInNode) pred;
-            Subst[] subs = pred1.getSubsts();
-            Context c1 = c;
-            for (int i = 0; i < subs.length; i++)
-            {
-                Subst sub = subs[i];
-                c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true));
-            }
-            this.collectPrimedLocs(pred1.getBody(), c, tbl);
-            return;
-        }
-
-        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
-        case APSubstInKind: {
-            APSubstInNode pred1 = (APSubstInNode) pred;
-            Subst[] subs = pred1.getSubsts();
-            Context c1 = c;
-            for (int i = 0; i < subs.length; i++)
-            {
-                Subst sub = subs[i];
-                c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true));
+            // Process all the theorems:
+            TheoremNode[] thms = expr1.getTheorems();
+            for (int i = 0; i < thms.length; i++) {
+              this.processConstants(thms[i]);
             }
-            this.collectPrimedLocs(pred1.getBody(), c, tbl);
-            return;
-        }
 
-
-            /***********************************************************************
-            * LabelKind case added by LL on 13 Jun 2007.                           *
-            ***********************************************************************/
-        case LabelKind: {
-            LabelNode pred1 = (LabelNode) pred;
-            this.collectPrimedLocs(pred1.getBody(), c, tbl);
             return;
         }
-        }
-    }
-
-    private final void collectPrimedLocsAppl(OpApplNode pred, Context c, ObjLongTable tbl)
-    {
-        ExprOrOpArgNode[] args = pred.getArgs();
-        SymbolNode opNode = pred.getOperator();
-        int opcode = BuiltInOPs.getOpCode(opNode.getName());
-
-        switch (opcode) {
-        case OPCODE_fa: // FcnApply
-        {
-            this.collectPrimedLocs(args[0], c, tbl);
-            break;
-        }
-        case OPCODE_ite: // IfThenElse
-        {
-            this.collectPrimedLocs(args[1], c, tbl);
-            this.collectPrimedLocs(args[2], c, tbl);
-            break;
-        }
-        case OPCODE_case: // Case
-        {
-            for (int i = 0; i < args.length; i++)
-            {
-                OpApplNode pair = (OpApplNode) args[i];
-                this.collectPrimedLocs(pair.getArgs()[1], c, tbl);
-            }
-            break;
-        }
-        case OPCODE_eq:
-        case OPCODE_in: {
-            SymbolNode var = this.getPrimedVar(args[0], c, false);
-            if (var != null && var.getName().getVarLoc() != -1)
-            {
-                tbl.put(pred.toString(), 0);
-            }
-            break;
-        }
-        case OPCODE_cl: // ConjList
-        case OPCODE_dl: // DisjList
-        case OPCODE_be: // BoundedExists
-        case OPCODE_bf: // BoundedForall
-        case OPCODE_land:
-        case OPCODE_lor:
-        case OPCODE_implies:
-        case OPCODE_nop: // This case added 13 Nov 2009 by LL to handle subexpression names.
-          {
-            for (int i = 0; i < args.length; i++)
-            {
-                this.collectPrimedLocs(args[i], c, tbl);
-            }
-            break;
-        }
-        case OPCODE_unchanged: {
-            this.collectUnchangedLocs(args[0], c, tbl);
-            break;
-        }
-        case OPCODE_aa: // AngleAct <A>_e
-        {
-            this.collectPrimedLocs(args[0], c, tbl);
-            break;
-        }
-        case OPCODE_sa: // [A]_e
-        {
-            this.collectPrimedLocs(args[0], c, tbl);
-            tbl.put(args[1].toString(), 0);
-            break;
-        }
-        default: {
-            if (opcode == 0)
-            {
-                Object val = this.lookup(opNode, c, false);
-
-                if (val instanceof OpDefNode)
-                {
-                    OpDefNode opDef = (OpDefNode) val;
-                    // Following added by LL on 10 Apr 2010 to avoid infinite 
-                    // recursion for recursive operator definitions
-                    if (opDef.getInRecursive()) {
-                        return ;
-                    }
-                    Context c1 = this.getOpContext(opDef, args, c, true);
-                    this.collectPrimedLocs(opDef.getBody(), c1, tbl);
-                } else if (val instanceof LazyValue)
-                {
-                    LazyValue lv = (LazyValue) val;
-                    this.collectPrimedLocs(lv.expr, lv.con, tbl);
-                }
-            }
-        }
-        }
-    }
-
-    private final void collectUnchangedLocs(SemanticNode expr, Context c, ObjLongTable tbl)
-    {
-        if (expr instanceof OpApplNode)
-        {
+        case OpApplKind: {
             OpApplNode expr1 = (OpApplNode) expr;
             SymbolNode opNode = expr1.getOperator();
-            UniqueString opName = opNode.getName();
-            int opcode = BuiltInOPs.getOpCode(opName);
-
-            if (opName.getVarLoc() >= 0)
+            Object val = this.defns.get(opNode.getName());
+            if (val != null)
             {
-                // a state variable:
-                tbl.put(expr.toString(), 0);
-                return;
-            }
-
-            ExprOrOpArgNode[] args = expr1.getArgs();
-            if (opcode == OPCODE_tup)
+                opNode.setToolObject(toolId, val);
+            } else
             {
-                // a tuple:
+                SemanticNode[] args = expr1.getArgs();
                 for (int i = 0; i < args.length; i++)
                 {
-                    this.collectUnchangedLocs(args[i], c, tbl);
+                    if (args[i] != null)
+                    {
+                        this.processConstants(args[i]);
+                    }
                 }
-                return;
-            }
-
-            if (opcode == 0 && args.length == 0)
-            {
-                // a 0-arity operator:
-                Object val = this.lookup(opNode, c, false);
-                if (val instanceof OpDefNode)
+                ExprNode[] bnds = expr1.getBdedQuantBounds();
+                for (int i = 0; i < bnds.length; i++)
                 {
-                    this.collectUnchangedLocs(((OpDefNode) val).getBody(), c, tbl);
-                    return;
+                    this.processConstants(bnds[i]);
                 }
             }
-        }
-        return;
-    }
-
-    /**
-     * This method only returns an approximation of the level of the
-     * expression.  The "real" level is at most the return value. Adding
-     * <name, ValOne> to the context means that there is no need to
-     * compute level for name.
-     *
-     * Note that this method does not work if called on a part of an
-     * EXCEPT expression.
-     */
-    public final int getLevelBound(SemanticNode expr, Context c)
-    {
-        switch (expr.getKind()) {
-        case OpApplKind: {
-            OpApplNode expr1 = (OpApplNode) expr;
-            return this.getLevelBoundAppl(expr1, c);
+            return;
         }
         case LetInKind: {
             LetInNode expr1 = (LetInNode) expr;
             OpDefNode[] letDefs = expr1.getLets();
-            int letLen = letDefs.length;
-            Context c1 = c;
-            int level = 0;
-            for (int i = 0; i < letLen; i++)
+            for (int i = 0; i < letDefs.length; i++)
             {
-                OpDefNode opDef = letDefs[i];
-                level = Math.max(level, this.getLevelBound(opDef.getBody(), c1));
-                c1 = c1.cons(opDef, ValOne);
+                this.processConstants(letDefs[i].getBody());
             }
-            return Math.max(level, this.getLevelBound(expr1.getBody(), c1));
+            this.processConstants(expr1.getBody());
+            return;
         }
         case SubstInKind: {
             SubstInNode expr1 = (SubstInNode) expr;
             Subst[] subs = expr1.getSubsts();
-            int slen = subs.length;
-            Context c1 = c;
-            for (int i = 0; i < slen; i++)
+            for (int i = 0; i < subs.length; i++)
             {
-                Subst sub = subs[i];
-                c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true));
+                this.processConstants(subs[i].getExpr());
             }
-            return this.getLevelBound(expr1.getBody(), c1);
+            this.processConstants(expr1.getBody());
+            return;
         }
 
         // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
         case APSubstInKind: {
             APSubstInNode expr1 = (APSubstInNode) expr;
             Subst[] subs = expr1.getSubsts();
-            int slen = subs.length;
-            Context c1 = c;
-            for (int i = 0; i < slen; i++)
+            for (int i = 0; i < subs.length; i++)
             {
-                Subst sub = subs[i];
-                c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true));
+                this.processConstants(subs[i].getExpr());
             }
-            return this.getLevelBound(expr1.getBody(), c1);
+            this.processConstants(expr1.getBody());
+            return;
         }
 
 
-            /***********************************************************************
-            * LabelKind case added by LL on 13 Jun 2007.                           *
-            ***********************************************************************/
-        case LabelKind: {
-            LabelNode expr1 = (LabelNode) expr;
-            return this.getLevelBound(expr1.getBody(), c);
-        }
-        default: {
-            return 0;
-        }
+        case NumeralKind: {
+            NumeralNode expr1 = (NumeralNode) expr;
+            IntValue val = IntValue.gen(expr1.val());
+            // LL added this test on 20 Jul 2011; otherwise
+            // TLC treats a number bigger than MAX_VALUE 
+            // (2^31-1 or 2,147,483,647) as if it equals 0.
+            if (expr1.bigVal() != null) {
+            	Assert.fail(EC.TLC_INTEGER_TOO_BIG, expr1.toString());
+                return;
+            }
+            expr1.setToolObject(toolId, val);
+            return;
         }
-    }
-
-    private final int getLevelBoundAppl(OpApplNode expr, Context c)
-    {
-        SymbolNode opNode = expr.getOperator();
-        UniqueString opName = opNode.getName();
-        int opcode = BuiltInOPs.getOpCode(opName);
-
-        if (BuiltInOPs.isTemporal(opcode))
-        {
-            return 3; // Conservative estimate
+        case DecimalKind: {
+            DecimalNode expr1 = (DecimalNode) expr; // SZ: using typed variable
+            Assert.fail(EC.TLC_CANT_HANDLE_REAL_NUMBERS, expr1.toString());
+            return;
         }
-
-        if (BuiltInOPs.isAction(opcode))
-        {
-            return 2; // Conservative estimate
+        case StringKind: {
+            StringNode expr1 = (StringNode) expr;
+            StringValue val = new StringValue(expr1.getRep());
+            expr1.setToolObject(toolId, val);
+            return;
         }
-
-        if (opcode == OPCODE_enabled)
-        {
-            return 1; // Conservative estimate
+        case AssumeKind: {
+            AssumeNode expr1 = (AssumeNode) expr;
+            this.processConstants(expr1.getAssume());
+            return;
         }
-
-        int level = 0;
-        ExprNode[] bnds = expr.getBdedQuantBounds();
-        for (int i = 0; i < bnds.length; i++)
-        {
-            level = Math.max(level, this.getLevelBound(bnds[i], c));
+        // On 13 Nov 2009, Yuan Yu added the following case, which was
+        // needed to allow Theorem and Assumption names to be used as 
+        // expressions.
+        //
+        case TheoremKind:
+          {
+        TheoremNode expr1 = (TheoremNode)expr;
+        this.processConstants(expr1.getTheorem());
+        return;
+          }
+        case OpArgKind: {
+            SymbolNode opArgNode = ((OpArgNode) expr).getOp();
+            if (opArgNode.getKind() == UserDefinedOpKind)
+            {   OpDefNode opdef = (OpDefNode) opArgNode ;
+                if (! processedDefs.contains(opdef)) {
+                	processedDefs.add(opdef) ;
+                	this.processConstants(opdef.getBody());
+                }
+            }
+            return;
         }
-
-        if (opcode == OPCODE_rfs)
-        {
-            // For recursive function, don't compute level of the function body
-            // again in the recursive call.
-            SymbolNode fname = expr.getUnbdedQuantSymbols()[0];
-            c = c.cons(fname, ValOne);
+            /***********************************************************************
+             * LabelKind case added by LL on 13 Jun 2007.                           *
+             ***********************************************************************/
+        case LabelKind: {
+            LabelNode expr1 = (LabelNode) expr;
+            this.processConstants(expr1.getBody());
         }
-
-        ExprOrOpArgNode[] args = expr.getArgs();
-        int alen = args.length;
-        for (int i = 0; i < alen; i++)
-        {
-            if (args[i] != null)
-            {
-                level = Math.max(level, this.getLevelBound(args[i], c));
-            }
         }
+    }
 
-        if (opcode == 0)
+    private final Hashtable<String, Serializable> makeConstantTable(Vect<Vect<String>> consts)
+    {
+        Hashtable<String, Serializable> constTbl = new Hashtable<>();
+        for (int i = 0; i < consts.size(); i++)
         {
-            // This operator is a user-defined operator.
-            if (opName.getVarLoc() >= 0)
-                return 1;
-
-            Object val = this.lookup(opNode, c, false);
-            if (val instanceof OpDefNode)
+            Vect<String> line = (Vect<String>) consts.elementAt(i);
+            int len = line.size();
+            String name = (String) line.elementAt(0);
+            if (len <= 2)
             {
-                OpDefNode opDef = (OpDefNode) val;
-                c = c.cons(opNode, ValOne);
-                level = Math.max(level, this.getLevelBound(opDef.getBody(), c));
-            } else if (val instanceof LazyValue)
+                constTbl.put(name, line.elementAt(1));
+            } else
             {
-                LazyValue lv = (LazyValue) val;
-                level = Math.max(level, this.getLevelBound(lv.expr, lv.con));
+                Object val = constTbl.get(name);
+                if (val == null)
+                {
+                    OpRcdValue opVal = new OpRcdValue();
+                    opVal.addLine(line);
+                    constTbl.put(name, opVal);
+                } else
+                {
+                    OpRcdValue opVal = (OpRcdValue) val;
+                    int arity = ((IValue[]) opVal.domain.elementAt(0)).length;
+                    if (len != arity + 2)
+                    {
+                        Assert.fail(EC.TLC_CONFIG_OP_ARITY_INCONSISTENT, name);
+                    }
+                    opVal.addLine(line);
+                }
             }
         }
-        return level;
-    }
-
-    public FilenameToStream getResolver()
-    {
-        return resolver;
+        return constTbl;
     }
 
     /** 
-     * The level of the expression according to level checking.
-     * static method, does not change instance state 
+     * Initialize the spec constants using the config file.  
      */
-    public static int getLevel(LevelNode expr, Context c)
+    private final Hashtable initializeConstants()
     {
-        HashSet lpSet = expr.getLevelParams();
-        if (lpSet.isEmpty())
-            return expr.getLevel();
-
-        int level = expr.getLevel();
-        Iterator iter = lpSet.iterator();
-        while (iter.hasNext())
+        Vect<Vect<String>> consts = this.config.getConstants();
+        if (consts == null)
         {
-            SymbolNode param = (SymbolNode) iter.next();
-            Object res = c.lookup(param, true);
-            if (res != null)
-            {
-                if (res instanceof LazyValue)
-                {
-                    LazyValue lv = (LazyValue) res;
-                    int plevel = getLevel((LevelNode) lv.expr, lv.con);
-                    level = (plevel > level) ? plevel : level;
-                } else if (res instanceof OpDefNode)
-                {
-                    int plevel = getLevel((LevelNode) res, c);
-                    level = (plevel > level) ? plevel : level;
-                }
-            }
+            return new Hashtable<>();
         }
-        return level;
+        return this.makeConstantTable(consts);
     }
 
-    /**
-     * Static method, does not change instance state
-     * @param expr
-     * @param subs
-     * @return
-     */
-    private static final ExprNode addSubsts(ExprNode expr, List subs)
+    private final Hashtable<String, Hashtable> initializeModConstants()
     {
-        ExprNode res = expr;
-
-        while (!subs.isEmpty())
+        Hashtable<String, ?> modConstants = this.config.getModConstants();
+        Hashtable<String, Hashtable> constants = new Hashtable<>();
+        Enumeration<String> mods = modConstants.keys();
+        while (mods.hasMoreElements())
         {
-            SubstInNode sn = (SubstInNode) subs.car();
-            res = new SubstInNode(sn.stn, sn.getSubsts(), res, sn.getInstantiatingModule(), sn.getInstantiatedModule());
-            subs = subs.cdr();
+            String modName = mods.nextElement();
+            constants.put(modName, this.makeConstantTable((Vect<Vect<String>>) modConstants.get(modName)));
         }
-        return res;
+        return constants;
     }
+
+	public ModuleNode getRootModule() {
+		return rootModule;
+	}
+
+	public ExternalModuleTable getModuleTbl() {
+		return moduleTbl;
+	}
+
+	public OpDeclNode[] getVariablesNodes() {
+		return variablesNodes;
+	}
+	
+	public Vect<Action> getInitPred() {
+		return initPredVec;
+	}
+
+	public Action getNextPred() {
+		return nextPred;
+	}
+
+	public Action[] getTemporal() {
+		return temporals;
+	}
+
+	public String[] getTemporalNames() {
+		return temporalNames;
+	}
+
+	public Action[] getImpliedTemporals() {
+		return impliedTemporals;
+	}
+
+	public String[] getImpliedTemporalNames() {
+		return impliedTemporalNames;
+	}
+
+	public Action[] getInvariants() {
+		return invariants;
+	}
+
+	public String[] getInvariantsNames() {
+		return invNames;
+	}
+
+	public Action[] getImpliedInits() {
+		return impliedInits;
+	}
+
+	public String[] getImpliedInitNames() {
+		return impliedInitNames;
+	}
+
+	public Action[] getImpliedActions() {
+		return impliedActions;
+	}
+
+	public String[] getImpliedActionNames() {
+		return impliedActNames;
+	}
+
+	public ExprNode[] getModelConstraints() {
+		return modelConstraints;
+	}
+
+	public ExprNode[] getActionConstraints() {
+		return actionConstraints;
+	}
+
+	public ExprNode[] getAssumptions() {
+		return assumptions;
+	}
+
+	public boolean[] getAssumptionIsAxiom() {
+		return assumptionIsAxiom;
+	}
+
+	public SpecObj getSpecObj() {
+		return specObj;
+	}
+
+	public Defns getUnprocessedDefns() {
+		return snapshot;
+	}
 }
diff --git a/tlatools/src/tlc2/tool/impl/SymbolNodeValueLookupProvider.java b/tlatools/src/tlc2/tool/impl/SymbolNodeValueLookupProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..e56b9b850d2ebfab196cae52f7c0481999cbe9b0
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/SymbolNodeValueLookupProvider.java
@@ -0,0 +1,256 @@
+package tlc2.tool.impl;
+
+import tla2sany.semantic.APSubstInNode;
+import tla2sany.semantic.ASTConstants;
+import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.ExprOrOpArgNode;
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.LabelNode;
+import tla2sany.semantic.LetInNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.OpArgNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.Subst;
+import tla2sany.semantic.SubstInNode;
+import tla2sany.semantic.SymbolNode;
+import tlc2.tool.BuiltInOPs;
+import tlc2.tool.ToolGlobals;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.impl.EvaluatingValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.LazyValue;
+import tlc2.value.impl.MethodValue;
+import util.UniqueString;
+
+public interface SymbolNodeValueLookupProvider {
+    /* Return the variable if expr is a state variable. Otherwise, null. */
+	default SymbolNode getVar(final SemanticNode expr, final Context c, final boolean cutoff, final int forToolId) {
+		if (expr instanceof OpApplNode) {
+			SymbolNode opNode = ((OpApplNode) expr).getOperator();
+
+			if (opNode.getArity() == 0) {
+				boolean isVarDecl = (opNode.getKind() == ASTConstants.VariableDeclKind);
+				Object val = lookup(opNode, c, cutoff && isVarDecl, forToolId);
+
+				if (val instanceof LazyValue) {
+					LazyValue lval = (LazyValue) val;
+					return getVar(lval.expr, lval.con, cutoff, forToolId);
+				}
+				if (val instanceof OpDefNode) {
+					return getVar(((OpDefNode) val).getBody(), c, cutoff, forToolId);
+				}
+				if (isVarDecl) {
+					return opNode;
+				}
+			}
+		}
+		return null;
+	}
+
+	default Object lookup(final SymbolNode opNode, final Context c, final boolean cutoff, final int forToolId) {
+		final boolean isVarDecl = (opNode.getKind() == ASTConstants.VariableDeclKind);
+		Object result = c.lookup(opNode, cutoff && isVarDecl);
+		if (result != null) {
+			return result;
+		}
+
+		result = opNode.getToolObject(forToolId);
+		if (result != null) {
+			return WorkerValue.mux(result);
+		}
+
+		if (opNode.getKind() == ASTConstants.UserDefinedOpKind) {
+			// Changed by LL on 10 Apr 2011 from
+			//
+			// result = ((OpDefNode) opNode).getBody().getToolObject(toolId);
+			//
+			// to the following
+			ExprNode body = ((OpDefNode) opNode).getBody();
+			result = body.getToolObject(forToolId);
+			while ((result == null) && (body.getKind() == ASTConstants.SubstInKind)) {
+				body = ((SubstInNode) body).getBody();
+				result = body.getToolObject(forToolId);
+			}
+			// end change
+
+			if (result != null) {
+				return result;
+			}
+		}
+		return opNode;
+	}
+
+	default Object getVal(final ExprOrOpArgNode expr, final Context c, final boolean cachable, final int forToolId) {
+		return getVal(expr, c, cachable, CostModel.DO_NOT_RECORD, forToolId);
+	}
+
+	default Object getVal(final ExprOrOpArgNode expr, final Context c, final boolean cachable, final CostModel cm, final int forToolId) {
+		if (!LazyValue.LAZYEVAL_OFF && expr instanceof OpApplNode) {
+			final OpApplNode oan = (OpApplNode) expr;
+			// Do not create a LazyValue that "points to" another LazyValue.
+			// Related:
+			// https://github.com/tlaplus/tlaplus/issues/113
+			// https://github.com/tlaplus/tlaplus/issues/798
+			final Object l = c.lookup(oan.getOperator());
+			if (l instanceof LazyValue) {
+				return l;
+			}
+			return new LazyValue(expr, c, cachable, cm);
+		}
+		if (expr instanceof ExprNode) {
+			return new LazyValue(expr, c, cachable, cm);
+		}
+		final SymbolNode opNode = ((OpArgNode) expr).getOp();
+		return lookup(opNode, c, false, forToolId);
+	}
+
+	default Context getOpContext(final OpDefNode opDef, final ExprOrOpArgNode[] args, final Context c, final boolean cachable, final int forToolId) {
+		return getOpContext(opDef, args, c, cachable, CostModel.DO_NOT_RECORD, forToolId);
+	}
+
+	default Context getOpContext(final OpDefNode opDef, final ExprOrOpArgNode[] args, final Context c,
+			final boolean cachable, final CostModel cm, int forToolId) {
+		final FormalParamNode[] formals = opDef.getParams();
+		final int alen = args.length;
+		Context c1 = c;
+		for (int i = 0; i < alen; i++) {
+			Object aval = getVal(args[i], c, cachable, cm, forToolId);
+			c1 = c1.cons(formals[i], aval);
+		}
+		return c1;
+	}
+
+    /**
+     * This method only returns an approximation of the level of the
+     * expression.  The "real" level is at most the return value. Adding
+     * <name, ValOne> to the context means that there is no need to
+     * compute level for name.
+     *
+     * Note that this method does not work if called on a part of an
+     * EXCEPT expression.
+     */
+	default int getLevelBound(final SemanticNode expr, final Context c, final int forToolId) {
+		switch (expr.getKind()) {
+			case ASTConstants.OpApplKind: {
+				final OpApplNode expr1 = (OpApplNode) expr;
+				return getLevelBoundAppl(expr1, c, forToolId);
+			}
+			case ASTConstants.LetInKind: {
+				final LetInNode expr1 = (LetInNode) expr;
+				final OpDefNode[] letDefs = expr1.getLets();
+				final int letLen = letDefs.length;
+				Context c1 = c;
+				int level = 0;
+				for (int i = 0; i < letLen; i++) {
+					OpDefNode opDef = letDefs[i];
+					level = Math.max(level, getLevelBound(opDef.getBody(), c1, forToolId));
+					c1 = c1.cons(opDef, IntValue.ValOne);
+				}
+				return Math.max(level, getLevelBound(expr1.getBody(), c1, forToolId));
+			}
+			case ASTConstants.SubstInKind: {
+				final SubstInNode expr1 = (SubstInNode) expr;
+				final Subst[] subs = expr1.getSubsts();
+				final int slen = subs.length;
+				Context c1 = c;
+				for (int i = 0; i < slen; i++) {
+					final Subst sub = subs[i];
+					c1 = c1.cons(sub.getOp(), getVal(sub.getExpr(), c, true, forToolId));
+				}
+				return getLevelBound(expr1.getBody(), c1, forToolId);
+			}
+
+			// Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+			case ASTConstants.APSubstInKind: {
+				final APSubstInNode expr1 = (APSubstInNode) expr;
+				final Subst[] subs = expr1.getSubsts();
+				final int slen = subs.length;
+				Context c1 = c;
+				for (int i = 0; i < slen; i++) {
+					final Subst sub = subs[i];
+					c1 = c1.cons(sub.getOp(), getVal(sub.getExpr(), c, true, forToolId));
+				}
+				return getLevelBound(expr1.getBody(), c1, forToolId);
+			}
+
+			/***********************************************************************
+			 * LabelKind case added by LL on 13 Jun 2007. *
+			 ***********************************************************************/
+			case ASTConstants.LabelKind: {
+				final LabelNode expr1 = (LabelNode) expr;
+				return getLevelBound(expr1.getBody(), c, forToolId);
+			}
+			default: {
+				return 0;
+			}
+		}
+	}
+
+	/**
+	 * Users will likely want to call only {@link #getLevelBound(SemanticNode, Context, int)} - this
+	 * 	method is called from that method in certain cases.
+	 */
+	default int getLevelBoundAppl(final OpApplNode expr, Context c, final int forToolId) {
+		final SymbolNode opNode = expr.getOperator();
+		final UniqueString opName = opNode.getName();
+		final int opcode = BuiltInOPs.getOpCode(opName);
+
+		if (BuiltInOPs.isTemporal(opcode)) {
+			return 3; // Conservative estimate
+		}
+
+		if (BuiltInOPs.isAction(opcode)) {
+			return 2; // Conservative estimate
+		}
+
+		if (opcode == ToolGlobals.OPCODE_enabled) {
+			return 1; // Conservative estimate
+		}
+
+		int level = 0;
+		final ExprNode[] bnds = expr.getBdedQuantBounds();
+		for (int i = 0; i < bnds.length; i++) {
+			level = Math.max(level, getLevelBound(bnds[i], c, forToolId));
+		}
+
+		if (opcode == ToolGlobals.OPCODE_rfs) {
+			// For recursive function, don't compute level of the function body
+			// again in the recursive call.
+			SymbolNode fname = expr.getUnbdedQuantSymbols()[0];
+			c = c.cons(fname, IntValue.ValOne);
+		}
+
+		final ExprOrOpArgNode[] args = expr.getArgs();
+		final int alen = args.length;
+		for (int i = 0; i < alen; i++) {
+			if (args[i] != null) {
+				level = Math.max(level, getLevelBound(args[i], c, forToolId));
+			}
+		}
+
+		if (opcode == 0) {
+			// This operator is a user-defined operator.
+			if (opName.getVarLoc() >= 0)
+				return 1;
+
+			final Object val = lookup(opNode, c, false, forToolId);
+			if (val instanceof OpDefNode) {
+				final OpDefNode opDef = (OpDefNode) val;
+				c = c.cons(opNode, IntValue.ValOne);
+				level = Math.max(level, getLevelBound(opDef.getBody(), c, forToolId));
+			} else if (val instanceof LazyValue) {
+				final LazyValue lv = (LazyValue) val;
+				level = Math.max(level, getLevelBound(lv.expr, lv.con, forToolId));
+            } else if (val instanceof EvaluatingValue) {
+            	final EvaluatingValue ev = (EvaluatingValue) val;
+            	level = Math.max(level, ev.getMinLevel());
+            } else if (val instanceof MethodValue) {
+            	final MethodValue mv = (MethodValue) val;
+            	level = Math.max(level, mv.getMinLevel());
+			}
+		}
+		return level;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/impl/TLAClass.java b/tlatools/src/tlc2/tool/impl/TLAClass.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6a0de3ab32d948284306792ab5613539afe2c39
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/TLAClass.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:18:28 PST by lamport
+//      modified on Mon Jan 29 16:21:11 PST 2001 by yuanyu
+
+package tlc2.tool.impl;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import tlc2.output.EC;
+import util.Assert;
+import util.FilenameToStream;
+
+/**
+ * 
+ * @author Yuan Yu, Simon Zambrovski
+ * @version $Id$
+ */
+public class TLAClass
+{
+    /* Load a class from a file. */
+    private final String pkg;
+	private final FilenameToStream resolver;
+
+    public TLAClass(String pkg, FilenameToStream resolver)
+    {
+        this.resolver = resolver;
+		if (pkg.length() != 0 && pkg.charAt(pkg.length() - 1) != '.')
+        {
+            this.pkg = pkg + '.';
+        } else
+        {
+            this.pkg = pkg;
+        }
+    }
+
+	/**
+	 * This method attempts to load the java class with the given name.
+	 * 
+	 * Loading classes is done in three steps:
+	 * 
+	 * <ul>
+	 * <li>1) TLC's {@link FilenameToStream} resolver is used to locate a class
+	 * file in the resolver's lookup path. Check {@link FilenameToStream}'s
+	 * documentation on the lookup order. If a class file with name "name.class"
+	 * can be found, it is loaded.</li>
+	 * <li>2) With the unqualified name, we try to lookup the class in the
+	 * regular VM's CLASSPATH.</li>
+	 * <li>3) As a last resort, we try to load a class fully qualified with the
+	 * package name given in {@link TLAClass#pkg}.</li>
+	 * </ul>
+	 * <p>
+	 * If no class could be loaded, <code>null</code> is returned.
+	 **/
+    public synchronized Class loadClass(String name)
+    {
+        Class cl = null;
+        try
+        {
+        	try {
+        		if (resolver != null) {
+        			final File module = resolver.resolve(name + ".class", false);
+        			if (module != null && module.getAbsoluteFile() != null) {
+        				final URL url = module.getAbsoluteFile().getParentFile().toURI().toURL();
+        				cl = new URLClassLoader(new URL[] {url}).loadClass(name);
+        			}
+        		}
+        	} catch (Exception ignored1) {
+        		/*SKIP*/
+        	} finally {
+        		if (cl == null) {
+        			try
+        			{
+        				cl = Class.forName(name);
+        			} catch (Exception e)
+        			{ /*SKIP*/
+        			}
+        		}
+        	}
+            if (cl == null)
+            {
+                try
+                {
+                    cl = Class.forName(this.pkg + name);
+                } catch (Exception e)
+                { /*SKIP*/
+                }
+            }
+        } catch (Throwable e)
+        {
+            Assert.fail(EC.TLC_ERROR_REPLACING_MODULES, new String[] { name, 
+                       (e.getMessage()==null)?e.toString():e.getMessage() });
+        }
+        return cl;
+    }
+    
+//
+//    public static void main(String argv[])
+//    {
+//        TLAClass tc = new TLAClass("tlc2.module");
+//        Class c = tc.loadClass("Strings"); // must set CLASSPATH correctly
+//        System.err.println("c = " + c);
+//        // Class c1 = tc.loadClass("Class");
+//        // System.err.println("c1 = " + c1);
+//    }
+//
+}
diff --git a/tlatools/src/tlc2/tool/TLARegistry.java b/tlatools/src/tlc2/tool/impl/TLARegistry.java
similarity index 83%
rename from tlatools/src/tlc2/tool/TLARegistry.java
rename to tlatools/src/tlc2/tool/impl/TLARegistry.java
index 4350e3ddf8f8da20d3b36b8021e381f4d913153c..30b9c1d3ed2e0adc649f9469a77ccefa16320ad9 100644
--- a/tlatools/src/tlc2/tool/TLARegistry.java
+++ b/tlatools/src/tlc2/tool/impl/TLARegistry.java
@@ -3,7 +3,7 @@
 // Last modified on Mon 30 Apr 2007 at 13:18:28 PST by lamport
 //      modified on Fri Sep 15 11:06:03 PDT 2000 by yuanyu
 
-package tlc2.tool;
+package tlc2.tool.impl;
 
 import java.util.Enumeration;
 import java.util.Hashtable;
@@ -13,14 +13,13 @@ import java.util.Hashtable;
  * <br><b>Note:</b>
  * 
  * @author Simon Zambrovski
- * @version $Id$
  */
 public class TLARegistry {
 
-  private static Hashtable javaToTLA = new Hashtable();
+  private static final Hashtable<String, String> javaToTLA = new Hashtable<String, String>();
 
   public static String get(String name) {
-    return (String)javaToTLA.get(name);
+    return javaToTLA.get(name);
   }
 
   /**
@@ -30,7 +29,7 @@ public class TLARegistry {
    * @return the previous value, if one
    */
   public static String put(String tname, String jname) {
-    return (String)javaToTLA.put(tname, jname);
+    return javaToTLA.put(tname, jname);
   }
 
   public static String mapName(String name) {
@@ -41,7 +40,7 @@ public class TLARegistry {
   /* Used only for debugging. */
   public static String allNames() {
     StringBuffer sb = new StringBuffer("{");
-    Enumeration eNames = javaToTLA.keys();
+    Enumeration<String> eNames = javaToTLA.keys();
     if (eNames.hasMoreElements()) {
       sb.append(eNames.nextElement());
     }
diff --git a/tlatools/src/tlc2/tool/impl/Tool.java b/tlatools/src/tlc2/tool/impl/Tool.java
new file mode 100644
index 0000000000000000000000000000000000000000..93785eb86c511dfba5d854897526c450a5e8885c
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/Tool.java
@@ -0,0 +1,3524 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Thu  2 Aug 2007 at 10:25:48 PST by lamport
+//      modified on Fri Jan  4 22:46:57 PST 2002 by yuanyu
+
+package tlc2.tool.impl;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.List;
+
+import tla2sany.parser.SyntaxTreeNode;
+import tla2sany.semantic.APSubstInNode;
+import tla2sany.semantic.ExprNode;
+import tla2sany.semantic.ExprOrOpArgNode;
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.LabelNode;
+import tla2sany.semantic.LetInNode;
+import tla2sany.semantic.LevelConstants;
+import tla2sany.semantic.LevelNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.OpArgNode;
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.OpDefOrDeclNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.Subst;
+import tla2sany.semantic.SubstInNode;
+import tla2sany.semantic.SymbolNode;
+import tla2sany.semantic.ThmOrAssumpDefNode;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.Action;
+import tlc2.tool.BuiltInOPs;
+import tlc2.tool.EvalControl;
+import tlc2.tool.EvalException;
+import tlc2.tool.IActionItemList;
+import tlc2.tool.IContextEnumerator;
+import tlc2.tool.INextStateFunctor;
+import tlc2.tool.IStateFunctor;
+import tlc2.tool.ITool;
+import tlc2.tool.StateVec;
+import tlc2.tool.TLCState;
+import tlc2.tool.TLCStateFun;
+import tlc2.tool.TLCStateInfo;
+import tlc2.tool.TLCStateMut;
+import tlc2.tool.ToolGlobals;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.util.ExpectInlined;
+import tlc2.util.IdThread;
+import tlc2.util.Vect;
+import tlc2.value.IFcnLambdaValue;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.ValueConstants;
+import tlc2.value.Values;
+import tlc2.value.impl.Applicable;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.EvaluatingValue;
+import tlc2.value.impl.FcnLambdaValue;
+import tlc2.value.impl.FcnParams;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.LazyValue;
+import tlc2.value.impl.MVPerm;
+import tlc2.value.impl.MVPerms;
+import tlc2.value.impl.MethodValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.OpLambdaValue;
+import tlc2.value.impl.OpValue;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.Reducible;
+import tlc2.value.impl.SetCapValue;
+import tlc2.value.impl.SetCupValue;
+import tlc2.value.impl.SetDiffValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.SetOfFcnsValue;
+import tlc2.value.impl.SetOfRcdsValue;
+import tlc2.value.impl.SetOfTuplesValue;
+import tlc2.value.impl.SetPredValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.SubsetValue;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.UnionValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueEnumeration;
+import tlc2.value.impl.ValueExcept;
+import tlc2.value.impl.ValueVec;
+import util.Assert;
+import util.FilenameToStream;
+import util.TLAConstants;
+import util.UniqueString;
+
+/**
+ * This class provides useful methods for tools like model checker
+ * and simulator.
+ *
+ * It's instance serves as a spec handle
+ * This is one of two places in TLC, where not all messages are retrieved from the message printer,
+ * but constructed just here in the code.
+ */
+public abstract class Tool
+    extends Spec
+    implements ValueConstants, ToolGlobals, ITool
+{
+	
+  public static final Value[] EmptyArgs = new Value[0];
+
+  protected final Action[] actions;     // the list of TLA actions.
+  private Vect<Action> actionVec = new Vect<>(10);
+
+  /**
+   * Creates a new tool handle
+   */
+  public Tool(String specFile, String configFile) {
+	  this(new File(specFile), specFile, configFile, null);
+  }
+  
+  public Tool(String specFile, String configFile, FilenameToStream resolver) {
+	  this(new File(specFile), specFile, configFile, resolver);
+  }
+
+  private Tool(File specDir, String specFile, String configFile, FilenameToStream resolver)
+  {
+	  this(specDir.isAbsolute() ? specDir.getParent() : "", specFile, configFile, resolver);
+  }
+  
+  public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver)
+  {
+      super(specDir, specFile, configFile, resolver);
+
+      // Initialize state.
+      TLCStateMut.setTool(this);
+      
+		Action next = this.getNextStateSpec();
+		if (next == null) {
+			this.actions = new Action[0];
+		} else {
+			this.getActions(next);
+			int sz = this.actionVec.size();
+			this.actions = new Action[sz];
+			for (int i = 0; i < sz; i++) {
+				this.actions[i] = (Action) this.actionVec.elementAt(i);
+			}
+		}
+  }
+
+  Tool(Tool other) {
+	  super(other);
+	  this.actions = other.actions;
+	  this.actionVec = other.actionVec;
+  }
+
+	/**
+   * This method returns the set of all possible actions of the
+   * spec, and sets the actions field of this object. In fact, we
+   * could simply treat the next predicate as one "giant" action.
+   * But for efficiency, we preprocess the next state predicate by
+   * splitting it into a set of actions for the maximum prefix
+   * of disjunction and existential quantification.
+   */
+  @Override
+  public final Action[] getActions() {
+    return this.actions;
+  }
+
+	private final void getActions(final Action next) {
+		this.getActions(next.pred, next.con, next.getOpDef(), next.cm);
+	}
+
+  private final void getActions(SemanticNode next, Context con, final OpDefNode opDefNode, CostModel cm) {
+    switch (next.getKind()) {
+    case OpApplKind:
+      {
+        OpApplNode next1 = (OpApplNode)next;
+        this.getActionsAppl(next1, con, opDefNode, cm);
+        return;
+      }
+    case LetInKind:
+      {
+        LetInNode next1 = (LetInNode)next;
+        this.getActions(next1.getBody(), con, opDefNode, cm);
+        return;
+      }
+    case SubstInKind:
+      {
+        SubstInNode next1 = (SubstInNode)next;
+        Subst[] substs = next1.getSubsts();
+        if (substs.length == 0) {
+          this.getActions(next1.getBody(), con, opDefNode, cm);
+        }
+        else {
+          Action action = new Action(next1, con, opDefNode);
+          this.actionVec.addElement(action);
+        }
+        return;
+      }
+
+      // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+      case APSubstInKind:
+        {
+          APSubstInNode next1 = (APSubstInNode)next;
+          Subst[] substs = next1.getSubsts();
+          if (substs.length == 0) {
+            this.getActions(next1.getBody(), con, opDefNode, cm);
+          }
+          else {
+            Action action = new Action(next1, con, opDefNode);
+            this.actionVec.addElement(action);
+          }
+          return;
+        }
+
+    /***********************************************************************
+    * LabelKind class added by LL on 13 Jun 2007.                          *
+    ***********************************************************************/
+    case LabelKind:
+      {
+        LabelNode next1 = (LabelNode)next;
+        this.getActions(next1.getBody(), con, opDefNode, cm);
+        return;
+      }
+    default:
+      {
+        Assert.fail("The next state relation is not a boolean expression.\n" + next);
+      }
+    }
+  }
+
+  private final void getActionsAppl(OpApplNode next, Context con, final OpDefNode actionName, CostModel cm) {
+    ExprOrOpArgNode[] args = next.getArgs();
+    SymbolNode opNode = next.getOperator();
+    int opcode = BuiltInOPs.getOpCode(opNode.getName());
+
+    if (opcode == 0) {
+      Object val = this.lookup(opNode, con, false);
+
+      if (val instanceof OpDefNode) {
+        OpDefNode opDef = (OpDefNode)val;
+        opcode = BuiltInOPs.getOpCode(opDef.getName());
+        if (opcode == 0) {
+          try {
+            FormalParamNode[] formals = opDef.getParams();
+            int alen = args.length;
+            int argLevel = 0;
+            for (int i = 0; i < alen; i++) {
+              argLevel = args[i].getLevel();
+              if (argLevel != 0) break;
+            }
+            if (argLevel == 0) {
+              Context con1 = con;
+              for (int i = 0; i < alen; i++) {
+                IValue aval = this.eval(args[i], con, TLCState.Empty, cm);
+                con1 = con1.cons(formals[i], aval);
+              }
+              this.getActions(opDef.getBody(), con1, opDef, cm);
+              return;
+            }
+          }
+          catch (Throwable e) { /*SKIP*/ }
+        }
+      }
+      if (opcode == 0) {
+        Action action = new Action(next, con, (OpDefNode) opNode);
+        this.actionVec.addElement(action);
+        return;
+      }
+    }
+
+    switch (opcode) {
+    case OPCODE_be:     // BoundedExists
+      {
+        int cnt = this.actionVec.size();
+        try {
+          ContextEnumerator Enum =
+            this.contexts(next, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear, cm);
+          Context econ;
+          while ((econ = Enum.nextElement()) != null) {
+            this.getActions(args[0], econ, actionName, cm);
+          }
+        }
+        catch (Throwable e) {
+          Action action = new Action(next, con, actionName);
+          this.actionVec.removeAll(cnt);
+          this.actionVec.addElement(action);
+        }
+        return;
+      }
+    case OPCODE_dl:     // DisjList
+    case OPCODE_lor:
+      {
+        for (int i = 0; i < args.length; i++) {
+          this.getActions(args[i], con, actionName, cm);
+        }
+        return;
+      }
+    default:
+      {
+        // We handle all the other builtin operators here.
+        Action action = new Action(next, con, actionName);
+        this.actionVec.addElement(action);
+        return;
+      }
+    }
+  }
+
+  /*
+   * This method returns the set of possible initial states that
+   * satisfies the initial state predicate. Initial state predicate
+   * can be under-specified.  Too many possible initial states will
+   * probably make tools like TLC useless.
+   */
+  @Override
+  public final StateVec getInitStates() {
+	  final StateVec initStates = new StateVec(0);
+	  getInitStates(initStates);
+	  return initStates;
+  }
+
+  @Override
+  public final void getInitStates(IStateFunctor functor) {
+	  Vect<Action> init = this.getInitStateSpec();
+	  ActionItemList acts = ActionItemList.Empty;
+      // MAK 09/11/2018: Tail to head iteration order cause the first elem added with
+      // acts.cons to be acts tail. This fixes the bug/funny behavior that the init
+      // predicate Init == A /\ B /\ C /\ D was evaluated in the order A, D, C, B (A
+      // doesn't get added to acts at all).
+	  for (int i = (init.size() - 1); i > 0; i--) {
+		  Action elem = (Action)init.elementAt(i);
+		  acts = (ActionItemList) acts.cons(elem, IActionItemList.PRED);
+	  }
+	  if (init.size() != 0) {
+		  Action elem = (Action)init.elementAt(0);
+		  TLCState ps = TLCState.Empty.createEmpty();
+		  this.getInitStates(elem.pred, acts, elem.con, ps, functor, elem.cm);
+	  }
+  }
+
+  /* Create the state specified by pred.  */
+  @Override
+  public final TLCState makeState(SemanticNode pred) {
+    ActionItemList acts = ActionItemList.Empty;
+    TLCState ps = TLCState.Empty.createEmpty();
+    StateVec states = new StateVec(0);
+    this.getInitStates(pred, acts, Context.Empty, ps, states, acts.cm);
+    if (states.size() != 1) {
+      Assert.fail("The predicate does not specify a unique state." + pred);
+    }
+    TLCState state = states.elementAt(0);
+    if (!this.isGoodState(state)) {
+      Assert.fail("The state specified by the predicate is not complete." + pred);
+    }
+    return state;
+  }
+
+  protected void getInitStates(SemanticNode init, ActionItemList acts,
+                                   Context c, TLCState ps, IStateFunctor states, CostModel cm) {
+        switch (init.getKind()) {
+        case OpApplKind:
+          {
+            OpApplNode init1 = (OpApplNode)init;
+            this.getInitStatesAppl(init1, acts, c, ps, states, cm);
+            return;
+          }
+        case LetInKind:
+          {
+            LetInNode init1 = (LetInNode)init;
+            this.getInitStates(init1.getBody(), acts, c, ps, states, cm);
+            return;
+          }
+        case SubstInKind:
+          {
+            SubstInNode init1 = (SubstInNode)init;
+            Subst[] subs = init1.getSubsts();
+            Context c1 = c;
+            for (int i = 0; i < subs.length; i++) {
+              Subst sub = subs[i];
+              c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, coverage ? sub.getCM() : cm, toolId));
+            }
+            this.getInitStates(init1.getBody(), acts, c1, ps, states, cm);
+            return;
+          }
+        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+        case APSubstInKind:
+          {
+            APSubstInNode init1 = (APSubstInNode)init;
+            Subst[] subs = init1.getSubsts();
+            Context c1 = c;
+            for (int i = 0; i < subs.length; i++) {
+              Subst sub = subs[i];
+              c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, cm, toolId));
+            }
+            this.getInitStates(init1.getBody(), acts, c1, ps, states, cm);
+            return;
+          }
+        // LabelKind class added by LL on 13 Jun 2007
+        case LabelKind:
+          {
+            LabelNode init1 = (LabelNode)init;
+            this.getInitStates(init1.getBody(), acts, c, ps, states, cm);
+            return;
+          }
+        default:
+          {
+            Assert.fail("The init state relation is not a boolean expression.\n" + init);
+          }
+        }
+  }
+
+  private final void getInitStates(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm) {
+		if (acts.isEmpty()) {
+			if (coverage) {
+				cm.incInvocations();
+				cm.getRoot().incInvocations();
+			}
+			states.addElement(ps.copy());
+			return;
+		} else if (ps.allAssigned()) {
+			// MAK 05/25/2018: If all values of the initial state have already been
+			// assigned, there is no point in further trying to assign values. Instead, all
+			// remaining statements (ActionItemList) can just be evaluated for their boolean
+			// value.
+			// This optimization is especially useful to check inductive invariants which
+			// require TLC to generate a very large set of initial states.
+			while (!acts.isEmpty()) {
+				final Value bval = this.eval(acts.carPred(), acts.carContext(), ps, TLCState.Empty, EvalControl.Init, acts.cm);
+				if (!(bval instanceof BoolValue)) {
+					//TODO Choose more fitting error message.
+					Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING,
+							new String[] { "initial states", "boolean", bval.toString(), acts.pred.toString() });
+				}
+				if (!((BoolValue) bval).val) {
+					if (coverage) {
+						// Increase "states found".
+						cm.getRoot().incSecondary();
+					}
+					return;
+				}
+				// Move on to the next action in the ActionItemList.
+				acts = acts.cdr();
+			}
+			if (coverage) {
+				cm.incInvocations();
+				cm.getRoot().incInvocations();
+			}
+			states.addElement(ps.copy());
+			return;
+		}
+		// Assert.check(act.kind > 0 || act.kind == -1);
+		ActionItemList acts1 = acts.cdr();
+		this.getInitStates(acts.carPred(), acts1, acts.carContext(), ps, states, acts.cm);
+	  }
+
+  protected void getInitStatesAppl(OpApplNode init, ActionItemList acts,
+                                       Context c, TLCState ps, IStateFunctor states, CostModel cm) {
+    if (coverage) {cm = cm.get(init);}
+        ExprOrOpArgNode[] args = init.getArgs();
+        int alen = args.length;
+        SymbolNode opNode = init.getOperator();
+        int opcode = BuiltInOPs.getOpCode(opNode.getName());
+
+        if (opcode == 0) {
+          // This is a user-defined operator with one exception: it may
+          // be substed by a builtin operator. This special case occurs
+          // when the lookup returns an OpDef with opcode # 0.
+          Object val = this.lookup(opNode, c, ps, false);
+
+          if (val instanceof OpDefNode) {
+            OpDefNode opDef = (OpDefNode)val;
+            opcode = BuiltInOPs.getOpCode(opDef.getName());
+            if (opcode == 0) {
+              // Context c1 = this.getOpContext(opDef, args, c, false);
+              Context c1 = this.getOpContext(opDef, args, c, true, cm, toolId);
+              this.getInitStates(opDef.getBody(), acts, c1, ps, states, cm);
+              return;
+            }
+          }
+          // Added 13 Nov 2009 by LL to fix Yuan's fix.
+          /*********************************************************************
+          * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
+          * imported with parameterized instantiation.                         *
+          *********************************************************************/
+          if (val instanceof ThmOrAssumpDefNode) {
+            ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val;
+            opcode = BuiltInOPs.getOpCode(opDef.getName());
+            Context c1 = this.getOpContext(opDef, args, c, true);
+            this.getInitStates(opDef.getBody(), acts, c1, ps, states, cm);
+            return;
+          }
+
+          if (val instanceof LazyValue) {
+            LazyValue lv = (LazyValue)val;
+            if (lv.getValue() == null || lv.isUncachable()) {
+              this.getInitStates(lv.expr, acts, lv.con, ps, states, cm);
+              return;
+            }
+            val = lv.getValue();
+          }
+
+          Object bval = val;
+          if (alen == 0) {
+            if (val instanceof MethodValue) {
+              bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Init);
+            } else if (val instanceof EvaluatingValue) {
+              // Allow EvaluatingValue overwrites to have zero arity.
+              bval = ((EvaluatingValue) val).eval(this, args, c, ps, TLCState.Empty, EvalControl.Init, cm);
+            }
+          }
+          else {
+            if (val instanceof OpValue) {
+          	  bval = ((OpValue) val).eval(this, args, c, ps, TLCState.Empty, EvalControl.Init, cm);
+            }
+          }
+
+          if (opcode == 0)
+          {
+            if (!(bval instanceof BoolValue))
+            {
+              Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "initial states", "boolean",
+                        bval.toString(), init.toString() });
+            }
+            if (((BoolValue) bval).val)
+            {
+              this.getInitStates(acts, ps, states, cm);
+            }
+            return;
+          }
+        }
+
+        switch (opcode) {
+        case OPCODE_dl:     // DisjList
+        case OPCODE_lor:
+          {
+            for (int i = 0; i < alen; i++) {
+              this.getInitStates(args[i], acts, c, ps, states, cm);
+            }
+            return;
+          }
+        case OPCODE_cl:     // ConjList
+        case OPCODE_land:
+          {
+            for (int i = alen-1; i > 0; i--) {
+              acts = (ActionItemList) acts.cons(args[i], c, cm, i);
+            }
+            this.getInitStates(args[0], acts, c, ps, states, cm);
+            return;
+          }
+        case OPCODE_be:     // BoundedExists
+          {
+            SemanticNode body = args[0];
+            ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Init, cm);
+            Context c1;
+            while ((c1 = Enum.nextElement()) != null) {
+              this.getInitStates(body, acts, c1, ps, states, cm);
+            }
+            return;
+          }
+        case OPCODE_bf:     // BoundedForall
+          {
+            SemanticNode body = args[0];
+            ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Init, cm);
+            Context c1 = Enum.nextElement();
+            if (c1 == null) {
+              this.getInitStates(acts, ps, states, cm);
+            }
+            else {
+              ActionItemList acts1 = acts;
+              Context c2;
+              while ((c2 = Enum.nextElement()) != null) {
+                acts1 = (ActionItemList) acts1.cons(body, c2, cm, IActionItemList.PRED);
+              }
+              this.getInitStates(body, acts1, c1, ps, states, cm);
+            }
+            return;
+          }
+        case OPCODE_ite:    // IfThenElse
+          {
+            Value guard = this.eval(args[0], c, ps, TLCState.Empty, EvalControl.Init, cm);
+            if (!(guard instanceof BoolValue)) {
+              Assert.fail("In computing initial states, a non-boolean expression (" +
+                          guard.getKindString() + ") was used as the condition " +
+                          "of an IF.\n" + init);
+            }
+            int idx = (((BoolValue)guard).val) ? 1 : 2;
+            this.getInitStates(args[idx], acts, c, ps, states, cm);
+            return;
+          }
+        case OPCODE_case:   // Case
+          {
+            SemanticNode other = null;
+            for (int i = 0; i < alen; i++) {
+              OpApplNode pair = (OpApplNode)args[i];
+              ExprOrOpArgNode[] pairArgs = pair.getArgs();
+              if (pairArgs[0] == null) {
+                other = pairArgs[1];
+              }
+              else {
+                Value bval = this.eval(pairArgs[0], c, ps, TLCState.Empty, EvalControl.Init, cm);
+                if (!(bval instanceof BoolValue)) {
+                  Assert.fail("In computing initial states, a non-boolean expression (" +
+                              bval.getKindString() + ") was used as a guard condition" +
+                              " of a CASE.\n" + pairArgs[1]);
+                }
+                if (((BoolValue)bval).val) {
+                  this.getInitStates(pairArgs[1], acts, c, ps, states, cm);
+                  return;
+                }
+              }
+            }
+            if (other == null) {
+              Assert.fail("In computing initial states, TLC encountered a CASE with no" +
+                          " conditions true.\n" + init);
+            }
+            this.getInitStates(other, acts, c, ps, states, cm);
+            return;
+          }
+        case OPCODE_fa:     // FcnApply
+          {
+            Value fval = this.eval(args[0], c, ps, TLCState.Empty, EvalControl.Init, cm);
+            if (fval instanceof FcnLambdaValue) {
+              FcnLambdaValue fcn = (FcnLambdaValue)fval;
+              if (fcn.fcnRcd == null) {
+                Context c1 = this.getFcnContext(fcn, args, c, ps, TLCState.Empty, EvalControl.Init, cm);
+                this.getInitStates(fcn.body, acts, c1, ps, states, cm);
+                return;
+              }
+              fval = fcn.fcnRcd;
+            }
+            else if (!(fval instanceof Applicable)) {
+              Assert.fail("In computing initial states, a non-function (" +
+                          fval.getKindString() + ") was applied as a function.\n" + init);
+            }
+            Applicable fcn = (Applicable) fval;
+            Value argVal = this.eval(args[1], c, ps, TLCState.Empty, EvalControl.Init, cm);
+            Value bval = fcn.apply(argVal, EvalControl.Init);
+            if (!(bval instanceof BoolValue))
+            {
+              Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "initial states", "boolean",
+                      init.toString() });
+            }
+            if (((BoolValue)bval).val) {
+              this.getInitStates(acts, ps, states, cm);
+            }
+            return;
+          }
+        case OPCODE_eq:
+          {
+            SymbolNode var = this.getVar(args[0], c, false, toolId);
+            if (var == null || var.getName().getVarLoc() < 0) {
+              Value bval = this.eval(init, c, ps, TLCState.Empty, EvalControl.Init, cm);
+              if (!((BoolValue)bval).val) {
+                return;
+              }
+            }
+            else {
+              UniqueString varName = var.getName();
+              IValue lval = ps.lookup(varName);
+              Value rval = this.eval(args[1], c, ps, TLCState.Empty, EvalControl.Init, cm);
+              if (lval == null) {
+                ps = ps.bind(varName, rval);
+                this.getInitStates(acts, ps, states, cm);
+                ps.unbind(varName);
+                return;
+              }
+              else {
+                if (!lval.equals(rval)) {
+                  return;
+                }
+              }
+            }
+            this.getInitStates(acts, ps, states, cm);
+            return;
+          }
+        case OPCODE_in:
+          {
+            SymbolNode var = this.getVar(args[0], c, false, toolId);
+            if (var == null || var.getName().getVarLoc() < 0) {
+              Value bval = this.eval(init, c, ps, TLCState.Empty, EvalControl.Init, cm);
+              if (!((BoolValue)bval).val) {
+                return;
+              }
+            }
+            else {
+              UniqueString varName = var.getName();
+              Value lval = (Value) ps.lookup(varName);
+              Value rval = this.eval(args[1], c, ps, TLCState.Empty, EvalControl.Init, cm);
+              if (lval == null) {
+                if (!(rval instanceof Enumerable)) {
+                  Assert.fail("In computing initial states, the right side of \\IN" +
+                              " is not enumerable.\n" + init);
+                }
+                ValueEnumeration Enum = ((Enumerable)rval).elements();
+                Value elem;
+                while ((elem = Enum.nextElement()) != null) {
+                  ps.bind(varName, elem);
+                  this.getInitStates(acts, ps, states, cm);
+                  ps.unbind(varName);
+                }
+                return;
+              }
+              else {
+                if (!rval.member(lval)) {
+                  return;
+                }
+              }
+            }
+            this.getInitStates(acts, ps, states, cm);
+            return;
+          }
+        case OPCODE_implies:
+          {
+            Value lval = this.eval(args[0], c, ps, TLCState.Empty, EvalControl.Init, cm);
+            if (!(lval instanceof BoolValue)) {
+              Assert.fail("In computing initial states of a predicate of form" +
+                          " P => Q, P was " + lval.getKindString() + "\n." + init);
+            }
+            if (((BoolValue)lval).val) {
+              this.getInitStates(args[1], acts, c, ps, states, cm);
+            }
+            else {
+              this.getInitStates(acts, ps, states, cm);
+            }
+            return;
+          }
+        // The following case added by LL on 13 Nov 2009 to handle subexpression names.
+        case OPCODE_nop:
+        {
+           this.getInitStates(args[0], acts, c, ps, states, cm);
+           return;
+        }
+        default:
+          {
+            // For all the other builtin operators, simply evaluate:
+            Value bval = this.eval(init, c, ps, TLCState.Empty, EvalControl.Init, cm);
+            if (!(bval instanceof BoolValue)) {
+
+              Assert.fail("In computing initial states, TLC expected a boolean expression," +
+                          "\nbut instead found " + bval + ".\n" + init);
+            }
+            if (((BoolValue)bval).val) {
+              this.getInitStates(acts, ps, states, cm);
+            }
+            return;
+          }
+        }
+  }
+  
+  /**
+   * This method returns the set of next states when taking the action
+   * in the given state.
+   */
+  @Override
+  public final StateVec getNextStates(Action action, TLCState state) {
+	  return getNextStates(action, action.con, state);
+  }
+  
+  public final StateVec getNextStates(final Action action, final Context ctx, final TLCState state) {
+    ActionItemList acts = ActionItemList.Empty;
+    TLCState s1 = TLCState.Empty.createEmpty();
+    StateVec nss = new StateVec(0);
+    this.getNextStates(action, action.pred, acts, ctx, state, s1, nss, action.cm);
+    if (coverage) { action.cm.incInvocations(nss.size()); }
+    return nss;
+  }
+  
+  @Override
+  public final boolean getNextStates(final INextStateFunctor functor, final TLCState state) {
+	  for (int i = 0; i < actions.length; i++) {
+			final Action action = actions[i];
+			this.getNextStates(action, action.pred, ActionItemList.Empty, action.con, state, TLCState.Empty.createEmpty(),
+					functor, action.cm);
+		}
+		return false;
+  }
+
+  protected abstract TLCState getNextStates(final Action action, SemanticNode pred, ActionItemList acts, Context c,
+                                       TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm);
+  
+  protected final TLCState getNextStatesImpl(final Action action, SemanticNode pred, ActionItemList acts, Context c,
+              TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm) {
+        switch (pred.getKind()) {
+        case OpApplKind:
+          {
+            OpApplNode pred1 = (OpApplNode)pred;
+            if (coverage) {cm = cm.get(pred);}
+            return this.getNextStatesAppl(action, pred1, acts, c, s0, s1, nss, cm);
+          }
+        case LetInKind:
+          {
+            LetInNode pred1 = (LetInNode)pred;
+            return this.getNextStates(action, pred1.getBody(), acts, c, s0, s1, nss, cm);
+          }
+        case SubstInKind:
+          {
+            return getNextStatesImplSubstInKind(action, (SubstInNode) pred, acts, c, s0, s1, nss, cm);
+          }
+        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+        case APSubstInKind:
+          {
+            return getNextStatesImplApSubstInKind(action, (APSubstInNode) pred, acts, c, s0, s1, nss, cm);
+          }
+        // LabelKind class added by LL on 13 Jun 2007
+        case LabelKind:
+          {
+            LabelNode pred1 = (LabelNode)pred;
+            return this.getNextStates(action, pred1.getBody(), acts, c, s0, s1, nss, cm);
+          }
+        default:
+          {
+            Assert.fail("The next state relation is not a boolean expression.\n" + pred);
+          }
+        }
+    	return s1;
+  }
+
+  @ExpectInlined
+  private final TLCState getNextStatesImplSubstInKind(final Action action, SubstInNode pred1, ActionItemList acts, Context c, TLCState s0, TLCState s1, INextStateFunctor nss, final CostModel cm) {
+  	Subst[] subs = pred1.getSubsts();
+  	int slen = subs.length;
+  	Context c1 = c;
+  	for (int i = 0; i < slen; i++) {
+  	  Subst sub = subs[i];
+  	  c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, coverage ? sub.getCM() : cm, toolId));
+  	}
+  	return this.getNextStates(action, pred1.getBody(), acts, c1, s0, s1, nss, cm);
+  }
+  
+  @ExpectInlined
+  private final TLCState getNextStatesImplApSubstInKind(final Action action, APSubstInNode pred1, ActionItemList acts, Context c, TLCState s0, TLCState s1, INextStateFunctor nss, final CostModel cm) {
+  	Subst[] subs = pred1.getSubsts();
+  	int slen = subs.length;
+  	Context c1 = c;
+  	for (int i = 0; i < slen; i++) {
+  	  Subst sub = subs[i];
+  	  c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, cm, toolId));
+  	}
+  	return this.getNextStates(action, pred1.getBody(), acts, c1, s0, s1, nss, cm);
+  }
+  
+  @ExpectInlined
+  private final TLCState getNextStates(final Action action, ActionItemList acts, final TLCState s0, final TLCState s1,
+          final INextStateFunctor nss, CostModel cm) {
+	  final TLCState copy = getNextStates0(action, acts, s0, s1, nss, cm);
+	  if (coverage && copy != s1) {
+		  cm.incInvocations();
+	  }
+	  return copy;
+  }
+
+  @ExpectInlined
+  private final TLCState getNextStates0(final Action action, ActionItemList acts, final TLCState s0, final TLCState s1,
+                                       final INextStateFunctor nss, CostModel cm) {
+    if (acts.isEmpty()) {
+      nss.addElement(s0, action, s1);
+      return s1.copy();
+    } else if (s1.allAssigned()) {
+    	return getNextStatesAllAssigned(action, acts, s0, s1, nss, cm);
+    }
+
+    final int kind = acts.carKind();
+    SemanticNode pred = acts.carPred();
+    Context c = acts.carContext();
+    ActionItemList acts1 = acts.cdr();
+    cm = acts.cm;
+    if (kind > 0) {
+      return this.getNextStates(action, pred, acts1, c, s0, s1, nss, cm);
+    }
+    else if (kind == -1) {
+      return this.getNextStates(action, pred, acts1, c, s0, s1, nss, cm);
+    }
+    else if (kind == -2) {
+      return this.processUnchanged(action, pred, acts1, c, s0, s1, nss, cm);
+    }
+    else {
+      IValue v1 = this.eval(pred, c, s0, cm);
+      IValue v2 = this.eval(pred, c, s1, cm);
+      if (!v1.equals(v2)) {
+    	  if (coverage) {
+    		  return this.getNextStates(action, acts1, s0, s1, nss, cm);
+    	  } else {
+    		  return this.getNextStates0(action, acts1, s0, s1, nss, cm);
+    	  }
+      }
+    }
+    return s1;
+  }
+  
+  private final TLCState getNextStatesAllAssigned(final Action action, ActionItemList acts, final TLCState s0, final TLCState s1,
+		  								final INextStateFunctor nss, final CostModel cm) {
+	  int kind = acts.carKind();
+	  SemanticNode pred = acts.carPred();
+	  Context c = acts.carContext();
+      CostModel cm2 = acts.cm;
+	  while (!acts.isEmpty()) {
+		  if (kind > 0 || kind == -1) {
+			  final Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm2);
+			  if (!(bval instanceof BoolValue)) {
+				  // TODO Choose more fitting error message.
+				  Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING,
+						  new String[] { "next states", "boolean", bval.toString(), acts.pred.toString() });
+			  }
+			  if (!((BoolValue) bval).val) {
+				  return s1;
+			  }
+		  } else if (kind == -2) {
+			  // Identical to default handling below (line 876). Ignored during this optimization.
+			  return this.processUnchanged(action, pred, acts.cdr(), c, s0, s1, nss, cm2);
+		  } else {
+			  final IValue v1 = this.eval(pred, c, s0, cm2);
+			  final IValue v2 = this.eval(pred, c, s1, cm2);
+			  if (v1.equals(v2)) {
+				  return s1;
+			  }
+		  }
+		  // Move on to the next action in the ActionItemList.
+		  acts = acts.cdr();
+		  pred = acts.carPred();
+		  c = acts.carContext();
+		  kind = acts.carKind();
+          cm2 = acts.cm;
+	  }
+	  nss.addElement(s0, action, s1);
+	  return s1.copy();
+  }
+
+  /* getNextStatesAppl */
+
+  @ExpectInlined
+  protected abstract TLCState getNextStatesAppl(final Action action, OpApplNode pred, ActionItemList acts, Context c,
+          TLCState s0, TLCState s1, INextStateFunctor nss, final CostModel cm);
+  
+  protected final TLCState getNextStatesApplImpl(final Action action, final OpApplNode pred, final ActionItemList acts, final Context c,
+                                           final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) {
+        final ExprOrOpArgNode[] args = pred.getArgs();
+        final int alen = args.length;
+        final SymbolNode opNode = pred.getOperator();
+
+        int opcode = BuiltInOPs.getOpCode(opNode.getName());
+
+        if (opcode == 0) {
+          // This is a user-defined operator with one exception: it may
+          // be substed by a builtin operator. This special case occurs
+          // when the lookup returns an OpDef with opcode # 0.
+          Object val = this.lookup(opNode, c, s0, false);
+
+          if (val instanceof OpDefNode) {
+				final OpDefNode opDef = (OpDefNode) val;
+				opcode = BuiltInOPs.getOpCode(opDef.getName());
+				if (opcode == 0) {
+					return this.getNextStates(action, opDef.getBody(), acts, this.getOpContext(opDef, args, c, true, cm, toolId), s0, s1, nss, cm);
+	            }
+          }
+
+          // Added by LL 13 Nov 2009 to fix Yuan's fix
+          /*********************************************************************
+           * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
+           * imported with parameterized instantiation.                         *
+           *********************************************************************/
+          if (val instanceof ThmOrAssumpDefNode) {
+            final ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val;
+            return this.getNextStates(action, opDef.getBody(), acts, this.getOpContext(opDef, args, c, true), s0, s1, nss, cm);
+          }
+
+          if (val instanceof LazyValue) {
+            final LazyValue lv = (LazyValue)val;
+            if (lv.getValue() == null || lv.isUncachable()) {
+              return this.getNextStates(action, lv.expr, acts, lv.con, s0, s1, nss, lv.cm);
+            }
+            val = lv.getValue();
+          }
+
+          //TODO If all eval/apply in getNextStatesApplEvalAppl would be side-effect free (ie. not mutate args, c, s0,...), 
+          // this call could be moved into the if(opcode==0) branch below. However, opcode!=0 will only be the case if
+          // OpDefNode above has been substed with a built-in operator. In other words, a user defines an operator Op1,
+          // and re-defines Op1 with a TLA+ built-in one in a TLC model (not assumed to be common). => No point in trying
+          // to move this call into if(opcode==0) because this will be the case most of the time anyway.
+          final Object bval = getNextStatesApplEvalAppl(alen, args, c, s0, s1, cm, val);
+
+	      // opcode == 0 is a user-defined operator.
+          if (opcode == 0)
+          {
+            return getNextStatesApplUsrDefOp(action, pred, acts, s0, s1, nss, cm, bval);
+          }
+        }
+
+        return getNextStatesApplSwitch(action, pred, acts, c, s0, s1, nss, cm, args, alen, opcode);
+  }
+  
+  private final Object getNextStatesApplEvalAppl(final int alen, final ExprOrOpArgNode[] args, final Context c,
+			final TLCState s0, final TLCState s1, final CostModel cm, final Object val) {
+	      if (alen == 0) {
+        if (val instanceof MethodValue) {
+        	return ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear);
+        } else if (val instanceof EvaluatingValue) {
+        	return ((EvaluatingValue)val).eval(this, args, c, s0, s1, EvalControl.Clear, cm);
+       }
+      }
+      else {
+        if (val instanceof OpValue) { // EvaluatingValue sub-class of OpValue!
+       	  return ((OpValue) val).eval(this, args, c, s0, s1, EvalControl.Clear, cm);
+        }
+      }
+      return val;
+  }
+
+  private final TLCState getNextStatesApplUsrDefOp(final Action action, final OpApplNode pred, final ActionItemList acts, final TLCState s0,
+		final TLCState s1, final INextStateFunctor nss, final CostModel cm, final Object bval) {
+	if (!(bval instanceof BoolValue))
+	{
+	  Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean",
+	          bval.toString(), pred.toString() });
+	}
+	if (((BoolValue) bval).val)
+	{
+	  if (coverage) {
+		  return this.getNextStates(action, acts, s0, s1, nss, cm);
+	  } else {
+		  return this.getNextStates0(action, acts, s0, s1, nss, cm);
+	  }
+	}
+	return s1;
+  }
+
+  private final TLCState getNextStatesApplSwitch(final Action action, final OpApplNode pred, final ActionItemList acts, final Context c, final TLCState s0,
+		final TLCState s1, final INextStateFunctor nss, final CostModel cm, final ExprOrOpArgNode[] args, final int alen, final int opcode) {
+	TLCState resState = s1;
+	switch (opcode) {
+	case OPCODE_cl:     // ConjList
+	case OPCODE_land:
+	  {
+	    ActionItemList acts1 = acts;
+	    for (int i = alen - 1; i > 0; i--) {
+	      acts1 = (ActionItemList) acts1.cons(args[i], c, cm, i);
+	    }
+	    return this.getNextStates(action, args[0], acts1, c, s0, s1, nss, cm);
+	  }
+	case OPCODE_dl:     // DisjList
+	case OPCODE_lor:
+	  {
+	    for (int i = 0; i < alen; i++) {
+	      resState = this.getNextStates(action, args[i], acts, c, s0, resState, nss, cm);
+	    }
+	    return resState;
+	  }
+	case OPCODE_be:     // BoundedExists
+	  {
+	    SemanticNode body = args[0];
+	    ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear, cm);
+	    Context c1;
+	    while ((c1 = Enum.nextElement()) != null) {
+	      resState = this.getNextStates(action, body, acts, c1, s0, resState, nss, cm);
+	    }
+	    return resState;
+	  }
+	case OPCODE_bf:     // BoundedForall
+	  {
+	    SemanticNode body = args[0];
+	    ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear, cm);
+	    Context c1 = Enum.nextElement();
+	    if (c1 == null) {
+	      resState = this.getNextStates(action, acts, s0, s1, nss, cm);
+	    }
+	    else {
+	      ActionItemList acts1 = acts;
+	      Context c2;
+	      while ((c2 = Enum.nextElement()) != null) {
+	        acts1 = (ActionItemList) acts1.cons(body, c2, cm, IActionItemList.PRED);
+	      }
+	      resState = this.getNextStates(action, body, acts1, c1, s0, s1, nss, cm);
+	    }
+	    return resState;
+	  }
+	case OPCODE_fa:     // FcnApply
+	  {
+	    Value fval = this.eval(args[0], c, s0, s1, EvalControl.KeepLazy, cm);
+	    if (fval instanceof FcnLambdaValue) {
+	      FcnLambdaValue fcn = (FcnLambdaValue)fval;
+	      if (fcn.fcnRcd == null) {
+	        Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Clear, cm);
+	        return this.getNextStates(action, fcn.body, acts, c1, s0, s1, nss, fcn.cm);
+	      }
+	      fval = fcn.fcnRcd;
+	    }
+	    if (!(fval instanceof Applicable)) {
+	      Assert.fail("In computing next states, a non-function (" +
+	                  fval.getKindString() + ") was applied as a function.\n" + pred);
+	    }
+	    Applicable fcn = (Applicable)fval;
+	    Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Clear, cm);
+	    Value bval = fcn.apply(argVal, EvalControl.Clear);
+	    if (!(bval instanceof BoolValue)) {
+	      Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "next states", "boolean",
+	              pred.toString() });
+	    }
+	    if (((BoolValue)bval).val) {
+	      return this.getNextStates(action, acts, s0, s1, nss, cm);
+	    }
+	    return resState;
+	  }
+	case OPCODE_aa:     // AngleAct <A>_e
+	  {
+	    ActionItemList acts1 = (ActionItemList) acts.cons(args[1], c, cm, IActionItemList.CHANGED);
+	    return this.getNextStates(action, args[0], acts1, c, s0, s1, nss, cm);
+	  }
+	case OPCODE_sa:     // [A]_e
+	  {
+	    /* The following two lines of code did not work, and were changed by
+	     * YuanYu to mimic the way \/ works.  Change made
+	     *  11 Mar 2009, with LL sitting next to him.
+	     */
+	      //    this.getNextStates(action, args[0], acts, c, s0, s1, nss);
+	      //    return this.processUnchanged(args[1], acts, c, s0, s1, nss);
+	    resState = this.getNextStates(action, args[0], acts, c, s0, resState, nss, cm);
+	    return this.processUnchanged(action, args[1], acts, c, s0, resState, nss, cm);
+	  }
+	case OPCODE_ite:    // IfThenElse
+	  {
+	    Value guard = this.eval(args[0], c, s0, s1, EvalControl.Clear, cm);
+	    if (!(guard instanceof BoolValue)) {
+	      Assert.fail("In computing next states, a non-boolean expression (" +
+	                  guard.getKindString() + ") was used as the condition of" +
+	                  " an IF." + pred);
+	    }
+	    if (((BoolValue)guard).val) {
+	      return this.getNextStates(action, args[1], acts, c, s0, s1, nss, cm);
+	    }
+	    else {
+	      return this.getNextStates(action, args[2], acts, c, s0, s1, nss, cm);
+	    }
+	  }
+	case OPCODE_case:   // Case
+	  {
+	    SemanticNode other = null;
+	    for (int i = 0; i < alen; i++) {
+	      OpApplNode pair = (OpApplNode)args[i];
+	      ExprOrOpArgNode[] pairArgs = pair.getArgs();
+	      if (pairArgs[0] == null) {
+	        other = pairArgs[1];
+	      }
+	      else {
+	        Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Clear, coverage ? cm.get(args[i]) : cm);
+	        if (!(bval instanceof BoolValue)) {
+	          Assert.fail("In computing next states, a non-boolean expression (" +
+	                      bval.getKindString() + ") was used as a guard condition" +
+	                      " of a CASE.\n" + pairArgs[1]);
+	        }
+	        if (((BoolValue)bval).val) {
+	          return this.getNextStates(action, pairArgs[1], acts, c, s0, s1, nss, coverage ? cm.get(args[i]) : cm);
+	        }
+	      }
+	    }
+	    if (other == null) {
+	      Assert.fail("In computing next states, TLC encountered a CASE with no" +
+	                  " conditions true.\n" + pred);
+	    }
+	    return this.getNextStates(action, other, acts, c, s0, s1, nss, coverage ? cm.get(args[alen - 1]) : cm);
+	  }
+	case OPCODE_eq:
+	  {
+	    SymbolNode var = this.getPrimedVar(args[0], c, false);
+	    // Assert.check(var.getName().getVarLoc() >= 0);
+	    if (var == null) {
+	      Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm);
+	      if (!((BoolValue)bval).val) {
+	        return resState;
+	      }
+	    }
+	    else {
+	      UniqueString varName = var.getName();
+	      IValue lval = s1.lookup(varName);
+	      Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear, cm);
+	      if (lval == null) {
+	        resState.bind(varName, rval);
+	        resState = this.getNextStates(action, acts, s0, resState, nss, cm);
+	        resState.unbind(varName);
+	        return resState;
+	      }
+	      else if (!lval.equals(rval)) {
+	        return resState;
+	      }
+	    }
+	    return this.getNextStates(action, acts, s0, s1, nss, cm);
+	  }
+	case OPCODE_in:
+	  {
+	    SymbolNode var = this.getPrimedVar(args[0], c, false);
+	    // Assert.check(var.getName().getVarLoc() >= 0);
+	    if (var == null) {
+	      Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm);
+	      if (!((BoolValue)bval).val) {
+	        return resState;
+	      }
+	    }
+	    else {
+	      UniqueString varName = var.getName();
+	      Value lval = (Value) s1.lookup(varName);
+	      Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear, cm);
+	      if (lval == null) {
+	        if (!(rval instanceof Enumerable)) {
+	          Assert.fail("In computing next states, the right side of \\IN" +
+	                      " is not enumerable.\n" + pred);
+	        }
+	        ValueEnumeration Enum = ((Enumerable)rval).elements();
+	        Value elem;
+	        while ((elem = Enum.nextElement()) != null) {
+	          resState.bind(varName, elem);
+	          resState = this.getNextStates(action, acts, s0, resState, nss, cm);
+	          resState.unbind(varName);
+	        }
+	        return resState;
+	      }
+	      else if (!rval.member(lval)) {
+	        return resState;
+	      }
+	    }
+	    return this.getNextStates(action, acts, s0, s1, nss, cm);
+	  }
+	case OPCODE_implies:
+	  {
+	    Value bval = this.eval(args[0], c, s0, s1, EvalControl.Clear, cm);
+	    if (!(bval instanceof BoolValue)) {
+	      Assert.fail("In computing next states of a predicate of the form" +
+	                  " P => Q, P was\n" + bval.getKindString() + ".\n" + pred);
+	    }
+	    if (((BoolValue)bval).val) {
+	      return this.getNextStates(action, args[1], acts, c, s0, s1, nss, cm);
+	    }
+	    else {
+	      return this.getNextStates(action, acts, s0, s1, nss, cm);
+	    }
+	  }
+	case OPCODE_unchanged:
+	  {
+	    return this.processUnchanged(action, args[0], acts, c, s0, s1, nss, cm);
+	  }
+	case OPCODE_cdot:
+	  {
+	    Assert.fail("The current version of TLC does not support action composition.");
+	    /***
+	    TLCState s01 = TLCStateFun.Empty;
+	    StateVec iss = new StateVec(0);
+	    this.getNextStates(action, args[0], ActionItemList.Empty, c, s0, s01, iss);
+	    int sz = iss.size();
+	    for (int i = 0; i < sz; i++) {
+	      s01 = iss.elementAt(i);
+	      this.getNextStates(action, args[1], acts, c, s01, s1, nss);
+	    }
+	    ***/
+	    return s1;
+	  }
+	// The following case added by LL on 13 Nov 2009 to handle subexpression names.
+	case OPCODE_nop:
+	{
+	    return this.getNextStates(action, args[0], acts, c, s0, s1, nss, cm);
+	}
+	default:
+	  {
+	    // We handle all the other builtin operators here.
+	    Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm);
+	    if (!(bval instanceof BoolValue)) {
+	      Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean",
+	              bval.toString(), pred.toString() });
+	    }
+	    if (((BoolValue)bval).val) {
+	      resState = this.getNextStates(action, acts, s0, s1, nss, cm);
+	    }
+	    return resState;
+	  }
+	}
+  }
+  
+  /* processUnchanged */
+
+  @ExpectInlined
+  protected abstract TLCState processUnchanged(final Action action, SemanticNode expr, ActionItemList acts, Context c,
+                                          TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm);
+  
+  protected final TLCState processUnchangedImpl(final Action action, SemanticNode expr, ActionItemList acts, Context c,
+          TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm) {
+    if (coverage){cm = cm.get(expr);}
+        SymbolNode var = this.getVar(expr, c, false, toolId);
+        TLCState resState = s1;
+        if (var != null) {
+            return processUnchangedImplVar(action, expr, acts, s0, s1, nss, var, cm);
+        }
+
+        if (expr instanceof OpApplNode) {
+          OpApplNode expr1 = (OpApplNode)expr;
+          ExprOrOpArgNode[] args = expr1.getArgs();
+          int alen = args.length;
+          SymbolNode opNode = expr1.getOperator();
+          UniqueString opName = opNode.getName();
+          int opcode = BuiltInOPs.getOpCode(opName);
+
+          if (opcode == OPCODE_tup) {
+            return processUnchangedImplTuple(action, acts, c, s0, s1, nss, args, alen, cm, coverage ? cm.get(expr1) : cm);
+          }
+
+          if (opcode == 0 && alen == 0) {
+            // a 0-arity operator:
+            return processUnchangedImpl0Arity(action, expr, acts, c, s0, s1, nss, cm, opNode, opName);
+          }
+        }
+
+        IValue v0 = this.eval(expr, c, s0, cm);
+        Value v1 = this.eval(expr, c, s1, null, EvalControl.Clear, cm);
+        if (v0.equals(v1)) {
+          resState = this.getNextStates(action, acts, s0, s1, nss, cm);
+        }
+        return resState;
+  }
+
+  @ExpectInlined
+  private final TLCState processUnchangedImpl0Arity(final Action action, final SemanticNode expr, final ActionItemList acts,
+			final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm,
+			final SymbolNode opNode, final UniqueString opName) {
+		final Object val = this.lookup(opNode, c, false);
+	
+		if (val instanceof OpDefNode) {
+		  return this.processUnchanged(action, ((OpDefNode)val).getBody(), acts, c, s0, s1, nss, cm);
+		}
+		else if (val instanceof LazyValue) {
+		  final LazyValue lv = (LazyValue)val;
+		  return this.processUnchanged(action, lv.expr, acts, lv.con, s0, s1, nss, cm);
+		}
+		else {
+		  Assert.fail("In computing next states, TLC found the identifier\n" +
+		              opName + " undefined in an UNCHANGED expression at\n" + expr);
+		}
+		return this.getNextStates(action, acts, s0, s1, nss, cm);
+  }
+
+  @Override
+  public final IValue eval(SemanticNode expr, Context c, TLCState s0) {
+	    return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear, CostModel.DO_NOT_RECORD);
+	  }
+
+  @ExpectInlined
+  private final TLCState processUnchangedImplTuple(final Action action, ActionItemList acts, Context c, TLCState s0, TLCState s1, INextStateFunctor nss,
+  		ExprOrOpArgNode[] args, int alen, CostModel cm, CostModel cmNested) {
+  	// a tuple:
+  	if (alen != 0) {
+  	  ActionItemList acts1 = acts;
+  	  for (int i = alen-1; i > 0; i--) {
+  	    acts1 = (ActionItemList) acts1.cons(args[i], c, cmNested, IActionItemList.UNCHANGED);
+  	  }
+  	  return this.processUnchanged(action, args[0], acts1, c, s0, s1, nss, cmNested);
+  	}
+  	return this.getNextStates(action, acts, s0, s1, nss, cm);
+  }
+  
+  @ExpectInlined
+  private final TLCState processUnchangedImplVar(final Action action, SemanticNode expr, ActionItemList acts, TLCState s0, TLCState s1, INextStateFunctor nss,
+  		SymbolNode var, final CostModel cm) {
+          TLCState resState = s1;
+          // expr is a state variable:
+          final UniqueString varName = var.getName();
+          final IValue val0 = s0.lookup(varName);
+          final IValue val1 = s1.lookup(varName);
+          if (val1 == null) {
+		  	resState.bind(varName, val0);
+            if (coverage) {
+            	resState = this.getNextStates(action, acts, s0, resState, nss, cm);
+            } else {
+            	resState = this.getNextStates0(action, acts, s0, resState, nss, cm);
+            }
+		  	resState.unbind(varName);
+          }
+          else if (val0.equals(val1)) {
+              if (coverage) {
+                  resState = this.getNextStates(action, acts, s0, s1, nss, cm);
+              } else {
+                  resState = this.getNextStates0(action, acts, s0, s1, nss, cm);
+              }
+          }
+          else {
+        	  MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString(), expr.toString()});
+          }
+          return resState;
+  }
+    
+  /* eval */
+
+  /* Special version of eval for state expressions. */
+  @Override
+  public final IValue eval(SemanticNode expr, Context c, TLCState s0, CostModel cm) {
+    return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear, cm);
+  }
+  
+	  @Override
+	public final IValue eval(SemanticNode expr, Context c, TLCState s0,
+              TLCState s1, final int control) {
+		  return eval(expr, c, s0, s1, control, CostModel.DO_NOT_RECORD);
+	  }
+  /*
+   * This method evaluates the expression expr in the given context,
+   * current state, and partial next state.
+   */
+  public abstract Value eval(SemanticNode expr, Context c, TLCState s0,
+                          TLCState s1, final int control, final CostModel cm);
+  
+  @ExpectInlined
+  protected final Value evalImpl(final SemanticNode expr, final Context c, final TLCState s0,
+          final TLCState s1, final int control, CostModel cm) {
+        switch (expr.getKind()) {
+        /***********************************************************************
+        * LabelKind class added by LL on 13 Jun 2007.                          *
+        ***********************************************************************/
+        case LabelKind:
+          {
+            LabelNode expr1 = (LabelNode) expr;
+            return this.eval(expr1.getBody(), c, s0, s1, control, cm);
+          }
+        case OpApplKind:
+          {
+            OpApplNode expr1 = (OpApplNode)expr;
+            if (coverage) {cm = cm.get(expr);}
+            return this.evalAppl(expr1, c, s0, s1, control, cm);
+          }
+        case LetInKind:
+          {
+            return evalImplLetInKind((LetInNode) expr, c, s0, s1, control, cm);
+          }
+        case SubstInKind:
+          {
+            return evalImplSubstInKind((SubstInNode) expr, c, s0, s1, control, cm);
+          }
+        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+        case APSubstInKind:
+          {
+            return evalImplApSubstInKind((APSubstInNode) expr, c, s0, s1, control, cm);
+          }
+        case NumeralKind:
+        case DecimalKind:
+        case StringKind:
+          {
+            return (Value) WorkerValue.mux(expr.getToolObject(toolId));
+          }
+        case AtNodeKind:
+          {
+            return (Value)c.lookup(EXCEPT_AT);
+          }
+        case OpArgKind:
+          {
+            return evalImplOpArgKind((OpArgNode) expr, c, s0, s1, cm);
+          }
+        default:
+          {
+            Assert.fail("Attempted to evaluate an expression that cannot be evaluated.\n" +
+                        expr);
+            return null;     // make compiler happy
+          }
+        }
+  }
+
+  @ExpectInlined
+  private final Value evalImplLetInKind(LetInNode expr1, Context c, TLCState s0, TLCState s1, final int control, final CostModel cm) {
+	OpDefNode[] letDefs = expr1.getLets();
+	int letLen = letDefs.length;
+	Context c1 = c;
+	for (int i = 0; i < letLen; i++) {
+	  OpDefNode opDef = letDefs[i];
+	  if (opDef.getArity() == 0) {
+	    Value rhs = new LazyValue(opDef.getBody(), c1, cm);
+	    c1 = c1.cons(opDef, rhs);
+	  }
+	}
+	return this.eval(expr1.getBody(), c1, s0, s1, control, cm);
+  }
+
+  @ExpectInlined
+  private final Value evalImplSubstInKind(SubstInNode expr1, Context c, TLCState s0, TLCState s1, final int control, final CostModel cm) {
+  	Subst[] subs = expr1.getSubsts();
+  	int slen = subs.length;
+  	Context c1 = c;
+  	for (int i = 0; i < slen; i++) {
+  	  Subst sub = subs[i];
+  	  c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, coverage ? sub.getCM() : cm, toolId));
+  	}
+  	return this.eval(expr1.getBody(), c1, s0, s1, control, cm);
+  }
+    
+  @ExpectInlined
+  private final Value evalImplApSubstInKind(APSubstInNode expr1, Context c, TLCState s0, TLCState s1, final int control, final CostModel cm) {
+  	Subst[] subs = expr1.getSubsts();
+  	int slen = subs.length;
+  	Context c1 = c;
+  	for (int i = 0; i < slen; i++) {
+  	  Subst sub = subs[i];
+  	  c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, cm, toolId));
+  	}
+  	return this.eval(expr1.getBody(), c1, s0, s1, control, cm);
+  }
+  
+  @ExpectInlined
+  private final Value evalImplOpArgKind(OpArgNode expr1, Context c, TLCState s0, TLCState s1, final CostModel cm) {
+  	SymbolNode opNode = expr1.getOp();
+  	Object val = this.lookup(opNode, c, false);
+  	if (val instanceof OpDefNode) {
+  	  return setSource(expr1, new OpLambdaValue((OpDefNode)val, this, c, s0, s1, cm));
+  	}
+  	return (Value)val;
+  }
+  
+  /* evalAppl */
+  
+  @ExpectInlined
+  protected abstract Value evalAppl(final OpApplNode expr, Context c, TLCState s0,
+          TLCState s1, final int control, final CostModel cm);
+
+  protected final Value evalApplImpl(final OpApplNode expr, Context c, TLCState s0,
+                              TLCState s1, final int control, CostModel cm) {
+    if (coverage){
+    	cm = cm.getAndIncrement(expr);
+    }
+        ExprOrOpArgNode[] args = expr.getArgs();
+        SymbolNode opNode = expr.getOperator();
+        int opcode = BuiltInOPs.getOpCode(opNode.getName());
+
+        if (opcode == 0) {
+          // This is a user-defined operator with one exception: it may
+          // be substed by a builtin operator. This special case occurs
+          // when the lookup returns an OpDef with opcode # 0.
+          Object val = this.lookup(opNode, c, s0, EvalControl.isPrimed(control));
+
+          // First, unlazy if it is a lazy value. We cannot use the cached
+          // value when s1 == null or isEnabled(control).
+			if (val instanceof LazyValue) {
+				final LazyValue lv = (LazyValue) val;
+				if (s1 == null) {
+					val = this.eval(lv.expr, lv.con, s0, null, control, lv.getCostModel());
+			    } else if (lv.isUncachable() || EvalControl.isEnabled(control)) {
+					// Never use cached LazyValues in an ENABLED expression. This is why all
+					// this.enabled* methods pass EvalControl.Enabled (the only exception being the
+					// call on line line 2799 which passes EvalControl.Primed). This is why we can
+			    	// be sure that ENALBED expressions are not affected by the caching bug tracked
+			    	// in Github issue 113 (see below).
+					val = this.eval(lv.expr, lv.con, s0, s1, control, lv.getCostModel());
+				} else {
+					val = lv.getValue();
+					if (val == null) {
+						final Value res = this.eval(lv.expr, lv.con, s0, s1, control, lv.getCostModel());
+						// This check has been suggested by Yuan Yu on 01/15/2018:
+						//
+						// If init-states are being generated, level has to be <= ConstantLevel for
+						// caching/LazyValue to be allowed. If next-states are being generated, level
+						// has to be <= VariableLevel. The level indicates if the expression to be
+						// evaluated contains only constants, constants & variables, constants & 
+						// variables and primed variables (thus action) or is a temporal formula.
+						//
+						// This restriction is in place to fix Github issue 113
+						// (https://github.com/tlaplus/tlaplus/issues/113) - 
+						// TLC can generate invalid sets of init or next-states caused by broken
+						// LazyValue evaluation. The related tests are AssignmentInit* and
+						// AssignmentNext*. Without this fix, TLC essentially reuses a stale lv.val when
+						// it needs to re-evaluate res because the actual operands to eval changed.
+						// Below is Leslie's formal description of the bug:
+						// 
+						// The possible initial values of some variable  var  are specified by a subformula
+						// 
+						// F(..., var, ...)
+						// 
+						// in the initial predicate, for some operator F such that expanding the
+						// definition of F results in a formula containing more than one occurrence of
+						// var , not all occurring in separate disjuncts of that formula.
+						// 
+						// The possible next values of some variable  var  are specified by a subformula
+						// 
+						// F(..., var', ...)
+						// 
+						// in the next-state relation, for some operator F such that expanding the
+						// definition of F results in a formula containing more than one occurrence of
+						// var' , not all occurring in separate disjuncts of that formula.
+						// 
+						// An example of the first case is an initial predicate  Init  defined as follows:
+						// 
+						// VARIABLES x, ...
+						// F(var) == \/ var \in 0..99 /\ var % 2 = 0
+						//           \/ var = -1
+						// Init == /\ F(x)
+						//         /\ ...
+						// 
+						// The error would not appear if  F  were defined by:
+						// 
+						// F(var) == \/ var \in {i \in 0..99 : i % 2 = 0}
+						//           \/ var = -1
+						// 
+						// or if the definition of  F(x)  were expanded in  Init :
+						// 
+						// Init == /\ \/ x \in 0..99 /\ x % 2 = 0
+						//            \/ x = -1
+						//         /\ ...
+						// 
+						// A similar example holds for case 2 with the same operator F and the
+						// next-state formula
+						// 
+						// Next == /\ F(x')
+						//         /\ ...
+						// 
+						// The workaround is to rewrite the initial predicate or next-state relation so
+						// it is not in the form that can cause the bug. The simplest way to do that is
+						// to expand (in-line) the definition of F in the definition of the initial
+						// predicate or next-state relation.
+						//
+						// Note that EvalControl.Init is only set in the scope of this.getInitStates*,
+						// but not in the scope of methods such as this.isInModel, this.isGoodState...
+						// which are invoked by DFIDChecker and ModelChecker#doInit and doNext. These
+						// invocation however don't pose a problem with regards to issue 113 because
+						// they don't generate the set of initial or next states but get passed fully
+						// generated/final states.
+						//
+						// !EvalControl.isInit(control) means Tool is either processing the spec in
+						// this.process* as part of initialization or that next-states are being
+						// generated. The latter case has to restrict usage of cached LazyValue as
+						// discussed above.
+						final int level = ((LevelNode) lv.expr).getLevel(); // cast to LevelNode is safe because LV only subclass of SN.
+						if ((EvalControl.isInit(control) && level <= LevelConstants.ConstantLevel)
+								|| (!EvalControl.isInit(control) && level <= LevelConstants.VariableLevel)) {
+							// The performance benefits of caching values is generally debatable. The time
+							// it takes TLC to check a reasonable sized model of the PaxosCommit [1] spec is
+							// ~2h with, with limited caching due to the fix for issue 113 or without
+							// caching. There is no measurable performance difference even though the change
+							// for issue 113 reduces the cache hits from ~13 billion to ~4 billion. This was
+							// measured with an instrumented version of TLC.
+							// [1] general/performance/PaxosCommit/  
+							lv.setValue(res);
+						}
+						val = res;
+					}
+				}
+
+			}
+
+			Value res = null;
+          if (val instanceof OpDefNode) {
+            OpDefNode opDef = (OpDefNode)val;
+            opcode = BuiltInOPs.getOpCode(opDef.getName());
+            if (opcode == 0) {
+              Context c1 = this.getOpContext(opDef, args, c, true, cm, toolId);
+              res = this.eval(opDef.getBody(), c1, s0, s1, control, cm);
+            }
+          }
+          else if (val instanceof Value) {
+            res = (Value)val;
+            int alen = args.length;
+            if (alen == 0) {
+              if (val instanceof MethodValue) {
+                res = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear);
+              } else if (val instanceof EvaluatingValue) {
+            	  // Allow EvaluatingValue overwrites to have zero arity.
+            	  res = ((EvaluatingValue) val).eval(this, args, c, s0, s1, control, cm);
+              }
+            }
+            else {
+              if (val instanceof OpValue) {
+            	  res = ((OpValue) val).eval(this, args, c, s0, s1, control, cm);
+               } 
+            }
+          }
+          /*********************************************************************
+          * The following added by Yuan Yu on 13 Nov 2009 to allow theorem an  *
+          * assumption names to be used as expressions.                        *
+          *                                                                    *
+          * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
+          * imported with parameterized instantiation.                         *
+          *********************************************************************/
+          else if (val instanceof ThmOrAssumpDefNode) {
+//            Assert.fail("Trying to evaluate the theorem or assumption name `"
+//                         + opNode.getName() + "'. \nUse `" + opNode.getName()
+//                         + "!:' instead.\n" +expr);
+            ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val ;
+            Context c1 = this.getOpContext(opDef, args, c, true);
+            return this.eval(opDef.getBody(), c1, s0, s1, control, cm);
+          }
+          else {
+            Assert.fail(EC.TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR,
+                new String[] { opNode.getName().toString(), expr.toString() });
+          }
+          if (opcode == 0) {
+            return res;
+          }
+        }
+
+        switch (opcode) {
+        case OPCODE_bc:     // BoundedChoose
+          {
+            SemanticNode pred = args[0];
+            SemanticNode inExpr = expr.getBdedQuantBounds()[0];
+            Value inVal = this.eval(inExpr, c, s0, s1, control, cm);
+            if (!(inVal instanceof Enumerable)) {
+              Assert.fail("Attempted to compute the value of an expression of\n" +
+                          "form CHOOSE x \\in S: P, but S was not enumerable.\n" + expr);
+            }
+
+            // To fix Bugzilla Bug 279 : TLC bug caused by TLC's not preserving the semantics of CHOOSE
+            // (@see tlc2.tool.BugzillaBug279Test),
+            // the statement
+            //
+            //    inVal.normalize();
+            //
+            // was replaced by the following by LL on 7 Mar 2012.  This fix has not yet received
+            // the blessing of Yuan Yu, so it should be considered to be provisional.
+            // 
+            //     Value convertedVal = inVal.ToSetEnum();
+            //       if (convertedVal != null) {
+            //         inVal = convertedVal;
+            //       } else {
+            //         inVal.normalize();
+            //     }
+            // end of fix.
+            
+            // MAK 09/22/2018:
+			// The old fix above has the undesired side effect of enumerating inVal. In
+			// other words, e.g. a SUBSET 1..8 would be enumerated and normalized into a
+			// SetEnumValue. This is expensive and especially overkill, if the CHOOSE
+			// predicate holds for most if not all elements of inVal. In this case, we
+            // don't want to fully enumerate inVal but instead return the first element
+			// obtained from Enumerable#elements for which the predicate holds. Thus,
+			// Enumerable#elements(Ordering) has been added by which we make the requirement
+			// for elements to be normalized explicit. Implementor of Enumerable, such as
+			// SubsetValue are then free to implement elements that returns elements in
+			// normalized order without converting SubsetValue into SetEnumValue first.
+            
+            inVal.normalize();
+
+            ValueEnumeration enumSet = ((Enumerable)inVal).elements(Enumerable.Ordering.NORMALIZED);
+            FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0];
+            boolean isTuple = expr.isBdedQuantATuple()[0];
+            if (isTuple) {
+              // Identifier tuple case:
+              int cnt = bvars.length;
+              Value val;
+              while ((val = enumSet.nextElement()) != null) {
+                TupleValue tv = (TupleValue) val.toTuple();
+                if (tv == null || tv.size() != cnt) {
+                  Assert.fail("Attempted to compute the value of an expression of form\n" +
+                              "CHOOSE <<x1, ... , xN>> \\in S: P, but S was not a set\n" +
+                              "of N-tuples.\n" + expr);
+                }
+                Context c1 = c;
+                for (int i = 0; i < cnt; i++) {
+                  c1 = c1.cons(bvars[i], tv.elems[i]);
+                }
+                Value bval = this.eval(pred, c1, s0, s1, control, cm);
+                if (!(bval instanceof BoolValue)) {
+                  Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
+                }
+                if (((BoolValue)bval).val) {
+                  return (Value) val;
+                }
+              }
+            }
+            else {
+              // Simple identifier case:
+              SymbolNode name = bvars[0];
+              Value val;
+              while ((val = enumSet.nextElement()) != null) {
+                Context c1 = c.cons(name, val);
+                Value bval = this.eval(pred, c1, s0, s1, control, cm);
+                if (!(bval instanceof BoolValue)) {
+                  Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
+                }
+                if (((BoolValue)bval).val) {
+                  return (Value) val;
+                }
+              }
+            }
+            Assert.fail("Attempted to compute the value of an expression of form\n" +
+                        "CHOOSE x \\in S: P, but no element of S satisfied P.\n" + expr);
+            return null;    // make compiler happy
+          }
+        case OPCODE_be:     // BoundedExists
+          {
+            ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control, cm);
+            SemanticNode body = args[0];
+            Context c1;
+            while ((c1 = Enum.nextElement()) != null) {
+              Value bval = this.eval(body, c1, s0, s1, control, cm);
+              if (!(bval instanceof BoolValue)) {
+                Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
+              }
+              if (((BoolValue)bval).val) {
+                return BoolValue.ValTrue;
+              }
+            }
+            return BoolValue.ValFalse;
+          }
+        case OPCODE_bf:     // BoundedForall
+          {
+            ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control, cm);
+            SemanticNode body = args[0];
+            Context c1;
+            while ((c1 = Enum.nextElement()) != null) {
+              Value bval = this.eval(body, c1, s0, s1, control, cm);
+              if (!(bval instanceof BoolValue)) {
+                Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
+              }
+              if (!((BoolValue)bval).val) {
+                return BoolValue.ValFalse;
+              }
+            }
+            return BoolValue.ValTrue;
+          }
+        case OPCODE_case:   // Case
+          {
+            int alen = args.length;
+            SemanticNode other = null;
+            for (int i = 0; i < alen; i++) {
+              OpApplNode pairNode = (OpApplNode)args[i];
+              ExprOrOpArgNode[] pairArgs = pairNode.getArgs();
+              if (pairArgs[0] == null) {
+                other = pairArgs[1];
+                if (coverage) { cm = cm.get(pairNode); }
+               }
+              else {
+                Value bval = this.eval(pairArgs[0], c, s0, s1, control, coverage ? cm.get(pairNode) : cm);
+                if (!(bval instanceof BoolValue)) {
+                  Assert.fail("A non-boolean expression (" + bval.getKindString() +
+                              ") was used as a condition of a CASE. " + pairArgs[0]);
+                }
+                if (((BoolValue)bval).val) {
+                  return this.eval(pairArgs[1], c, s0, s1, control, coverage ? cm.get(pairNode) : cm);
+                }
+              }
+            }
+            if (other == null) {
+              Assert.fail("Attempted to evaluate a CASE with no conditions true.\n" + expr);
+            }
+            return this.eval(other, c, s0, s1, control, cm);
+          }
+        case OPCODE_cp:     // CartesianProd
+          {
+            int alen = args.length;
+            Value[] sets = new Value[alen];
+            for (int i = 0; i < alen; i++) {
+              sets[i] = this.eval(args[i], c, s0, s1, control, cm);
+            }
+            return setSource(expr, new SetOfTuplesValue(sets, cm));
+          }
+        case OPCODE_cl:     // ConjList
+          {
+            int alen = args.length;
+            for (int i = 0; i < alen; i++) {
+              Value bval = this.eval(args[i], c, s0, s1, control, cm);
+              if (!(bval instanceof BoolValue)) {
+                Assert.fail("A non-boolean expression (" + bval.getKindString() +
+                            ") was used as a formula in a conjunction.\n" + args[i]);
+              }
+              if (!((BoolValue)bval).val) {
+                return BoolValue.ValFalse;
+              }
+            }
+            return BoolValue.ValTrue;
+          }
+        case OPCODE_dl:     // DisjList
+          {
+            int alen = args.length;
+            for (int i = 0; i < alen; i++) {
+              Value bval = this.eval(args[i], c, s0, s1, control, cm);
+              if (!(bval instanceof BoolValue)) {
+                Assert.fail("A non-boolean expression (" + bval.getKindString() +
+                            ") was used as a formula in a disjunction.\n" + args[i]);
+              }
+              if (((BoolValue)bval).val) {
+                return BoolValue.ValTrue;
+              }
+            }
+            return BoolValue.ValFalse;
+          }
+        case OPCODE_exc:    // Except
+          {
+            int alen = args.length;
+            Value result = this.eval(args[0], c, s0, s1, control, cm);
+            // SZ: variable not used ValueExcept[] expts = new ValueExcept[alen-1];
+            for (int i = 1; i < alen; i++) {
+              OpApplNode pairNode = (OpApplNode)args[i];
+              ExprOrOpArgNode[] pairArgs = pairNode.getArgs();
+              SemanticNode[] cmpts = ((OpApplNode)pairArgs[0]).getArgs();
+
+              Value[] lhs = new Value[cmpts.length];
+              for (int j = 0; j < lhs.length; j++) {
+                lhs[j] = this.eval(cmpts[j], c, s0, s1, control,  coverage ? cm.get(pairNode).get(pairArgs[0]) : cm);
+              }
+              Value atVal = result.select(lhs);
+              if (atVal == null) {
+                // Do nothing but warn:
+                  MP.printWarning(EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD, new String[]{args[0].toString()});
+              }
+              else {
+                Context c1 = c.cons(EXCEPT_AT, atVal);
+                Value rhs = this.eval(pairArgs[1], c1, s0, s1, control,  coverage ? cm.get(pairNode) : cm);
+                ValueExcept vex = new ValueExcept(lhs, rhs);
+                result = (Value) result.takeExcept(vex);
+              }
+            }
+            return result;
+          }
+        case OPCODE_fa:     // FcnApply
+          {
+            Value result = null;
+            Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(control), cm);
+            if ((fval instanceof FcnRcdValue) ||
+                (fval instanceof FcnLambdaValue)) {
+              Applicable fcn = (Applicable)fval;
+              Value argVal = this.eval(args[1], c, s0, s1, control, cm);
+              result = fcn.apply(argVal, control);
+            }
+            else if ((fval instanceof TupleValue) ||
+                     (fval instanceof RecordValue)) {
+              Applicable fcn = (Applicable)fval;
+              if (args.length != 2) {
+                Assert.fail("Attempted to evaluate an expression of form f[e1, ... , eN]" +
+                            "\nwith f a tuple or record and N > 1.\n" + expr);
+              }
+              Value aval = this.eval(args[1], c, s0, s1, control, cm);
+              result = fcn.apply(aval, control);
+            }
+            else {
+              Assert.fail("A non-function (" + fval.getKindString() + ") was applied" +
+                          " as a function.\n" + expr);
+            }
+            return result;
+          }
+        case OPCODE_fc:     // FcnConstructor
+        case OPCODE_nrfs:   // NonRecursiveFcnSpec
+        case OPCODE_rfs:    // RecursiveFcnSpec
+          {
+            FormalParamNode[][] formals = expr.getBdedQuantSymbolLists();
+            boolean[] isTuples = expr.isBdedQuantATuple();
+            ExprNode[] domains = expr.getBdedQuantBounds();
+
+            Value[] dvals = new Value[domains.length];
+            boolean isFcnRcd = true;
+            for (int i = 0; i < dvals.length; i++) {
+              dvals[i] = this.eval(domains[i], c, s0, s1, control, cm);
+              isFcnRcd = isFcnRcd && (dvals[i] instanceof Reducible);
+            }
+            FcnParams params = new FcnParams(formals, isTuples, dvals);
+
+            SemanticNode fbody = args[0];
+            FcnLambdaValue fval = (FcnLambdaValue) setSource(expr, new FcnLambdaValue(params, fbody, this, c, s0, s1, control, cm));
+            if (opcode == OPCODE_rfs) {
+              SymbolNode fname = expr.getUnbdedQuantSymbols()[0];
+              fval.makeRecursive(fname);
+              isFcnRcd = false;
+            }
+            if (isFcnRcd && !EvalControl.isKeepLazy(control)) {
+              return (Value) fval.toFcnRcd();
+            }
+            return fval;
+          }
+        case OPCODE_ite:    // IfThenElse
+          {
+            Value bval = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(bval instanceof BoolValue)) {
+              Assert.fail("A non-boolean expression (" + bval.getKindString() +
+                          ") was used as the condition of an IF.\n" + expr);
+            }
+            if (((BoolValue)bval).val) {
+              return this.eval(args[1], c, s0, s1, control, cm);
+            }
+            return this.eval(args[2], c, s0, s1, control, cm);
+          }
+        case OPCODE_rc:     // RcdConstructor
+          {
+            int alen = args.length;
+            UniqueString[] names = new UniqueString[alen];
+            Value[] vals = new Value[alen];
+            for (int i = 0; i < alen; i++) {
+              OpApplNode pairNode = (OpApplNode)args[i];
+              ExprOrOpArgNode[] pair = pairNode.getArgs();
+              names[i] = ((StringValue)pair[0].getToolObject(toolId)).getVal();
+              vals[i] = this.eval(pair[1], c, s0, s1, control, coverage ? cm.get(pairNode) : cm);
+            }
+            return setSource(expr, new RecordValue(names, vals, false, cm));
+          }
+        case OPCODE_rs:     // RcdSelect
+          {
+            Value rval = this.eval(args[0], c, s0, s1, control, cm);
+            Value sval = (Value) WorkerValue.mux(args[1].getToolObject(toolId));
+            if (rval instanceof RecordValue) {
+              Value result = (Value) ((RecordValue)rval).select(sval);
+              if (result == null) {
+                Assert.fail("Attempted to select nonexistent field " + sval + " from the" +
+                            " record\n" + Values.ppr(rval.toString()) + "\n" + expr);
+              }
+              return result;
+            }
+            else {
+              FcnRcdValue fcn = (FcnRcdValue) rval.toFcnRcd();
+              if (fcn == null) {
+                Assert.fail("Attempted to select field " + sval + " from a non-record" +
+                            " value " + Values.ppr(rval.toString()) + "\n" + expr);
+              }
+              return fcn.apply(sval, control);
+            }
+          }
+        case OPCODE_se:     // SetEnumerate
+          {
+            int alen = args.length;
+            ValueVec vals = new ValueVec(alen);
+            for (int i = 0; i < alen; i++) {
+              vals.addElement(this.eval(args[i], c, s0, s1, control, cm));
+            }
+            return setSource(expr, new SetEnumValue(vals, false, cm));
+          }
+        case OPCODE_soa:    // SetOfAll: {e(x) : x \in S}
+          {
+            ValueVec vals = new ValueVec();
+            ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control, cm);
+            SemanticNode body = args[0];
+            Context c1;
+            while ((c1 = Enum.nextElement()) != null) {
+              Value val = this.eval(body, c1, s0, s1, control, cm);
+              vals.addElement(val);
+              // vals.addElement1(val);
+            }
+            return setSource(expr, new SetEnumValue(vals, false, cm));
+          }
+        case OPCODE_sor:    // SetOfRcds
+          {
+            int alen = args.length;
+            UniqueString names[] = new UniqueString[alen];
+            Value vals[] = new Value[alen];
+            for (int i = 0; i < alen; i++) {
+              OpApplNode pairNode = (OpApplNode)args[i];
+              ExprOrOpArgNode[] pair = pairNode.getArgs();
+              names[i] = ((StringValue)pair[0].getToolObject(toolId)).getVal();
+              vals[i] = this.eval(pair[1], c, s0, s1, control, coverage ? cm.get(pairNode) : cm);
+            }
+            return setSource(expr, new SetOfRcdsValue(names, vals, false, cm));
+          }
+        case OPCODE_sof:    // SetOfFcns
+          {
+            Value lhs = this.eval(args[0], c, s0, s1, control, cm);
+            Value rhs = this.eval(args[1], c, s0, s1, control, cm);
+            return setSource(expr, new SetOfFcnsValue(lhs, rhs, cm));
+          }
+        case OPCODE_sso:    // SubsetOf
+          {
+            SemanticNode pred = args[0];
+            SemanticNode inExpr = expr.getBdedQuantBounds()[0];
+            Value inVal = this.eval(inExpr, c, s0, s1, control, cm);
+            boolean isTuple = expr.isBdedQuantATuple()[0];
+            FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0];
+            if (inVal instanceof Reducible) {
+              ValueVec vals = new ValueVec();
+              ValueEnumeration enumSet = ((Enumerable)inVal).elements();
+              Value elem;
+              if (isTuple) {
+                while ((elem = enumSet.nextElement()) != null) {
+                  Context c1 = c;
+                  Value[] tuple = ((TupleValue)elem).elems;
+                  for (int i = 0; i < bvars.length; i++) {
+                    c1 = c1.cons(bvars[i], tuple[i]);
+                  }
+                  Value bval = this.eval(pred, c1, s0, s1, control, cm);
+                  if (!(bval instanceof BoolValue)) {
+                    Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" +
+                                " when P was " + bval.getKindString() + ".\n" + pred);
+                  }
+                  if (((BoolValue)bval).val) {
+                    vals.addElement(elem);
+                  }
+                }
+              }
+              else {
+                SymbolNode idName = bvars[0];
+                while ((elem = enumSet.nextElement()) != null) {
+                  Context c1 = c.cons(idName, elem);
+                  Value bval = this.eval(pred, c1, s0, s1, control, cm);
+                  if (!(bval instanceof BoolValue)) {
+                    Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" +
+                                " when P was " + bval.getKindString() + ".\n" + pred);
+                  }
+                  if (((BoolValue)bval).val) {
+                    vals.addElement(elem);
+                  }
+                }
+              }
+              return setSource(expr, new SetEnumValue(vals, inVal.isNormalized(), cm));
+            }
+            else if (isTuple) {
+              return setSource(expr, new SetPredValue(bvars, inVal, pred, this, c, s0, s1, control, cm));
+            }
+            else {
+              return setSource(expr, new SetPredValue(bvars[0], inVal, pred, this, c, s0, s1, control, cm));
+            }
+          }
+        case OPCODE_tup:    // Tuple
+          {
+            int alen = args.length;
+            Value[] vals = new Value[alen];
+            for (int i = 0; i < alen; i++) {
+              vals[i] = this.eval(args[i], c, s0, s1, control, cm);
+            }
+            return setSource(expr, new TupleValue(vals, cm));
+          }
+        case OPCODE_uc:     // UnboundedChoose
+          {
+            Assert.fail("TLC attempted to evaluate an unbounded CHOOSE.\n" +
+                        "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" +
+                        expr);
+            return null;    // make compiler happy
+          }
+        case OPCODE_ue:     // UnboundedExists
+          {
+            Assert.fail("TLC attempted to evaluate an unbounded \\E.\n" +
+                        "Make sure that the expression is of form \\E x \\in S: P(x).\n" +
+                        expr);
+            return null;    // make compiler happy
+          }
+        case OPCODE_uf:     // UnboundedForall
+          {
+            Assert.fail("TLC attempted to evaluate an unbounded \\A.\n" +
+                        "Make sure that the expression is of form \\A x \\in S: P(x).\n" +
+                        expr);
+            return null;    // make compiler happy
+          }
+        case OPCODE_lnot:
+          {
+            Value arg = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(arg instanceof BoolValue)) {
+              Assert.fail("Attempted to apply the operator ~ to a non-boolean\n(" +
+                          arg.getKindString() + ")\n" + expr);
+            }
+            return (((BoolValue)arg).val) ? BoolValue.ValFalse : BoolValue.ValTrue;
+          }
+        case OPCODE_subset:
+          {
+            Value arg = this.eval(args[0], c, s0, s1, control, cm);
+			return setSource(expr, new SubsetValue(arg, cm));
+          }
+        case OPCODE_union:
+          {
+            Value arg = this.eval(args[0], c, s0, s1, control, cm);
+            return setSource(expr, UnionValue.union(arg));
+          }
+        case OPCODE_domain:
+          {
+            Value arg = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(arg instanceof Applicable)) {
+              Assert.fail("Attempted to apply the operator DOMAIN to a non-function\n(" +
+                          arg.getKindString() + ")\n" + expr);
+            }
+            return setSource(expr, ((Applicable)arg).getDomain());
+          }
+        case OPCODE_enabled:
+          {
+            TLCState sfun = TLCStateFun.Empty;
+            Context c1 = Context.branch(c);
+            sfun = this.enabled(args[0], ActionItemList.Empty, c1, s0, sfun, cm);
+            return (sfun != null) ? BoolValue.ValTrue : BoolValue.ValFalse;
+          }
+        case OPCODE_eq:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            return (arg1.equals(arg2)) ? BoolValue.ValTrue : BoolValue.ValFalse;
+          }
+        case OPCODE_land:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(arg1 instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form P /\\ Q" +
+                          " when P was\n" + arg1.getKindString() + ".\n" + expr);
+            }
+            if (((BoolValue)arg1).val) {
+              Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+              if (!(arg2 instanceof BoolValue)) {
+                Assert.fail("Attempted to evaluate an expression of form P /\\ Q" +
+                            " when Q was\n" + arg2.getKindString() + ".\n" + expr);
+              }
+              return arg2;
+            }
+            return BoolValue.ValFalse;
+          }
+        case OPCODE_lor:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(arg1 instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form P \\/ Q" +
+                          " when P was\n" + arg1.getKindString() + ".\n" + expr);
+            }
+            if (((BoolValue)arg1).val) {
+              return BoolValue.ValTrue;
+            }
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            if (!(arg2 instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form P \\/ Q" +
+                          " when Q was\n" + arg2.getKindString() + ".\n" + expr);
+            }
+            return arg2;
+          }
+        case OPCODE_implies:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(arg1 instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form P => Q" +
+                          " when P was\n" + arg1.getKindString() + ".\n" + expr);
+            }
+            if (((BoolValue)arg1).val) {
+              Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+              if (!(arg2 instanceof BoolValue)) {
+                Assert.fail("Attempted to evaluate an expression of form P => Q" +
+                            " when Q was\n" + arg2.getKindString() + ".\n" + expr);
+              }
+              return arg2;
+            }
+            return BoolValue.ValTrue;
+          }
+        case OPCODE_equiv:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            if (!(arg1 instanceof BoolValue) || !(arg2 instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form P <=> Q" +
+                          " when P or Q was not a boolean.\n" + expr);
+            }
+            BoolValue bval1 = (BoolValue)arg1;
+            BoolValue bval2 = (BoolValue)arg2;
+            return (bval1.val == bval2.val) ? BoolValue.ValTrue : BoolValue.ValFalse;
+          }
+        case OPCODE_noteq:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            return arg1.equals(arg2) ? BoolValue.ValFalse : BoolValue.ValTrue;
+          }
+        case OPCODE_subseteq:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            if (!(arg1 instanceof Enumerable)) {
+              Assert.fail("Attempted to evaluate an expression of form S \\subseteq T," +
+                          " but S was not enumerable.\n" + expr);
+            }
+            return ((Enumerable) arg1).isSubsetEq(arg2);
+          }
+        case OPCODE_in:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            return (arg2.member(arg1)) ? BoolValue.ValTrue : BoolValue.ValFalse;
+          }
+        case OPCODE_notin:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            return (arg2.member(arg1)) ? BoolValue.ValFalse : BoolValue.ValTrue;
+          }
+        case OPCODE_setdiff:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            if (arg1 instanceof Reducible) {
+              return setSource(expr, ((Reducible)arg1).diff(arg2));
+            }
+            return setSource(expr, new SetDiffValue(arg1, arg2));
+          }
+        case OPCODE_cap:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            if (arg1 instanceof Reducible) {
+              return setSource(expr, ((Reducible)arg1).cap(arg2));
+            }
+            else if (arg2 instanceof Reducible) {
+              return setSource(expr, ((Reducible)arg2).cap(arg1));
+            }
+            return setSource(expr, new SetCapValue(arg1, arg2));
+          }
+        case OPCODE_nop:
+          // Added by LL on 2 Aug 2007
+          {
+            return eval(args[0], c, s0, s1, control, cm);
+          }
+        case OPCODE_cup:
+          {
+            Value arg1 = this.eval(args[0], c, s0, s1, control, cm);
+            Value arg2 = this.eval(args[1], c, s0, s1, control, cm);
+            if (arg1 instanceof Reducible) {
+              return setSource(expr, ((Reducible)arg1).cup(arg2));
+            }
+            else if (arg2 instanceof Reducible) {
+              return setSource(expr, ((Reducible)arg2).cup(arg1));
+            }
+            return setSource(expr, new SetCupValue(arg1, arg2, cm));
+          }
+        case OPCODE_prime:
+          {
+        	  // MAK 03/2019:  Cannot reproduce this but without this check the nested evaluation
+        	  // fails with a NullPointerException which subsequently is swallowed. This makes it 
+        	  // impossible for a user to diagnose what is going on.  Since I cannot reproduce the
+        	  // actual expression, I leave this commented for.  I recall an expression along the
+        	  // lines of:
+        	  //    ...
+        	  //    TLCSet(23, CHOOSE p \in pc: pc[p] # pc[p]')
+        	  //    ...
+        	  // The fail statement below is obviously too generic to be useful and needs to be
+        	  // clarified if the actual cause has been identified.
+//        	  if (s1 == null) {
+//                  Assert.fail("Attempted to evaluate the following expression," +
+//                          " but expression failed to evaluate.\n" + expr);
+//        	  }
+            return this.eval(args[0], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm);
+          }
+        case OPCODE_unchanged:
+          {
+            Value v0 = this.eval(args[0], c, s0, TLCState.Empty, control, cm);
+            Value v1 = this.eval(args[0], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm);
+            return (v0.equals(v1)) ? BoolValue.ValTrue : BoolValue.ValFalse;
+          }
+        case OPCODE_aa:     // <A>_e
+          {
+            Value res = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(res instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form <A>_e," +
+                          " but A was not a boolean.\n" + expr);
+            }
+            if (!((BoolValue)res).val) {
+              return BoolValue.ValFalse;
+            }
+            Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control, cm);
+            Value v1 = this.eval(args[1], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm);
+            return v0.equals(v1) ? BoolValue.ValFalse : BoolValue.ValTrue;
+          }
+        case OPCODE_sa:     // [A]_e
+          {
+            Value res = this.eval(args[0], c, s0, s1, control, cm);
+            if (!(res instanceof BoolValue)) {
+              Assert.fail("Attempted to evaluate an expression of form [A]_e," +
+                          " but A was not a boolean.\n" + expr);
+            }
+            if (((BoolValue)res).val) {
+              return BoolValue.ValTrue;
+            }
+            Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control, cm);
+            Value v1 = this.eval(args[1], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm);
+            return (v0.equals(v1)) ? BoolValue.ValTrue : BoolValue.ValFalse;
+          }
+        case OPCODE_cdot:
+          {
+            Assert.fail("The current version of TLC does not support action composition.");
+            /***
+            TLCState s01 = TLCStateFun.Empty;
+            StateVec iss = new StateVec(0);
+            this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss);
+            int sz = iss.size();
+            for (int i = 0; i < sz; i++) {
+              s01 = iss.elementAt(i);
+              this.eval(args[1], c, s01, s1, control);
+            }
+            ***/
+            return null;    // make compiler happy
+          }
+        case OPCODE_sf:     // SF
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"SF", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_wf:     // WF
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"WF", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_te:     // TemporalExists
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\EE", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_tf:     // TemporalForAll
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\AA", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_leadto:
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a ~> b", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_arrow:
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a -+-> formula", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_box:
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"[]A", expr.toString()});
+            return null;    // make compiler happy
+          }
+        case OPCODE_diamond:
+          {
+            Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"<>A", expr.toString()});
+            return null;    // make compiler happy
+          }
+
+        default:
+          {
+            Assert.fail("TLC BUG: could not evaluate this expression.\n" + expr);
+            return null;
+          }
+        }
+  }
+
+  protected abstract Value setSource(final SemanticNode expr, final Value value);
+
+  /**
+   * This method determines if the argument is a valid state.  A state
+   * is good iff it assigns legal explicit values to all the global
+   * state variables.
+   */
+  @Override
+  public final boolean isGoodState(TLCState state) {
+    return state.allAssigned();
+  }
+
+  /* This method determines if a state satisfies the model constraints. */
+  @Override
+  public final boolean isInModel(TLCState state) throws EvalException {
+    ExprNode[] constrs = this.getModelConstraints();
+    for (int i = 0; i < constrs.length; i++) {
+      IValue bval = this.eval(constrs[i], Context.Empty, state, CostModel.DO_NOT_RECORD);
+      if (!(bval instanceof BoolValue)) {
+        Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()});
+      }
+      if (!((BoolValue)bval).val) return false;
+    }
+    return true;
+  }
+
+  /* This method determines if a pair of states satisfy the action constraints. */
+  @Override
+  public final boolean isInActions(TLCState s1, TLCState s2) throws EvalException {
+    ExprNode[] constrs = this.getActionConstraints();
+    for (int i = 0; i < constrs.length; i++) {
+      Value bval = this.eval(constrs[i], Context.Empty, s1, s2, EvalControl.Clear, CostModel.DO_NOT_RECORD);
+      if (!(bval instanceof BoolValue)) {
+        Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()});
+      }
+      if (!((BoolValue)bval).val) return false;
+    }
+    return true;
+  }
+  
+  @Override
+  public final boolean hasStateOrActionConstraints() {
+	  return this.getModelConstraints().length > 0 || this.getActionConstraints().length > 0;
+  }
+  
+  @Override
+  public final TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1) {
+		  return enabled(pred, ActionItemList.Empty, c, s0, s1, CostModel.DO_NOT_RECORD);
+  }
+  
+  @Override
+  public final TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1, ExprNode subscript, final int ail) {
+      ActionItemList acts = (ActionItemList) ActionItemList.Empty.cons(subscript, c, CostModel.DO_NOT_RECORD, ail);
+	  return enabled(pred, acts, c, s0, s1, CostModel.DO_NOT_RECORD);
+  }
+  
+  @Override
+  public final TLCState enabled(SemanticNode pred, IActionItemList acts, Context c, TLCState s0, TLCState s1) {
+		  return enabled(pred, acts, c, s0, s1, CostModel.DO_NOT_RECORD);
+  }
+
+  /**
+   * This method determines if an action is enabled in the given state.
+   * More precisely, it determines if (act.pred /\ (sub' # sub)) is
+   * enabled in the state s and context act.con.
+   */
+  @Override
+  public abstract TLCState enabled(SemanticNode pred, IActionItemList acts,
+                                Context c, TLCState s0, TLCState s1, CostModel cm);
+
+  protected final TLCState enabledImpl(SemanticNode pred, ActionItemList acts,
+          Context c, TLCState s0, TLCState s1, CostModel cm) {
+        switch (pred.getKind()) {
+        case OpApplKind:
+          {
+            OpApplNode pred1 = (OpApplNode)pred;
+            return this.enabledAppl(pred1, acts, c, s0, s1, cm);
+          }
+        case LetInKind:
+          {
+            LetInNode pred1 = (LetInNode)pred;
+            OpDefNode[] letDefs = pred1.getLets();
+            Context c1 = c;
+            for (int i = 0; i < letDefs.length; i++) {
+              OpDefNode opDef = letDefs[i];
+              if (opDef.getArity() == 0) {
+                Value rhs = new LazyValue(opDef.getBody(), c1, cm);
+                c1 = c1.cons(opDef, rhs);
+              }
+            }
+            return this.enabled(pred1.getBody(), acts, c1, s0, s1, cm);
+          }
+        case SubstInKind:
+          {
+            SubstInNode pred1 = (SubstInNode)pred;
+            Subst[] subs = pred1.getSubsts();
+            int slen = subs.length;
+            Context c1 = c;
+            for (int i = 0; i < slen; i++) {
+              Subst sub = subs[i];
+              c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, coverage ? sub.getCM() : cm, toolId));
+            }
+            return this.enabled(pred1.getBody(), acts, c1, s0, s1, cm);
+          }
+        // Added by LL on 13 Nov 2009 to handle theorem and assumption names.
+        case APSubstInKind:
+          {
+            APSubstInNode pred1 = (APSubstInNode)pred;
+            Subst[] subs = pred1.getSubsts();
+            int slen = subs.length;
+            Context c1 = c;
+            for (int i = 0; i < slen; i++) {
+              Subst sub = subs[i];
+              c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, cm, toolId));
+            }
+            return this.enabled(pred1.getBody(), acts, c1, s0, s1, cm);
+          }
+        // LabelKind class added by LL on 13 Jun 2007
+        case LabelKind:
+          {
+            LabelNode pred1 = (LabelNode)pred;
+            return this.enabled(pred1.getBody(), acts, c, s0, s1, cm);
+          }
+        default:
+          {
+            // We should not compute enabled on anything else.
+            Assert.fail("Attempted to compute ENABLED on a non-boolean expression.\n" + pred);
+            return null;    // make compiler happy
+          }
+        }
+  }
+
+  private final TLCState enabled(ActionItemList acts, TLCState s0, TLCState s1, CostModel cm) {
+    if (acts.isEmpty()) return s1;
+
+    final int kind = acts.carKind();
+    SemanticNode pred = acts.carPred();
+    Context c = acts.carContext();
+    cm = acts.cm;
+    ActionItemList acts1 = acts.cdr();
+    if (kind > IActionItemList.CONJUNCT) {
+      TLCState res = this.enabled(pred, acts1, c, s0, s1, cm);
+      return res;
+    }
+    else if (kind == IActionItemList.PRED) {
+      TLCState res = this.enabled(pred, acts1, c, s0, s1, cm);
+      return res;
+    }
+    if (kind == IActionItemList.UNCHANGED) {
+      TLCState res = this.enabledUnchanged(pred, acts1, c, s0, s1, cm);
+      return res;
+    }
+
+    Value v1 = this.eval(pred, c, s0, TLCState.Empty, EvalControl.Enabled, cm);
+	// We are now in ENABLED and primed state. Second TLCState parameter being null
+	// effectively disables LazyValue in evalAppl (same effect as
+	// EvalControl.setPrimed(EvalControl.Enabled)).
+    Value v2 = this.eval(pred, c, s1, null, EvalControl.Primed, cm);
+
+    if (v1.equals(v2)) return null;
+    TLCState res = this.enabled(acts1, s0, s1, cm);
+    return res;
+  }
+
+  protected abstract TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm);
+
+  protected final TLCState enabledApplImpl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm)
+  {
+    if (coverage) {cm = cm.get(pred);}
+        ExprOrOpArgNode[] args = pred.getArgs();
+        int alen = args.length;
+        SymbolNode opNode = pred.getOperator();
+        int opcode = BuiltInOPs.getOpCode(opNode.getName());
+
+        if (opcode == 0)
+        {
+          // This is a user-defined operator with one exception: it may
+          // be substed by a builtin operator. This special case occurs
+          // when the lookup returns an OpDef with opcode # 0.
+          Object val = this.lookup(opNode, c, s0, false);
+
+          if (val instanceof OpDefNode)
+          {
+            OpDefNode opDef = (OpDefNode) val;
+            opcode = BuiltInOPs.getOpCode(opDef.getName());
+            if (opcode == 0)
+            {
+              // Context c1 = this.getOpContext(opDef, args, c, false);
+              Context c1 = this.getOpContext(opDef, args, c, true, cm, toolId);
+              return this.enabled(opDef.getBody(), acts, c1, s0, s1, cm);
+            }
+          }
+
+
+          // Added 13 Nov 2009 by LL to handle theorem or assumption names
+          /*********************************************************************
+          * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode    *
+          * imported with parameterized instantiation.                         *
+          *********************************************************************/
+          if (val instanceof ThmOrAssumpDefNode)
+          {
+            ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val;
+            Context c1 = this.getOpContext(opDef, args, c, true);
+            return this.enabled(opDef.getBody(), acts, c1, s0, s1, cm);
+          }
+
+
+          if (val instanceof LazyValue)
+          {
+            LazyValue lv = (LazyValue) val;
+            return this.enabled(lv.expr, acts, lv.con, s0, s1, lv.cm);
+          }
+
+          Object bval = val;
+          if (alen == 0)
+          {
+            if (val instanceof MethodValue)
+            {
+              bval = ((MethodValue) val).apply(EmptyArgs, EvalControl.Clear); // EvalControl.Clear is ignored by MethodValuea#apply
+            } else if (val instanceof EvaluatingValue) {
+              bval = ((EvaluatingValue) val).eval(this, args, c, s0, s1, EvalControl.Enabled, cm);
+            }
+          } else
+          {
+            if (val instanceof OpValue)
+            {
+            	bval = ((OpValue) val).eval(this, args, c, s0, s1, EvalControl.Enabled, cm);
+             }
+          }
+
+          if (opcode == 0)
+          {
+            if (!(bval instanceof BoolValue))
+            {
+              Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean",
+                      bval.toString(), pred.toString() });
+            }
+            if (((BoolValue) bval).val)
+            {
+              return this.enabled(acts, s0, s1, cm);
+            }
+            return null;
+          }
+        }
+
+        switch (opcode) {
+        case OPCODE_aa: // AngleAct <A>_e
+          {
+        	  ActionItemList acts1 = (ActionItemList) acts.cons(args[1], c, cm, IActionItemList.CHANGED);
+            return this.enabled(args[0], acts1, c, s0, s1, cm);
+          }
+        case OPCODE_be: // BoundedExists
+          {
+            SemanticNode body = args[0];
+            ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled, cm);
+            Context c1;
+            while ((c1 = Enum.nextElement()) != null)
+            {
+              TLCState s2 = this.enabled(body, acts, c1, s0, s1, cm);
+              if (s2 != null) {
+                return s2;
+              }
+            }
+            return null;
+          }
+        case OPCODE_bf: // BoundedForall
+          {
+            SemanticNode body = args[0];
+            ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled, cm);
+            Context c1 = Enum.nextElement();
+            if (c1 == null)
+            {
+              return this.enabled(acts, s0, s1, cm);
+            }
+            ActionItemList acts1 = acts;
+            Context c2;
+            while ((c2 = Enum.nextElement()) != null)
+            {
+              acts1 = (ActionItemList) acts1.cons(body, c2, cm, IActionItemList.PRED);
+            }
+            return this.enabled(body, acts1, c1, s0, s1, cm);
+          }
+        case OPCODE_case: // Case
+          {
+            SemanticNode other = null;
+            for (int i = 0; i < alen; i++)
+            {
+              OpApplNode pair = (OpApplNode) args[i];
+              ExprOrOpArgNode[] pairArgs = pair.getArgs();
+              if (pairArgs[0] == null)
+              {
+                other = pairArgs[1];
+              } else
+              {
+                Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Enabled, cm);
+                if (!(bval instanceof BoolValue))
+                {
+                  Assert.fail("In computing ENABLED, a non-boolean expression(" + bval.getKindString()
+                          + ") was used as a guard condition" + " of a CASE.\n" + pairArgs[1]);
+                }
+                if (((BoolValue) bval).val)
+                {
+                  return this.enabled(pairArgs[1], acts, c, s0, s1, cm);
+                }
+              }
+            }
+            if (other == null)
+            {
+              Assert.fail("In computing ENABLED, TLC encountered a CASE with no" + " conditions true.\n" + pred);
+            }
+            return this.enabled(other, acts, c, s0, s1, cm);
+          }
+        case OPCODE_cl: // ConjList
+        case OPCODE_land:
+          {
+            ActionItemList acts1 = acts;
+            for (int i = alen - 1; i > 0; i--)
+            {
+              acts1 = (ActionItemList) acts1.cons(args[i], c, cm, i);
+            }
+            return this.enabled(args[0], acts1, c, s0, s1, cm);
+          }
+        case OPCODE_dl: // DisjList
+        case OPCODE_lor:
+          {
+            for (int i = 0; i < alen; i++)
+            {
+              TLCState s2 = this.enabled(args[i], acts, c, s0, s1, cm);
+              if (s2 != null) {
+                return s2;
+              }
+            }
+            return null;
+          }
+        case OPCODE_fa: // FcnApply
+          {
+            Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(EvalControl.Enabled), cm); // KeepLazy does not interfere with EvalControl.Enabled in this.evalAppl
+            if (fval instanceof FcnLambdaValue)
+            {
+              FcnLambdaValue fcn = (FcnLambdaValue) fval;
+              if (fcn.fcnRcd == null)
+              {
+                Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Enabled, cm); // EvalControl.Enabled passed on to nested this.evalAppl
+                return this.enabled(fcn.body, acts, c1, s0, s1, cm);
+              }
+              fval = fcn.fcnRcd;
+            }
+            if (fval instanceof Applicable)
+            {
+              Applicable fcn = (Applicable) fval;
+              Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Enabled, cm);
+              Value bval = fcn.apply(argVal, EvalControl.Enabled); // EvalControl.Enabled not taken into account by any subclass of Applicable
+              if (!(bval instanceof BoolValue))
+              {
+                Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "ENABLED", "boolean",
+                        pred.toString() });
+              }
+              if (!((BoolValue) bval).val) {
+                return null;
+              }
+            } else
+            {
+              Assert.fail("In computing ENABLED, a non-function (" + fval.getKindString()
+                      + ") was applied as a function.\n" + pred);
+            }
+            return this.enabled(acts, s0, s1, cm);
+          }
+        case OPCODE_ite: // IfThenElse
+          {
+            Value guard = this.eval(args[0], c, s0, s1, EvalControl.Enabled, cm);
+            if (!(guard instanceof BoolValue))
+            {
+              Assert.fail("In computing ENABLED, a non-boolean expression(" + guard.getKindString()
+                      + ") was used as the guard condition" + " of an IF.\n" + pred);
+            }
+            int idx = (((BoolValue) guard).val) ? 1 : 2;
+            return this.enabled(args[idx], acts, c, s0, s1, cm);
+          }
+        case OPCODE_sa: // SquareAct [A]_e
+          {
+            TLCState s2 = this.enabled(args[0], acts, c, s0, s1, cm);
+            if (s2 != null) {
+              return s2;
+            }
+            return this.enabledUnchanged(args[1], acts, c, s0, s1, cm);
+          }
+        case OPCODE_te: // TemporalExists
+        case OPCODE_tf: // TemporalForAll
+          {
+            Assert.fail("In computing ENABLED, TLC encountered temporal quantifier.\n" + pred);
+            return null; // make compiler happy
+          }
+        case OPCODE_uc: // UnboundedChoose
+          {
+            Assert.fail("In computing ENABLED, TLC encountered unbounded CHOOSE. "
+                    + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + pred);
+            return null; // make compiler happy
+          }
+        case OPCODE_ue: // UnboundedExists
+          {
+            Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. "
+                    + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + pred);
+            return null; // make compiler happy
+          }
+        case OPCODE_uf: // UnboundedForall
+          {
+            Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. "
+                    + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + pred);
+            return null; // make compiler happy
+          }
+        case OPCODE_sf: // SF
+          {
+            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[]{ "SF", pred.toString()});
+            return null; // make compiler happy
+          }
+        case OPCODE_wf: // WF
+          {
+            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "WF", pred.toString() });
+            return null; // make compiler happy
+          }
+        case OPCODE_box:
+          {
+            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "[]", pred.toString() });
+            return null; // make compiler happy
+          }
+        case OPCODE_diamond:
+          {
+            Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "<>", pred.toString() });
+            return null; // make compiler happy
+          }
+        case OPCODE_unchanged:
+          {
+            return this.enabledUnchanged(args[0], acts, c, s0, s1, cm);
+          }
+        case OPCODE_eq:
+          {
+            SymbolNode var = this.getPrimedVar(args[0], c, true);
+            if (var == null)
+            {
+              Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled, cm);
+              if (!((BoolValue) bval).val) {
+                return null;
+              }
+            } else
+            {
+              UniqueString varName = var.getName();
+              IValue lval = s1.lookup(varName);
+              Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled, cm);
+              if (lval == null)
+              {
+                TLCState s2 = s1.bind(var, rval);
+                return this.enabled(acts, s0, s2, cm);
+              } else
+              {
+                if (!lval.equals(rval)) {
+                  return null;
+                }
+              }
+            }
+            return this.enabled(acts, s0, s1, cm);
+          }
+        case OPCODE_implies:
+          {
+            Value bval = this.eval(args[0], c, s0, s1, EvalControl.Enabled, cm);
+            if (!(bval instanceof BoolValue))
+            {
+              Assert.fail("While computing ENABLED of an expression of the form" + " P => Q, P was "
+                      + bval.getKindString() + ".\n" + pred);
+            }
+            if (((BoolValue) bval).val)
+            {
+              return this.enabled(args[1], acts, c, s0, s1, cm);
+            }
+            return this.enabled(acts, s0, s1, cm);
+          }
+        case OPCODE_cdot:
+          {
+            Assert.fail("The current version of TLC does not support action composition.");
+            /***
+            TLCState s01 = TLCStateFun.Empty;
+            StateVec iss = new StateVec(0);
+            this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss);
+            int sz = iss.size();
+            for (int i = 0; i < sz; i++) {
+              s01 = iss.elementAt(i);
+              TLCState s2 = this.enabled(args[1], acts, c, s01, s1);
+              if (s2 != null) return s2;
+            }
+            ***/
+            return null; // make compiler happy
+          }
+        case OPCODE_leadto:
+          {
+            Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a ~> b).\n" + pred);
+            return null; // make compiler happy
+          }
+        case OPCODE_arrow:
+          {
+            Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a -+-> formula).\n" + pred);
+            return null; // make compiler happy
+          }
+        case OPCODE_in:
+          {
+            SymbolNode var = this.getPrimedVar(args[0], c, true);
+            if (var == null)
+            {
+                Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled, cm);
+                if (!((BoolValue) bval).val) {
+                  return null;
+                }
+            } else
+            {
+              UniqueString varName = var.getName();
+              Value lval = (Value) s1.lookup(varName);
+              Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled, cm);
+              if (lval == null)
+              {
+                if (!(rval instanceof Enumerable))
+                {
+                  Assert.fail("The right side of \\IN is not enumerable.\n" + pred);
+                }
+                ValueEnumeration Enum = ((Enumerable) rval).elements();
+                Value val;
+                while ((val = Enum.nextElement()) != null)
+                {
+                  TLCState s2 = s1.bind(var, val);
+                  s2 = this.enabled(acts, s0, s2, cm);
+                  if (s2 != null) {
+                    return s2;
+                  }
+                }
+                return null;
+              } else
+              {
+                if (!rval.member(lval)) {
+                  return null;
+                }
+              }
+            }
+            return this.enabled(acts, s0, s1, cm);
+          }
+        // The following case added by LL on 13 Nov 2009 to handle subexpression names.
+        case OPCODE_nop:
+          {
+            return this.enabled(args[0], acts, c, s0, s1, cm);
+          }
+
+        default:
+          {
+            // We handle all the other builtin operators here.
+            Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled, cm);
+            if (!(bval instanceof BoolValue))
+            {
+              Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean",
+                      bval.toString(), pred.toString() });
+            }
+            if (((BoolValue) bval).val)
+            {
+              return this.enabled(acts, s0, s1, cm);
+            }
+            return null;
+          }
+        }
+  }
+
+  protected abstract TLCState enabledUnchanged(SemanticNode expr, ActionItemList acts,
+                                          Context c, TLCState s0, TLCState s1, CostModel cm);
+  
+  protected final TLCState enabledUnchangedImpl(SemanticNode expr, ActionItemList acts,
+            Context c, TLCState s0, TLCState s1, CostModel cm) {
+	    if (coverage) {cm = cm.get(expr);}
+        SymbolNode var = this.getVar(expr, c, true, toolId);
+        if (var != null) {
+          // a state variable, e.g. UNCHANGED var1
+          UniqueString varName = var.getName();
+          Value v0 = this.eval(expr, c, s0, s1, EvalControl.Enabled, cm);
+          IValue v1 = s1.lookup(varName);
+          if (v1 == null) {
+            s1 = s1.bind(var, v0);
+            return this.enabled(acts, s0, s1, cm);
+          }
+          if (v1.equals(v0)) {
+            return this.enabled(acts, s0, s1, cm);
+          }
+          MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString() , expr.toString()});
+          return null;
+        }
+
+        if (expr instanceof OpApplNode) {
+          OpApplNode expr1 = (OpApplNode)expr;
+          ExprOrOpArgNode[] args = expr1.getArgs();
+          int alen = args.length;
+          SymbolNode opNode = expr1.getOperator();
+          UniqueString opName = opNode.getName();
+          int opcode = BuiltInOPs.getOpCode(opName);
+
+          if (opcode == OPCODE_tup) {
+            // a tuple, e.g. UNCHANGED <<var1, var2>>
+            if (alen != 0) {
+              ActionItemList acts1 = acts;
+              for (int i = 1; i < alen; i++) {
+                acts1 = (ActionItemList) acts1.cons(args[i], c, cm, IActionItemList.UNCHANGED);
+              }
+              return this.enabledUnchanged(args[0], acts1, c, s0, s1, cm);
+            }
+            return this.enabled(acts, s0, s1, cm);
+          }
+
+          if (opcode == 0 && alen == 0) {
+            // a 0-arity operator:
+            Object val = this.lookup(opNode, c, false);
+
+            if (val instanceof LazyValue) {
+              LazyValue lv = (LazyValue)val;
+              return this.enabledUnchanged(lv.expr, acts, lv.con, s0, s1, cm);
+            }
+            else if (val instanceof OpDefNode) {
+              return this.enabledUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1, cm);
+            }
+            else if (val == null) {
+              Assert.fail("In computing ENABLED, TLC found the undefined identifier\n" +
+                          opName + " in an UNCHANGED expression at\n" + expr);
+            }
+            return this.enabled(acts, s0, s1, cm);
+          }
+        }
+
+        final Value v0 = this.eval(expr, c, s0, TLCState.Empty, EvalControl.Enabled, cm);
+        // We are in ENABLED and primed but why pass only primed? This appears to
+        // be the only place where we call eval from the ENABLED scope without
+        // additionally passing EvalControl.Enabled. Not passing Enabled allows a 
+        // cached LazyValue could be used (see comments above on line 1384).
+        // 
+        // The current scope is a nested UNCHANGED in an ENABLED and evaluation is set
+        // to primed. However, UNCHANGED e equals e' = e , so anything primed in e
+        // becomes double-primed in ENABLED UNCHANGED e. This makes it illegal TLA+
+        // which is rejected by SANY's level checking. A perfectly valid spec - where
+        // e is not primed - but that also causes this code path to be taken is 23 below:
+        // 
+        // -------- MODULE 23 ---------
+        // VARIABLE t
+        // op(var) == var
+        // Next == /\ (ENABLED (UNCHANGED op(t)))
+        //         /\ (t'= t)
+        // Spec == (t = 0) /\ [][Next]_t
+        // ============================
+        // 
+        // However, spec 23 causes the call to this.eval(...) below to throw an
+        // EvalException either with EvalControl.Primed. The exception's message is
+        // "In evaluation, the identifier t is either undefined or not an operator."
+        // indicating that this code path is buggy.
+        // 
+        // If this bug is ever fixed to make TLC accept spec 23, EvalControl.Primed
+        // should likely be rewritten to EvalControl.setPrimed(EvalControl.Enabled)
+        // to disable reusage of LazyValues on line ~1384 above.
+		final Value v1 = this.eval(expr, c, s1, TLCState.Empty, EvalControl.Primed, cm);
+        if (!v0.equals(v1)) {
+          return null;
+        }
+        return this.enabled(acts, s0, s1, cm);
+  }
+
+  /* This method determines if the action predicate is valid in (s0, s1). */
+  @Override
+  public final boolean isValid(Action act, TLCState s0, TLCState s1) {
+    Value val = this.eval(act.pred, act.con, s0, s1, EvalControl.Clear, act.cm);
+    if (!(val instanceof BoolValue)) {
+      Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", act.pred.toString()});
+    }
+    return ((BoolValue)val).val;
+  }
+
+  /* Returns true iff the predicate is valid in the state. */
+  @Override
+  public final boolean isValid(Action act, TLCState state) {
+    return this.isValid(act, state, TLCState.Empty);
+  }
+
+  /* Returns true iff the predicate is valid in the state. */
+  @Override
+  public final boolean isValid(Action act) {
+    return this.isValid(act, TLCState.Empty, TLCState.Empty);
+  }
+
+  @Override
+  public final boolean isValid(ExprNode expr) {
+    IValue val = this.eval(expr, Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD);
+    if (!(val instanceof BoolValue)) {
+      Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()});
+    }
+    return ((BoolValue)val).val;
+  }
+
+  @Override
+  public final int checkAssumptions() {
+      final ExprNode[] assumps = getAssumptions();
+      final boolean[] isAxiom = getAssumptionIsAxiom();
+      for (int i = 0; i < assumps.length; i++)
+      {
+          try
+          {
+              if ((!isAxiom[i]) && !isValid(assumps[i]))
+              {
+                  return MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString());
+              }
+          } catch (final Exception e)
+          {
+              // Assert.printStack(e);
+              return MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+                      new String[] { assumps[i].toString(), e.getMessage() });
+          }
+      }
+      return EC.NO_ERROR;
+  }
+  
+    /* Reconstruct the initial state whose fingerprint is fp. */
+	@Override
+	public final TLCStateInfo getState(final long fp) {
+		class InitStateSelectorFunctor implements IStateFunctor {
+			private final long fp;
+			public TLCState state;
+			public InitStateSelectorFunctor(long fp) {
+				this.fp = fp;
+			}
+			@Override
+			public Object addElement(TLCState state) {
+				if (state == null) {
+					return null;
+				} else if (this.state != null) {
+					// Always return the first match found. Do not let later matches override
+					// this.state. This is in line with the original implementation that called
+					// getInitStates().
+					return null;
+				} else if (fp == state.fingerPrint()) {
+					this.state = state;
+					// TODO Stop generation of initial states preemptively. E.g. make the caller of
+					// addElement check for a special return value such as this (the functor).
+				}
+				return null;
+			}
+		}
+		// Registry a selector that extract out of the (possibly) large set of initial
+		// states the one identified by fp. The functor pattern has the advantage
+		// compared to this.getInitStates(), that it kind of streams the initial states
+		// to the functor whereas getInitStates() stores _all_ init states in a set
+		// which is traversed afterwards. This is also consistent with
+		// ModelChecker#DoInitFunctor. Using the functor pattern for the processing of
+		// init states in ModelChecker#doInit but calling getInitStates() here results
+		// in a bug during error trace generation when the set of initial states is too
+		// large for getInitStates(). Earlier TLC would have refused to run the model
+		// during ModelChecker#doInit.
+		final InitStateSelectorFunctor functor = new InitStateSelectorFunctor(fp);
+		this.getInitStates(functor);
+		if (functor.state != null) {
+			final String info = "<Initial predicate>";
+			final TLCStateInfo tlcStateInfo = new TLCStateInfo(functor.state, info, 1, fp);
+			return tlcStateInfo;
+		}
+		return null;
+	}
+
+  /**
+	 * Reconstruct the next state of state s whose fingerprint is fp.
+	 *
+	 * @return Returns the TLCState wrapped in TLCStateInfo. TLCStateInfo stores
+	 *         the stateNumber (relative to the given sinfo) and a pointer to
+	 *         the predecessor.
+	 */
+  @Override
+  public final TLCStateInfo getState(long fp, TLCStateInfo sinfo) {
+    final TLCStateInfo tlcStateInfo = getState(fp, sinfo.state);
+    if (tlcStateInfo == null) {
+      throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT);
+    }
+    tlcStateInfo.stateNumber = sinfo.stateNumber + 1;
+    tlcStateInfo.predecessorState = sinfo;
+    tlcStateInfo.fp = fp;
+    return tlcStateInfo;
+  }
+
+  /* Reconstruct the next state of state s whose fingerprint is fp. */
+  @Override
+  public final TLCStateInfo getState(long fp, TLCState s) {
+	  IdThread.setCurrentState(s);
+    for (int i = 0; i < this.actions.length; i++) {
+      Action curAction = this.actions[i];
+      StateVec nextStates = this.getNextStates(curAction, s);
+      for (int j = 0; j < nextStates.size(); j++) {
+        TLCState state = nextStates.elementAt(j);
+        long nfp = state.fingerPrint();
+        if (fp == nfp) {
+        	state.setPredecessor(s);
+          return new TLCStateInfo(state, curAction.getLocation());
+        }
+      }
+    }
+    return null;
+  }
+
+  /* Reconstruct the info for s1.   */
+  @Override
+  public final TLCStateInfo getState(TLCState s1, TLCState s) {
+	  IdThread.setCurrentState(s);
+    for (int i = 0; i < this.actions.length; i++) {
+      Action curAction = this.actions[i];
+      StateVec nextStates = this.getNextStates(curAction, s);
+      for (int j = 0; j < nextStates.size(); j++) {
+        TLCState state = nextStates.elementAt(j);
+        if (s1.equals(state)) {
+        	state.setPredecessor(s);
+          return new TLCStateInfo(state, curAction.getLocation());
+        }
+      }
+    }
+    return null;
+  }
+
+  /* Return the set of all permutations under the symmetry assumption. */
+  @Override
+  public final IMVPerm[] getSymmetryPerms() {
+    final String name = this.config.getSymmetry();
+    if (name.length() == 0) { return null; }
+    final Object symm = this.unprocessedDefns.get(name);
+    if (symm == null) {
+      Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "symmetry function", name});
+    }
+    if (!(symm instanceof OpDefNode)) {
+      Assert.fail("The symmetry function " + name + " must specify a set of permutations.");
+    }
+    final OpDefNode opDef = (OpDefNode)symm;
+    // This calls tlc2.module.TLC.Permutations(Value) and returns a Value of |fcns|
+    // = n! where n is the capacity of the symmetry set.
+    final IValue fcns = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD);
+    if (!(fcns instanceof Enumerable) || !(fcns instanceof SetEnumValue)) {
+      Assert.fail("The symmetry operator must specify a set of functions.");
+    }
+    final List<Value> values = ((SetEnumValue)fcns).elements().all();
+    for (final Value v : values) {
+    	if (!(v instanceof FcnRcdValue)) {
+    		Assert.fail("The symmetry values must be function records.");
+    	}
+    }
+    final ExprOrOpArgNode[] argNodes = ((OpApplNode)opDef.getBody()).getArgs();
+    // In the case where the config defines more than one set which is symmetric, they will pass through the
+    //		enumerable size() check even if they are single element sets
+    final StringBuilder cardinalityOneSetList = new StringBuilder();
+    int offenderCount = 0;
+    if (argNodes.length >= values.size()) {
+    	// If equal, we have as many values as we have permuted sets => we have all 1-element sets;
+    	//		if greater than, then we have a heterogenous cardinality of sets, including 0 element sets.
+    	for (final ExprOrOpArgNode node : argNodes) {
+			addToSubTwoSizedSymmetrySetList(node, cardinalityOneSetList);
+			offenderCount++;
+    	}
+    }
+    
+    final IMVPerm[] subgroup;
+    if (offenderCount == 0) {
+        subgroup = MVPerms.permutationSubgroup((Enumerable)fcns);
+        final HashSet<ModelValue> subgroupMembers = new HashSet<>();
+        for (final IMVPerm imvp : subgroup) {
+        	if (imvp instanceof MVPerm) { // should always be the case
+        		subgroupMembers.addAll(((MVPerm)imvp).getAllModelValues());
+        	}
+        }
+        for (final ExprOrOpArgNode node : argNodes) {
+        	final SetEnumValue enumValue = getSetEnumValueFromArgumentNode(node);
+        	
+        	if (enumValue != null) {
+        		final ValueEnumeration ve = enumValue.elements();
+        		
+        		boolean found = false;
+        		Value v;
+        		while ((v = ve.nextElement()) != null) {
+        			if ((v instanceof ModelValue) && subgroupMembers.contains(v)) {
+        				found = true;
+        				break;
+        			}
+        		}
+        		
+        		if (!found) {
+    				addToSubTwoSizedSymmetrySetList(node, cardinalityOneSetList);
+    				offenderCount++;
+        		}
+        	}
+        }
+    } else {
+    	subgroup = null;
+    }
+    
+    if (offenderCount > 0) {
+      final String plurality = (offenderCount > 1) ? "s" : "";
+      final String antiPlurality = (offenderCount > 1) ? "" : "s";
+      final String toHaveConjugation = (offenderCount > 1) ? "have" : "has";
+      
+      MP.printWarning(EC.TLC_SYMMETRY_SET_TOO_SMALL,
+    		  	  	  new String[] { plurality, cardinalityOneSetList.toString(), toHaveConjugation, antiPlurality });
+    }
+    
+    return subgroup;
+  }
+  
+  /**
+   * Teases the original spec name for the set out of node and appends it to the {@code StringBuilder} instance.
+   */
+  private void addToSubTwoSizedSymmetrySetList(final ExprOrOpArgNode node, final StringBuilder cardinalityOneSetList) {
+		final SyntaxTreeNode tn = (SyntaxTreeNode)node.getTreeNode();
+		final String image = tn.getHumanReadableImage();
+		final String alias;
+	    if (image.startsWith(TLAConstants.BuiltInOperators.PERMUTATIONS)) {
+		  final int imageLength = image.length();
+		  alias = image.substring((TLAConstants.BuiltInOperators.PERMUTATIONS.length() + 1),
+				  						  (imageLength - 1));
+	    } else {
+	    	alias = image;
+	    }
+		final String specDefinitionName = this.config.getOverridenSpecNameForConfigName(alias);
+		final String displayDefinition = (specDefinitionName != null) ? specDefinitionName : alias;
+		
+		if (cardinalityOneSetList.length() > 0) {
+			cardinalityOneSetList.append(", and ");
+		}
+
+		cardinalityOneSetList.append(displayDefinition);
+  }
+  
+  /**
+   * @param node
+   * @return if the node represents a permutation, this will return the {@link SetEnumValue} instance contains its
+   * 					model values
+   */
+  private SetEnumValue getSetEnumValueFromArgumentNode(final ExprOrOpArgNode node) {
+	  if (node instanceof OpApplNode) {
+		  final OpApplNode permutationNode = (OpApplNode)node;
+		  if (permutationNode.getOperator() instanceof OpDefNode) {
+			  final OpDefNode operator = (OpDefNode)permutationNode.getOperator();
+			  if (TLAConstants.BuiltInOperators.PERMUTATIONS.equals(operator.getName().toString())) {
+				  final ExprOrOpArgNode[] operands = permutationNode.getArgs();
+				  if ((operands.length == 1)
+						  && (operands[0] instanceof OpApplNode)
+						  && (((OpApplNode)operands[0]).getOperator() instanceof OpDefOrDeclNode)) {
+					  final Object o = ((OpDefOrDeclNode)((OpApplNode)operands[0]).getOperator()).getToolObject(toolId);
+					  
+					  if (o instanceof SetEnumValue) {
+						  return (SetEnumValue)o;
+					  } else if (o instanceof WorkerValue) {
+						  // If TLC was started with a -workers N specification, N > 1, o will be a WorkerValue instance
+						  final WorkerValue wv = (WorkerValue)o;
+						  final Object unwrapped = WorkerValue.mux(wv);
+						  
+						  if (unwrapped instanceof SetEnumValue) {
+							  return (SetEnumValue)unwrapped;
+						  }
+					  }
+				  }
+			  }
+		  }
+	  }
+
+	  return null;
+  }
+
+  @Override
+  public final boolean hasSymmetry() {
+    if (this.config == null) {
+      return false;
+    }
+    final String name = this.config.getSymmetry();
+    return name.length() > 0;
+  }
+  @Override
+  public final Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args,
+          Context c, TLCState s0, TLCState s1,
+          final int control) {
+	  return getFcnContext(fcn, args, c, s0, s1, control, CostModel.DO_NOT_RECORD);
+  }
+
+  @Override
+  public final Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args,
+                                     Context c, TLCState s0, TLCState s1,
+                                     final int control, CostModel cm) {
+    Context fcon = fcn.getCon();
+    int plen = fcn.getParams().length();
+    FormalParamNode[][] formals = fcn.getParams().getFormals();
+    Value[] domains = (Value[]) fcn.getParams().getDomains();
+    boolean[] isTuples = fcn.getParams().isTuples();
+    Value argVal = this.eval(args[1], c, s0, s1, control, cm);
+
+    if (plen == 1) {
+      if (!domains[0].member(argVal)) {
+        Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) +
+                    ",\nthe first argument is:\n" + Values.ppr(argVal.toString()) +
+                    "which is not in its domain.\n" + args[0]);
+      }
+      if (isTuples[0]) {
+        FormalParamNode[] ids = formals[0];
+        TupleValue tv = (TupleValue) argVal.toTuple();
+        if (tv == null || argVal.size() != ids.length) {
+          Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                      ",\nthe argument is:\n" + Values.ppr(argVal.toString()) +
+                      "which does not match its formal parameter.\n" + args[0]);
+        }
+        Value[] elems = tv.elems;
+        for (int i = 0; i < ids.length; i++) {
+          fcon = fcon.cons(ids[i], elems[i]);
+        }
+      }
+      else {
+        fcon = fcon.cons(formals[0][0], argVal);
+      }
+    }
+    else {
+      TupleValue tv = (TupleValue) argVal.toTuple();
+      if (tv == null) {
+        Assert.fail("Attempted to apply a function to an argument not in its" +
+                    " domain.\n" + args[0]);
+      }
+      int argn = 0;
+      Value[] elems = tv.elems;
+      for (int i = 0; i < formals.length; i++) {
+        FormalParamNode[] ids = formals[i];
+        Value domain = domains[i];
+        if (isTuples[i]) {
+          if (!domain.member(elems[argn])) {
+            Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) +
+                        ",\nthe argument number " + (argn+1) + " is:\n" +
+                        Values.ppr(elems[argn].toString()) +
+                        "\nwhich is not in its domain.\n" + args[0]);
+          }
+          TupleValue tv1 = (TupleValue) elems[argn++].toTuple();
+          if (tv1 == null || tv1.size() != ids.length) {
+            Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) +
+                        ",\nthe argument number " + argn + " is:\n" +
+                        Values.ppr(elems[argn-1].toString()) +
+                        "which does not match its formal parameter.\n" + args[0]);
+          }
+          Value[] avals = tv1.elems;
+          for (int j = 0; j < ids.length; j++) {
+            fcon = fcon.cons(ids[j], avals[j]);
+          }
+        }
+        else {
+          for (int j = 0; j < ids.length; j++) {
+            if (!domain.member(elems[argn])) {
+              Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) +
+                          ",\nthe argument number " + (argn+1) + " is:\n" +
+                          Values.ppr(elems[argn].toString()) +
+                          "which is not in its domain.\n" + args[0]);
+            }
+            fcon = fcon.cons(ids[j], elems[argn++]);
+          }
+        }
+      }
+    }
+    return fcon;
+  }
+
+  @Override
+  public final IContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0,
+          TLCState s1, final int control) {
+	  return contexts(appl, c, s0, s1, control, CostModel.DO_NOT_RECORD);
+  }
+  
+  /* A context enumerator for an operator application. */
+  public final ContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0,
+                                          TLCState s1, final int control, CostModel cm) {
+    FormalParamNode[][] formals = appl.getBdedQuantSymbolLists();
+    boolean[] isTuples = appl.isBdedQuantATuple();
+    ExprNode[] domains = appl.getBdedQuantBounds();
+
+    int flen = formals.length;
+    int alen = 0;
+    for (int i = 0; i < flen; i++) {
+      alen += (isTuples[i]) ? 1 : formals[i].length;
+    }
+    Object[] vars = new Object[alen];
+    ValueEnumeration[] enums = new ValueEnumeration[alen];
+    int idx = 0;
+    for (int i = 0; i < flen; i++) {
+      Value boundSet = this.eval(domains[i], c, s0, s1, control, cm);
+      if (!(boundSet instanceof Enumerable)) {
+        Assert.fail("TLC encountered a non-enumerable quantifier bound\n" +
+                    Values.ppr(boundSet.toString()) + ".\n" + domains[i]);
+      }
+      FormalParamNode[] farg = formals[i];
+      if (isTuples[i]) {
+        vars[idx] = farg;
+        enums[idx++] = ((Enumerable)boundSet).elements();
+      }
+      else {
+        for (int j = 0; j < farg.length; j++) {
+          vars[idx] = farg[j];
+          enums[idx++] = ((Enumerable)boundSet).elements();
+        }
+      }
+    }
+    return new ContextEnumerator(vars, enums, c);
+  }
+
+    // These three are expected by implementing the {@link ITool} interface; they used
+    //		to mirror exactly methods that our parent class ({@link Spec}) implemented
+    //		however those methods have changed signature with refactoring done for 
+    //		Issue #393
+	@Override
+	public Context getOpContext(OpDefNode odn, ExprOrOpArgNode[] args, Context ctx, boolean b) {
+		return getOpContext(odn, args, ctx, b, toolId);
+	}
+	
+	@Override
+	public Object lookup(SymbolNode opNode, Context con, boolean b) {
+		return lookup(opNode, con, b, toolId);
+	}
+	
+	@Override
+	public Object getVal(ExprOrOpArgNode expr, Context con, boolean b) {
+		return getVal(expr, con, b, toolId);
+	}
+}
diff --git a/tlatools/src/tlc2/tool/impl/WorkerValue.java b/tlatools/src/tlc2/tool/impl/WorkerValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..7fbe4f7874e8f8fed4864f4aa8d8ea81c63568d3
--- /dev/null
+++ b/tlatools/src/tlc2/tool/impl/WorkerValue.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.impl;
+
+import tla2sany.semantic.OpDefNode;
+import tla2sany.semantic.SemanticNode;
+import tlc2.TLCGlobals;
+import tlc2.tool.IWorker;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.IValue;
+
+/*
+Root Cause:
+---------
+
+1) [Values](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/value/impl/Value.java) can be (deeply) nested.
+
+1) Values transition through the following sequence of states:  New (instantiated), normalized, and fingerprinted.  Transitioning from one state to another is not thread-safe.  Non-technical speaking, a value is both the symbolic and the explicit entity.
+
+2) Normalization of an (enumerable) value causes its elements to be sorted and duplicates to be removed.  It does not cause new elements to be  generated/enumerated. For example, normalizing the [SetPredValue](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/value/impl/SetPredValue.java), corresponding to the expression ```tla { s \in SUBSET S : s # {} }```, causes ```S``` to be sorted and any duplicate elements to be removed.
+
+3) Fingerprinting an enumerable value causes its elements to be explicitly enumerated.  For example, the SetPredValue from before causes all subsets ```s``` to be enumerated.
+
+4) At startup, TLC eagerly evaluates zero-arity constant definitions and operators which involves creating values.  These values are normalized but not fingerprinted!  For example, the operator ```NonEmptySubsS == { s \in SUBSET S : s # {} }``` will be evaluated to a SetPredValue that is stored in the ```NonEmptySubsS``` node of the semantic graph.  We denote the set of values in the semantic graph with V<sub>SG</sub>.
+
+5) Generating a [TLCState](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/tool/TLCTrace.java) during the evaluation of the next-state relation, assigns newly created values to variables.  Before a TLCState is enqueued in the [StateQueue](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/tool/queue/StateQueue.java), its values are fingerprinted.  Thus, values of TLCStates can safely be shared across [Workers](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/tool/Worker.java).
+
+Lets assume a spec with one variable ```x``` whose value - in all states - is some set.  Furthermore, let ```P``` be the property ```[](x \in NonEmptySubsS)```.  For simplicity reasons,  we assume:
+
+1)  ```P``` is not checked on the initial states
+2) Two Workers check next states simultaneously
+
+Under these two assumptions, both Workers will race to enumerate (fingerprint) ```NonEmptySubsS```.
+
+Note that the fact that we had to assume 1) and 2) explains why this race condition didn't surfaced earlier.  However, the assumptions are true in real-world executions of TLC.  For example, the evaluation of a more complex NonEmptySubsS, with a deeply nested hierarchy of values, may not require the evaluation of nested values in the initial states.
+
+---------------
+
+@xxyzzn came up with another scenario that might lead to the calculation of bogus fingerprints:
+
+```tla
+EXTENDS Integers, Sequences
+VARIABLE x, y
+
+Foo == { s \in SUBSET {1,2,3,4} : s # {} }
+
+Init == x = << >> /\ y \in 1..2
+
+Next == x' \in {<<i, Foo>> : i \in 1..2} /\ UNCHANGED y
+```
+
+Variable ```y``` only exists so that the state-graph has two initial states (sequentially generated).  A single initial state would restrict the first evaluation of Next to a single Worker (only a single initial state to dequeue from the ```StateQueue```) and thus fingerprinting cannot occur concurrently.
+
+Attempted Fix A:
+---------------------
+
+[At startup](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/impl/SpecProcessor.java#L202-L281), (eagerly) fingerprint all values of V<sub>SG</sub>.
+
+Problem: Not all values in V<sub>SG</sub> need not be fingerprinted to completely check a spec.  For some specs such as [CarTalkPuzzle](https://github.com/tlaplus/tlaplus/issues/361#issuecomment-543256201), this is even prohibitive because explicit enumeration is infeasible: TLC will hang at startup.
+
+Deferring fingerprinting of V<sub>SG</sub> until after the assumptions have been checked, such as when the initial states are (atomically) generated, is no general solution.
+
+Proposed Fix A:
+--------------------
+
+Properly synchronize values such that no race conditions are possible if two or more Workers enumerate them concurrently.  
+
+Disadvantages:  
+1) It creates a scalability bottleneck: Workers will content for the synchronized ```NonEmptySubs```on every state that is generated.
+2) Engineering effort to correctly synchronize the Value type hierarchy without introducing dead-locks.
+
+Proposed Fix B:
+--------------------
+
+For each worker, create a copy of V<sub>SG</sub> (technically borrows from the concept of  a ```ThreadLocal```).
+
+Disadvantages:  
+1) Requires a type check (and potentially cast) on every [lookup](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/impl/Spec.java#L448-L497) that occurrs during the [evaluation of the next-state relation](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/impl/Tool.java#L782-L814).  An optimization would be to [eventually](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/AbstractChecker.java#L496) garbage-collect the copies of V<sub>SG</sub> to at least skip the type cast once one copy is completely fingerprinted.
+2) [Value#deepCopy](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/value/IValue.java#L104)  - required to create the copies of V<sub>SG</sub> - is [broken for most value types](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/value/impl/SubsetValue.java#L232) (can probably be fixed).
+*/
+public class WorkerValue {
+	
+	/*
+	 * Demuxing is supposed to be called only once per sn/opDef whereas muxing is called many many times.
+	 */
+	
+    public static Object demux(final OpDefEvaluator spec, final OpDefNode opDef) {
+    	return demux(spec, opDef, opDef);
+    }
+    
+    public static Object demux(final OpDefEvaluator spec, final SemanticNode sn, final OpDefNode opDef) {
+    	final IValue defVal = spec.eval(opDef.getBody(), Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD);
+    	defVal.deepNormalize();
+    	
+    	if (defVal.mutates() && TLCGlobals.getNumWorkers() > 1) {
+    		final IValue[] values = new IValue[TLCGlobals.getNumWorkers()];
+    		values[0] = defVal;
+
+    		for (int i = 1; i < values.length; i++) {
+    			// Ideally, we could invoke IValue#deepCopy here instead of evaluating opDef again.  However,
+    			// IValue#deepCopy doesn't create copies for most values.
+    			values[i] = spec.eval(opDef.getBody(), Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD);
+    			values[i].deepNormalize();
+    		}
+    		
+    		return new WorkerValue(values);
+    	} else {
+    		return defVal;
+    	}
+    }
+    
+	public static Object mux(final Object result) {
+		if (!(result instanceof WorkerValue)) {
+			return result;
+		}
+		
+		final WorkerValue vp = (WorkerValue) result;
+		final Thread t = Thread.currentThread();
+		if (t instanceof IWorker) {
+			final IWorker w = (IWorker) t;
+			return vp.values[w.myGetId()];
+		} else {
+			return vp.values[0];
+		}
+	}
+	
+	private final IValue[] values;
+
+	private WorkerValue(IValue[] values) {
+		this.values = values;
+	}
+}
diff --git a/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java b/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java
index b2865fbd0a95d58389e5a29af446c35719fd8e45..c45d7af348b208b14a52eabdb664061fe67c9785 100644
--- a/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java
+++ b/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java
@@ -4,14 +4,17 @@
 
 package tlc2.tool.liveness;
 
+import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 import tlc2.output.EC;
@@ -26,10 +29,11 @@ import util.FileUtil;
  * 
  * - A {@link DiskGraph} has a 1:1 relationship with {@link OrderOfSolution}
  * 
- * - Logically stores the triple of state, tableaux, link (transitions)
+ * - Logically stores a set of triples that represent the liveness/behavior graph (see Manna/Pnuelli book).
+ * - Technically, it stores the triple of <<state (fingerprint), tableau node index, link (transitions)>>
  * -- Technically does *not* store States, but only a state's fingerprints
  * --- Stores a fingerprint split into 2 ints (low & high part of a fingerprint)
- * -- Stores the index of the tableaux, not the tableaux itself
+ * -- Stores the index of the tableau node, not the tableau node itself
  * --- The TableauGraphNode (TBGraphNode) instance can be obtained by reading
  *     the DiskGraph triple into a GraphNode instance and calling 
  *     GraphNode#getTNode(TBGraph). One obviously has to have access to the TBGraph
@@ -69,11 +73,16 @@ public abstract class AbstractDiskGraph {
 	public static final long MAX_LINK = 0x7FFFFFFFFFFFFFFFL;
 
 	public static boolean isFilePointer(long loc) {
+		// TODO Does not check >= 0 and thus accepts TableauDiskGraph.UNDONE as
+		// ptr.
 		return loc < MAX_PTR;
 	}
 
 	private final String chkptName;
 	protected final String metadir;
+	/**
+	 * @see tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG
+	 */
 	protected final BufferedRandomAccessFile nodeRAF;
 	protected final BufferedRandomAccessFile nodePtrRAF;
 	protected final LongVec initNodes;
@@ -107,11 +116,26 @@ public abstract class AbstractDiskGraph {
 		return this.initNodes;
 	}
 
+	/**
+	 * Creates a fixed size in-memory cache of {@link GraphNode}'s. A disk
+	 * lookup is avoid in {@link AbstractDiskGraph#getNode(long, int, long)} on
+	 * each cache hit. The cache is destroyed by
+	 * {@link AbstractDiskGraph#destroyCache()}.
+	 */
 	public final void createCache() {
-		// Make array length a function of the available (heap) memory
+		// Make array length a function of the available (heap) memory. Could
+		// approximate the required memory by taking the size of the on-disk
+		// files into account, but think of hash collisions!
 		this.gnodes = new GraphNode[65536];
 	}
 
+	/**
+	 * Destroys the fixed size in-memory cache created by
+	 * {@link AbstractDiskGraph#createCache()}. This should be done if liveness
+	 * checking wants to destroy in-memory {@link GraphNode} nodes to start a
+	 * new liveness check on them (e.g. to replace SCC link numbers with the
+	 * original disk ptr location).
+	 */
 	public final void destroyCache() {
 		this.gnodes = null;
 	}
@@ -161,11 +185,18 @@ public abstract class AbstractDiskGraph {
 		return ptr;
 	}
 	
+	/**
+	 * @return true iff the given {@link GraphNode} has already been added to
+	 *         this {@link AbstractDiskGraph}.
+	 */
+	protected abstract boolean checkDuplicate(GraphNode node);
+
 	public abstract GraphNode getNode(long fingerprint, int tableauIdx) throws IOException;
 	
 	/**
 	 * @return true iff the given GraphNode belongs to the set of initial
-	 *         states.
+	 *         states. Inefficient, only use for auxiliary use cases (e.g.
+	 *         visualization of the liveness graph (toDotViz())).
 	 */
 	protected boolean isInitState(final GraphNode gnode) {
 		final int numOfInits = initNodes.size();
@@ -199,7 +230,7 @@ public abstract class AbstractDiskGraph {
 		return gnode1;
 	}
 	
-	public final GraphNode getNodeFromDisk(final long stateFP, final int tidx, final long ptr) throws IOException {
+	protected final GraphNode getNodeFromDisk(final long stateFP, final int tidx, final long ptr) throws IOException {
 		// If the node is not found in the in-memory cache, the ptr has to be
 		// positive. BufferedRandomAccessFile#seek will throw an IOException due
 		// to "negative seek offset" anyway. Lets catch it early on!
@@ -228,20 +259,133 @@ public abstract class AbstractDiskGraph {
 		this.nodePtrRAF.seek(ptr);
 	}
 
+	/**
+	 * This methods reads the node PTR file from disk (the ptr file is the
+	 * smaller file ptrs_N of the pair ptrs_N and nodes_N).
+	 * <p>
+	 * The ptr file contains tuples <<fingerprint, tableau idx, ptr location>>
+	 * for all fingerprints times all tableau indices (the corresponding nodes
+	 * file contains the outgoing arcs of the node described in the ptr file).
+	 * <p>
+	 * The reason why the nodePtrTable has to be re-made by calling this method
+	 * prior to running the SCC search, is because the ptr location is
+	 * eventually overwritten with the nodes link number used by SCC search.
+	 * <p>
+	 * makeNodePtrTbl maintains/does not overwrite the isDone state of the node,
+	 * which - iff true - causes SCC search to skip/ignore the node.
+	 * 
+	 * @param ptr
+	 *            The length of the ptr file up to which this method reads.
+	 * @throws IOException
+	 *             Reading the file failed
+	 */
 	protected abstract void makeNodePtrTbl(final long ptr) throws IOException;
 
-	/* Return the link assigned to the node. */
+	/* Link information for SCC search */
+	
+	/**
+	 * Return the link assigned to the node via putLink() or -1 if the node has
+	 * no link assigned yet. Unless -1, the link is in interval [
+	 * {@link AbstractDiskGraph#MAX_PTR}, {@link AbstractDiskGraph#MAX_LINK}]
+	 * 
+	 * @param state
+	 *            The state's fingerprint
+	 * @param tidx
+	 *            The corresponding tableau index
+	 */
 	public abstract long getLink(long state, int tidx);
 
 	/**
-	 * Assign link to node. If a link has already been assigned to the node,
-	 * does nothing by simply returning the existing link. Otherwise, add <node,
-	 * link> into the table and return -1.
+	 * Assign link to node during SCC search. If a link has already been
+	 * assigned to the node, does nothing by simply returning the existing link.
+	 * Otherwise, add &lt;node, link&gt; into the table and return -1. The link
+	 * overwrites the previous value of elem (file pointer into nodes_N) in the
+	 * nodePtrTable.
+	 * <p>
+	 * The link has to be in the range [{@link AbstractDiskGraph#MAX_PTR},
+	 * {@link AbstractDiskGraph#MAX_LINK}). {AbstractDiskGraph#MAX_LINK} is used
+	 * to exclude nodes from being explored by SCC search twice (see
+	 * {@link AbstractDiskGraph#setMaxLink(long, int)}.
+	 * 
+	 * @param state
+	 *            The state's fingerprint
+	 * @param tidx
+	 *            The corresponding tableau index
 	 */
 	public abstract long putLink(long state, int tidx, long link);
 
+	/**
+	 * Assigns the maximum possible link number to the given node &lt;state,
+	 * tidx&gt;. This results in that the node is skipped/ignored if it turns up
+	 * as a node during SCC's depth-first-search.
+	 * 
+	 * @param state
+	 *            The state's fingerprint
+	 * @param tidx
+	 *            The corresponding tableau index
+	 */
 	public abstract void setMaxLink(long state, int tidx);
 
+	/* End link information for SCC search */
+
+	public boolean checkInvariants(final int slen, final int alen) {
+		// Make sure there are no redundant transitions.
+		final Iterator<GraphNode> itr = iterator();
+		while (itr.hasNext()) {
+			final GraphNode gn = itr.next();
+			if (!gn.checkInvariants(slen, alen)) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	/* start iteration */
+	
+    private Iterator<GraphNode> iterator() {
+		try {
+			// reverse ptr file to beginning
+			this.nodePtrRAF.seek(0);
+			
+			final long length = this.nodePtrRAF.length();
+	        
+			return new Iterator<GraphNode>() {
+
+				/* (non-Javadoc)
+				 * @see java.util.Iterator#hasNext()
+				 */
+				public boolean hasNext() {
+					return nodePtrRAF.getFilePointer() < length;
+				}
+
+				/* (non-Javadoc)
+				 * @see java.util.Iterator#next()
+				 */
+				public GraphNode next() {
+					try {
+						long fp = nodePtrRAF.readLong();
+						int tidx = nodePtrRAF.readInt();
+						long loc = nodePtrRAF.readLongNat();
+						return getNodeFromDisk(fp, tidx, loc);
+					} catch (IOException e) {
+						throw new RuntimeException(e);
+					}
+				}
+
+				/* (non-Javadoc)
+				 * @see java.util.Iterator#remove()
+				 */
+				public void remove() {
+					throw new UnsupportedOperationException("Not supported!");
+				}
+			};
+		} catch (IOException e1) {
+			throw new RuntimeException(e1);
+		}
+    }
+	
+	/* end iteration */
+	
 	/**
 	 * Return the shortest path (inclusive and in reverse order) from some
 	 * initial state to state. The path is a vector of states <s1, s2, ..., sn>,
@@ -261,6 +405,17 @@ public abstract class AbstractDiskGraph {
 	 */
 	public abstract long size();
 	
+	/**
+	 * @return The size of both disk files (ptrs and nodes) measured in bytes.
+	 *         Can be incorrect during short periods when the graph is being
+	 *         recreated ({@link #makeNodePtrTbl()}) or nodes are read from
+	 *         disk ({@link #getNodeFromDisk(long, int, long)}). It is up to
+	 *         the caller to take this into account.
+	 * @throws IOException
+	 */
+	public long getSizeOnDisk() throws IOException {
+		return this.nodePtrRAF.length() + this.nodeRAF.length();
+	}
 	
 	public long getSizeAtLastCheck() {
 		return sizeAtCheck;
@@ -280,7 +435,77 @@ public abstract class AbstractDiskGraph {
 	 * and call something similar to: 'dot -T svg graphviz.txt -o
 	 * "Graphviz.svg"'. It obviously needs Graphviz (http://www.graphviz.org).
 	 */
-	public abstract String toDotViz();
+	public abstract String toDotViz(final OrderOfSolution oos);
+
+	protected String toDotVizLegend(final OrderOfSolution oos) {
+		final StringBuffer sb = new StringBuffer();
+		sb.append("subgraph cluster_legend {");
+		sb.append("graph[style=bold];");
+		sb.append("label = \"PossibleErrorModel\" style=\"solid\"\n");
+		sb.append("node [ labeljust=\"l\",shape=record ]\n");
+		
+		// State checks
+		int i = 1;
+		LiveExprNode[] checkState = oos.getCheckState();
+		for (LiveExprNode liveExprNode : checkState) {
+			sb.append(String.format("S%s [label=\"S%s: %s\"]", i, i++, node2dot(liveExprNode)));
+			sb.append("\n");
+		}
+		// Actions checks
+		i = 1;
+		checkState = oos.getCheckAction();
+		for (LiveExprNode liveExprNode : checkState) {
+			sb.append(String.format("A%s [label=\"A%s: %s\"]", i, i++, node2dot(liveExprNode)));
+			sb.append("\n");
+		}
+		
+		sb.append("}");
+		return sb.toString();
+	}
+	
+	protected static String node2dot(final LiveExprNode node) {
+		// Replace "\" with "\\" and """ with "\"".	Replace "<" and ">" with "\<" and "\>".
+		return node.toString().replace("\\", "\\\\").replace("\"", "\\\"").replace("<", "\\<").replace(">", "\\>").trim()
+				.replace("\n", "\\l"); // Do not remove remaining (i.e. no dangling/leading) "\n". 
+	}
+
+	
+	/**
+	 * Only useful for debugging.
+	 * 
+	 * Writes the current {@link AbstractDiskGraph} to the given {@link File}.
+	 * <p>
+	 * For the Eclipse IDE there exists a handy plug-in that automatically
+	 * renders a .dot file when selected in the package explorer. Just follow
+	 * the installation instructions at
+	 * https://github.com/abstratt/eclipsegraphviz
+	 * 
+	 * @param oos
+	 *            Length of state checks
+	 * @param alen
+	 *            Length of action checks
+	 * @param file
+	 *            Destination
+	 */
+	public final void writeDotViz(final OrderOfSolution oos, final File file) {
+		this.createCache();
+
+		try {
+			final BufferedWriter bwr = new BufferedWriter(new FileWriter(file));
+
+			// write contents of StringBuffer to a file
+			bwr.write(toDotViz(oos));
+
+			// flush the stream
+			bwr.flush();
+
+			// close the stream
+			bwr.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		this.destroyCache();
+	}
 
 	/* Checkpoint. */
 	public synchronized final void beginChkpt() throws IOException {
diff --git a/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java b/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java
index f22e9b260bebae973731c6e6000c13bebe58fcd0..346fb99f8b7fe7d199fc7fa49a7071ed1a9de003 100644
--- a/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java
+++ b/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java
@@ -40,6 +40,14 @@ public abstract class AbstractGraphNode {
 		return this.checks.get(i);
 	}
 
+	public BitVector getCheckAction(int slen, int alen, int nodeIdx) {
+		final BitVector bv = new BitVector(alen);
+		for (int j = 0; j < alen; j++) {
+			bv.set(j, getCheckAction(slen, alen, nodeIdx, j));
+		}
+		return bv;
+	}
+
 	public boolean getCheckAction(int slen, int alen, int nodeIdx, int i) {
 		int pos = slen + alen * nodeIdx + i;
 		return this.checks.get(pos);
diff --git a/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java b/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java
new file mode 100644
index 0000000000000000000000000000000000000000..7442f798fbc92e0da0d77f4d30863f4ef49ee09f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import java.io.IOException;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.ITool;
+import tlc2.tool.TLCState;
+import tlc2.tool.Worker;
+import tlc2.util.SetOfStates;
+import tlc2.util.statistics.IBucketStatistics;
+
+/**
+ * {@link AddAndCheckLiveCheck} is a subclass of LiveCheck that always runs
+ * liveness checking after each and every insertion of a new node into the
+ * behavior graph. It is meant for TESTING ONLY!!! as it severely
+ * degrades TLC's performance/scalability.
+ * <p>
+ * The key difference between {@link AddAndCheckLiveCheck} and {@link LiveCheck}
+ * is that both methods are synchronized to only allow one {@link Worker} to add
+ * a state at a time and that liveness checking is after every insertion.
+ */
+public class AddAndCheckLiveCheck extends LiveCheck {
+
+	public AddAndCheckLiveCheck(ITool tool, String metadir, IBucketStatistics stats) throws IOException {
+		super(tool, metadir, stats);
+		MP.printWarning(EC.UNIT_TEST, new String[]{ "!!!WARNING: TLC is running in inefficient unit testing mode!!!", ""} );
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.LiveCheck#addInitState(tlc2.tool.TLCState, long)
+	 */
+	@Override
+	public synchronized void addInitState(ITool tool, TLCState state, long stateFP) {
+		super.addInitState(tool, state, stateFP);
+		try {
+			check0(tool, false);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.LiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates)
+	 */
+	@Override
+	public synchronized void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException {
+		super.addNextState(tool, s0, fp0, nextStates);
+		try {
+			check0(tool, false);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/tool/liveness/BEGraph.java b/tlatools/src/tlc2/tool/liveness/BEGraph.java
index 3a73d7fbb27c9b212c9e63d4fcda0a2270094f1b..ffe148cdecfb5b81db809ebf1b06c9ef5f64ca41 100644
--- a/tlatools/src/tlc2/tool/liveness/BEGraph.java
+++ b/tlatools/src/tlc2/tool/liveness/BEGraph.java
@@ -15,12 +15,12 @@ public class BEGraph {
 	/**
 	 * BEGraph represents the behaviour graph.
 	 */
-	public Vect initNodes;
+	public Vect<BEGraphNode> initNodes;
 	public String metadir;
 	public NodeTable allNodes;
 
 	public BEGraph(String metadir, boolean isBT) {
-		this.initNodes = new Vect();
+		this.initNodes = new Vect<>();
 		this.metadir = metadir;
 		this.allNodes = new NodeTable(127, isBT);
 	}
@@ -95,7 +95,7 @@ public class BEGraph {
 			}
 		}
 		// Get the path following parent pointers:
-		Vect path = new Vect();
+		Vect<BEGraphNode> path = new Vect<>();
 		BEGraphNode curNode = end;
 		while (curNode != null) {
 			path.addElement(curNode);
diff --git a/tlatools/src/tlc2/tool/liveness/DiskGraph.java b/tlatools/src/tlc2/tool/liveness/DiskGraph.java
index 060aaf3d3fb0e9a89b53085c9ecd9dcd75f63b82..2db8224739a605f772c2d54fd59f5cabb845ca24 100644
--- a/tlatools/src/tlc2/tool/liveness/DiskGraph.java
+++ b/tlatools/src/tlc2/tool/liveness/DiskGraph.java
@@ -65,23 +65,31 @@ public class DiskGraph extends AbstractDiskGraph {
 	}
 	
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#putNode(tlc2.tool.liveness.GraphNode, long)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#putNode(tlc2.tool.liveness.GraphNode, long)
 	 */
 	protected void putNode(GraphNode node, long ptr) {
 		this.nodePtrTbl.put(node.stateFP, ptr);
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#getLink(long, int)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#checkDuplicate(tlc2.tool.liveness.GraphNode)
+	 */
+	protected boolean checkDuplicate(final GraphNode node) {
+		return this.nodePtrTbl.get(node.stateFP) != -1;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#getLink(long, int)
 	 */
 	public long getLink(long state, int tidx) {
 		return this.nodePtrTbl.get(state);
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#putLink(long, int, long)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#putLink(long, int, long)
 	 */
 	public long putLink(long state, int tidx, long link) {
+		assert MAX_PTR <= link && link < MAX_LINK; 
 		int loc = this.nodePtrTbl.getLoc(state);
 		long oldLink = this.nodePtrTbl.getByLoc(loc);
 		if (!isFilePointer(oldLink)) {
@@ -105,9 +113,7 @@ public class DiskGraph extends AbstractDiskGraph {
 		this.nodePtrRAF.seek(0);
 		while (this.nodePtrRAF.getFilePointer() < ptr) {
 			long fp = this.nodePtrRAF.readLong();
-			// SZ Jul 13, 2009: removed to kill the warning
-			// SZ Feb 20, 2009: variable never read locally
-			// int tidx =
+			// skip the tableau idx that is not used by DiskGraph.
 			this.nodePtrRAF.readInt();
 			long loc = this.nodePtrRAF.readLongNat();
 			this.nodePtrTbl.put(fp, loc);
@@ -168,10 +174,11 @@ public class DiskGraph extends AbstractDiskGraph {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#toDotViz()
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#toDotViz(tlc2.tool.liveness.OrderOfSolution)
 	 */
-	public final String toDotViz() {
-
+	public final String toDotViz(final OrderOfSolution oos) {
+		final int slen = oos.getCheckState().length;
+		final int alen = oos.getCheckAction().length;
 		// The following code relies on gnodes not being null, thus safeguard
 		// against accidental invocations.
 		// Essentially one has to wrap the toDotViz call with
@@ -186,6 +193,9 @@ public class DiskGraph extends AbstractDiskGraph {
 			sb.append("digraph DiskGraph {\n");
 			sb.append("nodesep = 0.7\n");
 			sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom
+			sb.append(toDotVizLegend(oos));
+			sb.append("subgraph cluster_graph {\n"); 
+	        sb.append("color=\"white\";\n"); // no border.
 			long nodePtr = this.nodeRAF.getFilePointer();
 			long nodePtrPtr = this.nodePtrRAF.getFilePointer();
 			long len = this.nodePtrRAF.length();
@@ -195,9 +205,9 @@ public class DiskGraph extends AbstractDiskGraph {
 				int tidx = nodePtrRAF.readInt();
 				long loc = nodePtrRAF.readLongNat();
 				GraphNode gnode = this.getNode(fp, tidx, loc);
-				sb.append(gnode.toDotViz(isInitState(gnode), false));
+				sb.append(gnode.toDotViz(isInitState(gnode), false, slen, alen));
 			}
-			sb.append("}");
+			sb.append("}}");
 			this.nodeRAF.seek(nodePtr);
 			this.nodePtrRAF.seek(nodePtrPtr);
 		} catch (IOException e) {
diff --git a/tlatools/src/tlc2/tool/liveness/DotLivenessStateWriter.java b/tlatools/src/tlc2/tool/liveness/DotLivenessStateWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ec361f7206238a9351c67932d651579a0d5f16a
--- /dev/null
+++ b/tlatools/src/tlc2/tool/liveness/DotLivenessStateWriter.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import java.io.IOException;
+
+import tlc2.tool.TLCState;
+import tlc2.util.BitVector;
+import tlc2.util.DotStateWriter;
+import tlc2.util.IStateWriter;
+
+public class DotLivenessStateWriter extends DotStateWriter implements ILivenessStateWriter {
+	
+	public DotLivenessStateWriter(IStateWriter aStateWriter) throws IOException {
+		super(aStateWriter.getDumpFileName().replace(".dot", "_liveness.dot"), "");
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode)
+	 */
+	public void writeState(TLCState state, TBGraphNode tableauNode) {
+		
+		// Marker the state as an initial state by using a filled style.
+		this.writer.append("\"");
+		this.writer.append(Long.toString(state.fingerPrint()));
+		this.writer.append(".");
+		this.writer.append(Integer.toString(tableauNode.getIndex()));
+		this.writer.append("\"");
+		this.writer.append(" [style = filled]");
+		this.writer.append(" [label=\"");
+		this.writer.append(states2dot(state));
+		this.writer.append("\n#" + Long.toString(state.fingerPrint()) + "."
+				+ tableauNode.getIndex() + "#");
+		this.writer.append("\"]");
+		this.writer.append("\n");
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, boolean)
+	 */
+	public void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor,
+			TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew) {
+		writeState(state, tableauNode, successor, tableauNodeSuccessor, actionChecks, from, length, successorStateIsNew, Visualization.DEFAULT);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor,
+			TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visualization) {
+
+		final String successorsFP = Long.toString(successor.fingerPrint());
+
+		// Write the transition
+		this.writer.append("\"");
+		this.writer.append(Long.toString(state.fingerPrint()));
+		this.writer.append(".");
+		this.writer.append(Integer.toString(tableauNode.getIndex()));
+		this.writer.append("\"");
+		this.writer.append(" -> ");
+		this.writer.append("\"");
+		this.writer.append(successorsFP);
+		this.writer.append(".");
+		this.writer.append(Integer.toString(tableauNodeSuccessor.getIndex()));
+		this.writer.append("\"");
+		if (visualization == Visualization.STUTTERING) {
+			this.writer.append(" [style=\"dashed\"]");
+		}
+		if (visualization == Visualization.DOTTED) {
+			this.writer.append(" [style=\"dotted\"]");
+		}
+		if (length > 0) {
+			this.writer.append(" [label=\"" + actionChecks.toString(from, length, 't', 'f') + "\"]");
+		}
+		this.writer.append(";\n");
+
+		// If the successor is new, print the state's label. Labels are printed
+		// when writeState sees the successor. It does not print the label for
+		// the current state. If it would print the label for the current state,
+		// the init state labels would be printed twice.
+		if (successorStateIsNew) {
+			// Write the successor's label
+			this.writer.append("\"");
+			this.writer.append(successorsFP);
+			this.writer.append(".");
+			this.writer.append(Integer.toString(tableauNodeSuccessor.getIndex()));
+			this.writer.append("\"");
+			this.writer.append(" [label=\"");
+			this.writer.append(states2dot(successor));
+			this.writer.append(
+					"\n#" + Long.toString(successor.fingerPrint()) + "."
+							+ tableauNodeSuccessor.getIndex() + "#");
+			this.writer.append("\"]");
+			this.writer.append(";\n");
+		}
+	}
+
+	protected static String tableauNode2dot(final TBGraphNode tableauNode) {
+		// Replace "\" with "\\" and """ with "\"".	
+		return tableauNode.toString().replace("\\", "\\\\").replace("\"", "\\\"").trim();
+	}
+}
diff --git a/tlatools/src/tlc2/tool/liveness/GraphNode.java b/tlatools/src/tlc2/tool/liveness/GraphNode.java
index 69b43598bbf9a84c68e4a885570990c2cadf38f8..1c0371f4464bb74a640b15331a3531e5cc002000 100644
--- a/tlatools/src/tlc2/tool/liveness/GraphNode.java
+++ b/tlatools/src/tlc2/tool/liveness/GraphNode.java
@@ -6,11 +6,19 @@
 package tlc2.tool.liveness;
 
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
 
 import tlc2.util.BitVector;
 import tlc2.util.BufferedRandomAccessFile;
 
 public class GraphNode extends AbstractGraphNode {
+	/**
+	 * The record size indicates the number of integers used by each transition
+	 * in the array of nnodes (2x32bit to keep the fp and 32bit to keep the tableau
+	 * idx).
+	 */
+	private static final int NNODE_RECORD_SIZE = 3;
 	/**
 	 * GraphNode is a node in the behaviour graph. We're going to only store
 	 * fingerprints of states, rather than actual states. So, as we encounter
@@ -77,34 +85,36 @@ public class GraphNode extends AbstractGraphNode {
 	}
 
 	public final long getStateFP(int i) {
-		long high = this.nnodes[3 * i];
-		long low = this.nnodes[3 * i + 1];
+		long high = this.nnodes[NNODE_RECORD_SIZE * i];
+		long low = this.nnodes[NNODE_RECORD_SIZE * i + 1];
 		return (high << 32) | (low & 0xFFFFFFFFL);
 	}
 
 	public final int getTidx(int i) {
-		return this.nnodes[3 * i + 2];
+		return this.nnodes[NNODE_RECORD_SIZE * i + 2];
 	}
 
 	public final int succSize() {
-		// offset being != -1 indicates that the nnodes array has been
+		// offset being != NO_FREE_SLOTS indicates that the nnodes array has been
 		// overallocated in preparation to batch-insert transitions but the
 		// transitions have not been added yet. In this case the nnodes.length /
-		// 3 is *not* the actual number of transitions, offset / 3 is!
-		if (this.offset != -1) {
-			return this.offset / 3;
+		// NNODE_RECORD_SIZE is *not* the actual number of transitions, offset / NNODE_RECORD_SIZE is!
+		if (this.offset != NO_FREE_SLOTS) {
+			return this.offset / NNODE_RECORD_SIZE;
 		}
-		return this.nnodes.length / 3;
+		return this.nnodes.length / NNODE_RECORD_SIZE;
 	}
 
 	/**
 	 * Points to the first available slot in {@link GraphNode#nnodes} iff free
-	 * slots are available. "-1" indicates no free slots are available.
+	 * slots are available. "NO_FREE_SLOTS" indicates no free slots are available.
 	 * 
 	 * @see GraphNode#allocate(int)
 	 */
-	private int offset = -1;
+	private int offset = NO_FREE_SLOTS;
 
+	private static final int NO_FREE_SLOTS = -1;
+	
 	/**
 	 * Allocates memory for subsequent
 	 * {@link GraphNode#addTransition(long, int, int, int, boolean[])} calls.
@@ -135,7 +145,7 @@ public class GraphNode extends AbstractGraphNode {
 	 */
 	private final void allocate(final int transitions) {
 		final int len = this.nnodes.length;
-		int[] newNodes = new int[len + (3 * transitions)];
+		int[] newNodes = new int[len + (NNODE_RECORD_SIZE * transitions)];
 		System.arraycopy(this.nnodes, 0, newNodes, 0, len);
 		this.nnodes = newNodes;
 
@@ -188,7 +198,7 @@ public class GraphNode extends AbstractGraphNode {
 				}
 			}
 		}
-		if (this.offset == -1) {
+		if (this.offset == NO_FREE_SLOTS) {
 			// Have to create a new slot regardless of 0 or negative hint, thus
 			// Math.max...
 			this.allocate(Math.max(allocationHint, 1));
@@ -196,9 +206,9 @@ public class GraphNode extends AbstractGraphNode {
 		this.nnodes[this.offset] = (int) (fp >>> 32);
 		this.nnodes[this.offset + 1] = (int) (fp & 0xFFFFFFFFL);
 		this.nnodes[this.offset + 2] = tidx;
-		this.offset = this.offset + 3;
+		this.offset = this.offset + NNODE_RECORD_SIZE;
 		if (this.offset == this.nnodes.length) {
-			this.offset = -1;
+			this.offset = NO_FREE_SLOTS;
 		}
 	}
 
@@ -213,14 +223,14 @@ public class GraphNode extends AbstractGraphNode {
 	 */
 	public int realign() {
 		int result = 0;
-		// It is a noop iff offset == -1
-		if (this.offset != -1) {
-			result = (this.nnodes.length - this.offset) / 3;
+		// It is a noop iff offset == NO_FREE_SLOTS
+		if (this.offset != NO_FREE_SLOTS) {
+			result = (this.nnodes.length - this.offset) / NNODE_RECORD_SIZE;
 			// shrink newNodes to correct size
 			int[] newNodes = new int[this.offset];
 			System.arraycopy(this.nnodes, 0, newNodes, 0, newNodes.length);
 			this.nnodes = newNodes;
-			this.offset = -1;
+			this.offset = NO_FREE_SLOTS;
 		}
 		return result;
 	}
@@ -240,14 +250,14 @@ public class GraphNode extends AbstractGraphNode {
 		// been
 		// reached. The free slot detection work with the allocation offset that
 		// points to the end of the filled slots (slots are filled in ascending
-		// order). If offset is marked invalid ("-1"), the nnodes buffer is
+		// order). If offset is marked invalid ("NO_FREE_SLOTS"), the nnodes buffer is
 		// completely occupied and has to be searched to the end.
-		if (this.offset != -1) {
+		if (this.offset != NO_FREE_SLOTS) {
 			len = offset;
 		}
 		int high = (int) (fp >>> 32);
 		int low = (int) (fp & 0xFFFFFFFFL);
-		for (int i = 0; i < len; i += 3) {
+		for (int i = 0; i < len; i += NNODE_RECORD_SIZE) {
 			if (this.nnodes[i] == high && this.nnodes[i + 1] == low && this.nnodes[i + 2] == tidx) {
 				return true;
 			}
@@ -255,6 +265,93 @@ public class GraphNode extends AbstractGraphNode {
 		return false;
 	}
 
+	public boolean checkInvariants(final int slen, final int alen) {
+		final Set<Transition> transitions = new HashSet<Transition>();
+		for (int i = 0; i < succSize(); i++) {
+			final Transition t = new Transition(getStateFP(i), getTidx(i), getCheckAction(slen, alen, i));
+			transitions.add(t);
+		}
+		return transitions.size() == succSize();
+	}
+	
+	public Set<Transition> getTransition() {
+		return getTransition(0, 0);
+	}
+	
+	public Set<Transition> getTransition(final int slen, final int alen) {
+		final Set<Transition> transitions = new HashSet<Transition>();
+		for (int i = 0; i < succSize(); i++) {
+			final BitVector bv = new BitVector(alen);
+			for (int j = 0; j < alen; j++) {
+				if (getCheckAction(slen, alen, i, j)) {
+					bv.set(j);
+				}
+			}
+			transitions.add(new Transition(getStateFP(i), getTidx(i), bv));
+		}
+		return transitions;
+	}
+	
+	public static class Transition {
+
+		private final long fp;
+		private final int tidx;
+		private final BitVector bv;
+
+		public Transition(long fp, int tidx, BitVector bv) {
+			this.fp = fp;
+			this.tidx = tidx;
+			this.bv = bv;
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Object#hashCode()
+		 */
+		public int hashCode() {
+			final int prime = 31;
+			int result = 1;
+			result = prime * result + ((bv == null) ? 0 : bv.hashCode());
+			result = prime * result + (int) (fp ^ (fp >>> 32));
+			result = prime * result + tidx;
+			return result;
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Transition other = (Transition) obj;
+			if (bv == null) {
+				if (other.bv != null)
+					return false;
+			} else if (!bv.equals(other.bv))
+				return false;
+			if (fp != other.fp)
+				return false;
+			if (tidx != other.tidx)
+				return false;
+			return true;
+		}
+		
+		public BitVector getChecks() {
+			return bv;
+		}
+
+		public long getFP() {
+			return fp;
+		}
+		
+		public int getTidx() {
+			return tidx;
+		}
+	}
+
 	/* Return the tableau graph node used by this. */
 	public final TBGraphNode getTNode(TBGraph tableau) {
 		return tableau.getNode(this.tindex);
@@ -268,6 +365,7 @@ public class GraphNode extends AbstractGraphNode {
 	 * @throws IOException
 	 */
 	void write(final BufferedRandomAccessFile nodeRAF) throws IOException {
+		assert offset == NO_FREE_SLOTS; // assert that nnodes hasn't been overallocated.
 		// Write nnodes
 		final int cnt = nnodes.length;
 		nodeRAF.writeNat(cnt);
@@ -288,50 +386,105 @@ public class GraphNode extends AbstractGraphNode {
 		// Read checks
 		checks = new BitVector();
 		checks.read(nodeRAF);
+		
+		assert offset == NO_FREE_SLOTS;
 	}
 
 	public final String toString() {
+		// A GraphNode does not know the action length. This is kept elsewhere in the code.
+		return toString(0).replace("[] ", "");
+	}
+
+	public final String toString(final int alen) {
 		StringBuffer buf = new StringBuffer();
 		buf.append("<" + this.stateFP + "," + this.tindex + "> --> ");
-		int size = this.nnodes.length;
-		if (size != 0) {
-			long high = this.nnodes[0];
-			long low = this.nnodes[1];
-			long fp = (high << 32) | (low & 0xFFFFFFFFL);
-			buf.append("<" + fp + "," + this.nnodes[2] + ">");
-		}
-		for (int i = 3; i < size; i += 3) {
+		for (int i = 0; i < succSize(); i++) {
+			// action checks
+			buf.append("[");
+			for (int j = 0; j < alen; j++) {
+				if (getCheckAction(0, 2, i, j)) {
+					buf.append("t");
+				} else {
+					buf.append("f");
+				}
+			}
+			buf.append("] ");
+			// fingerprint/tableau id
+			buf.append("<" + getStateFP(i) + "," + getTidx(i) + ">");
 			buf.append(", ");
-			long high = this.nnodes[i];
-			long low = this.nnodes[i + 1];
-			long fp = (high << 32) | (low & 0xFFFFFFFFL);
-			buf.append("<" + fp + "," + this.nnodes[i + 2] + ">");
 		}
-		return buf.toString();
+		return buf.substring(0, buf.length() - ", ".length()); // chop off dangling ", "
 	}
 
-	public String toDotViz(final boolean isInitState, final boolean hasTableau) {
-		String id = (""+this.stateFP).substring(0,3);
+	public String toDotViz(final boolean isInitState, final boolean hasTableau, final int slen, final int alen) {
+		return toDotViz(isInitState, hasTableau, slen, alen, null);
+	}
+
+	public String toDotViz(final boolean isInitState, final boolean hasTableau, final int slen, final int alen, TableauNodePtrTable filter) {
+		// The node's id including its tidx if any. It uses the complete
+		// fingerprint.
+		String id = Long.toString(this.stateFP);
 		if (hasTableau) {
 			id += "." + this.tindex;
 		}
-		
+			
+		// Nodes label and a marker if it is an init state. The label is
+		// shortened to 8 chars max to avoid screen clutter. It's possible
+		// that the resulting graph will have multiple nodes with an identical
+		// label iff the first 6 (+2) chars of their fingerprint match. However
+		// the graph will still contain all nodes regardless of the label
+		// collision due to id.
+		String label = Long.toString(this.stateFP).substring(0, 6) + (hasTableau ? "." + this.tindex : "");
+		if (slen > 0) {
+			label += "\n";
+			for (int i = 0; i < slen; i++) {
+				if (getCheckState(i)) {
+					label += "t";
+				} else {
+					label += "f";
+				}
+			}
+		}
 		final StringBuffer buf = new StringBuffer();
 		if (isInitState) {
-			buf.append(id + " [style = filled]\n"); // node's label
+			buf.append("\"" + id + "\" [style = filled][label = \"" + label + "\"]\n"); // node's label
+		} else {
+			buf.append("\"" + id + "\" [label = \"" + label + "\"]\n");
 		}
-		int size = this.nnodes.length;
-		for (int i = 0; i < size; i += 3) {
-			buf.append(id + " -> ");
-			long high = this.nnodes[i];
-			long low = this.nnodes[i + 1];
-			long fp = (high << 32) | (low & 0xFFFFFFFFL);
+		
+		// Each outgoing transition
+		for (int i = 0; i < succSize(); i++) {
+			final long stateFP = getStateFP(i);
+			final int tidx = getTidx(i);
+			
+			// If a filter is given, check if this node is in filter
+			if (filter != null && filter.get(stateFP, tidx) == -1) {
+				continue;
+			}
+			
+			String fp = Long.toString(stateFP);
+//			if (fp == this.stateFP) {
+//				// skip self loops if edge count to large for dotViz to handle.
+//				continue;
+//			}
+			
+			buf.append("\"" + id + "\" -> ");
 			if (hasTableau) {
-				buf.append(("" + fp).substring(0, 3) + "." + this.nnodes[i + 2]);
+				buf.append(("\"" + fp) + "." + tidx + "\"");
 			} else {
 				//Omit tableau index when it's -1 (indicating no tableau)
-				buf.append(("" + fp).substring(0, 3));
+				buf.append(("\"" + fp) + "\"");
+			}
+			
+			buf.append(" [label=\"");
+			for (int j = 0; j < alen; j++) {
+				if (getCheckAction(slen, alen, i, j)) {
+					buf.append("t");
+				} else {
+					buf.append("f");
+				}
 			}
+			buf.append("\"];");
 			buf.append("\n");
 		}
 		return buf.toString();
diff --git a/tlatools/src/tlc2/tool/liveness/ILiveCheck.java b/tlatools/src/tlc2/tool/liveness/ILiveCheck.java
index 171a00672f42ed8b829429dc06ea9a16959de998..688ad737a155a213b5e0e65621cf5baf0fa92743 100644
--- a/tlatools/src/tlc2/tool/liveness/ILiveCheck.java
+++ b/tlatools/src/tlc2/tool/liveness/ILiveCheck.java
@@ -1,11 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
 package tlc2.tool.liveness;
 
 import java.io.IOException;
 
+import tlc2.tool.ITool;
 import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
-import tlc2.util.LongVec;
+import tlc2.util.SetOfStates;
 import tlc2.util.statistics.IBucketStatistics;
 
 public interface ILiveCheck {
@@ -14,35 +39,40 @@ public interface ILiveCheck {
 	 * This method records that state is an initial state in the behavior graph.
 	 * It is called when a new initial state is generated.
 	 */
-	void addInitState(TLCState state, long stateFP);
+	void addInitState(ITool tool, TLCState state, long stateFP);
 
 	/**
 	 * This method adds new nodes into the behavior graph induced by s0. It is
 	 * called after the successors of s0 are computed.
 	 */
-	void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException;
+	void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException;
+	
+	/**
+	 * true iff a call to {@link ILiveCheck#check(ITool, boolean)} would indeed result in liveness checking.
+	 */
+	boolean doLiveCheck();
 	
 	/**
 	 * Check liveness properties for the current (potentially partial) state graph. Returns
 	 * true iff it finds no errors.
-	 * 
 	 * @param forceCheck
 	 *            Always checks liveness if true, otherwise heuristics about the
 	 *            partial graph are taken into account if it is worthwhile to
 	 *            check liveness.
-	 * @return true iff it finds no errors or if liveness has not been checked
-	 *         on the partial graph because it was deemed worthless.
+	 * 
+	 * @return <code>EC.NO_ERROR</code> iff it finds no errors or if liveness has not been checked
+	 *         on the partial graph because it was deemed worthless. Otherwise an EC.
 	 */
-	boolean check(boolean forceCheck) throws Exception;
+	int check(ITool tool, boolean forceCheck) throws Exception;
 
 	/**
 	 * No states can be added with add*State once finalCheck has been called.
 	 * 
 	 * @see ILiveCheck#check()
-	 * @return
+   * @return an error code, or <code>EC.NO_ERROR</code> on success
 	 * @throws Exception
 	 */
-	boolean finalCheck() throws Exception;
+	int finalCheck(ITool tool) throws Exception;
 
 	/* simulation mode */
 	
@@ -59,19 +89,17 @@ public interface ILiveCheck {
 	 * is done as part of checkTrace.
 	 * <p>
 	 * checkTrace can be called multiple times until ILiveCheck has been closed (see close()).
-	 * 
 	 * @param trace
+	 * 
 	 * @throws IOException
 	 * @throws InterruptedException
 	 */
-	void checkTrace(final StateVec trace) throws IOException, InterruptedException;
+	void checkTrace(ITool tool, final StateVec trace) throws IOException, InterruptedException;
 	
 	/* auxiliary methods */
 	
 	String getMetaDir();
 
-	Tool getTool();
-
 	IBucketStatistics getOutDegreeStatistics();
 
 	ILiveChecker getChecker(int idx);
diff --git a/tlatools/src/tlc2/tool/liveness/ILiveChecker.java b/tlatools/src/tlc2/tool/liveness/ILiveChecker.java
index 2c5bb45180eadf427157d006fa25bb7592eb7845..f4b84ca16fdeafca24932bdcf3ea265ae793869f 100644
--- a/tlatools/src/tlc2/tool/liveness/ILiveChecker.java
+++ b/tlatools/src/tlc2/tool/liveness/ILiveChecker.java
@@ -1,11 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
 package tlc2.tool.liveness;
 
 import java.io.IOException;
 
-import tlc2.tool.StateVec;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
 import tlc2.util.BitVector;
-import tlc2.util.LongVec;
+import tlc2.util.SetOfStates;
 
 public interface ILiveChecker {
 
@@ -13,17 +39,19 @@ public interface ILiveChecker {
 	 * This method records that state is an initial state in the behavior graph.
 	 * It is called when a new initial state is generated.
 	 */
-	void addInitState(TLCState state, long stateFP);
+	void addInitState(ITool tool, TLCState state, long stateFP);
 
 	/**
 	 * This method adds new nodes into the behavior graph induced by s0. It is
 	 * called after the successors of s0 are computed.
 	 */
-	void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs, BitVector checkActionResults,
-			boolean[] checkStateResults) throws IOException;
+	void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates,
+			BitVector checkActionResults, boolean[] checkStateResults) throws IOException;
 
 	AbstractDiskGraph getDiskGraph();
 
 	OrderOfSolution getSolution();
 
+	void close() throws IOException;
+
 }
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java b/tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee3ea670da4b674559f6fd077dc659b2a8546fe3
--- /dev/null
+++ b/tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import tlc2.tool.TLCState;
+import tlc2.util.BitVector;
+import tlc2.util.IStateWriter;
+
+public interface ILivenessStateWriter extends IStateWriter {
+	void writeState(TLCState state, TBGraphNode tableauNode);
+
+	void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew);
+
+	void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visulation);
+}
diff --git a/tlatools/src/tlc2/tool/liveness/LNAction.java b/tlatools/src/tlc2/tool/liveness/LNAction.java
index 99dbb03ab6c41aafeab229daec2389117b4c8283..4f8cc8ee220be367044d211c402a66d79cf1f840 100644
--- a/tlatools/src/tlc2/tool/liveness/LNAction.java
+++ b/tlatools/src/tlc2/tool/liveness/LNAction.java
@@ -8,11 +8,11 @@ package tlc2.tool.liveness;
 import tla2sany.semantic.ExprNode;
 import tlc2.output.EC;
 import tlc2.tool.EvalControl;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.Context;
-import tlc2.value.BoolValue;
-import tlc2.value.Value;
+import tlc2.value.IBoolValue;
+import tlc2.value.IValue;
 import util.Assert;
 import util.WrongInvocationException;
 
@@ -59,10 +59,10 @@ public class LNAction extends LiveExprNode {
 		return true;
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		if (this.subscript != null) {
-			Value v1 = tool.eval(this.subscript, con, s1, TLCState.Empty, EvalControl.Clear);
-			Value v2 = tool.eval(this.subscript, con, s2, null, EvalControl.Clear);
+			IValue v1 = tool.eval(this.subscript, con, s1, TLCState.Empty, EvalControl.Clear);
+			IValue v2 = tool.eval(this.subscript, con, s2, null, EvalControl.Clear);
 			boolean isStut = v1.equals(v2);
 			if (this.isBox) {
 				if (isStut) {
@@ -74,11 +74,11 @@ public class LNAction extends LiveExprNode {
 				}
 			}
 		}
-		Value val = tool.eval(this.body, con, s1, s2, EvalControl.Clear);
-		if (!(val instanceof BoolValue)) {
+		IValue val = tool.eval(this.body, con, s1, s2, EvalControl.Clear);
+		if (!(val instanceof IBoolValue)) {
 			Assert.fail(EC.TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE);
 		}
-		return ((BoolValue) val).val;
+		return ((IBoolValue) val).getVal();
 	}
 
 	public final void toString(StringBuffer sb, String padding) {
diff --git a/tlatools/src/tlc2/tool/liveness/LNAll.java b/tlatools/src/tlc2/tool/liveness/LNAll.java
index 40c86b77faeab571300745a9e03ea3b72e058771..9223226cb472b296f7df4fd8985f37efd2ddf823 100644
--- a/tlatools/src/tlc2/tool/liveness/LNAll.java
+++ b/tlatools/src/tlc2/tool/liveness/LNAll.java
@@ -6,8 +6,8 @@
 package tlc2.tool.liveness;
 
 import tlc2.output.EC;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import util.Assert;
 
 /**
@@ -39,7 +39,7 @@ class LNAll extends LiveExprNode {
 		return this.body.containAction();
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		Assert.fail(EC.TLC_LIVE_CANNOT_EVAL_FORMULA, ALWAYS);
 		return false; // make compiler happy
 	}
diff --git a/tlatools/src/tlc2/tool/liveness/LNBool.java b/tlatools/src/tlc2/tool/liveness/LNBool.java
index b3d5264f3c7d4a439646d0ddc71cdfccb46ccba6..399fbc27b78fc1b3c89f6a8e3018eca999ad0afe 100644
--- a/tlatools/src/tlc2/tool/liveness/LNBool.java
+++ b/tlatools/src/tlc2/tool/liveness/LNBool.java
@@ -5,8 +5,8 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 
 class LNBool extends LiveExprNode {
 	public static final LNBool TRUE = new LNBool(true);
@@ -18,7 +18,7 @@ class LNBool extends LiveExprNode {
 		this.b = b;
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		return this.b;
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/LNConj.java b/tlatools/src/tlc2/tool/liveness/LNConj.java
index 4a7b12ee4dcd3bc31f98218667884a953928fe9a..38f0309af3b8e078d27103740d761cdb4ff68279 100644
--- a/tlatools/src/tlc2/tool/liveness/LNConj.java
+++ b/tlatools/src/tlc2/tool/liveness/LNConj.java
@@ -5,28 +5,28 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.Vect;
 
 class LNConj extends LiveExprNode {
-	private final Vect conjs; // The conjuncts
+	private final Vect<LiveExprNode> conjs; // The conjuncts
 	private int info;
 
 	public LNConj(int size) {
-		this.conjs = new Vect(size);
+		this.conjs = new Vect<>(size);
 		this.info = 0;
 	}
 
 	public LNConj(LiveExprNode n) {
-		this.conjs = new Vect(1);
+		this.conjs = new Vect<>(1);
 		this.conjs.addElement(n);
 		int level = n.getLevel();
 		this.info = n.containAction() ? level + 8 : level;
 	}
 
 	public LNConj(LiveExprNode n1, LiveExprNode n2) {
-		this.conjs = new Vect(2);
+		this.conjs = new Vect<>(2);
 		this.conjs.addElement(n1);
 		this.conjs.addElement(n2);
 		boolean hasAct = n1.containAction() || n2.containAction();
@@ -34,7 +34,7 @@ class LNConj extends LiveExprNode {
 		this.info = hasAct ? level + 8 : level;
 	}
 
-	public LNConj(Vect conjs) {
+	public LNConj(Vect<LiveExprNode> conjs) {
 		this.conjs = conjs;
 		boolean hasAct = false;
 		int level = 0;
@@ -76,7 +76,7 @@ class LNConj extends LiveExprNode {
 		return (this.info & 8) > 0;
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		int sz = this.conjs.size();
 		for (int i = 0; i < sz; i++) {
 			LiveExprNode item = (LiveExprNode) this.conjs.elementAt(i);
@@ -171,7 +171,7 @@ class LNConj extends LiveExprNode {
 		}
 
 		// We now construct the cross product:
-		Vect nes = new Vect(count);
+		Vect<LiveExprNode> nes = new Vect<>(count);
 		int total = 1;
 		for (int i = 0; i < count; i++) {
 			LiveExprNode elem = temp[i];
@@ -194,7 +194,7 @@ class LNConj extends LiveExprNode {
 			return new LNConj(nes);
 		}
 		int nesSize = nes.size();
-		Vect res = new Vect(total);
+		Vect<LiveExprNode> res = new Vect<>(total);
 		for (int i = 0; i < total; i++) {
 			res.addElement(new LNConj(nesSize));
 		}
diff --git a/tlatools/src/tlc2/tool/liveness/LNDisj.java b/tlatools/src/tlc2/tool/liveness/LNDisj.java
index cbf32ead1a3683110bd799e61f46750ff2819bf6..9a1a8843635c21e62c52ca049add1637a89e8144 100644
--- a/tlatools/src/tlc2/tool/liveness/LNDisj.java
+++ b/tlatools/src/tlc2/tool/liveness/LNDisj.java
@@ -5,28 +5,28 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.Vect;
 
 class LNDisj extends LiveExprNode {
-	private final Vect disjs; // The disjuncts
+	private final Vect<LiveExprNode> disjs; // The disjuncts
 	private int info;
 
 	public LNDisj(int size) {
-		this.disjs = new Vect(size);
+		this.disjs = new Vect<>(size);
 		this.info = 0;
 	}
 
 	public LNDisj(LiveExprNode n) {
-		this.disjs = new Vect(1);
+		this.disjs = new Vect<>(1);
 		this.disjs.addElement(n);
 		int level = n.getLevel();
 		this.info = n.containAction() ? level + 8 : level;
 	}
 
 	public LNDisj(LiveExprNode n1, LiveExprNode n2) {
-		this.disjs = new Vect(2);
+		this.disjs = new Vect<>(2);
 		this.disjs.addElement(n1);
 		this.disjs.addElement(n2);
 		boolean hasAct = n1.containAction() || n2.containAction();
@@ -34,7 +34,7 @@ class LNDisj extends LiveExprNode {
 		this.info = hasAct ? level + 8 : level;
 	}
 
-	public LNDisj(Vect disjs) {
+	public LNDisj(Vect<LiveExprNode> disjs) {
 		this.disjs = disjs;
 		boolean hasAct = false;
 		int level = 0;
@@ -76,7 +76,7 @@ class LNDisj extends LiveExprNode {
 		return (this.info & 8) > 0;
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		int sz = disjs.size();
 		for (int i = 0; i < sz; i++) {
 			LiveExprNode item = (LiveExprNode) disjs.elementAt(i);
diff --git a/tlatools/src/tlc2/tool/liveness/LNEven.java b/tlatools/src/tlc2/tool/liveness/LNEven.java
index c08ded5e84a88ed8143891f4577d27776d373461..3967a7d47ef30dca6f8264c0ce6afc306b1af1bb 100644
--- a/tlatools/src/tlc2/tool/liveness/LNEven.java
+++ b/tlatools/src/tlc2/tool/liveness/LNEven.java
@@ -6,8 +6,8 @@
 package tlc2.tool.liveness;
 
 import tlc2.output.EC;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import util.Assert;
 
 /**
@@ -36,7 +36,7 @@ class LNEven extends LiveExprNode {
 		return this.body.containAction();
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		Assert.fail(EC.TLC_LIVE_CANNOT_EVAL_FORMULA, EVENTUALLY);
 		return false; // make compiler happy
 	}
diff --git a/tlatools/src/tlc2/tool/liveness/LNNeg.java b/tlatools/src/tlc2/tool/liveness/LNNeg.java
index 439023e73abb91c79bdb89bd1cafca4c91f46fed..0602b5e08999043835234798b1d74b003ca35cea 100644
--- a/tlatools/src/tlc2/tool/liveness/LNNeg.java
+++ b/tlatools/src/tlc2/tool/liveness/LNNeg.java
@@ -5,8 +5,8 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 
 public class LNNeg extends LiveExprNode {
 	private final LiveExprNode body;
@@ -27,7 +27,7 @@ public class LNNeg extends LiveExprNode {
 		return this.body.containAction();
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		return !this.body.eval(tool, s1, s2);
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/LNNext.java b/tlatools/src/tlc2/tool/liveness/LNNext.java
index ee09c5146d1c7516f494a25d3f8a161039a043e2..c15ee7002ad21ce46eb5f83fac1c37cbde3b1f30 100644
--- a/tlatools/src/tlc2/tool/liveness/LNNext.java
+++ b/tlatools/src/tlc2/tool/liveness/LNNext.java
@@ -5,8 +5,8 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 
 class LNNext extends LiveExprNode {
 	private final LiveExprNode body;
@@ -27,7 +27,7 @@ class LNNext extends LiveExprNode {
 		return this.body.containAction();
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		return this.body.eval(tool, s2, TLCState.Empty);
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/LNState.java b/tlatools/src/tlc2/tool/liveness/LNState.java
index 59f786a7c4d1c7bd46d5e29dbf2b4bb4e082515d..664bdb36070915d1ae7315360cf31ecd735288a0 100644
--- a/tlatools/src/tlc2/tool/liveness/LNState.java
+++ b/tlatools/src/tlc2/tool/liveness/LNState.java
@@ -5,8 +5,8 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.Context;
 
 abstract class LNState extends LiveExprNode {
@@ -25,7 +25,7 @@ abstract class LNState extends LiveExprNode {
 		return false;
 	}
 
-	public final boolean eval(Tool tool, TLCState s) {
+	public final boolean eval(ITool tool, TLCState s) {
 		return this.eval(tool, s, TLCState.Empty);
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/LNStateAST.java b/tlatools/src/tlc2/tool/liveness/LNStateAST.java
index c37e787ab93735f879068a6ddcb5ca92cc05d941..2618eb8dcfe1159b8c3e6101dd425c80462a2e2f 100644
--- a/tlatools/src/tlc2/tool/liveness/LNStateAST.java
+++ b/tlatools/src/tlc2/tool/liveness/LNStateAST.java
@@ -10,11 +10,11 @@ import tla2sany.semantic.ExprNode;
 import tla2sany.semantic.OpApplNode;
 import tla2sany.st.TreeNode;
 import tlc2.output.EC;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.Context;
-import tlc2.value.BoolValue;
-import tlc2.value.Value;
+import tlc2.value.IBoolValue;
+import tlc2.value.IValue;
 import util.Assert;
 
 /**
@@ -35,12 +35,12 @@ class LNStateAST extends LNState {
 		return this.body;
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
-		Value val = tool.eval(this.body, getContext(), s1);
-		if (!(val instanceof BoolValue)) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
+		IValue val = tool.eval(this.body, getContext(), s1);
+		if (!(val instanceof IBoolValue)) {
 			Assert.fail(EC.TLC_LIVE_STATE_PREDICATE_NON_BOOL);
 		}
-		return ((BoolValue) val).val;
+		return ((IBoolValue) val).getVal();
 	}
 
 	public final void toString(StringBuffer sb, String padding) {
diff --git a/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java b/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java
index 8f33d7ee6d484324ba23cfd0288c2e3c45f35732..3f75a50664ef91eccdf251d8300d7ef42ff6012e 100644
--- a/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java
+++ b/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java
@@ -9,10 +9,10 @@ import tla2sany.parser.SyntaxTreeNode;
 import tla2sany.semantic.ExprNode;
 import tla2sany.semantic.OpApplNode;
 import tla2sany.st.TreeNode;
-import tlc2.tool.ActionItemList;
+import tlc2.tool.IActionItemList;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
 import tlc2.tool.TLCStateFun;
-import tlc2.tool.Tool;
 import tlc2.util.Context;
 
 /**
@@ -41,19 +41,19 @@ class LNStateEnabled extends LNState {
 		this.isBox = isBox;
 	}
 
-	public final boolean eval(Tool tool, TLCState s1, TLCState s2) {
+	public final boolean eval(ITool tool, TLCState s1, TLCState s2) {
 		// Note that s2 is useless.
 		if (this.isBox && this.subscript != null) {
 			return true;
 		}
 
-		ActionItemList acts = ActionItemList.Empty;
 		TLCState sfun = TLCStateFun.Empty;
 		Context c1 = Context.branch(getContext());
 		if (this.subscript != null) {
-			acts = acts.cons(this.subscript, c1, -3);
+			sfun = tool.enabled(this.pred, c1, s1, sfun, this.subscript, IActionItemList.CHANGED);
+		} else {
+			sfun = tool.enabled(this.pred, c1, s1, sfun);
 		}
-		sfun = tool.enabled(this.pred, acts, c1, s1, sfun);
 		return sfun != null;
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/LiveCheck.java b/tlatools/src/tlc2/tool/liveness/LiveCheck.java
index 4aa5c699bbbef22f729027b8c661c26bd6daad1c..f253e135f1c81f10e042d0f2165b50f1341b843a 100644
--- a/tlatools/src/tlc2/tool/liveness/LiveCheck.java
+++ b/tlatools/src/tlc2/tool/liveness/LiveCheck.java
@@ -9,50 +9,61 @@ import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
-
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import tlc2.TLC;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.Action;
+import tlc2.tool.ITool;
+import tlc2.tool.ModelChecker;
 import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.BitVector;
-import tlc2.util.FP64;
-import tlc2.util.LongVec;
-import tlc2.util.statistics.DummyBucketStatistics;
+import tlc2.util.IStateWriter;
+import tlc2.util.IStateWriter.Visualization;
+import tlc2.util.NoopStateWriter;
+import tlc2.util.SetOfStates;
 import tlc2.util.statistics.IBucketStatistics;
 import util.Assert;
-import util.SimpleFilenameToStream;
 
 public class LiveCheck implements ILiveCheck {
 
-	private final Action[] actions;
-	private final Tool myTool;
 	private final String metadir;
 	private final IBucketStatistics outDegreeGraphStats;
 	private final ILiveChecker[] checker;
 	
-	// SZ: fields not read locally
-	// private static OrderOfSolution currentOOS;
-	// private static DiskGraph currentDG;
-	// private static PossibleErrorModel currentPEM;
-
-	public LiveCheck(Tool tool, Action[] acts, String mdir, IBucketStatistics bucketStatistics) throws IOException {
-		this(tool, acts, Liveness.processLiveness(tool), mdir, bucketStatistics);
+	public LiveCheck(ITool tool, String mdir, IBucketStatistics bucketStatistics) throws IOException {
+		this(tool, Liveness.processLiveness(tool), mdir, bucketStatistics, new NoopStateWriter());
+	}
+	
+	public LiveCheck(ITool tool, String mdir, IBucketStatistics bucketStatistics, IStateWriter stateWriter) throws IOException {
+		this(tool, Liveness.processLiveness(tool), mdir, bucketStatistics, stateWriter);
+	}
+	
+	public LiveCheck(ITool tool, OrderOfSolution[] solutions, String mdir, IBucketStatistics bucketStatistics) throws IOException {
+		this(tool, solutions, mdir, bucketStatistics, new NoopLivenessStateWriter());
 	}
 
-	public LiveCheck(Tool tool, Action[] acts, OrderOfSolution[] solutions, String mdir, IBucketStatistics bucketStatistics) throws IOException {
-		myTool = tool;
-		actions = acts;
+	public LiveCheck(ITool tool, OrderOfSolution[] solutions, String mdir, IBucketStatistics bucketStatistics, IStateWriter stateWriter) throws IOException {
 		metadir = mdir;
 		outDegreeGraphStats = bucketStatistics;
 		checker = new ILiveChecker[solutions.length];
 		for (int soln = 0; soln < solutions.length; soln++) {
+			final ILivenessStateWriter writer = stateWriter.isNoop() || !stateWriter.isDot()
+					? new NoopLivenessStateWriter()
+					: new DotLivenessStateWriter(stateWriter);
 			if (!solutions[soln].hasTableau()) {
-				checker[soln] = new LiveChecker(solutions[soln], soln, bucketStatistics);
+				checker[soln] = new LiveChecker(solutions[soln], soln, bucketStatistics, writer);
 			} else {
-				checker[soln] = new TableauLiveChecker(solutions[soln], soln, bucketStatistics);
+				checker[soln] = new TableauLiveChecker(solutions[soln], soln, bucketStatistics, writer);
 			}
 		}
 	}
@@ -60,16 +71,16 @@ public class LiveCheck implements ILiveCheck {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#addInitState(tlc2.tool.TLCState, long)
 	 */
-	public void addInitState(TLCState state, long stateFP) {
+	public void addInitState(ITool tool, TLCState state, long stateFP) {
 		for (int i = 0; i < checker.length; i++) {
-			checker[i].addInitState(state, stateFP);
+			checker[i].addInitState(tool, state, stateFP);
 		}
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec)
+	 * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates)
 	 */
-	public void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException {
+	public void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException {
 		for (int i = 0; i < checker.length; i++) {
 			final ILiveChecker check = checker[i];
 			final OrderOfSolution oos = check.getSolution();
@@ -83,50 +94,92 @@ public class LiveCheck implements ILiveCheck {
 			// to hold the result and loop over actions x successors twice
 			// (here and down below). This is a little price to pay for significantly
 			// increased concurrency.
+			//
+			// The actions have to be checked here because - in the light of
+			// symmetry - while we still have access to the actual successor
+			// state rather than just its fingerprint that represents all states
+			// in the symmetry set. Unless super-symmetry is in place (the
+			// actions checks for all states in the symmetry set evaluate to the
+			// same value), the "smallest" (see
+			// tlc2.tool.TLCStateMut.fingerPrint()) cannot be used as a
+			// replacement state to check the actions.
 			final BitVector checkActionResults = new BitVector(alen * nextStates.size());
 			for (int sidx = 0; sidx < nextStates.size(); sidx++) {
-				final TLCState s1 = nextStates.elementAt(sidx);
-				oos.checkAction(s0, s1, checkActionResults, alen * sidx);
+				final TLCState s1 = nextStates.next();
+				oos.checkAction(tool, s0, s1, checkActionResults, alen * sidx);
 			}
-			check.addNextState(s0, fp0, nextStates, nextFPs, checkActionResults, oos.checkState(s0));
+			nextStates.resetNext();
+			check.addNextState(tool, s0, fp0, nextStates, checkActionResults, oos.checkState(tool, s0));
+			
+			// Write the content of the current graph to a file in GraphViz
+			// format. Useful when debugging!
+//			check.getDiskGraph().writeDotViz(oos, new java.io.File(
+//					metadir + java.io.File.separator + "dgraph_" + i + "_" + System.currentTimeMillis() + ".dot"));
 		}
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#check(boolean)
+	 * @see tlc2.tool.liveness.ILiveCheck#doLiveCheck()
 	 */
-	public boolean check(boolean forceCheck) throws Exception {
+	public boolean doLiveCheck() {
+		for (int i = 0; i < checker.length; i++) {
+			// If one of the disk graph's size has increased by the given
+			// percentage, run liveness checking.
+			//
+			// TODO Alternatively:
+			//
+			// - LL suggest to dedicate a fixed fraction of model checking time
+			// to liveness checking.
+			//
+			// - The level could be taken into account. Unless the level
+			// (height) of the graph increases, no new cycle won't be found
+			// anyway (all other aspects of liveness checking are checked as
+			// part of regular safety checking).
+			//
+			// - The authors of the Divine model checker describe an algorithm
+			// in http://dx.doi.org/10.1109/ASE.2003.1240299
+			// that counts the "Back-level Edges" and runs liveness checking upon
+			// a counter reaching a certain (user defined?!) threshold.
+			//
+			final AbstractDiskGraph diskGraph = checker[i].getDiskGraph();
+			final long sizeAtLastCheck = diskGraph.getSizeAtLastCheck();
+			final long sizeCurrently = diskGraph.size();
+			final double delta = (sizeCurrently - sizeAtLastCheck) / (sizeAtLastCheck * 1.d);
+			if (delta > TLCGlobals.livenessThreshold) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	@Override
+	public int check(ITool tool, boolean forceCheck) throws Exception {
 		if (forceCheck) {
-			return check0(false);
+			return check0(tool, false);
+		}
+		if (!TLCGlobals.doLiveness()) {
+			// The user requested to only check liveness once, on the complete
+			// state graph.
+			return EC.NO_ERROR;
 		}
 		for (int i = 0; i < checker.length; i++) {
-			// If anyone of the disk graphs has increased by the given
-			// percentage, run liveness checking. This is the best heuristic I
-			// can come up with quickly.
-			//
-			// TODO Alternatively the level could be taken
-			// into account. Unless the level (height) of the graph increases, 
-			// no new cycle won't be found anyway. All other aspects of liveness
-			// checking are checked as part of regular safety checking.
+			// see note in doLiveCheck() above!
 			final AbstractDiskGraph diskGraph = checker[i].getDiskGraph();
 			final long sizeAtLastCheck = diskGraph.getSizeAtLastCheck();
 			final long sizeCurrently = diskGraph.size();
 			final double delta = (sizeCurrently - sizeAtLastCheck) / (sizeAtLastCheck * 1.d);
 			if (delta > TLCGlobals.livenessThreshold) {
-				return check0(false);
+				return check0(tool, false);
 			}
 		}
-		
-		return true;
+		return EC.NO_ERROR;
 	}
 	
-	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#finalCheck()
-	 */
-	public boolean finalCheck() throws InterruptedException, IOException {
+	@Override
+	public int finalCheck(ITool tool) throws InterruptedException, IOException {
 		// Do *not* re-create the nodePtrTable after the check which takes a
 		// while for larger disk graphs.
-		return check0(true);
+		return check0(tool, true);
 	}
 	
 	/**
@@ -135,13 +188,17 @@ public class LiveCheck implements ILiveCheck {
 	 *            liveness check. If this is the final/last check, it's pointless
 	 *            to re-create the nodePtrTable.
 	 */
-	private boolean check0(final boolean finalCheck) throws InterruptedException, IOException {
+	protected int check0(final ITool tool, final boolean finalCheck) throws InterruptedException, IOException {
+		final long startTime = System.currentTimeMillis();
+		
+		// Sum up the number of nodes in all disk graphs to indicate the amount
+		// of work to be done by liveness checking.
 		long sum = 0L;
 		for (int i = 0; i < checker.length; i++) {
 			sum += checker[i].getDiskGraph().size();
 		}
-		MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS,
-				new String[] { "current", Long.toString(sum) });
+		MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS, new String[] { finalCheck ? "complete" : "current",
+				Long.toString(sum), checker.length == 1 ? "" : checker.length + " branches of " });
 
 		// Copy the array of checkers into a concurrent-enabled queue
 		// that allows LiveWorker threads to easily get the next 
@@ -160,75 +217,107 @@ public class LiveCheck implements ILiveCheck {
 		final BlockingQueue<ILiveChecker> queue = new ArrayBlockingQueue<ILiveChecker>(checker.length);
 		queue.addAll(Arrays.asList(checker));
 
-		int slen = checker.length;
-		int wNum = Math.min(slen, TLCGlobals.getNumWorkers());
-
-		if (wNum == 1) {
-			LiveWorker worker = new LiveWorker(0, this, queue);
-			worker.run();
-		} else {
-			final LiveWorker[] workers = new LiveWorker[wNum];
-			for (int i = 0; i < wNum; i++) {
-				workers[i] = new LiveWorker(i, this, queue);
-				workers[i].start();
-			}
-			for (int i = 0; i < wNum; i++) {
-				workers[i].join();
+		
+		/*
+		 * A LiveWorker below can either complete a unit of work a) without finding a
+		 * liveness violation, b) finds a violation, or c) fails to check because of an
+		 * exception/error (such as going out of memory). In case an LW fails to check,
+		 * we still wait for all other LWs to complete. A subset of the LWs might have
+		 * found a violation. In other words, the OOM of an LW has lower precedence than
+		 * a violation found by another LW. However, if any LW fails to check, we terminate
+		 * model checking after all LWs completed.
+		 */
+		final int wNum = TLCGlobals.doSequentialLiveness() ? 1 : Math.min(checker.length, TLCGlobals.getNumWorkers());
+		final ExecutorService pool = Executors.newFixedThreadPool(wNum);
+		// CS is really just a container around the set of Futures returned by the pool. It saves us from
+		// creating a low-level array.
+		final CompletionService<Boolean> completionService = new ExecutorCompletionService<Boolean>(pool);
+
+		for (int i = 0; i < wNum; i++) {
+			completionService.submit(new LiveWorker(tool, i, wNum, this, queue, finalCheck));
+		}
+		// Wait for all LWs to complete.
+		pool.shutdown();
+		pool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // wait forever
+
+		// Check if any one of the LWs found a violation (ignore failures for now).
+		ExecutionException ee = null;
+		for (int i = 0; i < wNum; i++) {
+			try {
+				final Future<Boolean> future = completionService.take();
+				if (future.get()) {
+					MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS_END,
+							TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime));
+					return EC.TLC_TEMPORAL_PROPERTY_VIOLATED;
+				}
+			} catch (final ExecutionException e) {
+				// handled below!
+				ee = e;
 			}
 		}
-
-		if (LiveWorker.hasErrFound()) {
-			return false;
+		// Terminate if any one of the LWs failed c)
+		if (ee != null) {
+			final Throwable cause = ee.getCause();
+			if (cause instanceof OutOfMemoryError) {
+				MP.printError(EC.SYSTEM_OUT_OF_MEMORY_LIVENESS, cause);
+			} else if (cause instanceof StackOverflowError) {
+				MP.printError(EC.SYSTEM_STACK_OVERFLOW, cause);
+			} else if (cause != null) {
+				MP.printError(EC.GENERAL, cause);
+			} else {
+				MP.printError(EC.GENERAL, ee);
+			}
+			System.exit(1);
 		}
-
+		
 		// Reset after checking unless it's the final check:
 		if (finalCheck == false) {
 			for (int i = 0; i < checker.length; i++) {
 				checker[i].getDiskGraph().makeNodePtrTbl();
 			}
 		}
-		return true;
+		MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS_END, TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime));
+		
+		return EC.NO_ERROR;
 	}
 	
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#checkTrace(tlc2.tool.StateVec)
 	 */
-	public void checkTrace(final StateVec stateTrace) throws InterruptedException, IOException {
+	public void checkTrace(ITool tool, final StateVec stateTrace) throws InterruptedException, IOException {
 		// Add the first state to the LiveCheck as the current init state
-		addInitState(stateTrace.elementAt(0), stateTrace.elementAt(0).fingerPrint());
+		addInitState(tool, stateTrace.elementAt(0), stateTrace.elementAt(0).fingerPrint());
 		
 		// Add the remaining states...
-		final StateVec successor = new StateVec(2);
-		final LongVec successorFP = new LongVec(2);
+		final SetOfStates successors = new SetOfStates(stateTrace.size() * 2);
 
 		// For all states except last one add the successor
 		// (which is the next state in stateTrace).
 		for (int i = 0; i < stateTrace.size() - 1; i++) {
 			// Empty out old successors.
-			successor.clear();
-			successorFP.reset();
+			successors.clear();
 			
 			// Calculate the current state's fingerprint
 			final TLCState tlcState = stateTrace.elementAt(i);
 			final long fingerPrint = tlcState.fingerPrint();
 
 			// Add state itself to allow stuttering
-			successor.addElement(tlcState);
-			successorFP.addElement(fingerPrint);
+			successors.put(tlcState);
 			
 			// Add the successor in the trace
-			successor.addElement(stateTrace.elementAt(i + 1));
-			successorFP.addElement(stateTrace.elementAt(i + 1).fingerPrint());
-			addNextState(tlcState, fingerPrint, successor, successorFP);
+			final TLCState successor = stateTrace.elementAt(i + 1);
+			successors.put(successor);
+			addNextState(tool, tlcState, fingerPrint, successors);
 		}
 		
 		// Add last state in trace for which *no* successors have been generated
 		final TLCState lastState = stateTrace.elementAt(stateTrace.size() - 1);
-		addNextState(lastState, lastState.fingerPrint(), new StateVec(0), new LongVec(0));
+		addNextState(tool, lastState, lastState.fingerPrint(), new SetOfStates(0));
 		
 		// Do *not* re-create the nodePtrTbl when it is thrown away anyway.
-		if (!check0(true)) {
-			throw new LiveException();
+		final int result = check0(tool, true);
+		if (result != EC.NO_ERROR) {
+			throw new LiveException(result);
 		}
 		
 		// We are done with the current subsequence of the behavior. Reset LiveCheck
@@ -243,13 +332,6 @@ public class LiveCheck implements ILiveCheck {
 		return metadir;
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#getTool()
-	 */
-	public Tool getTool() {
-		return myTool;
-	}
-
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#getOutDegreeStatistics()
 	 */
@@ -277,7 +359,7 @@ public class LiveCheck implements ILiveCheck {
 	 */
 	public void close() throws IOException {
 		for (int i = 0; i < checker.length; i++) {
-			checker[i].getDiskGraph().close();
+			checker[i].close();
 		}
 	}
 
@@ -342,11 +424,14 @@ public class LiveCheck implements ILiveCheck {
 	}
 	
 	static abstract class AbstractLiveChecker implements ILiveChecker {
-
+		
+		protected final ILivenessStateWriter writer;
+		
 		protected final OrderOfSolution oos;
 
-		public AbstractLiveChecker(OrderOfSolution oos) {
+		public AbstractLiveChecker(OrderOfSolution oos, ILivenessStateWriter writer) {
 			this.oos = oos;
+			this.writer = writer;
 		}
 
 		/* (non-Javadoc)
@@ -355,30 +440,41 @@ public class LiveCheck implements ILiveCheck {
 		public OrderOfSolution getSolution() {
 			return oos;
 		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.tool.liveness.ILiveChecker#close()
+		 */
+		public void close() throws IOException {
+			if (!ModelChecker.VETO_CLEANUP) {
+				this.getDiskGraph().close();
+			}
+			this.writer.close();
+		}
 	}
 	
 	private class LiveChecker extends AbstractLiveChecker {
 
 		private final DiskGraph dgraph;
 
-		public LiveChecker(OrderOfSolution oos, int soln, IBucketStatistics bucketStatistics)
+		public LiveChecker(OrderOfSolution oos, int soln, IBucketStatistics bucketStatistics, ILivenessStateWriter writer)
 			throws IOException {
-			super(oos);
+			super(oos, writer);
 			this.dgraph = new DiskGraph(metadir, soln, bucketStatistics);
 		}
 
 		/* (non-Javadoc)
 		 * @see tlc2.tool.liveness.LiveCheck.ILiveChecker#addInitState(tlc2.tool.TLCState, long)
 		 */
-		public void addInitState(TLCState state, long stateFP) {
+		public void addInitState(ITool tool, TLCState state, long stateFP) {
 			dgraph.addInitNode(stateFP, -1);
+			writer.writeState(state);
 		}
 
 		/* (non-Javadoc)
-		 * @see tlc2.tool.liveness.LiveCheck.ILiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec, tlc2.util.BitVector, boolean[])
+		 * @see tlc2.tool.liveness.ILiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates, tlc2.util.BitVector, boolean[])
 		 */
-		public void addNextState(final TLCState s0, final long fp0, final StateVec nextStates, final LongVec nextFPs,
-				final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException {
+		public void addNextState(ITool tool, final TLCState s0, final long fp0,
+				final SetOfStates nextStates, final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException {
 			int cnt = 0;
 			// if there is no tableau ...
 			final int succCnt = nextStates.size();
@@ -388,7 +484,8 @@ public class LiveCheck implements ILiveCheck {
 				final int s = node0.succSize();
 				node0.setCheckState(checkStateResults);
 				for (int sidx = 0; sidx < succCnt; sidx++) {
-					final long successor = nextFPs.elementAt(sidx);
+					final TLCState successorState = nextStates.next();
+					final long successor = successorState.fingerPrint();
 					// Only add the transition if:
 					// a) The successor itself has not been written to disk
 					//    TODO Why is an existing successor ignored?
@@ -414,17 +511,21 @@ public class LiveCheck implements ILiveCheck {
 					} else {
 						cnt++;
 					}
+					writer.writeState(s0, successorState, checkActionResults, sidx * alen, alen, ptr1 == -1);
 				}
+				nextStates.resetNext();
 				// In simulation mode (see Simulator), it's possible that this
 				// method is called multiple times for the same state (s0/fp0)
 				// but with changing successors caused by the random successor
 				// selection. If the successor is truly new (it has not been
 				// added before), the GraphNode instance has to be updated
-				// (creating a new record on disk). However, when the the
-				// successor parameter happens to pass known successors, there
-				// is no point in adding the GraphNode again. It is assumed that
-				// it wouldn't invalidate the result, but it wastes disk space.
-				if (s < node0.succSize()) {
+				// (creating a new record on disk). However, when the successor
+				// parameter happens to pass known successors only, there is no
+				// point in adding the GraphNode again. It would just waste disk
+				// space.
+				// The amount of successors is either 0 (no new successor has
+				// been added) or used to be less than it is now.
+				if ((s == 0 && s == node0.succSize()) || s < node0.succSize()) {
 					node0.realign(); // see node0.addTransition() hint
 					// Add a node for the current state. It gets added *after*
 					// all transitions have been added because addNode
@@ -449,33 +550,34 @@ public class LiveCheck implements ILiveCheck {
 
 		private final TableauDiskGraph dgraph;
 
-		public TableauLiveChecker(OrderOfSolution oos, int soln, IBucketStatistics statistics)
+		public TableauLiveChecker(OrderOfSolution oos, int soln, IBucketStatistics statistics, ILivenessStateWriter writer)
 				throws IOException {
-			super(oos);
+			super(oos, writer);
 			this.dgraph = new TableauDiskGraph(metadir, soln, statistics);
 		}
 
 		/* (non-Javadoc)
 		 * @see tlc2.tool.liveness.LiveChecker#addInitState(tlc2.tool.TLCState, long)
 		 */
-		public void addInitState(final TLCState state, final long stateFP) {
+		public void addInitState(final ITool tool, final TLCState state, final long stateFP) {
 			// (state, tnode) is a root node if tnode is an initial tableau
 			// node and tnode is consistent with state.
 			int initCnt = oos.getTableau().getInitCnt();
 			for (int i = 0; i < initCnt; i++) {
 				TBGraphNode tnode = oos.getTableau().getNode(i);
-				if (tnode.isConsistent(state, myTool)) {
-					dgraph.addInitNode(stateFP, tnode.index);
-					dgraph.recordNode(stateFP, tnode.index);
+				if (tnode.isConsistent(state, tool)) {
+					dgraph.addInitNode(stateFP, tnode.getIndex());
+					dgraph.recordNode(stateFP, tnode.getIndex());
+					writer.writeState(state, tnode);
 				}
 			}
 		}
 
 		/* (non-Javadoc)
-		 * @see tlc2.tool.liveness.LiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec)
+		 * @see tlc2.tool.liveness.ILiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates, tlc2.util.BitVector, boolean[])
 		 */
-		public void addNextState(final TLCState s0, final long fp0, final StateVec nextStates, final LongVec nextFPs,
-				final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException {
+		public void addNextState(final ITool tool, final TLCState s0, final long fp0,
+				final SetOfStates nextStates, final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException {
 			int cnt = 0;
 			final int succCnt = nextStates.size();
 			
@@ -487,21 +589,21 @@ public class LiveCheck implements ILiveCheck {
 			// trades speed for additional memory usage (BitVector).
 			final TBGraph tableau = oos.getTableau();
 			final BitVector consistency = new BitVector(tableau.size() * succCnt);
-			@SuppressWarnings("unchecked")
 			final Enumeration<TBGraphNode> elements = tableau.elements();
 			while(elements.hasMoreElements()) {
 				final TBGraphNode tableauNode = elements.nextElement();
 				for (int sidx = 0; sidx < succCnt; sidx++) {
-					final TLCState s1 = nextStates.elementAt(sidx);
-					if(tableauNode.isConsistent(s1, myTool)) {
+					final TLCState s1 = nextStates.next();
+					if(tableauNode.isConsistent(s1, tool)) {
 						// BitVector is divided into a segment for each
 						// tableau node. Inside each segment, addressing is done
 						// via each state. Use identical addressing below
 						// where the lookup is done (plus 1 accounts for
 						// zero-based addressing).
-						consistency.set((tableauNode.index * succCnt) + sidx);
+						consistency.set((tableauNode.getIndex() * succCnt) + sidx);
 					}
 				}
+				nextStates.resetNext();
 			}
 			
 			// At this point only constant time operations are allowed =>
@@ -537,40 +639,37 @@ public class LiveCheck implements ILiveCheck {
 					final int s = node0.succSize();
 					node0.setCheckState(checkStateResults);
 					for (int sidx = 0; sidx < succCnt; sidx++) {
-						final TLCState s1 = nextStates.elementAt(sidx);
-						final long successor = nextFPs.elementAt(sidx);
+						final TLCState s1 = nextStates.next();
+						final long successor = s1.fingerPrint();
 						final boolean isDone = dgraph.isDone(successor);
 						for (int k = 0; k < tnode0.nextSize(); k++) {
 							final TBGraphNode tnode1 = tnode0.nextAt(k);
 							// Check if the successor is new
-							long ptr1 = dgraph.getPtr(successor, tnode1.index);
-							if (ptr1 == -1) {
-								if (consistency.get((tnode1.index * succCnt) + sidx)) { // see note on addressing above
-									node0.addTransition(successor, tnode1.index, checkStateResults.length,
-											alen, checkActionResults, sidx * alen,
-											allocationHint - cnt++);
-									// Record that we have seen <fp1,
-									// tnode1>. If fp1 is done, we have
-									// to compute the next states for <fp1,
-									// tnode1>.
-									dgraph.recordNode(successor, tnode1.index);
+							final long ptr1 = dgraph.getPtr(successor, tnode1.getIndex());
+							if (consistency.get((tnode1.getIndex() * succCnt) + sidx)
+									&& (ptr1 == -1 || !node0.transExists(successor, tnode1.getIndex()))) {
+								node0.addTransition(successor, tnode1.getIndex(), checkStateResults.length, alen,
+										checkActionResults, sidx * alen, allocationHint - cnt);
+								writer.writeState(s0, tnode0, s1, tnode1, checkActionResults, sidx * alen, alen, true);
+								// Record that we have seen <fp1,
+								// tnode1>. If fp1 is done, we have
+								// to compute the next states for <fp1,
+								// tnode1>.
+								if (ptr1 == -1) {
+									dgraph.recordNode(successor, tnode1.getIndex());
 									if (isDone) {
-										addNextState(s1, successor, tnode1, oos, dgraph);
+										addNextState(tool, s1, successor, tnode1, oos, dgraph);
 									}
 								}
-							} else if (!node0.transExists(successor, tnode1.index)) {
-								node0.addTransition(successor, tnode1.index, checkStateResults.length,
-										alen, checkActionResults, sidx * alen, allocationHint
-												- cnt++);
-							} else {
-								// Increment cnt even if addTrasition is not called. After all, 
-								// the for loop has completed yet another iteration.
-								cnt++;
 							}
+							// Increment cnt even if addTrasition is not called. After all, 
+							// the for loop has completed yet another iteration.
+							cnt++;
 						}
 					}
+					nextStates.resetNext();
 					// See same case in LiveChecker#addNextState
-					if (s < node0.succSize()) {
+					if ((s == 0 && s == node0.succSize()) || s < node0.succSize()) {
 						node0.realign(); // see node0.addTransition() hint
 						dgraph.addNode(node0);
 					} else {
@@ -584,75 +683,81 @@ public class LiveCheck implements ILiveCheck {
 		}
 
 		/**
-		 * This method takes care of the case that a new node (s, t) is generated
-		 * after s has been done. In this case, we will have to compute the children
-		 * of (s, t). Hopefully, this case does not occur very frequently.
+		 * This method takes care of the case that a new node <<state, tableau>>
+		 * in the (state X tableau) graph is generated after the state itself
+		 * has been done. Done means that the state has been found during safety
+		 * checking in the state graph already, except that the node <<state,
+		 * tableau>> not been created.
+		 * <p>
+		 * In this case, we will have to generate the state graph successors of
+		 * the state and create the permutation of all successors with all
+		 * tableau nodes .
+		 * <p>
+		 * Hopefully, this case does not occur very frequently because it
+		 * generates successor nodes.
 		 */
-		private void addNextState(final TLCState s, final long fp, final TBGraphNode tnode, final OrderOfSolution oos, final TableauDiskGraph dgraph)
+		private void addNextState(final ITool tool, final TLCState s, final long fp, final TBGraphNode tnode, final OrderOfSolution oos, final TableauDiskGraph dgraph)
 				throws IOException {
-			final boolean[] checkStateRes = oos.checkState(s);
+			final boolean[] checkStateRes = oos.checkState(tool, s);
 			final int slen = checkStateRes.length;
 			final int alen = oos.getCheckAction().length;
-			final GraphNode node = dgraph.getNode(fp, tnode.index);
+			final GraphNode node = dgraph.getNode(fp, tnode.getIndex());
 			final int numSucc = node.succSize();
 			node.setCheckState(checkStateRes);
 
 			// see allocationHint of node.addTransition() invocations below
 			int cnt = 0;
 			
-			// Add edges induced by s -> s:
-			final BitVector checkActionResults = oos.checkAction(s, s, new BitVector(alen), 0);
-			
+			// Add edges induced by s -> s (self-loop) coming from the tableau
+			// graph:
 			final int nextSize = tnode.nextSize();
+			final BitVector checkActionResults = nextSize > 0 ? oos.checkAction(tool, s, s, new BitVector(alen), 0) : null;
 			for (int i = 0; i < nextSize; i++) {
 				final TBGraphNode tnode1 = tnode.nextAt(i);
-				final int tidx1 = tnode1.index;
+				final int tidx1 = tnode1.getIndex();
 				final long ptr1 = dgraph.getPtr(fp, tidx1);
-				if (ptr1 == -1) {
-					if (tnode1.isConsistent(s, myTool)) {
-						node.addTransition(fp, tidx1, slen, alen, checkActionResults, 0, (nextSize - cnt++));
-						dgraph.recordNode(fp, tnode1.index);
-						addNextState(s, fp, tnode1, oos, dgraph);
-					} else {
-						cnt++;
+				if (tnode1.isConsistent(s, tool) && (ptr1 == -1 || !node.transExists(fp, tidx1))) {
+					node.addTransition(fp, tidx1, slen, alen, checkActionResults, 0, (nextSize - cnt));
+					if (ptr1 == -1) {
+						dgraph.recordNode(fp, tnode1.getIndex());
+						addNextState(tool, s, fp, tnode1, oos, dgraph);
 					}
-				} else {
-					node.addTransition(fp, tidx1, slen, alen, checkActionResults, 0, (nextSize - cnt++));
 				}
+				cnt++;
 			}
 
-			// Add edges induced by s -> s1:
+			// Add edges induced by s -> s1 (where s1 is a successor of s in the
+			// state graph):
 			cnt = 0;
+			final Action[] actions = tool.getActions();
 			for (int i = 0; i < actions.length; i++) {
-				final StateVec nextStates = myTool.getNextStates(actions[i], s);
+				final StateVec nextStates = tool.getNextStates(actions[i], s);
 				final int nextCnt = nextStates.size();
 				for (int j = 0; j < nextCnt; j++) {
 					final TLCState s1 = nextStates.elementAt(j);
-					if (myTool.isInModel(s1) && myTool.isInActions(s, s1)) {
+					if (tool.isInModel(s1) && tool.isInActions(s, s1)) {
 						final long fp1 = s1.fingerPrint();
-						final BitVector checkActionRes = oos.checkAction(s, s1, new BitVector(alen), 0);
+						final BitVector checkActionRes = oos.checkAction(tool, s, s1, new BitVector(alen), 0);
 						boolean isDone = dgraph.isDone(fp1);
 						for (int k = 0; k < tnode.nextSize(); k++) {
 							final TBGraphNode tnode1 = tnode.nextAt(k);
-							final int tidx1 = tnode1.index;
+							final int tidx1 = tnode1.getIndex();
 							long ptr1 = dgraph.getPtr(fp1, tidx1);
 							final int total = actions.length * nextCnt * tnode.nextSize();
-							if (ptr1 == -1) {
-								if (tnode1.isConsistent(s1, myTool)) {
-									node.addTransition(fp1, tidx1, slen, alen, checkActionRes, 0, (total - cnt++));
-									// Record that we have seen <fp1, tnode1>. If
-									// fp1 is done, we have to compute the next
-									// states for <fp1, tnode1>.
+							if (tnode1.isConsistent(s1, tool) && (ptr1 == -1 || !node.transExists(fp1, tidx1))) {
+								node.addTransition(fp1, tidx1, slen, alen, checkActionRes, 0, (total - cnt));
+								writer.writeState(s, tnode, s1, tnode1, checkActionRes, 0, alen, false, Visualization.DOTTED);
+								// Record that we have seen <fp1, tnode1>. If
+								// fp1 is done, we have to compute the next
+								// states for <fp1, tnode1>.
+								if (ptr1 == -1) {
 									dgraph.recordNode(fp1, tidx1);
 									if (isDone) {
-										addNextState(s1, fp1, tnode1, oos, dgraph);
+										addNextState(tool, s1, fp1, tnode1, oos, dgraph);
 									}
 								}
-							} else if (!node.transExists(fp1, tidx1)) {
-								node.addTransition(fp1, tidx1, slen, alen, checkActionRes, 0, (total - cnt++));
-							} else {
-								cnt++;
 							}
+							cnt++;
 						}
 					} else {
 						cnt++;
@@ -672,56 +777,4 @@ public class LiveCheck implements ILiveCheck {
 			return dgraph;
 		}
 	}
-	
-	// Intended to be used in unit tests only!!! This is not part of the API!!!
-	static class TestHelper {
-		
-		/*
-		 * - EWD840 (with tableau) spec with N = 11 and maxSetSize = 9.000.000 => 12GB nodes file, 46.141.438 distinct states
-		 * - EWD840 (with tableau) spec with N = 12 and maxSetSize = 9.000.000 => 56GB, 201.334.782 dist. states 
-		 */
-		
-		// The Eclipse Launch configuration has to set the working directory
-		// (Arguments tab) to the parent directory of the folder containing the
-		// nodes_* and ptrs_* files. The parent folder has to contain the spec
-		// and config file both named "MC".
-		// metadir is the the name of the folder with the nodes_* and ptrs_*
-		// relative to the parent directory. The directory does *not* need to contain the
-		// backing file of the fingerprint set or the state queue files.
-		public static ILiveCheck recreateFromDisk(final String path) throws Exception {
-			// Don't know with which Polynomial the FP64 has been initialized, but
-			// the default is 0.
-			FP64.Init(0);
-			
-			// Most models won't need this, but let's increase this anyway.
-			TLCGlobals.setBound = 9000000;
-
-			// Re-create the tool to do the init states down below (LiveCheck#init
-			// doesn't really need tool).
-	        final Tool tool = new Tool("", "MC", "MC", new SimpleFilenameToStream());
-	        tool.init(true, null);
-	        
-			// Initialize tool's actions explicitly. LiveCheck#printTrace is
-			// going to access the actions and fails with a NPE unless
-			// initialized.
-	        tool.getActions();
-	        
-			final ILiveCheck liveCheck = new LiveCheck(tool, null, path, new DummyBucketStatistics());
-			
-			// Calling recover requires a .chkpt file to be able to re-create the
-			// internal data structures to continue with model checking. However, we
-			// only want to execute liveness checks on the current static disk
-			// graph. Thus, we don't need the .chkpt file.
-			//recover();
-			
-			// After recovery, one has to redo the init states
-			final StateVec initStates = tool.getInitStates();
-			for (int i = 0; i < initStates.size(); i++) {
-				TLCState state = initStates.elementAt(i);
-				liveCheck.addInitState(state, state.fingerPrint());
-			}
-			
-			return liveCheck; 
-		}
-	}
 }
diff --git a/tlatools/src/tlc2/tool/liveness/LiveCheck1.java b/tlatools/src/tlc2/tool/liveness/LiveCheck1.java
index d09308dc91ab06ba25f5c6faa7d55649b96854d0..c4f0811990a1368688b0c8db59f5044d7a3edcb6 100644
--- a/tlatools/src/tlc2/tool/liveness/LiveCheck1.java
+++ b/tlatools/src/tlc2/tool/liveness/LiveCheck1.java
@@ -7,21 +7,20 @@ package tlc2.tool.liveness;
 
 import java.io.IOException;
 
-import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.output.StatePrinter;
 import tlc2.tool.Action;
 import tlc2.tool.EvalException;
+import tlc2.tool.ITool;
 import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
 import tlc2.tool.TLCStateInfo;
-import tlc2.tool.Tool;
 import tlc2.util.FP64;
 import tlc2.util.LongObjTable;
-import tlc2.util.LongVec;
 import tlc2.util.MemObjectStack;
 import tlc2.util.ObjectStack;
+import tlc2.util.SetOfStates;
 import tlc2.util.Vect;
 import tlc2.util.statistics.DummyBucketStatistics;
 import tlc2.util.statistics.IBucketStatistics;
@@ -30,7 +29,7 @@ public class LiveCheck1 implements ILiveCheck {
 	/**
 	 * Implementation of liveness checking based on MP book.
 	 */
-	private Tool myTool;
+	private ITool myTool;
 	private String metadir = "";
 	private Action[] actions;
 	private OrderOfSolution[] solutions;
@@ -72,13 +71,13 @@ public class LiveCheck1 implements ILiveCheck {
 	 */
 	private BEGraphNode initNode = null;
 
-	public LiveCheck1(Tool tool) {
+	public LiveCheck1(ITool tool) {
 		myTool = tool;
 		solutions = Liveness.processLiveness(myTool);
 		bgraphs = new BEGraph[0];
 	}
 
-	public void init(Tool tool, Action[] acts, String mdir) {
+	public void init(ITool tool, Action[] acts, String mdir) {
 		myTool = tool;
 		metadir = mdir;
 		actions = acts;
@@ -115,14 +114,14 @@ public class LiveCheck1 implements ILiveCheck {
 	 * state trace (a sequence of states). Assume trace.length > 0. It returns
 	 * the set of initial states.
 	 */
-	Vect constructBEGraph(OrderOfSolution os) {
-		Vect initNodes = new Vect(1);
+	Vect<BEGraphNode> constructBEGraph(final ITool tool, OrderOfSolution os) {
+		Vect<BEGraphNode> initNodes = new Vect<>(1);
 		int slen = os.getCheckState().length;
 		int alen = os.getCheckAction().length;
 		TLCState srcState = stateTrace.elementAt(0); // the initial state
 		long srcFP = srcState.fingerPrint();
-		boolean[] checkStateRes = os.checkState(srcState);
-		boolean[] checkActionRes = os.checkAction(srcState, srcState);
+		boolean[] checkStateRes = os.checkState(tool, srcState);
+		boolean[] checkActionRes = os.checkAction(tool, srcState, srcState);
 		if (!os.hasTableau()) {
 			// If there is no tableau, construct begraph with trace.
 			LongObjTable allNodes = new LongObjTable(127);
@@ -137,12 +136,12 @@ public class LiveCheck1 implements ILiveCheck {
 				BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP);
 				if (destNode == null) {
 					destNode = new BEGraphNode(destFP);
-					destNode.setCheckState(os.checkState(srcState));
-					destNode.addTransition(destNode, slen, alen, os.checkAction(destState, destState));
-					srcNode.addTransition(destNode, slen, alen, os.checkAction(srcState, destState));
+					destNode.setCheckState(os.checkState(tool, srcState));
+					destNode.addTransition(destNode, slen, alen, os.checkAction(tool, destState, destState));
+					srcNode.addTransition(destNode, slen, alen, os.checkAction(tool, srcState, destState));
 					allNodes.put(destFP, destNode);
 				} else if (!srcNode.transExists(destNode)) {
-					srcNode.addTransition(destNode, slen, alen, os.checkAction(srcState, destState));
+					srcNode.addTransition(destNode, slen, alen, os.checkAction(tool, srcState, destState));
 				}
 				srcNode = destNode;
 				srcState = destState;
@@ -150,16 +149,16 @@ public class LiveCheck1 implements ILiveCheck {
 		} else {
 			// If there is tableau, construct begraph of (tableau X trace).
 			LongObjTable allNodes = new LongObjTable(255);
-			Vect srcNodes = new Vect();
+			Vect<BEGraphNode> srcNodes = new Vect<>();
 			int initCnt = os.getTableau().getInitCnt();
 			for (int i = 0; i < initCnt; i++) {
 				TBGraphNode tnode = os.getTableau().getNode(i);
 				if (tnode.isConsistent(srcState, myTool)) {
-					BEGraphNode destNode = new BTGraphNode(srcFP, tnode.index);
+					BEGraphNode destNode = new BTGraphNode(srcFP, tnode.getIndex());
 					destNode.setCheckState(checkStateRes);
 					initNodes.addElement(destNode);
 					srcNodes.addElement(destNode);
-					allNodes.put(FP64.Extend(srcFP, tnode.index), destNode);
+					allNodes.put(FP64.Extend(srcFP, tnode.getIndex()), destNode);
 				}
 			}
 			for (int i = 0; i < srcNodes.size(); i++) {
@@ -167,7 +166,7 @@ public class LiveCheck1 implements ILiveCheck {
 				TBGraphNode tnode = srcNode.getTNode(os.getTableau());
 				for (int j = 0; j < tnode.nextSize(); j++) {
 					TBGraphNode tnode1 = tnode.nextAt(j);
-					long destFP = FP64.Extend(srcFP, tnode1.index);
+					long destFP = FP64.Extend(srcFP, tnode1.getIndex());
 					BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP);
 					if (destNode != null) {
 						srcNode.addTransition(destNode, slen, alen, checkActionRes);
@@ -175,21 +174,21 @@ public class LiveCheck1 implements ILiveCheck {
 				}
 			}
 			for (int i = 1; i < stateTrace.size(); i++) {
-				Vect destNodes = new Vect();
+				Vect<BEGraphNode> destNodes = new Vect<>();
 				TLCState destState = stateTrace.elementAt(i);
 				long destStateFP = destState.fingerPrint();
-				checkStateRes = os.checkState(destState);
-				checkActionRes = os.checkAction(srcState, destState);
+				checkStateRes = os.checkState(myTool, destState);
+				checkActionRes = os.checkAction(tool, srcState, destState);
 				for (int j = 0; j < srcNodes.size(); j++) {
 					BEGraphNode srcNode = (BEGraphNode) srcNodes.elementAt(j);
 					TBGraphNode tnode = srcNode.getTNode(os.getTableau());
 					for (int k = 0; k < tnode.nextSize(); k++) {
 						TBGraphNode tnode1 = tnode.nextAt(k);
-						long destFP = FP64.Extend(destStateFP, tnode1.index);
+						long destFP = FP64.Extend(destStateFP, tnode1.getIndex());
 						BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP);
 						if (destNode == null) {
 							if (tnode1.isConsistent(destState, myTool)) {
-								destNode = new BTGraphNode(destStateFP, tnode1.index);
+								destNode = new BTGraphNode(destStateFP, tnode1.getIndex());
 								destNode.setCheckState(checkStateRes);
 								srcNode.addTransition(destNode, slen, alen, checkActionRes);
 								destNodes.addElement(destNode);
@@ -200,17 +199,17 @@ public class LiveCheck1 implements ILiveCheck {
 						}
 					}
 				}
-				checkActionRes = os.checkAction(destState, destState);
+				checkActionRes = os.checkAction(tool, destState, destState);
 				for (int j = 0; j < destNodes.size(); j++) {
 					BEGraphNode srcNode = (BEGraphNode) destNodes.elementAt(j);
 					TBGraphNode tnode = srcNode.getTNode(os.getTableau());
 					for (int k = 0; k < tnode.nextSize(); k++) {
 						TBGraphNode tnode1 = tnode.nextAt(k);
-						long destFP = FP64.Extend(destStateFP, tnode1.index);
+						long destFP = FP64.Extend(destStateFP, tnode1.getIndex());
 						BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP);
 						if (destNode == null) {
 							if (tnode1.isConsistent(destState, myTool)) {
-								destNode = new BTGraphNode(destStateFP, tnode1.index);
+								destNode = new BTGraphNode(destStateFP, tnode1.getIndex());
 								destNode.setCheckState(checkStateRes);
 								srcNode.addTransition(destNode, slen, alen, checkActionRes);
 								destNodes.addElement(destNode);
@@ -232,14 +231,14 @@ public class LiveCheck1 implements ILiveCheck {
 	 * This method adds new nodes into the behavior graph when a new initial
 	 * state is generated.
 	 */
-	public void addInitState(TLCState state, long stateFP) {
+	public void addInitState(ITool tool, TLCState state, long stateFP) {
 		for (int soln = 0; soln < solutions.length; soln++) {
 			OrderOfSolution os = solutions[soln];
 			BEGraph bgraph = bgraphs[soln];
 			int slen = os.getCheckState().length;
 			int alen = os.getCheckAction().length;
-			boolean[] checkStateRes = os.checkState(state);
-			boolean[] checkActionRes = os.checkAction(state, state);
+			boolean[] checkStateRes = os.checkState(tool, state);
+			boolean[] checkActionRes = os.checkAction(tool, state, state);
 			// Adding nodes and transitions:
 			if (!os.hasTableau()) {
 				// if there is no tableau ...
@@ -255,7 +254,7 @@ public class LiveCheck1 implements ILiveCheck {
 				for (int i = 0; i < initCnt; i++) {
 					TBGraphNode tnode = os.getTableau().getNode(i);
 					if (tnode.isConsistent(state, myTool)) {
-						BTGraphNode destNode = new BTGraphNode(stateFP, tnode.index);
+						BTGraphNode destNode = new BTGraphNode(stateFP, tnode.getIndex());
 						destNode.setCheckState(checkStateRes);
 						bgraph.addInitNode(destNode);
 						bgraph.allNodes.putBTNode(destNode);
@@ -270,14 +269,15 @@ public class LiveCheck1 implements ILiveCheck {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec)
+	 * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates)
 	 */
-	public void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException {
+	public void addNextState(final ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException {
 		for (int i = 0; i < nextStates.size(); i++) {
-			final TLCState s2 = nextStates.elementAt(i);
-			final long fp2 = nextFPs.elementAt(i);
-			addNextState(s0, fp0, s2, fp2);
+			final TLCState s2 = nextStates.next();
+			final long fp2 = s2.fingerPrint();
+			addNextState(tool, s0, fp0, s2, fp2);
 		}
+		nextStates.resetNext();
 	}
 
 	/**
@@ -285,7 +285,7 @@ public class LiveCheck1 implements ILiveCheck {
 	 * generated. The argument s2 is the new state. The argument s1 is parent
 	 * state of s2.
 	 */
-	public synchronized void addNextState(TLCState s1, long fp1, TLCState s2, long fp2) {
+	public synchronized void addNextState(final ITool tool, TLCState s1, long fp1, TLCState s2, long fp2) {
 		for (int soln = 0; soln < solutions.length; soln++) {
 			OrderOfSolution os = solutions[soln];
 			BEGraph bgraph = bgraphs[soln];
@@ -298,12 +298,12 @@ public class LiveCheck1 implements ILiveCheck {
 				BEGraphNode node2 = bgraph.allNodes.getBENode(fp2);
 				if (node2 == null) {
 					node2 = new BEGraphNode(fp2);
-					node2.setCheckState(os.checkState(s2));
-					node1.addTransition(node2, slen, alen, os.checkAction(s1, s2));
-					node2.addTransition(node2, slen, alen, os.checkAction(s2, s2));
+					node2.setCheckState(os.checkState(tool, s2));
+					node1.addTransition(node2, slen, alen, os.checkAction(tool, s1, s2));
+					node2.addTransition(node2, slen, alen, os.checkAction(tool, s2, s2));
 					bgraph.allNodes.putBENode(node2);
 				} else if (!node1.transExists(node2)) {
-					boolean[] checkActionRes = os.checkAction(s1, s2);
+					boolean[] checkActionRes = os.checkAction(tool, s1, s2);
 					node1.addTransition(node2, slen, alen, checkActionRes);
 				}
 			} else {
@@ -315,32 +315,32 @@ public class LiveCheck1 implements ILiveCheck {
 				}
 				boolean[] checkStateRes = null;
 				// Add edges induced by s1 --> s2:
-				boolean[] checkActionRes = os.checkAction(s1, s2);
+				boolean[] checkActionRes = os.checkAction(tool, s1, s2);
 				boolean[] checkActionRes1 = null;
 				for (int i = 0; i < srcNodes.length; i++) {
 					BTGraphNode srcNode = srcNodes[i];
 					TBGraphNode tnode = os.getTableau().getNode(srcNode.getIndex());
 					for (int j = 0; j < tnode.nextSize(); j++) {
 						TBGraphNode tnode1 = tnode.nextAt(j);
-						BTGraphNode destNode = bgraph.allNodes.getBTNode(fp2, tnode1.index);
+						BTGraphNode destNode = bgraph.allNodes.getBTNode(fp2, tnode1.getIndex());
 						if (destNode == null) {
 							if (tnode1.isConsistent(s2, myTool)) {
-								destNode = new BTGraphNode(fp2, tnode1.index);
+								destNode = new BTGraphNode(fp2, tnode1.getIndex());
 								if (checkStateRes == null) {
-									checkStateRes = os.checkState(s2);
+									checkStateRes = os.checkState(tool, s2);
 								}
 								destNode.setCheckState(checkStateRes);
 								srcNode.addTransition(destNode, slen, alen, checkActionRes);
 								int idx = bgraph.allNodes.putBTNode(destNode);
 								// add edges induced by s2 --> s2:
 								if (checkActionRes1 == null) {
-									checkActionRes1 = os.checkAction(s2, s2);
+									checkActionRes1 = os.checkAction(tool, s2, s2);
 								}
 								addNodesForStut(s2, fp2, destNode, checkStateRes, checkActionRes1, os, bgraph);
 								// if s2 is done, we have to do something for
 								// destNode:
 								if (bgraph.allNodes.isDone(idx)) {
-									addNextState(s2, fp2, destNode, os, bgraph);
+									addNextState(tool, s2, fp2, destNode, os, bgraph);
 								}
 							}
 						} else if (!srcNode.transExists(destNode)) {
@@ -363,10 +363,10 @@ public class LiveCheck1 implements ILiveCheck {
 		TBGraphNode tnode = node.getTNode(os.getTableau());
 		for (int i = 0; i < tnode.nextSize(); i++) {
 			TBGraphNode tnode1 = tnode.nextAt(i);
-			BTGraphNode destNode = bgraph.allNodes.getBTNode(fp, tnode1.index);
+			BTGraphNode destNode = bgraph.allNodes.getBTNode(fp, tnode1.getIndex());
 			if (destNode == null) {
 				if (tnode1.isConsistent(state, myTool)) {
-					destNode = new BTGraphNode(fp, tnode1.index);
+					destNode = new BTGraphNode(fp, tnode1.getIndex());
 					destNode.setCheckState(checkState);
 					node.addTransition(destNode, slen, alen, checkAction);
 					bgraph.allNodes.putBTNode(destNode);
@@ -383,7 +383,7 @@ public class LiveCheck1 implements ILiveCheck {
 	 * after s has been done. So, we still have to compute the children of (s,
 	 * t). Hopefully, this case will not occur very frequently.
 	 */
-	private void addNextState(TLCState s, long fp, BTGraphNode node, OrderOfSolution os, BEGraph bgraph) {
+	private void addNextState(final ITool tool, TLCState s, long fp, BTGraphNode node, OrderOfSolution os, BEGraph bgraph) {
 		TBGraphNode tnode = node.getTNode(os.getTableau());
 		int slen = os.getCheckState().length;
 		int alen = os.getCheckAction().length;
@@ -399,30 +399,30 @@ public class LiveCheck1 implements ILiveCheck {
 				boolean[] checkActionRes1 = null;
 				for (int k = 0; k < tnode.nextSize(); k++) {
 					TBGraphNode tnode1 = tnode.nextAt(k);
-					BTGraphNode destNode = bgraph.allNodes.getBTNode(fp1, tnode1.index);
+					BTGraphNode destNode = bgraph.allNodes.getBTNode(fp1, tnode1.getIndex());
 					if (destNode == null) {
 						if (tnode1.isConsistent(s1, myTool)) {
-							destNode = new BTGraphNode(fp1, tnode1.index);
+							destNode = new BTGraphNode(fp1, tnode1.getIndex());
 							if (checkStateRes == null) {
-								checkStateRes = os.checkState(s1);
+								checkStateRes = os.checkState(tool, s1);
 							}
 							if (checkActionRes == null) {
-								checkActionRes = os.checkAction(s, s1);
+								checkActionRes = os.checkAction(tool, s, s1);
 							}
 							destNode.setCheckState(checkStateRes);
 							node.addTransition(destNode, slen, alen, checkActionRes);
 							if (checkActionRes1 == null) {
-								checkActionRes1 = os.checkAction(s1, s1);
+								checkActionRes1 = os.checkAction(tool, s1, s1);
 							}
 							addNodesForStut(s1, fp1, destNode, checkStateRes, checkActionRes1, os, bgraph);
 							int idx = bgraph.allNodes.putBTNode(destNode);
 							if (bgraph.allNodes.isDone(idx)) {
-								addNextState(s1, fp1, destNode, os, bgraph);
+								addNextState(tool, s1, fp1, destNode, os, bgraph);
 							}
 						}
 					} else if (!node.transExists(destNode)) {
 						if (checkActionRes == null) {
-							checkActionRes = os.checkAction(s, s1);
+							checkActionRes = os.checkAction(tool, s, s1);
 						}
 						node.addTransition(destNode, slen, alen, checkActionRes);
 					}
@@ -436,16 +436,24 @@ public class LiveCheck1 implements ILiveCheck {
 			bgraphs[soln].allNodes.setDone(fp);
 		}
 	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILiveCheck#doLiveCheck()
+	 */
+	public boolean doLiveCheck() {
+		return true;
+	}
 
 	/**
 	 * Checks if the partial behavior graph constructed up to now contains any
 	 * "bad" cycle. A "bad" cycle gives rise to a violation of liveness
 	 * property.
 	 */
-	public synchronized boolean check(boolean forceCheck) {
+	@Override
+	public synchronized int check(ITool tool, boolean forceCheck) {
 		int slen = solutions.length;
 		if (slen == 0) {
-			return true;
+			return EC.NO_ERROR;
 		}
 
 		for (int soln = 0; soln < slen; soln++) {
@@ -469,18 +477,18 @@ public class LiveCheck1 implements ILiveCheck {
 		}
 		// Previous for loop with throw LivenessException anyway, thus no harm
 		// returning true regardless.
-		return true;
+		return EC.NO_ERROR;
 	}
 
 	/**
 	 * Checks if the behavior graph constructed from a state trace contains any
 	 * "bad" cycle.
 	 */
-	public void checkTrace(final StateVec trace) {
+	public synchronized void checkTrace(ITool tool, final StateVec trace) {
 		stateTrace = trace;
 		for (int soln = 0; soln < solutions.length; soln++) {
 			OrderOfSolution os = solutions[soln];
-			Vect initNodes = constructBEGraph(os);
+			Vect<BEGraphNode> initNodes = constructBEGraph(tool, os);
 
 			// Liveness.printTBGraph(os.tableau);
 			// ToolIO.err.println(os.behavior.toString());
@@ -645,13 +653,14 @@ public class LiveCheck1 implements ILiveCheck {
 		}
 
 		// Print the prefix:
-		TLCState lastState = null;
+		TLCState cycleState = null;
 		for (int i = 0; i < stateNum; i++) {
-			StatePrinter.printState(states[i], lastState, i + 1);
-			lastState = states[i].state;
+			StatePrinter.printState(states[i], cycleState, i + 1);
+			cycleState = states[i].state;
 		}
 
 		// Print the cycle:
+		TLCState lastState = cycleState;
 		int cyclePos = stateNum;
 		long[] fps = new long[cycleStack.size()];
 		int idx = fps.length;
@@ -671,15 +680,14 @@ public class LiveCheck1 implements ILiveCheck {
 			}
 		}
 		if (node.stateFP == lastState.fingerPrint()) {
-			StatePrinter.printStutteringState(++stateNum);
+			StatePrinter.printStutteringState(stateNum);
 		} else {
-			if (TLCGlobals.tool) {
-				// The parser in Tool mode is picky and does not detect the Back to State unless it's printed via MP.printState.
-				// See LiveWorker#printTrace(..)
-				MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + cyclePos }, (TLCState) null, -1);
-			} else {
-				MP.printMessage(EC.TLC_BACK_TO_STATE, "" + cyclePos);
-			}
+			sinfo = myTool.getState(cycleState.fingerPrint(), sinfo);
+			// The print stmts below claim there is a cycle, thus assert that
+			// there is indeed one. Index-based lookup into states array is
+			// reduced by one because cyclePos is human-readable.
+			assert cycleState.equals(sinfo.state);
+			StatePrinter.printBackToState(sinfo, cyclePos);
 		}
 	}
 
@@ -766,7 +774,7 @@ public class LiveCheck1 implements ILiveCheck {
 			// April
 			// 2012
 		}
-		throw new LiveException("LiveCheck: Found error trace.");
+		throw new LiveException(EC.TLC_TEMPORAL_PROPERTY_VIOLATED, "LiveCheck: Found error trace.");
 	}
 
 	/**
@@ -801,7 +809,7 @@ public class LiveCheck1 implements ILiveCheck {
 
 	/* This method checks whether a scc satisfies currentPEM. */
 	void checkComponent(BEGraphNode node) {
-		Vect nodes = extractComponent(node);
+		Vect<BEGraphNode> nodes = extractComponent(node);
 		if (nodes != null) {
 			PossibleErrorModel[] pems = currentOOS.getPems();
 			for (int i = 0; i < pems.length; i++) {
@@ -830,13 +838,13 @@ public class LiveCheck1 implements ILiveCheck {
 	 * trivial one. It also assigns a new number to all the nodes in the
 	 * component.
 	 */
-	Vect extractComponent(BEGraphNode node) {
+	Vect<BEGraphNode> extractComponent(BEGraphNode node) {
 		BEGraphNode node1 = (BEGraphNode) comStack.pop();
 		if (node == node1 && !node.transExists(node)) {
 			node.setNumber(MAX_FIRST);
 			return null;
 		}
-		Vect nodes = new Vect();
+		Vect<BEGraphNode> nodes = new Vect<>();
 		numFirstCom = secondNum++;
 		numSecondCom = thirdNum;
 		node1.setNumber(numFirstCom);
@@ -906,11 +914,9 @@ public class LiveCheck1 implements ILiveCheck {
 		return false;
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#finalCheck()
-	 */
-	public boolean finalCheck() throws Exception {
-		return check(true);
+	@Override
+	public int finalCheck(ITool tool) throws Exception {
+		return check(tool, true);
 	}
 
 	/* (non-Javadoc)
@@ -923,7 +929,7 @@ public class LiveCheck1 implements ILiveCheck {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#getTool()
 	 */
-	public Tool getTool() {
+	public ITool getTool() {
 		return myTool;
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/LiveException.java b/tlatools/src/tlc2/tool/liveness/LiveException.java
index 83f3ffd45b2b2827e7e9cca148e7cb87cd5303c4..783993063169ea03219367e4f9837ed68151ca83 100644
--- a/tlatools/src/tlc2/tool/liveness/LiveException.java
+++ b/tlatools/src/tlc2/tool/liveness/LiveException.java
@@ -7,11 +7,15 @@ package tlc2.tool.liveness;
 
 public class LiveException extends RuntimeException {
 
-	public LiveException() {
+	public final int errorCode;
+
+	public LiveException(int errorCode) {
 		super();
+		this.errorCode = errorCode;
 	}
 
-	public LiveException(String msg) {
+	public LiveException(int errorCode, String msg) {
 		super(msg);
+		this.errorCode = errorCode;
 	}
 }
diff --git a/tlatools/src/tlc2/tool/liveness/LiveExprNode.java b/tlatools/src/tlc2/tool/liveness/LiveExprNode.java
index 9e111bf648ccc7512db3d69a8468d31b43634a42..5322e39bf25c9b6375dc8a79100a10d2952d576e 100644
--- a/tlatools/src/tlc2/tool/liveness/LiveExprNode.java
+++ b/tlatools/src/tlc2/tool/liveness/LiveExprNode.java
@@ -5,8 +5,8 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 
 /**
  * LNConj - a conjunction. (contains list of conjuncts) LNDisj - a disjunction.
@@ -42,7 +42,13 @@ public abstract class LiveExprNode {
 	/* Returns true iff the expression contains action. */
 	public abstract boolean containAction();
 
-	public abstract boolean eval(Tool tool, TLCState s1, TLCState s2);
+	/**
+	 * @param s1 First state
+	 * @param s2 Second (successor) state
+	 * @param tool (Technical Tool implementation)
+	 * @return true iff both states are consistent with this {@link LiveExprNode}.
+	 */
+	public abstract boolean eval(ITool tool, TLCState s1, TLCState s2);
 
 	/* The string representation. */
 	public final String toString() {
diff --git a/tlatools/src/tlc2/tool/liveness/LiveWorker.java b/tlatools/src/tlc2/tool/liveness/LiveWorker.java
index 00dffb898622c0ae567a23fb182cea525eb68901..21f23c196f76b94ad51f4a501f4cdc6aacb59a37 100644
--- a/tlatools/src/tlc2/tool/liveness/LiveWorker.java
+++ b/tlatools/src/tlc2/tool/liveness/LiveWorker.java
@@ -5,59 +5,108 @@
 package tlc2.tool.liveness;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.output.StatePrinter;
 import tlc2.tool.EvalException;
-import tlc2.tool.TLCState;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCStateInfo;
-import tlc2.util.IdThread;
+import tlc2.util.IntStack;
 import tlc2.util.LongVec;
 import tlc2.util.MemIntQueue;
 import tlc2.util.MemIntStack;
+import tlc2.util.SynchronousDiskIntStack;
 import tlc2.util.statistics.BucketStatistics;
 import tlc2.util.statistics.IBucketStatistics;
 
-public class LiveWorker extends IdThread {
+/**
+ * {@link LiveWorker} is doing the heavy lifting of liveness checking:
+ * <ul>
+ * <li>Searches for strongly connected components (SCC) a.k.a. cycles in the
+ * liveness/behavior graph.</li>
+ * <li>Checks each SCC if it violates the liveness properties.</li>
+ * <li>In case of a violation, reconstructs and prints the error trace.</li>
+ * </ul>
+ */
+public class LiveWorker implements Callable<Boolean> {
+
+	/**
+	 * A marker that is pushed onto the dfsStack during SCC depth-first-search
+	 * to marker an explored nodes on the stack.
+	 * <p>
+	 * A node with a marker is on the comStack.
+	 */
+	private static final long SCC_MARKER = -42L;
 
 	public static final IBucketStatistics STATS = new BucketStatistics("Histogram SCC sizes", LiveWorker.class
 			.getPackage().getName(), "StronglyConnectedComponent sizes");
 	
 	private static int errFoundByThread = -1;
-	private static Object workerLock = new Object();
+	private static final Object workerLock = new Object();
 
 	private OrderOfSolution oos = null;
 	private AbstractDiskGraph dg = null;
 	private PossibleErrorModel pem = null;
 	private final ILiveCheck liveCheck;
 	private final BlockingQueue<ILiveChecker> queue;
+	private final boolean isFinalCheck;
+	/**
+	 * Total number of LiveWorkers simultaneously checking liveness.
+	 */
+	private final int numWorkers;
+
+	private final ITool tool;
 
-	public LiveWorker(int id, final ILiveCheck liveCheck, final BlockingQueue<ILiveChecker> queue) {
-		super(id);
+	private final int id;
+
+	public LiveWorker(final ITool tool, int id, int numWorkers, final ILiveCheck liveCheck, final BlockingQueue<ILiveChecker> queue, final boolean finalCheck) {
+		this.id = id;
+		this.tool = tool;
+		this.numWorkers = numWorkers;
 		this.liveCheck = liveCheck;
 		this.queue = queue;
+		this.isFinalCheck = finalCheck;
 	}
 
-	// Returns true iff an error has already found.
-	public static boolean hasErrFound() {
+	/**
+	 * Returns true iff an error has already been found.
+	 */
+	private static boolean hasErrFound() {
 		synchronized (workerLock) {
 			return (errFoundByThread != -1);
 		}
 	}
 
+	// True iff this LiveWorker found a liveness violation.zs
+	private static boolean hasErrFound(final int id) {
+		synchronized (workerLock) {
+			return (errFoundByThread == id);
+		}
+	}
+
 	/**
-	 * Returns true iff either an error has not found or the error is found by
-	 * this thread.
+	 * Returns true iff either an error has not been found or the error is found
+	 * by this thread.
+	 * <p>
+	 * This is used so that only one of the threads which have found an error
+	 * prints it.
 	 */
 	private/* static synchronized */boolean setErrFound() {
 		synchronized (workerLock) {
 			if (errFoundByThread == -1) {
-				errFoundByThread = this.myGetId(); // GetId();
+				errFoundByThread = this.id; // GetId();
 				return true;
-			} else if (errFoundByThread == this.myGetId()) { // (* GetId()) {
+			} else if (errFoundByThread == this.id) { // (* GetId()) {
 				return true;
 			}
 			return false;
@@ -69,34 +118,83 @@ public class LiveWorker extends IdThread {
 	 * http://en.wikipedia.org/wiki/Strongly_connected_component), and checks
 	 * each of them to see if it contains a counterexample.
 	 * <p>
-	 * It seems to be using the Path-based strong component algorithm
-	 * (http://en.wikipedia.org/wiki/Path-based_strong_component_algorithm).
-	 * This assumption however is incorrect! The second stack is not related to
-	 * the SCC algorithm. Thus, it is Tarjan's SCC algorithm at work
-	 * (http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm).
+	 * It is Tarjan's SCC algorithm at work:
+	 * <p>
+	 * The notable differences to the text book algorithm are:
+	 * <ul>
+	 * <li>It is implemented iteratively (probably to prevent StackOverflows)
+	 * </li>
+	 * <li>The lowLink number gets pushed onto the DFS stack</li>
+	 * <li>If a node is on the DFS stack is determined by checking if it has a
+	 * link number assigned</li>
+	 * <li>Once an SCC has been found, it is checked immediately for liveness
+	 * violations (there is no point it searching all SCCs if the first SCC
+	 * found already violates liveness)</li>
+	 * <li>Not all states are added to the set of unexplored nodes initially,
+	 * but only the model checking init states (all successors are known to be
+	 * reachable from the init states).</li>
+	 * <li>Liveness is checked periodically during model checking and thus
+	 * checkSccs runs on a partial graph. Thus some nodes are marked undone.
+	 * Those nodes are skipped by the SCC search.</li>
+	 * </ul>
+	 * @throws ExecutionException 
+	 * @throws InterruptedException 
+	 * 
+	 * @see http://en.wikipedia.org/wiki/Tarjan'
+	 *      s_strongly_connected_components_algorithm
+	 * @see http://dx.doi.org/10.1137%2F0201010
+	 * 
 	 */
-	private final void checkSccs() throws IOException {
+	private final void checkSccs(final ITool tool) throws IOException, InterruptedException, ExecutionException {
 		// Initialize this.dg:
 		this.dg.makeNodePtrTbl();
 		
 		// Initialize nodeQueue with initial states. The initial states stored 
 		// separately in the DiskGraph are resolved to their pointer location
 		// in the on-disk part of the DiskGraph.
-		// The pointer location is obviously used to:
+		// The pointer location generally is obviously used to:
 		// * Speed up disk lookups in the RandomAccessFile(s) backing up the DiskGraph
-		// * Seems to serve as a marker for when a node during SCCs is fully explored //VERIFY assumption!
+		// * Is replaced by the SCC link number the moment the node's successors
+		//   are explored during DFS search. At this point the ptr location isn't
+		//   needed anymore. The successors have been resolved.
 		// 
-		final MemIntQueue nodeQueue = new MemIntQueue(liveCheck.getMetaDir(), "root");
+		// From each node in nodeQueue the SCC search is started down below,
+		// which can subsequently add additional nodes into nodeQueue.
+		// 
+		// Contrary to plain Tarjan, not all vertices are added to the
+		// nodeQueue of unexplored states, but only the initial states. Since we
+		// know that all non-initial states are reachable from the set of
+		// initial states, this is sufficient to start with.
 		final LongVec initNodes = this.dg.getInitNodes();
 		final int numOfInits = initNodes.size();
+		// Allocate space for all initial states, assuming the majority of
+		// initial nodes will be done. Multiplied by 5 because of
+		// <<long, int, long>> per "record.
+		final MemIntQueue nodeQueue = new MemIntQueue(liveCheck.getMetaDir(), "root", (numOfInits / 2) * 5);
 		for (int j = 0; j < numOfInits; j += 2) {
 			final long state = initNodes.elementAt(j);
 			final int tidx = (int) initNodes.elementAt(j + 1);
 			final long ptr = this.dg.getLink(state, tidx);
-			if (ptr >= 0) { //QUESTION When is a ptr < 0?
+			// Check if the node <<state, tidx>> s is done. A node s is undone
+			// if it is an initial state which hasn't been explored yet. This is
+			// the case if s has been added via LiveChecker#addInitState but not
+			// yet via LiveChecker#addNextState. LiveChecker#addNextState fully
+			// explores the given init state s because it has access to s'
+			// successors.
+			if (ptr >= 0) {
+				// Make sure none of the init states has already been assigned a
+				// link number. That would indicate a bug in makeNodePtrTbl
+				// which is supposed to reset all link numbers to file ptrs.
+				assert DiskGraph.isFilePointer(ptr);
 				nodeQueue.enqueueLong(state);
 				nodeQueue.enqueueInt(tidx);
 				nodeQueue.enqueueLong(ptr);
+			} else {
+				// If this is the final check on the complete graph, no node is
+				// allowed to be undone. If it's not the final check, ptr has to
+				// be UNDONE (a non-UNDONE negative pointer is probably a bug).
+				// isFinalCheck => ptr # UNDONE
+				assert !isFinalCheck || ptr != TableauNodePtrTable.UNDONE;
 			}
 		}
 
@@ -104,85 +202,147 @@ public class LiveWorker extends IdThread {
 		final int slen = this.oos.getCheckState().length;
 		final int alen = this.oos.getCheckAction().length;
 		
-		// Tarjan's stack
-		final MemIntStack dfsStack = new MemIntStack(liveCheck.getMetaDir(), "dfs");
 		
-		// comStack is only being added to during the deep first search. It is passed
-		// to the checkComponent method while in DFS though.
-		//
-		// An Eclipse detailed formatter:
-		// StringBuffer buf = new StringBuffer(this.size);
-		// for (int i = 1; i < this.size; i+=5) {
-		// 	buf.append("state: ");
-		// 	buf.append(peakLong(size - i));
-		// 	buf.append("\n");
-		// 	buf.append(" Tableaux idx: ");
-		// 	buf.append(peakInt(size - i - 2));
-		// 	buf.append("\n");
-		// 	buf.append(" ptr loc: ");
-		// 	buf.append(peakLong(size - i - 3));
-		// 	buf.append("\n");
-		// }
-		// return buf.toString();
-		//
-		final MemIntStack comStack = new MemIntStack(liveCheck.getMetaDir(), "com");
+		// Synchronize all LiveWorker instances to consistently read free
+		// memory. This method is only called during initialization of SCC
+		// search, thus synchronization should not cause significant thread
+		// contention. We want a single LW to successfully allocate both
+		// dfsStack *and* comStack.
+		final IntStack dfsStack;
+		final IntStack comStack;
+		synchronized (LiveWorker.class) {
+			// Tarjan's stack
+			// Append thread id to name for unique disk files during concurrent SCC search
+			dfsStack = getStack(liveCheck.getMetaDir(), "dfs" + this.id);
+			
+			// comStack is only being added to during the deep first search. It is passed
+			// to the checkComponent method while in DFS though. Note that the nodes pushed
+			// onto comStack don't necessarily form a strongly connected component (see
+			// comment above this.checkComponent(...) below for more details).
+			//
+			// See tlc2.tool.liveness.LiveWorker.DetailedFormatter.toString(MemIntStack)
+			// which is useful during debugging.
+			comStack = getStack(liveCheck.getMetaDir(), "com" + this.id);
+		}
 
-		// Generate the SCCs and check if they contain any "bad" cycle.
-		while (nodeQueue.length() > 0) {
+		// Generate the SCCs and check if they contain a "bad" cycle.
+		while (nodeQueue.size() > 0) {
+			// Pick one of the unexplored nodes as root and start searching the
+			// reachable SCCs from it.
 			final long state = nodeQueue.dequeueLong();
 			final int tidx = nodeQueue.dequeueInt();
 			final long loc = nodeQueue.dequeueLong();
 
-			// Start computing SCCs with <state, tidx> as the root node:
+			// Reset (remove all elements) the stack. Logically a new SCC search
+			// is being started unrelated to the previous one.
 			dfsStack.reset();
 
+			// Push the first node onto the DFS stack which makes it the node
+			// from which the depth-first-search is being started.
 			dfsStack.pushLong(state);
 			dfsStack.pushInt(tidx);
 			dfsStack.pushLong(loc);
+			// Push the smallest possible link number (confusingly called
+			// MAX_PTR here but only because file pointers are < MAX_PTR) as the
+			// first link number.
+			// [0, MAX_PTR) for file pointers
+			// [MAX_PTR, MAX_LINK] for links
 			dfsStack.pushLong(DiskGraph.MAX_PTR);
 			long newLink = DiskGraph.MAX_PTR;
 
-			while (dfsStack.size() > 2) {
-				long lowLink = dfsStack.popLong();
+			while (dfsStack.size() >= 7) {
+				final long lowLink = dfsStack.popLong();
 				final long curLoc = dfsStack.popLong();
 				final int curTidx = dfsStack.popInt();
 				final long curState = dfsStack.popLong();
 				
+				// At this point curLoc is still a file pointer (small MAX_PTR)
+				// and not yet replaced by a link (MAX_PTR < curLoc < MAX_LINK).
+				assert DiskGraph.isFilePointer(curLoc);
 				
 				// The current node is explored iff curLoc < 0. If it is indeed fully explored,
 				// it means it has potentially found an SCC. Thus, check if this is the case
 				// for the current GraphNode.
-				if (curLoc < 0) {
+				// A node is fully explored if the nested loop over its
+				// successors down below in the else branch has not revealed any
+				// unexplored successors.
+				if (curLoc == SCC_MARKER) {
+					// Check if the current node's link is lowLink which
+					// indicates that the nodes on comStack up to <<curState,
+					// curTidx>> form an SCC.
+					// If curLink # lowLink, continue by pop'ing the next node
+					// from dfsStack. It can either be:
+					// - unexplored in which case the else branch is taken and
+					//   DFS continues.
+					// - be an intermediate node of the SCC and thus curLink #
+					//   lowLink for it too.
+					// - can be the start of the SCC (curLink = lowLink).
 					final long curLink = this.dg.getLink(curState, curTidx);
+					assert curLink < AbstractDiskGraph.MAX_LINK;
 					if (curLink == lowLink) {
-						// The states on the comStack from top to curState form
-						// a SCC.
-						// Check for "bad" cycle.
-						final boolean isOK = this.checkComponent(curState, curTidx, comStack);
+						// The states on the comStack from "top" to <<curState,
+						// curTidx>> form an SCC, thus check for "bad" cycle.
+						//
+						// The cycle does not necessarily include all states in
+						// comStack. "top" might very well be curState in which
+						// case only a single state is checked by
+						// checkComponent.
+						//
+						// The aforementioned case happens regularly when the
+						// behaviors to check don't have cycles at all (leaving
+						// single node cycles aside for the moment). The DFS
+						// followed each behavior from its initial state (see
+						// nodeQueue) all the way to the behavior's end state at
+						// which point DFS halts. Since DFS cannot continue
+						// (there are no successors) it calls checkComponent now
+						// with the current comStack and the end state as
+						// <<curState, curTidx>> effectively checking the
+						// topmost element of comStack. Unless this single state
+						// violates any liveness properties, it gets removed
+						// from comStack and DFS continues. Iff DFS still cannot
+						// continue because the predecessor to endstate
+						// (endstate - 1) has no more successors to explore
+						// either, it again calls checkComponent for the single
+						// element (endstate - 1). This goes on until either the
+						// initial state is reached or an intermediate state has
+						// unexplored successors with DFS.
+						final boolean isOK = this.checkComponent(tool, curState, curTidx, comStack);
 						if (!isOK) {
-							// Found a "bad" cycle, no point in searching for more SCCs. 
+							// Found a "bad" cycle of one to comStack.size()
+							// nodes, no point in searching for more SCCs as we
+							// are only interested in one counter-example at a
+							// time.
+							// checkComponent will have printed the
+							// counter-example by now.
 							return;
 						}
 					}
-					long plowLink = dfsStack.popLong();
-					if (lowLink < plowLink) {
-						plowLink = lowLink;
-					}
-					dfsStack.pushLong(plowLink);
+					// Replace previous lowLink (plowLink) with the minimum of
+					// the current lowLink and plowLink on the stack.
+					final long plowLink = dfsStack.popLong();
+					dfsStack.pushLong(Math.min(plowLink, lowLink));
 					
 				// No SCC found yet	
 				} else {
 					// Assign newLink to curState:
 					final long link = this.dg.putLink(curState, curTidx, newLink);
+					// link is -1 if newLink has been assigned to pair
+					// <<curState, curTidx>>. If the pair had been assigned a
+					// link before, the previous link in range [MAX_PTR,
+					// MAX_LINK] is returned. If the link is not -1, it means
+					// the node has been explored by this DFS search before.
 					if (link == -1) {
 						// Push curState back onto dfsStack, but make curState
 						// explored:
 						dfsStack.pushLong(lowLink);
 						dfsStack.pushLong(curState);
 						dfsStack.pushInt(curTidx);
-						dfsStack.pushLong(-1);
+						// Push a marker onto the stack that, if pop'ed as
+						// curLoc above causes branching to enter the true case
+						// of the if block.
+						dfsStack.pushLong(SCC_MARKER);
 
-						// Add curState to comStack:
+						// Add the tuple <<curState, curTidx, curLoc>> to comStack:
 						comStack.pushLong(curLoc);
 						comStack.pushInt(curTidx);
 						comStack.pushLong(curState);
@@ -190,52 +350,185 @@ public class LiveWorker extends IdThread {
 						// Look at all the successors of curState:
 						final GraphNode gnode = this.dg.getNode(curState, curTidx, curLoc);
 						final int succCnt = gnode.succSize();
-						long nextLowLink = newLink++;
+						long nextLowLink = newLink;
+						// DFS moved on to a new node, thus increment the newLink
+						// number by 1 for subsequent exploration.
+						newLink = newLink + 1;
 						for (int i = 0; i < succCnt; i++) {
 							final long nextState = gnode.getStateFP(i);
 							final int nextTidx = gnode.getTidx(i);
 							final long nextLink = this.dg.getLink(nextState, nextTidx);
+							// If <<nextState, nextTidx>> node's link is < 0 it
+							// means the node isn't "done" yet (see
+							// tlc2.tool.liveness.TableauNodePtrTable.UNDONE).
+							// A successor node t of gnode is undone if it is:
+							// - An initial state which hasn't been explored yet
+							// - t has not been added to the liveness disk graph
+							//   itself (only as the successor (transition) of
+							//   gnode).
+							//
+							// If it is >= 0, it either is a:
+							// - file pointer location
+							// - a previously assigned link (>= MAX_PTR)
+							//
+							// Iff nextLink == MAX_PTR, it means that the
+							// <<nextState, nextTidx>> successor node has been
+							// processed by checkComponent. The checks below
+							// will result in the successor node being skipped.
+							//
+							// It is possible that <<nextState, nextTidx>> =
+							// <<curState, curTid>> due to self loops. This is
+							// intended, as checkAction has to be evaluated for
+							// self loops too.
 							if (nextLink >= 0) {
+								// Check if the arc/transition from <<curState,
+								// curTidx>> to <<nextState, nextTidx>>
+								// satisfies ("P-satisfiable" MP page 422ff)
+								// its PEM's EAAction. If it does, 1/3 of the
+								// conditions for P-satisfiability are
+								// satisfied. Thus it makes sense to check the
+								// other 2/3 in checkComponent (AEAction &
+								// Fulfilling promises). If the EAAction does
+								// not hold, there is no point in checking the
+								// other 2/3. All must hold for
+								// P-satisfiability.
+								//
+								// This check is related to the fairness spec.
+								// Skip to check the other conjuncts of the PossibleErrorModel (PEM) if the
+								// action check is false (thus does not satisfy the PEM).
 								if (gnode.getCheckAction(slen, alen, i, eaaction)) {
+									// If the node's nextLink still points to
+									// disk, it means it has no link assigned
+									// yet which is the case if this node gets
+									// explored during DFS search the first
+									// time. Since it is new, add it to dfsStack
+									// to have it explored subsequently by DFS.
 									if (DiskGraph.isFilePointer(nextLink)) {
 										dfsStack.pushLong(nextState);
 										dfsStack.pushInt(nextTidx);
-										dfsStack.pushLong(nextLink);
-									} else if (nextLink < nextLowLink) {
-										nextLowLink = nextLink;
+										dfsStack.pushLong(nextLink); // nextLink is logically a ptr/loc here
+										// One would expect a (logical) lowLink
+										// being pushed (additionally to the
+										// ptr/loc in previous line) onto the
+										// stack here. However, it is pushed
+										// down below after all successors are
+										// on the stack and valid for the
+										// topmost successor. For the other
+										// successors below the topmost, a link
+										// number will be assigned subsequently.
+									} else {
+										// The node has been processed
+										// already, thus use the minimum of its link
+										// (nextLink) and nextLowLink.
+										nextLowLink = Math.min(nextLowLink, nextLink);
 									}
-								} else if (DiskGraph.isFilePointer(nextLink)) {
+								} else {
+									// The transition from <<curState, curTidx>>
+									// to <<nextState, nextTidx>> is not
+									// P-satisfiable and thus does not need to
+									// be checkComponent'ed. However, since we
+									// only added initial but no intermediate
+									// states to nodeQueue above, we have to add
+									// <<nextState, nextTidx>> to nodeQueue if
+									// it's still unprocessed (indicated by its
+									// on disk state). The current path
+									// potentially might be the only one by
+									// which DFS can reach it.
+									if (DiskGraph.isFilePointer(nextLink)) {
 									nodeQueue.enqueueLong(nextState);
 									nodeQueue.enqueueInt(nextTidx);
-									nodeQueue.enqueueLong(nextLink);
+									nodeQueue.enqueueLong(nextLink); // nextLink is logically a ptr/loc here
+									}
 								}
+							} else {
+								// If this is the final check on the complete
+								// graph, no node is allowed to be undone. If
+								// it's not the final check, nextLink has to be
+								// UNDONE (a non-UNDONE negative nextLink is
+								// probably a bug).
+								// isFinalCheck => nextLink # UNDONE
+								assert !isFinalCheck || nextLink != TableauNodePtrTable.UNDONE;
 							}
 						}
+						// Push the next lowLink onto stack on top of all
+						// successors. It is assigned to the topmost 
+						// successor only though.
 						dfsStack.pushLong(nextLowLink);
 					} else {
-						if (link < lowLink) {
-							lowLink = link;
-						}
-						dfsStack.pushLong(lowLink);
+						// link above wasn't "-1", thus it has to be a valid
+						// link in the known interval.
+						assert AbstractDiskGraph.MAX_PTR <= link && link <= AbstractDiskGraph.MAX_LINK; 
+						// Push the minimum of the two links onto the stack. If
+						// link == DiskGraph.MAX_PTR lowLink will always be the
+						// minimum (unless this graph has a gigantic amount of
+						// SCCs exceeding (MAX_LINK - MAX_PTR).
+						dfsStack.pushLong(Math.min(lowLink, link));
 					}
 				}
 			}
 		}
+		// Make sure all nodes on comStack have been checkComponent()'ed
+		assert comStack.size() == 0;
+	}
 
-		// After completing the checks, clean up:
-		// dfsStack.cleanup();
-		// comStack.cleanup();
+	private IntStack getStack(final String metaDir, final String name) throws IOException {
+		// It is unlikely that the stacks will fit into memory if the
+		// size of the behavior graph is larger relative to the available
+		// memory. Also take the total number of simultaneously running
+		// workers into account that have to share the available memory
+		// among each other.
+		try {
+			final double freeMemoryInBytes = (Runtime.getRuntime().freeMemory() / (numWorkers * 1d));
+			final long graphSizeInBytes = this.dg.getSizeOnDisk();
+			final double ratio = graphSizeInBytes / freeMemoryInBytes;
+			if (ratio > TLCGlobals.livenessGraphSizeThreshold) {
+				// Double SDIS's bufSize/pageSize by how much the graph size
+				// overshoots the free memory size, but limit page size to 1gb.
+				// Also, don't allocate more than what is available.
+				final int capacityInBytes = SynchronousDiskIntStack.BufSize << Math.min((int) ratio, 5);
+				if (capacityInBytes < freeMemoryInBytes) {
+					return new SynchronousDiskIntStack(metaDir, name, capacityInBytes);
+				} else {
+					// Use default SDIS which is 32mb of in-memory size
+					return new SynchronousDiskIntStack(metaDir, name);
+				}
+			}
+			// If the disk graph as a whole fits into memory, do not use a
+			// disk-backed SynchronousDiskIntStack.
+			return new MemIntStack(metaDir, name);
+		} catch (final OutOfMemoryError oom) {
+			System.gc();
+			// If the allocation above failed, be more conservative. If it fails to
+			// allocate even 16mb, TLC will subsequently terminate with a message about insufficient
+			// memory. 
+			final SynchronousDiskIntStack sdis = new SynchronousDiskIntStack(metaDir, name,
+					SynchronousDiskIntStack.BufSize / 2);
+			MP.printWarning(EC.GENERAL,
+					"Liveness checking will be extremely slow because TLC is running low on memory.\n"
+							+ "Try allocating more memory to the Java pool (heap) in future TLC runs.");
+			return sdis;
+		}
 	}
 
 	/**
-	 * For currentPEM, this method checks if the current scc satisfies its AEs
-	 * and is fulfilling. (We know the current scc satisfies the pem's EA.) If
-	 * satisfiable, this pem contains a counterexample, and this method then
-	 * calls printErrorTrace to print an error trace and returns false.
+	 * For currentPEM, this method checks if the current SCC satisfies its AEs
+	 * and is fulfilling (we know the current SCC satisfies the PEM's EA by the
+	 * nested EAaction in checkSccs() above.) If satisfiable, this PEM
+	 * contains a counterexample, and this method then calls printErrorTrace to
+	 * print an error trace and returns false.
+	 * <p>
+	 * Speaking in words of Manna & Pnueli (Page 422ff), it checks if ~&#966;
+	 * (which is PEM) is "P-satisfiable" (i.e. is there a computation that
+	 * satisfies &#968;). ~&#966; (called &#968; by MP) is the negation of the
+	 * liveness formula &#966; which has to be "P-valid" for the liveness
+	 * properties to be valid.
+	 * @throws ExecutionException 
+	 * @throws InterruptedException 
 	 */
-	private boolean checkComponent(final long state, final int tidx, final MemIntStack comStack) throws IOException {
-//		final int comStackSize = comStack.size();
-//		Assert.check(comStackSize > 0, EC.GENERAL);
+	private boolean checkComponent(final ITool tool, final long state, final int tidx, final IntStack comStack) throws IOException, InterruptedException, ExecutionException {
+		final long comStackSize = comStack.size();
+		// There is something to pop and each is a well formed tuple <<fp, tidx, loc>> 
+		assert comStackSize >= 5 && comStackSize % 5 == 0; // long + int + long
 		
 		long state1 = comStack.popLong();
 		int tidx1 = comStack.popInt();
@@ -259,10 +552,20 @@ public class LiveWorker extends IdThread {
 		// to be kept in com. However, it would destroy NodePtrTable's
 		// collision handling. NodePtrTable uses open addressing (see
 		// http://en.wikipedia.org/wiki/Open_addressing).
-		TableauNodePtrTable com = new TableauNodePtrTable(128);
+		//
+		// Initializing the TNPT with 128 buckets/slots is a significant memory
+		// overhead (especially when comStack contains < 10 elements) which
+		// regularly results in OutOfMemoryErrors being thrown. To alleviate the
+		// problem the key-space of the comStack elements could be checked and
+		// the minimum possible collision-free TNPT size be calculated.
+		// (Btw. the implementation uses a TNPT in the first place because it is
+		// passed on to printTrace iff an error is found. The implementation
+		// here could use a simple java.util.Map or HashTable technically.)
+		final TableauNodePtrTable com = new TableauNodePtrTable(128);
 		while (true) {
 			// Add <state1, tidx1> into com:
 			com.put(state1, tidx1, loc1);
+			assert AbstractDiskGraph.isFilePointer(loc1);
 			this.dg.setMaxLink(state1, tidx1);
 
 			// Get the next node of the component:
@@ -275,7 +578,7 @@ public class LiveWorker extends IdThread {
 			loc1 = comStack.popLong();
 		}
 		// Just parameter node in com OR com subset of comStack
-//		Assert.check(com.size() <= (comStackSize / 5), EC.GENERAL);
+		assert com.size() <= (comStackSize / 5);
 
 		STATS.addSample(com.size());
 
@@ -288,6 +591,7 @@ public class LiveWorker extends IdThread {
 		final boolean[] AEStateRes = new boolean[aeslen];
 		final boolean[] AEActionRes = new boolean[aealen];
 		final boolean[] promiseRes = new boolean[plen];
+		final int[] eaaction = this.pem.EAAction;
 
 		// Extract a node from the nodePtrTable "com".
 		// Note the upper limit is NodePtrTable#getSize() instead of
@@ -295,26 +599,46 @@ public class LiveWorker extends IdThread {
 		// NodePtrTable internally hashes the elements to buckets
 		// and isn't filled start to end. Thus, the code
 		// below iterates NodePtrTable front to end skipping null buckets.
-		int tsz = com.getSize();
+		//
+		// Note that the nodes are processed in random order (depending on a
+		// node's hash in TableauNodePtrTbl) and not in the order given by
+		// comStack. This is fine because the all checks have been evaluated
+		// eagerly during insertion into the liveness graph long before the
+		// SCC search started. Thus, the code here only has to check the 
+		// check results which can happen in any order.
+		final int tsz = com.getSize();
 		for (int ci = 0; ci < tsz; ci++) {
-			int[] nodes = com.getNodesByLoc(ci);
+			final int[] nodes = com.getNodesByLoc(ci);
 			if (nodes == null) {
 				// miss in NotePtrTable (null bucket)
 				continue;
 			}
 
 			state1 = TableauNodePtrTable.getKey(nodes);
-			for (int nidx = 2; nidx < nodes.length; nidx += com.getElemLength()) {
+			for (int nidx = 2; nidx < nodes.length; nidx += com.getElemLength()) { // nidx starts with 2 because [0][1] are the long fingerprint state1. 
 				tidx1 = TableauNodePtrTable.getTidx(nodes, nidx);
 				loc1 = TableauNodePtrTable.getElem(nodes, nidx);
 
-				GraphNode curNode = this.dg.getNode(state1, tidx1, loc1);
+				final GraphNode curNode = this.dg.getNode(state1, tidx1, loc1);
 
 				// Check AEState:
 				for (int i = 0; i < aeslen; i++) {
+					// Only ever set AEStateRes[i] to true, but never to false
+					// once it was true. It only matters if one state in com
+					// satisfies PEM's liveness property due to []<>~p (which is
+					// the inversion of <>[]p).
+					// 
+					// It obviously has to check all nodes in the component
+					// (com) if either of them violates AEState unless all
+					// elements of AEStateRes are true. From that point onwards,
+					// checking further states wouldn't make a difference.
 					if (!AEStateRes[i]) {
 						int idx = this.pem.AEState[i];
 						AEStateRes[i] = curNode.getCheckState(idx);
+						// Can stop checking AEStates the moment AEStateRes
+						// is completely set to true. However, most of the time
+						// aeslen is small and the compiler will probably optimize
+						// out.
 					}
 				}
 
@@ -322,17 +646,65 @@ public class LiveWorker extends IdThread {
 				// between the current node and a successor state. The current
 				// node has n successor states. For each pair, see iff the 
 				// successor is in the "com" NodePtrTablecheck, check actions
-				// and store the results in AEActionRes(ult).
-				int succCnt = curNode.succSize();
+				// and store the results in AEActionRes(ult). Note that the
+				// actions have long been checked in advance when the node was
+				// added to the graph and the actual state and not just its
+				// fingerprint was available. Here, the result is just being
+				// looked up.
+				final int succCnt = aealen > 0 ? curNode.succSize() : 0; // No point in looping successors if there are no AEActions to check on them.
 				for (int i = 0; i < succCnt; i++) {
-					long nextState = curNode.getStateFP(i);
-					int nextTidx = curNode.getTidx(i);
-					if (com.getLoc(nextState, nextTidx) != -1) {
-						for (int j = 0; j < aealen; j++) {
-							if (!AEActionRes[j]) {
-								int idx = this.pem.AEAction[j];
-								AEActionRes[j] = curNode.getCheckAction(slen, alen, i, idx);
-							}
+					final long nextState = curNode.getStateFP(i);
+					final int nextTidx = curNode.getTidx(i);
+					// For each successor <<nextState, nextTdix>> of curNode's
+					// successors check, if it is part of the currently
+					// processed SCC (com). Successors, which are not part of
+					// the current SCC have obviously no relevance here. After
+					// all, we check the SCC.
+					if (com.getLoc(nextState, nextTidx) == -1) {
+						continue;
+					}
+					// MAK 10/23/2018:
+					// Line 380 above "if(gnode.getCheckAction)" causes a transition A from state s
+					// -> t to be skipped even if a belongs to an SCC iff the transition A does not
+					// satisfy the EA action of the PossibleErrorModel (if the EA action(s) is not
+					// satisfied, the PEM cannot hold at all).
+					// However, some state graphs are such that there exists not just the transition
+					// A from s -> t but a second transition A' from t -> s - which satisfies the EA
+					// action(s) of the PEM. In the case of a "bidirectional" transition, the states
+					// s and t will be in the set of states 'com' (which make up the SCC). Thus, the
+					// transition A from s -> t will be incorrectly traversed here unless it is
+					// skipped (again). Not skipping the transition A will result in TLC reporting a
+					// (bogus) counterexample even if the liveness is not violated.
+					// 
+					// Consider the spec BT for which TLC incorrectly reports a liveness property 
+					// violation and prints a bogus counterexample:
+					//
+					// ---- BT -----
+					// EXTENDS Naturals
+					// VARIABLE x
+					// A == \/ x' = (x + 1) % 3
+					// B == x' \in 0..2
+					// Spec == (x=0) /\ [][A \/ B]_x/\ WF_x(A)
+					// Prop == Spec /\ WF_x(A) /\ []<><<A>>_x
+					// =============
+					//
+					// > Temporal properties were violated.
+					// > The following behavior constitutes a counter-example:
+					// > 1: <Initial predicate>
+					// > x = 0
+					// > 2: <A line xx...BT>
+					// > x = 1
+					// > 1: Back to state: <B line xx... BT>
+					//
+					// (see tlc2.tool.BidirectionalTransitions1Test and BidirectionalTransitions2Test)
+					if(!curNode.getCheckAction(slen, alen, i, eaaction)) {
+						continue;
+					}
+					for (int j = 0; j < aealen; j++) {
+						// Only set false to true, but never true to false. 
+						if (!AEActionRes[j]) {
+							final int idx = this.pem.AEAction[j];
+							AEActionRes[j] = curNode.getCheckAction(slen, alen, i, idx);
 						}
 					}
 				}
@@ -340,8 +712,8 @@ public class LiveWorker extends IdThread {
 				// Check that the component is fulfilling. (See MP page 453.)
 				// Note that the promises are precomputed and stored in oos.
 				for (int i = 0; i < plen; i++) {
-					LNEven promise = this.oos.getPromises()[i];
-					TBPar par = curNode.getTNode(this.oos.getTableau()).getPar();
+					final LNEven promise = this.oos.getPromises()[i];
+					final TBPar par = curNode.getTNode(this.oos.getTableau()).getPar();
 					if (par.isFulfilling(promise)) {
 						promiseRes[i] = true;
 					}
@@ -349,75 +721,381 @@ public class LiveWorker extends IdThread {
 			}
 		}
 
-		// We find a counterexample if all three conditions are satisfied.
+		// We find a counterexample if all three conditions are satisfied. If
+		// either of the conditions is false, it means the PEM does not hold and
+		// thus the liveness properties are not violated by the SCC.
+		//
+		// All AEState properties, AEActions and promises of PEM must be
+		// satisfied. If a single one isn't satisfied, the PEM as a whole isn't
+		// P-satisfiable. That's why it returns on the first false. As stated
+		// before, EAAction have already been checked if satisfiable.
+		// checkComponent is only called if the EA actions are satisfiable.
+		//
+		// Technically: No error is found if any of the AEStateRes, AEActionRes
+		// or promiseRes booleans is false.
 		for (int i = 0; i < aeslen; i++) {
 			if (!AEStateRes[i]) {
+//				writeDotViz(state, tidx, com, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator
+//						+ "pValidSCC" + System.currentTimeMillis() + ".dot"));
 				return true;
 			}
 		}
 		for (int i = 0; i < aealen; i++) {
 			if (!AEActionRes[i]) {
+//				writeDotViz(state, tidx, com, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator
+//						+ "pValidSCC" + System.currentTimeMillis() + ".dot"));
 				return true;
 			}
 		}
 		for (int i = 0; i < plen; i++) {
 			if (!promiseRes[i]) {
+//				writeDotViz(state, tidx, com, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator
+//						+ "pValidSCC" + System.currentTimeMillis() + ".dot"));
 				return true;
 			}
 		}
 		// This component must contain a counter-example because all three
-		// conditions are satisfied. So, print a counter-example!
+		// conditions are satisfied. So, print a counter-example (if this thread
+		// is the first one to find a counter-example)!
 		if (setErrFound()) {
-			this.printTrace(state, tidx, com);
+			this.printTrace(tool, state, tidx, com);
 		}
 		return false;
 	}
 
 	/* Check if the node <state, tidx> stutters. */
 	private boolean isStuttering(long state, int tidx, long loc) throws IOException {
-		int slen = this.oos.getCheckState().length;
-		int alen = this.oos.getCheckAction().length;
+		final int slen = this.oos.getCheckState().length;
+		final int alen = this.oos.getCheckAction().length;
 
-		GraphNode gnode = this.dg.getNode(state, tidx, loc);
-		int succCnt = gnode.succSize();
+		// Find the self loop and check its <>[]action
+		final GraphNode gnode = this.dg.getNode(state, tidx, loc);
+		final int succCnt = gnode.succSize();
 		for (int i = 0; i < succCnt; i++) {
-			long nextState = gnode.getStateFP(i);
-			int nextTidx = gnode.getTidx(i);
+			final long nextState = gnode.getStateFP(i);
+			final int nextTidx = gnode.getTidx(i);
 			if (state == nextState && tidx == nextTidx) {
 				return gnode.getCheckAction(slen, alen, i, this.pem.EAAction);
 			}
 		}
+		// <state, tidx> has no self loop, thus cannot stutter
 		return false;
 	}
 
 	/**
-	 * Print out the error state trace. The method first generates a "bad" cycle
-	 * from the current scc, and then generates a prefix path from some initial
-	 * state to the "bad" cycle in the state graph. The prefix path and the
-	 * "bad" cycle together forms a counter-example.
+	 * Print out the error state trace by finding a cycle in the given SCC. The
+	 * method first generates a "bad" cycle from the current scc, and then
+	 * generates a prefix path from some initial state to the "bad" cycle in the
+	 * state graph. The prefix path and the "bad" cycle together forms a
+	 * counter-example.
+	 * <p>
+	 * Additionally, the first part can be divided into the two while loops A)
+	 * and B). A) re-creates the sub-path of the error trace starting at the
+	 * start state of the SCC as given by the parameters and ends when all
+	 * states have be accumulated that -combined- violate the liveness
+	 * properties. Iff the last state after termination of A) is not equal to
+	 * the start state, there is a gap in the cycle. Thus, B) task is to close
+	 * the gap.
+	 * <p>
+	 * 
+	 * @see tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png for a
+	 *      sketch.
+	 * @see tlc2.tool.liveness.ErrorTraceConstructionTest which runs a spec that
+	 *      exemplifies the three staged error trace composition
+	 *      
+	 * @param state
+	 *            fingerprint of the state which is the "starting" state of the
+	 *            SCC in nodeTbl.
+	 * @param tidx
+	 *            tableau index pointing to the {@link TBGraph}. Corresponds to
+	 *            the state fingerprint. Combined <<state, tidx>> unique
+	 *            identify a node in the liveness/behavior graph.
+	 * @param nodeTbl
+	 *            The current SCC which is known to satisfy the
+	 *            {@link PossibleErrorModel} and thus violates the liveness
+	 *            properties.
+	 * @throws ExecutionException 
+	 * @throws InterruptedException 
 	 */
-	private void printTrace(final long state, final int tidx, final TableauNodePtrTable nodeTbl) throws IOException {
+	private void printTrace(ITool tool, final long state, final int tidx, final TableauNodePtrTable nodeTbl) throws IOException, InterruptedException, ExecutionException {
+//		writeDotViz(state, tidx, nodeTbl, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator
+//				+ "pSatisfiableSCC_" + System.currentTimeMillis() + ".dot"));
 
 		MP.printError(EC.TLC_TEMPORAL_PROPERTY_VIOLATED);
 		MP.printError(EC.TLC_COUNTER_EXAMPLE);
+		
+		/*
+		 * Use a dedicated thread to concurrently search a prefix-path from some
+		 * initial node to the state identified by <<state, tidx>>.
+		 */
+		final ExecutorService executor = Executors.newFixedThreadPool(1);
+		final Future<List<TLCStateInfo>> future = executor.submit(new Callable<List<TLCStateInfo>>() {
+			/* (non-Javadoc)
+			 * @see java.util.concurrent.Callable#call()
+			 */
+			public List<TLCStateInfo> call() throws Exception {
+				// Print the error trace. We first construct the prefix that
+				// led to the bad cycle. The nodes on prefix and cycleStack then
+				// form the complete counter example.
+				final LongVec prefix = LiveWorker.this.dg.getPath(state, tidx);
+				final int plen = prefix.size();
+				final List<TLCStateInfo> states = new ArrayList<TLCStateInfo>(plen);
+
+				// Recover the initial state:
+				//TODO This throws an ArrayIndexOutOfBounds if getPath returned a
+				// LongVec with just a single element. This happens when the parameter
+				// state is one of the init states already.
+				long fp = prefix.elementAt(plen - 1);
+				TLCStateInfo sinfo = tool.getState(fp);
+				if (sinfo == null) {
+					throw new EvalException(EC.TLC_FAILED_TO_RECOVER_INIT);
+				}
+				states.add(sinfo);
+
+				// Recover the successor states:
+				for (int i = plen - 2; i >= 0; i--) {
+					long curFP = prefix.elementAt(i);
+					// The prefix might contain duplicates if the path happens to walk
+					// along two (or more distinct states which differ in the tableau
+					// idx only (same fingerprint). From the counterexample perspective,
+					// this is irrelevant iff the identical fingerprints are contiguous.
+					// It won't be correct to shorten a path <<fp1,fp2,fp1>> to
+					// <<fp2,fp1>> though.
+					if (curFP != fp) {
+						sinfo = tool.getState(curFP, sinfo);
+						states.add(sinfo);	
+						fp = curFP;
+					}
+				}
+
+				// Print the prefix in reverse order of previous loop:
+				for (int i = 0; i < states.size(); i++) {
+					StatePrinter.printState(states.get(i));
+				}
+				return states;
+			}
+		});
+
+		/*
+		 * With the executor concurrently working on the prefix, let this thread
+		 * work on the postfix (cycle).
+		 */
+		final MemIntStack cycleStack = new MemIntStack(liveCheck.getMetaDir(), "cycle");
+		GraphNode curNode = dfsPostFix(state, tidx, nodeTbl, cycleStack);
+		
+		/*
+		 * If the cycle is not closed/completed (complete when startState ==
+		 * state), continue from the curNode at which the previous while loop
+		 * terminated and follow its successors until the start state shows up.
+		 */
+		final LongVec postfix = bfsPostFix(state, tidx, nodeTbl, curNode);
+
+		/*
+		 * At this point the cycle part of the error trace has been constructed.
+		 * cycleStack contains the states from the start state of the SCC up to
+		 * the state that violates all liveness properties. postfix contains the
+		 * suffix from the violating state back to the start state of the SCC.
+		 * Thus, append the reversed cycleStack onto postfix (cycleStack has the
+		 * last state at the top). Postfix then contains the minimal path in the
+		 * SCC that violates the liveness property.
+		 */
+		while (cycleStack.size() > 0) {
+			// Do not filter successive <<fp,tidx,permId>> here but do it below
+			// when the actual states get printed. See Test3.tla for reason why.
+			postfix.addElement(cycleStack.popLong());
+			cycleStack.popInt(); // ignore tableau idx. The tableau idx is
+									// irrelevant as <<fpA, tidx1>> and <<fpA,
+									// tidx2>> both map to the same state in the
+									// error trace.
+		}
+
+		// Wait for the prefix-path to be searched/generated and fully printed.
+		// get() is a blocking call that makes this thread wait for the executor
+		// to finish its job of searching and printing the prefix-path.
+		List<TLCStateInfo> states = new ArrayList<>(0);
+		try {
+			states = future.get();
+		} catch (ExecutionException ee) {
+			// Do not "leak" ExecutionException to user if root cause is actually an
+			// EvalException.
+			if (ee.getCause() instanceof EvalException) {
+				throw (EvalException) ee.getCause();
+			}
+			throw ee;
+		}
+		
+		/*
+		 * At this point everything from the initial state up to the start state
+		 * of the SCC has been printed. Now, print the states in postfix. Obtain
+		 * the last state from the prefix (which corresponds to <<state, tidx>>)
+		 * to use it to generate the next state. Obviously, we have to wait for
+		 * the prefix thread to be done for two reasons: a) the trace has to be
+		 * printed and b) we need the TLCState instance to generate the
+		 * successor states in the cycle.
+		 */
+		final TLCStateInfo cycleState = states.get(states.size() - 1);
+		
+		TLCStateInfo sinfo = cycleState;
+		for (int i = postfix.size() - 1; i >= 0; i--) {
+			final long curFP = postfix.elementAt(i);
+			// Only print the state if it differs from its predecessor. We don't
+			// want to print an identical state twice. This can happen if the
+			// loops A) and B) above added an identical state multiple times
+			// into cycleStack/postfix.
+			// The reason we don't simply compare the actual states is for
+			// efficiency reason. Regenerating the next state might be
+			// expensive.
+			if (curFP != sinfo.fingerPrint()) {
+				sinfo = tool.getState(curFP, sinfo);
+				StatePrinter.printState(sinfo);
+			}
+		}
+
+		/* All error trace states have been printed (prefix + cycleStack +
+		 * postfix). What is left is to print either the stuttering or the
+		 * back-to-cyclePos marker.
+		 */ 
+		
+		final int stateNumber = (int) cycleState.stateNumber; // if the cast causes problems the trace won't be comprehensible anyway.
+		if (sinfo.fingerPrint() == cycleState.fingerPrint()) {
+			StatePrinter.printStutteringState(stateNumber);
+		} else {
+			sinfo = tool.getState(cycleState.fingerPrint(), sinfo);
+			// The print stmts below claim there is a cycle, thus assert that
+			// there is indeed one. Index-based lookup into states array is
+			// reduced by one because cyclePos is human-readable.
+			assert cycleState.state.equals(sinfo.state);
+			StatePrinter.printBackToState(sinfo, stateNumber);
+		}
+	}
+
+	// BFS search
+	private LongVec bfsPostFix(final long state, final int tidx, final TableauNodePtrTable nodeTbl, GraphNode curNode)
+			throws IOException {
+		final int slen = this.oos.getCheckState().length;
+		final int alen = this.oos.getCheckAction().length;
+		final int[] eaaction = this.pem.EAAction;
+		
+		final LongVec postfix = new LongVec(16);
+		final long startState = curNode.stateFP;
+		final long startTidx = curNode.tindex;
+		
+		// B)
+		if (startState != state || startTidx != tidx) {
+			final MemIntQueue queue = new MemIntQueue(liveCheck.getMetaDir(), null);
+			long curState = startState;
+			int ploc = TableauNodePtrTable.NO_PARENT;
+			int curLoc = nodeTbl.getNodesLoc(curState);
+			int[] nodes = nodeTbl.getNodesByLoc(curLoc);
+			TableauNodePtrTable.setSeen(nodes);
+
+			// B1)
+			_done: while (true) {
+				// tloc, the index of the various tableau indices in nodes array
+				int tloc = TableauNodePtrTable.startLoc(nodes);
+				// Loop over all GraphNodes (differing by tableau indices only)
+				// in nodes until at end. When all GraphNodes are explored, the
+				// next GraphNode to explore is taken from the FIFO/queue and
+				// the search continues. If one GraphNode happens to match the
+				// searched for <<fp, tidx>>, the (forward) search stops and
+				// the reverse path is followed up to the start node (inner
+				// while loop).
+				while (tloc != TableauNodePtrTable.END_MARKER) {
+					final int curTidx = TableauNodePtrTable.getTidx(nodes, tloc);
+					final long curPtr = TableauNodePtrTable.getPtr(TableauNodePtrTable.getElem(nodes, tloc));
+					curNode = this.dg.getNode(curState, curTidx, curPtr);
+					final int succCnt = curNode.succSize();
+
+					// for each successor of curNode s, check if s is the
+					// destination state.
+					SUCCESSORS: for (int j = 0; j < succCnt; j++) {
+						final long nextState = curNode.getStateFP(j);
+						final int nextTidx = curNode.getTidx(j);
+
+						// Ignore self-loop because it cannot close the
+						// cycle/lasso. The seen state flag partially
+						// prevents exploring self-loops, but only if
+						// the tableau idx is the base idx (the seen 
+						// flag ignores the tableau idx entirely).
+						if (curState == nextState && curTidx == nextTidx) {
+							assert TableauNodePtrTable.isSeen(nodes);
+							continue SUCCESSORS;
+						}
+						
+						// Prevent bogus counterexample: Do not close the loop by taking an action which
+						// does not satisfy the PossibleErrorModel (read more about it on line 640 in
+						// checkComponent).
+						if(!curNode.getCheckAction(slen, alen, j, eaaction)) {
+							continue;
+						}
+						
+						if (nextState == state && nextTidx == tidx) {
+							// We have found a path from startState to state,
+							// now backtrack the path the outer loop took to get
+							// us here and add each state to postfix.
+							while (curState != startState) {
+								postfix.addElement(curState);
+								nodes = nodeTbl.getNodesByLoc(ploc);
+								curState = TableauNodePtrTable.getKey(nodes);
+								ploc = TableauNodePtrTable.getParent(nodes);
+							}
+							postfix.addElement(startState);
+							break _done;
+						}
 
+						// s is not equal to the destination state 'startState'.
+						// If s's successors are still unseen, add s to the
+						// queue to later explore it as well. Mark it seen
+						// to not explore it twice.
+						final int[] nodes1 = nodeTbl.getNodes(nextState);
+						if (nodes1 != null && !TableauNodePtrTable.isSeen(nodes1)) {
+							TableauNodePtrTable.setSeen(nodes1);
+							queue.enqueueLong(nextState);
+							queue.enqueueInt(curLoc);
+						}
+					}
+					tloc = TableauNodePtrTable.nextLoc(nodes, tloc);
+				}
+				// Create a parent pointer to later reverse the path in B2)
+				TableauNodePtrTable.setParent(nodes, ploc);
+				// Dequeue the next unexplored state from the queue.
+				curState = queue.dequeueLong();
+				ploc = queue.dequeueInt();
+				curLoc = nodeTbl.getNodesLoc(curState);
+				nodes = nodeTbl.getNodesByLoc(curLoc);
+			}
+		}
+		return postfix;
+	}
+
+	private GraphNode dfsPostFix(final long state, final int tidx, final TableauNodePtrTable nodeTbl, final MemIntStack cycleStack) throws IOException {
 		// First, find a "bad" cycle from the "bad" scc.
-		int slen = this.oos.getCheckState().length;
-		int alen = this.oos.getCheckAction().length;
-		boolean[] AEStateRes = new boolean[this.pem.AEState.length];
-		boolean[] AEActionRes = new boolean[this.pem.AEAction.length];
-		boolean[] promiseRes = new boolean[this.oos.getPromises().length];
+		final int slen = this.oos.getCheckState().length;
+		final int alen = this.oos.getCheckAction().length;
+		// The 3 boolean arrays are used to make sure that the same check result
+		// is exactly counted once.
+		final boolean[] AEStateRes = new boolean[this.pem.AEState.length];
+		final boolean[] AEActionRes = new boolean[this.pem.AEAction.length];
+		final boolean[] promiseRes = new boolean[this.oos.getPromises().length];
+		final int[] eaaction = this.pem.EAAction;
+		// The number/count of all liveness checks. The while loop A) terminates
+		// once it has accumulated all states that violate all checks (we know
+		// that the states in nodeTbl have to violate the liveness property
+		// because we are in printTrace already. checkComponent has already
+		// determined that there is a violation).
 		int cnt = AEStateRes.length + AEActionRes.length + promiseRes.length;
 
-		MemIntStack cycleStack = new MemIntStack(liveCheck.getMetaDir(), "cycle");
-
 		// Mark state as visited:
 		int[] nodes = nodeTbl.getNodes(state);
 		int tloc = nodeTbl.getIdx(nodes, tidx);
-		long ptr = TableauNodePtrTable.getElem(nodes, tloc);
+		final long ptr = TableauNodePtrTable.getElem(nodes, tloc);
 		TableauNodePtrTable.setSeen(nodes, tloc);
 
+		// A)
+		//
+		// Greedy DFS search for a path satisfying the PossibleErrorModel.
 		GraphNode curNode = this.dg.getNode(state, tidx, ptr);
+
 		while (cnt > 0) {
 			int cnt0 = cnt;
 
@@ -445,7 +1123,8 @@ public class LiveWorker extends IdThread {
 					break;
 				}
 
-				// Check AEAction:
+				// Check AEAction (which is a check of the out-arc of curNode to
+				// one of its successors):
 				long nextState1 = 0, nextState2 = 0;
 				int nextTidx1 = 0, nextTidx2 = 0;
 				int tloc1 = -1, tloc2 = -1;
@@ -459,18 +1138,26 @@ public class LiveWorker extends IdThread {
 					nodes = nodeTbl.getNodes(nextState);
 					if (nodes != null) {
 						tloc = nodeTbl.getIdx(nodes, nextTidx);
-						if (tloc != -1) {
-							// <nextState, nextTidx> is in nodeTbl.
-							nextState1 = nextState;
-							nextTidx1 = nextTidx;
-							tloc1 = tloc;
-							nodes1 = nodes;
-							for (int j = 0; j < this.pem.AEAction.length; j++) {
-								int idx = this.pem.AEAction[j];
-								if (!AEActionRes[j] && curNode.getCheckAction(slen, alen, i, idx)) {
-									AEActionRes[j] = true;
-									cnt--;
-								}
+						// See checkComponent line 637.
+						if (tloc == -1) {
+							continue;
+						}
+						// Prevent bogus counterexample: Do not close the loop by taking an action which
+						// does not satisfy the PossibleErrorModel (read more about it on line 640 in
+						// checkComponent).
+						if(!curNode.getCheckAction(slen, alen, i, eaaction)) {
+							continue;
+						}
+						// <nextState, nextTidx> is in nodeTbl.
+						nextState1 = nextState;
+						nextTidx1 = nextTidx;
+						tloc1 = tloc;
+						nodes1 = nodes;
+						for (int j = 0; j < this.pem.AEAction.length; j++) {
+							int idx = this.pem.AEAction[j];
+							if (!AEActionRes[j] && curNode.getCheckAction(slen, alen, i, idx)) {
+								AEActionRes[j] = true;
+								cnt--;
 							}
 						}
 					}
@@ -537,181 +1224,188 @@ public class LiveWorker extends IdThread {
 				TableauNodePtrTable.setSeen(nodes2, tloc2);
 			}
 		}
-
-		// All the conditions are satisfied. Find a path from curNode
-		// to state to form a cycle. Note that:
+		// All the conditions are satisfied. 
 		// 1. curNode has not been pushed on cycleStack.
-		// 2. nodeTbl is trashed after this operation.
+		// 2. nodeTbl is trashed after this operation, thus reset. Trashed means
+		// that some nodes are still marked seen being left-overs from the
+		// Depth-First search.
 		nodeTbl.resetElems();
-		final LongVec postfix = new LongVec(16);
-		long startState = curNode.stateFP;
-
-		if (startState != state) {
-			MemIntQueue queue = new MemIntQueue(liveCheck.getMetaDir(), null);
-			long curState = startState;
-			int ploc = -1;
-			int curLoc = nodeTbl.getNodesLoc(curState);
-			nodes = nodeTbl.getNodesByLoc(curLoc);
-			TableauNodePtrTable.setSeen(nodes);
-
-			_done: while (true) {
-				tloc = TableauNodePtrTable.startLoc(nodes);
-				while (tloc != -1) {
-					int curTidx = TableauNodePtrTable.getTidx(nodes, tloc);
-					long curPtr = TableauNodePtrTable.getPtr(TableauNodePtrTable.getElem(nodes, tloc));
-					curNode = this.dg.getNode(curState, curTidx, curPtr);
-					int succCnt = curNode.succSize();
-
-					for (int j = 0; j < succCnt; j++) {
-						long nextState = curNode.getStateFP(j);
-
-						if (nextState == state) {
-							// we have found a path from startState to state:
-							while (curState != startState) {
-								postfix.addElement(curState);
-								nodes = nodeTbl.getNodesByLoc(ploc);
-								curState = TableauNodePtrTable.getKey(nodes);
-								ploc = TableauNodePtrTable.getParent(nodes);
-							}
-							postfix.addElement(startState);
-							break _done;
-						}
+		
+		return curNode;
+	}
 
-						int[] nodes1 = nodeTbl.getNodes(nextState);
-						if (nodes1 != null && !TableauNodePtrTable.isSeen(nodes1)) {
-							TableauNodePtrTable.setSeen(nodes1);
-							queue.enqueueLong(nextState);
-							queue.enqueueInt(curLoc);
-						}
-					}
-					tloc = TableauNodePtrTable.nextLoc(nodes, tloc);
-				}
-				TableauNodePtrTable.setParent(nodes, ploc);
-				curState = queue.dequeueLong();
-				ploc = queue.dequeueInt();
-				curLoc = nodeTbl.getNodesLoc(curState);
-				nodes = nodeTbl.getNodesByLoc(curLoc);
+	public final Boolean call() throws IOException, InterruptedException, ExecutionException {
+		while (true) {
+			// Use poll() to get the next checker from the queue or null if
+			// there is none. Do *not* block when there are no more checkers
+			// available. Nobody is going to add new checkers to the queue.
+			final ILiveChecker checker = queue.poll();
+			if (checker == null || hasErrFound()) {
+				// Another thread has either found an error (violation of a
+				// liveness property) OR there is no more work (checker) to
+				// be done.
+				break;
 			}
-		}
 
-		// Now, print the error trace. We first construct the prefix that
-		// led to the bad cycle. The nodes on prefix and cycleStack then
-		// form the complete counter example.
-		int stateNum = 0;
-		LongVec prefix = this.dg.getPath(state, tidx);
-		int plen = prefix.size();
-		TLCStateInfo[] states = new TLCStateInfo[plen];
-
-		// Recover the initial state:
-		//TODO This throws an ArrayIndexOutOfBounds if getPath returned a
-		// LongVec with just a single element. This happens when the parameter
-		// state is one of the init states already.
-		long fp = prefix.elementAt(plen - 1);
-		TLCStateInfo sinfo = liveCheck.getTool().getState(fp);
-		if (sinfo == null) {
-			throw new EvalException(EC.TLC_FAILED_TO_RECOVER_INIT);
-		}
-		states[stateNum++] = sinfo;
-
-		// Recover the successor states:
-		//TODO Check if path.size has elements
-		for (int i = plen - 2; i >= 0; i--) {
-			long curFP = prefix.elementAt(i);
-			// The prefix might contain duplicates if the path happens to walk
-			// along two distinct states which differ in the tableau idx only
-			// (same fingerprint). From the counterexample perspective, this is
-			// irrelevant.
-			if (curFP != fp) {
-				sinfo = liveCheck.getTool().getState(curFP, sinfo.state);
-				if (sinfo == null) {
-					throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT);
+			this.oos = checker.getSolution();
+			this.dg = checker.getDiskGraph();
+			this.dg.createCache();
+			PossibleErrorModel[] pems = this.oos.getPems();
+			for (int i = 0; i < pems.length; i++) {
+				if (!hasErrFound()) {
+					this.pem = pems[i];
+					this.checkSccs(tool);
 				}
-				states[stateNum++] = sinfo;
-				fp = curFP;
 			}
+			this.dg.destroyCache();
+			// Record the size of the disk graph at the time its checked. This
+			// information is later used to decide if it it makes sense to
+			// run the next check on the larger but still *partial* graph.
+			this.dg.recordSize();
+			// If assertions are on (e.g. during unit testing) make sure
+			// that the disk graph's invariants hold.
+			assert this.dg.checkInvariants(oos.getCheckState().length, oos.getCheckAction().length);
 		}
+		return hasErrFound(this.id);
+	}
 
-		// Print the prefix:
-		TLCState lastState = null;
-		for (int i = 0; i < stateNum; i++) {
-			StatePrinter.printState(states[i], lastState, i + 1);
-			lastState = states[i].state;
-		}
+	public String toDotViz(final long state, final int tidx, TableauNodePtrTable tnpt) throws IOException {
+		final StringBuffer sb = new StringBuffer(tnpt.size() * 10);
+		sb.append("digraph TableauNodePtrTable {\n");
+		sb.append("nodesep = 0.7\n");
+		sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom
 
-		// Print the cycle:
-		int cyclePos = stateNum;
-		long cycleFP = fp;
-		while (cycleStack.size() > 0) {
-			postfix.addElement(cycleStack.popLong());
-			cycleStack.popInt(); // ignore tableau idx
-		}
-
-		// Assert.assert(fps.length > 0);
-		for (int i = postfix.size() - 1; i >= 0; i--) {
-			long curFP = postfix.elementAt(i);
-			if (curFP != fp) {
-				sinfo = liveCheck.getTool().getState(curFP, sinfo.state);
-				if (sinfo == null) {
-					throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT);
-				}
-				StatePrinter.printState(sinfo, lastState, ++stateNum);
-				lastState = sinfo.state;
-				fp = curFP;
+		final int tsz = tnpt.getSize();
+		for (int ci = 0; ci < tsz; ci++) {
+			final int[] nodes = tnpt.getNodesByLoc(ci);
+			if (nodes == null) {
+				// miss in TableauNodePtrTable (null bucket)
+				continue;
 			}
-		}
 
-		if (fp == cycleFP) {
-			StatePrinter.printStutteringState(++stateNum);
-		} else {
-			sinfo = liveCheck.getTool().getState(cycleFP, sinfo.state);
-			if (sinfo == null) {
-				throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT);
-			}
-			if (TLCGlobals.tool) {
-				MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + cyclePos }, (TLCState) null, -1);
-			} else {
-				MP.printMessage(EC.TLC_BACK_TO_STATE, "" + cyclePos);
+			long state1 = TableauNodePtrTable.getKey(nodes);
+			for (int nidx = 2; nidx < nodes.length; nidx += tnpt.getElemLength()) { // nidx starts with 2 because [0][1] are the long fingerprint state1. 
+				int tidx1 = TableauNodePtrTable.getTidx(nodes, nidx);
+				long loc1 = TableauNodePtrTable.getElem(nodes, nidx);
+
+				final GraphNode curNode = this.dg.getNode(state1, tidx1, loc1);
+				sb.append(curNode.toDotViz((state1 == state && tidx1 == tidx), true, oos.getCheckState().length,
+						oos.getCheckAction().length, tnpt));
 			}
 		}
+		
+		sb.append("}");
+		return sb.toString();
 	}
-
-	public final void run() {
+	
+	/**
+	 * Write the output of {@link LiveWorker#toDotViz(long, int, TableauNodePtrTable)} to the given file.
+	 * @param state
+	 * @param tidx
+	 * @param tnpt
+	 * @param file
+	 */
+	public void writeDotViz(final long state, final int tidx, final TableauNodePtrTable tnpt, final java.io.File file) {
+		// Ignore trivial SCCs consisting of a single node.
+		if (tnpt.size() <= 1) {
+			return;
+		}
+		
 		try {
-			while (true) {
-				// Use poll() to get the next checker from the queue or null if
-				// there is none. Do *not* block when there are no more checkers
-				// available. Nobody is going to add new checkers to the queue.
-				final ILiveChecker checker = queue.poll();
-				if (checker == null || hasErrFound()) {
-					// Another thread has either found an error (violation of a
-					// liveness property) OR there is no more work (checker) to
-					// be done.
-					break;
-				}
+			final java.io.BufferedWriter bwr = new java.io.BufferedWriter(new java.io.FileWriter(file));
 
-				this.oos = checker.getSolution();
-				this.dg = checker.getDiskGraph();
-				this.dg.createCache();
-				PossibleErrorModel[] pems = this.oos.getPems();
-				for (int i = 0; i < pems.length; i++) {
-					if (!hasErrFound()) {
-						this.pem = pems[i];
-						this.checkSccs();
-					}
-				}
-				this.dg.destroyCache();
-				// Record the size of the disk graph at the time its checked. This
-				// information is later used to decide if it it makes sense to
-				// run the next check on the larger but still *partial* graph.
-				this.dg.recordSize();
-			}
-		} catch (Exception e) {
-			MP.printError(EC.GENERAL, "checking liveness", e); // LL changed
-			// call 7 April
-			// 2012
-			// Assert.printStack(e);
-			return;
+			// write contents of StringBuffer to a file
+			bwr.write(toDotViz(state, tidx, tnpt));
+
+			// flush the stream
+			bwr.flush();
+
+			// close the stream
+			bwr.close();
+		} catch (IOException e) {
+			e.printStackTrace();
 		}
 	}
+	
+  	/*
+	 * The detailed formatter below can be activated in Eclipse's variable view
+	 * by choosing "New detailed formatter" from the MemIntQueue context menu.
+	 * Insert "LiveWorker.DetailedFormatter.toString(this);".
+	 */
+  	public static class DetailedFormatter {
+  		public static String toString(final MemIntStack comStack) {
+  			final int size = (int) comStack.size();
+			final StringBuffer buf = new StringBuffer(size / 5);
+  			for (int i = 0; i < comStack.size(); i+=5) {
+  				long loc = comStack.peakLong(size - i - 5);
+  				int tidx = comStack.peakInt(size - i - 3);
+  				long state = comStack.peakLong(size - i - 2);
+  				buf.append("state: ");
+  				buf.append(state);
+  				buf.append(" tidx: ");
+  				buf.append(tidx);
+  				buf.append(" loc: ");
+  				buf.append(loc);
+  				buf.append("\n");
+  			}
+ 			return buf.toString();
+  		}
+  	}
 
+  	/*
+	 * The detailed formatter below can be activated in Eclipse's variable view
+	 * by choosing "New detailed formatter" from the MemIntQueue context menu.
+	 * Insert "LiveWorker.DFSStackDetailedFormatter.toString(this);".
+	 * Unfortunately it collides with the comStack DetailedFormatter as both use
+	 * the same type MemIntStack. So you have to chose what you want to look at
+	 * while debugging.
+	 * Note that toString treats pops/pushes of nodes and
+	 * states atomically. If called during a node is only partially pushed onto
+	 * the stack, the detailed formatter will crash.
+	 */
+  	public static class DFSStackDetailedFormatter {
+  		public static String toString(final MemIntStack dfsStack) {
+  			final int size = (int) dfsStack.size();
+			final StringBuffer buf = new StringBuffer(size / 7); // approximate the size needed (buf will grow or shrink if needed)
+  			int i = 0;
+  			for (; i < dfsStack.size();) {
+  				// Peak element to see if it's a marker or not
+  				final long topElement = dfsStack.peakLong(size - i - 2);
+  				if (topElement == SCC_MARKER) {
+  					// It is the marker element
+  	  				buf.append("node [");
+  	  				buf.append(" fp: ");
+  	  				buf.append(dfsStack.peakLong(size - i - 5));
+  	  				buf.append(" tidx: ");
+  	  				buf.append(dfsStack.peakInt(size - i - 3));
+  	  				buf.append(" lowLink: ");
+  	  				buf.append(dfsStack.peakLong(size - i - 7) - DiskGraph.MAX_PTR);
+  	  				buf.append("]\n");
+  	  				// Increase i by the number of elements peaked
+  	  				i += 7;
+  				} else if (DiskGraph.isFilePointer(topElement)) {
+  					final long location = topElement;
+  	  				buf.append("succ [");
+  	  				buf.append(" fp: ");
+  	  				buf.append(dfsStack.peakLong(size - i - 5));
+  	  				buf.append(" tidx: ");
+  	  				buf.append(dfsStack.peakInt(size - i - 3));
+  	  				buf.append(" location: ");
+  	  				buf.append(location);
+  	  				buf.append("]\n");
+  	  				// Increase i by the number of elements peaked
+  	  				i += 5;
+  				} else if (topElement >= DiskGraph.MAX_PTR) {
+  					final long pLowLink = topElement - DiskGraph.MAX_PTR;
+  	  				buf.append("pLowLink: ");
+  	  				buf.append(pLowLink);
+  	  				buf.append("\n");
+  					i += 2;
+  				}
+  			}
+  			// Assert all elements are used up
+  			assert i == size;
+ 			return buf.toString();
+  		}
+  	}
 }
diff --git a/tlatools/src/tlc2/tool/liveness/Liveness.java b/tlatools/src/tlc2/tool/liveness/Liveness.java
index 9396869f9d55dcbe0d9b71621d050bb1fde09db9..77a71cf37aaae2960df20d5020adb62e8050d961 100644
--- a/tlatools/src/tlc2/tool/liveness/Liveness.java
+++ b/tlatools/src/tlc2/tool/liveness/Liveness.java
@@ -19,29 +19,30 @@ import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.Action;
 import tlc2.tool.BuiltInOPs;
-import tlc2.tool.ContextEnumerator;
 import tlc2.tool.EvalControl;
-import tlc2.tool.Spec;
+import tlc2.tool.IContextEnumerator;
+import tlc2.tool.ITool;
+import tlc2.tool.ModelChecker;
+import tlc2.tool.Specs;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.tool.ToolGlobals;
 import tlc2.util.Context;
 import tlc2.util.Vect;
-import tlc2.value.BoolValue;
-import tlc2.value.FcnLambdaValue;
-import tlc2.value.Value;
+import tlc2.value.IBoolValue;
+import tlc2.value.IFcnLambdaValue;
+import tlc2.value.IValue;
 import util.Assert;
 import util.ToolIO;
 
 public class Liveness implements ToolGlobals, ASTConstants {
 
-	private static LiveExprNode astToLive(Tool tool, ExprNode expr, Context con, int level) {
+	private static LiveExprNode astToLive(ITool tool, ExprNode expr, Context con, int level) {
 		if (level == 0) {
-			Value val = tool.eval(expr, con, TLCState.Empty);
-			if (!(val instanceof BoolValue)) {
+			IValue val = tool.eval(expr, con, TLCState.Empty);
+			if (!(val instanceof IBoolValue)) {
 				Assert.fail(EC.TLC_EXPECTED_VALUE, new String[] { "boolean", expr.toString() });
 			}
-			return (((BoolValue) val).val) ? LNBool.TRUE : LNBool.FALSE;
+			return ((IBoolValue) val).getVal() ? LNBool.TRUE : LNBool.FALSE;
 		} else if (level == 1) {
 			return new LNStateAST(expr, con);
 		} else {
@@ -58,7 +59,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 	 * the predicate body with []p. For the moment, we require that arguments to
 	 * predicates be computable from its context.
 	 */
-	private static LiveExprNode astToLive(Tool tool, ExprNode expr, Context con) {
+	private static LiveExprNode astToLive(ITool tool, ExprNode expr, Context con) {
 		switch (expr.getKind()) {
 		case OpApplKind: {
 			OpApplNode expr1 = (OpApplNode) expr;
@@ -80,7 +81,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 			return astToLive(tool, expr1.getBody(), con1);
 		}
 		default: {
-			int level = Spec.getLevel(expr, con);
+			int level = Specs.getLevel(expr, con);
 			if (level > 2) {
 				Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString());
 			}
@@ -89,7 +90,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		}
 	}
 
-	private static LiveExprNode astToLiveAppl(Tool tool, OpApplNode expr, Context con) {
+	private static LiveExprNode astToLiveAppl(ITool tool, OpApplNode expr, Context con) {
 		ExprOrOpArgNode[] args = expr.getArgs();
 		int alen = args.length;
 		SymbolNode opNode = expr.getOperator();
@@ -109,7 +110,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 						FormalParamNode[] formals = opDef.getParams();
 						Context con1 = con;
 						for (int i = 0; i < alen; i++) {
-							Value argVal = tool.eval(args[i], con, TLCState.Empty);
+							IValue argVal = tool.eval(args[i], con, TLCState.Empty);
 							con1 = con1.cons(formals[i], argVal);
 						}
 						LiveExprNode res = astToLive(tool, opDef.getBody(), con1);
@@ -121,12 +122,12 @@ public class Liveness implements ToolGlobals, ASTConstants {
 					} catch (Exception e) { /* SKIP */
 					}
 				}
-			} else if (val instanceof BoolValue) {
-				return (((BoolValue) val).val) ? LNBool.TRUE : LNBool.FALSE;
+			} else if (val instanceof IBoolValue) {
+				return ((IBoolValue) val).getVal() ? LNBool.TRUE : LNBool.FALSE;
 			}
 
 			if (opcode == 0) {
-				int level = Spec.getLevel(expr, con);
+				int level = Specs.getLevel(expr, con);
 				if (level > 2) {
 					Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString());
 				}
@@ -139,7 +140,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		{
 			ExprNode body = (ExprNode) args[0];
 			try {
-				ContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear);
+				IContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear);
 				Context con1;
 				LNDisj res = new LNDisj(0);
 				while ((con1 = Enum.nextElement()) != null) {
@@ -152,8 +153,9 @@ public class Liveness implements ToolGlobals, ASTConstants {
 				}
 				return astToLive(tool, expr, con, level);
 			} catch (Exception e) {
+				// Catching Exception here seem dangerous
 				// Assert.printStack(e);
-				int level = Spec.getLevel(expr, con);
+				int level = Specs.getLevel(expr, con);
 				if (level > 2) {
 					Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString());
 					;
@@ -165,7 +167,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		{
 			ExprNode body = (ExprNode) args[0];
 			try {
-				ContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear);
+				IContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear);
 				Context con1;
 				LNConj res = new LNConj(0);
 				while ((con1 = Enum.nextElement()) != null) {
@@ -178,11 +180,15 @@ public class Liveness implements ToolGlobals, ASTConstants {
 				}
 				return astToLive(tool, expr, con, level);
 			} catch (Exception e) {
+				// Catching Exception here seem dangerous
 				// Assert.printStack(e);
-				int level = Spec.getLevel(expr, con);
+				int level = Specs.getLevel(expr, con);
 				if (level > 2) {
-					Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString());
-					;
+					if (e instanceof Assert.TLCRuntimeException) {
+						Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, new String[] {expr.toString(), e.getMessage()});
+					} else {
+						Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString());
+					}
 				}
 				return astToLive(tool, expr, con, level);
 			}
@@ -216,20 +222,21 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		case OPCODE_fa: // FcnApply
 		{
 			try {
-				Value fval = tool.eval(args[0], con, TLCState.Empty);
-				if (fval instanceof FcnLambdaValue) {
-					FcnLambdaValue fcn = (FcnLambdaValue) fval;
-					if (fcn.fcnRcd == null) {
+				IValue fval = tool.eval(args[0], con, TLCState.Empty);
+				if (fval instanceof IFcnLambdaValue) {
+					IFcnLambdaValue fcn = (IFcnLambdaValue) fval;
+					if (!fcn.hasRcd()) {
 						// this could be a bug, since con1 is created but not
 						// used
 						// SZ Jul 13, 2009: removed to kill the warning
 						// SZ Feb 20, 2009: variable never read locally
 						// Context con1 =
 						tool.getFcnContext(fcn, args, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear);
-						return astToLive(tool, (ExprNode) fcn.body, con);
+						return astToLive(tool, (ExprNode) fcn.getBody(), con);
 					}
 				}
 			} catch (Exception e) { /* SKIP */
+				// Swallowing Exception here seem dangerous
 			}
 			int level = expr.getLevel();
 			if (level > 2) {
@@ -292,8 +299,9 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		}
 		case OPCODE_leadto: {
 			// F ~> G equals [](F => <>G), however TLC does not have an
-			// implementation for logical implication. Thus, the rule of material
-			// implication is used to transform it into a disjunct.
+			// implementation for logical implication. Thus, the rule of
+			// material implication ("->") is used to transform it into a
+			// disjunct.
 			LiveExprNode lnLeft = astToLive(tool, (ExprNode) args[0], con);
 			LiveExprNode lnRight = astToLive(tool, (ExprNode) args[1], con);
 			// expand a ~> b into [](-a \/ <>b) 
@@ -308,6 +316,12 @@ public class Liveness implements ToolGlobals, ASTConstants {
 			LiveExprNode lnArg = astToLive(tool, (ExprNode) args[0], con);
 			return new LNEven(lnArg);
 		}
+		case OPCODE_aa: { // AngleAct <A>_e
+			assert Specs.getLevel(expr, con) == 2;
+			final ExprNode body = (ExprNode) args[0]; // the A in <<A>>_e
+			final ExprNode subs = (ExprNode) args[1]; // the e in <<A>>_e
+			return new LNAction(body, con, subs, false);
+		}
 
 		// The following case added by LL on 13 Nov 2009 to handle subexpression
 		// names.
@@ -315,166 +329,34 @@ public class Liveness implements ToolGlobals, ASTConstants {
 			return astToLive(tool, (ExprNode) args[0], con);
 		}
 		default: {
-			// We handle all the other built-in operators here.
-			int level = Spec.getLevel(expr, con);
+			// We handle all the other built-in operators here. Surprisingly, even OPCODE_aa
+			// (AngleAct <A>_e) is handled here and not as the dedicated case statement below
+			// such that e gets passed as subscript to LNAction:
+			//
+			//		case OPCODE_aa: { // AngleAct <A>_e
+			//			assert Spec.getLevel(expr, con) == 2;
+			//			final ExprNode body = (ExprNode) args[0]; // the A in <<A>>_e
+			//			final ExprNode subscript = (ExprNode) args[1]; // the e in <<A>>_e
+			//			return new LNAction(body, con, subscript, false);
+			//		}
+			//
+			// The default handling here results in LNAction#subscript to be null skipping
+			// the subscript related branch in LNAction#eval(Tool, TLCState, TLCState). This
+			// poses no problem though because Tool#evalAppl eventually checks if e' = e.
+			int level = Specs.getLevel(expr, con);
 			if (level > 2) {
 				Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString());
-				;
 			}
 			return astToLive(tool, expr, con, level);
 		}
 		}
 	}
 
-	/**
-	 * Given a starting TBGraphNode, constructTableau constructs the tableau for
-	 * it. Read MP for details. It returns a list of all the nodes in the
-	 * tableau graph.
-	 */
-	private static TBGraph constructTableau(LiveExprNode tf, int idx) {
-		TBGraph allnodes = new TBGraph(tf);
-		TBPar initTerms = new TBPar(1);
-		initTerms.addElement(tf);
-		TBParVec pars = particleClosure(initTerms);
-
-		for (int i = 0; i < pars.size(); i++) {
-			TBGraphNode gn = new TBGraphNode(pars.parAt(i));
-			allnodes.addElement(gn);
-		}
-		allnodes.setInitCnt(allnodes.size());
-		// We now repeatedly compute the outlinks of each node:
-		for (int i = 0; i < allnodes.size(); i++) {
-			TBGraphNode gnSrc = (TBGraphNode) allnodes.elementAt(i);
-			TBPar imps = gnSrc.getPar().impliedSuccessors();
-			TBParVec succs = particleClosure(imps);
-			for (int j = 0; j < succs.size(); j++) {
-				TBPar par = succs.parAt(j);
-				TBGraphNode gnDst = findOrCreateNode(allnodes, par);
-				gnSrc.nexts.addElement(gnDst);
-			}
-		}
-		// Assign each node in the tableau an index.
-		for (int i = 0; i < allnodes.size(); i++) {
-			allnodes.getNode(i).index = idx++;
-		}
-		return allnodes;
-	}
-
-	/**
-	 * The method findOrCreateNode, given a list of particles, either finds the
-	 * particle in that list, or creates a new one and puts it in the list. If
-	 * it does create a node, then it also sticks that node into allnodes.
-	 */
-	private static TBGraphNode findOrCreateNode(Vect allnodes, TBPar par) {
-		for (int i = 0; i < allnodes.size(); i++) {
-			TBGraphNode gn = (TBGraphNode) allnodes.elementAt(i);
-			if (par.equals(gn.getPar())) {
-				return gn;
-			}
-		}
-		TBGraphNode gn = new TBGraphNode(par);
-		allnodes.addElement(gn);
-		return gn;
-	}
-
-	/**
-	 * The method particleClosure, given a list of terms (initially just a
-	 * single term), returns a list of all particles containing those terms.
-	 * It's a recursive tree search.
-	 */
-	private static TBParVec particleClosure(TBPar terms) {
-		TBPar positive_closure = terms.positiveClosure();
-		Vect alphas = positive_closure.alphaTriples();
-		Vect betas = positive_closure.betaTriples();
-		return particleClosure(terms, alphas, betas);
-	}
-
-	private static TBParVec particleClosure(TBPar terms, Vect alphas, Vect betas) {
-		// if terms is not locally consistent, then terminate.
-		if (!terms.isLocallyConsistent()) {
-			return new TBParVec(0);
-		}
-		// if terms is not alpha-closed, then close it.
-		// first, try alpha expansion
-		TBPar terms1 = terms;
-		for (int i = 0; i < terms1.size(); i++) {
-			LiveExprNode ln = terms1.exprAt(i);
-			LiveExprNode k1 = null, k2 = null;
-			if (ln instanceof LNAll) {
-				k1 = ((LNAll) ln).getBody();
-				k2 = new LNNext(ln);
-			} else if (ln instanceof LNConj) {
-				k1 = ((LNConj) ln).getBody(0);
-				k2 = ((LNConj) ln).getBody(1);
-			}
-			if (k1 != null) {
-				if (terms1.member(k1)) {
-					if (!terms1.member(k2)) {
-						terms1 = terms1.append(k2);
-					}
-				} else if (terms1.member(k2)) {
-					terms1 = terms1.append(k1);
-				} else {
-					terms1 = terms1.append(k1, k2);
-				}
-			}
-		}
-		// second, try alpha^-1 expansion
-		boolean done;
-		do {
-			done = true;
-			for (int i = 0; i < alphas.size(); i++) {
-				TBTriple alpha = (TBTriple) alphas.elementAt(i);
-				if (terms1.member(alpha.getB()) && terms1.member(alpha.getC()) && !terms1.member(alpha.getA())) {
-					terms1.addElement(alpha.getA());
-					done = false;
-				}
-			}
-		} while (!done);
-		// finally, recurse only when locally consistent
-		if ((terms1.size() > terms.size()) && (!terms1.isLocallyConsistent())) {
-			return new TBParVec(0);
-		}
-		return particleClosureBeta(terms1, alphas, betas);
-	}
-
-	private static TBParVec particleClosureBeta(TBPar terms, Vect alphas, Vect betas) {
-		// try a beta expansion
-		for (int i = 0; i < terms.size(); i++) {
-			LiveExprNode ln = terms.exprAt(i);
-			LiveExprNode k1 = null, k2 = null;
-			if (ln instanceof LNEven) {
-				k1 = ((LNEven) ln).getBody();
-				k2 = new LNNext(ln);
-			} else if (ln instanceof LNDisj) {
-				k1 = ((LNDisj) ln).getBody(0);
-				k2 = ((LNDisj) ln).getBody(1);
-			}
-			if ((k1 != null) && !terms.member(k1) && !terms.member(k2)) {
-				TBParVec ps1 = particleClosure(terms.append(k1), alphas, betas);
-				TBParVec ps2 = particleClosure(terms.append(k2), alphas, betas);
-				return ps1.union(ps2);
-			}
-		}
-		// try a beta^-1 expansion
-		for (int i = 0; i < betas.size(); i++) {
-			TBTriple beta = (TBTriple) betas.elementAt(i);
-			if ((terms.member(beta.getB()) || terms.member(beta.getC())) && !terms.member(beta.getA())) {
-				return particleClosure(terms.append(beta.getA()), alphas, betas);
-			}
-		}
-		// if there are not any more expansions to do, return the terms
-		// we've got as the only particle in a list of particles.
-		TBParVec res = new TBParVec(1);
-		res.addElement(terms);
-		return res;
-	}
-
 	/**
 	 * Parse the temporals and impliedTemporals given in the config file. It
 	 * returns null if there is nothing to check.
 	 */
-	private static LiveExprNode parseLiveness(Tool tool) {
+	private static LiveExprNode parseLiveness(ITool tool) {
 		Action[] fairs = tool.getTemporals();
 		LNConj lnc = new LNConj(fairs.length);
 		for (int i = 0; i < fairs.length; i++) {
@@ -512,15 +394,43 @@ public class Liveness implements ToolGlobals, ASTConstants {
 	 * manner in which things should ultimately be checked. This method returns
 	 * a handle, which can subsequently be passed to the other liveness things.
 	 *
-	 * Theory: we're looking for counterexamples to: spec /\ livespec => []inv
-	 * /\ livecheck i.e. (spec /\ livespec /\ <>-inv) \/ (spec /\ livespec /\
-	 * -livecheck) The first half of this disjunction (inv) is already checked
-	 * by the model checker on the fly. We're converting the second half into
-	 * normal form. We actually omit spec in what we produce. It will be left
-	 * implicit. So, the only job is to turn livespec /\ -livecheck: live1 /\
-	 * live2 ... /\ (-check1 \/ -check2 ...) into normal form.
+	 * Theory: we're looking for counterexamples to:
+	 * 
+	 * <pre>
+	 * spec /\ livespec => []inv /\ livecheck
+	 * </pre>
+	 * 
+	 * i.e.
+	 * 
+	 * <pre>
+	 * \/ (spec /\ livespec /\ <>-inv)
+	 * \/ (spec /\ livespec /\ -livecheck)
+	 * </pre>
+	 * 
+	 * <p>
+	 * The first half of this disjunction (inv) is already checked by the model
+	 * checker on the fly (@see
+	 * {@link ModelChecker#doNext(TLCState, tlc2.util.ObjLongTable)}).
+	 * <p>
+	 * We're converting the second half into <i>normal form</i>. We actually
+	 * omit spec in what we produce. It will be left implicit. So, the only job
+	 * is to turn:
+	 * 
+	 * <pre>
+	 * livespec /\ -livecheck
+	 * </pre>
+	 * 
+	 * into:
+	 * 
+	 * <pre>
+	 * live1 /\ live2 ... /\ (-check1 \/ -check2 ...)
+	 * </pre>
+	 * 
+	 * into <i>normal form</i>. livespec corresponds to the spec's
+	 * <i>fairness</i> formulae where check1, check2, ... are the actual
+	 * <i>liveness properties</i> to be checked.
 	 */
-	public static OrderOfSolution[] processLiveness(Tool tool) {
+	public static OrderOfSolution[] processLiveness(final ITool tool) {
 		LiveExprNode lexpr = parseLiveness(tool);
 
 		if (lexpr == null) {
@@ -538,20 +448,20 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		if ((lexpr instanceof LNBool) && !((LNBool) lexpr).b) {
 			return new OrderOfSolution[0]; // must be unsatisfiable
 		}
-		LNDisj dnf = (lexpr instanceof LNDisj) ? (LNDisj) lexpr : (new LNDisj(lexpr));
+		final LNDisj dnf = (lexpr instanceof LNDisj) ? (LNDisj) lexpr : (new LNDisj(lexpr));
 
 		// Now we will turn DNF into a format that can be tested by the
 		// tableau method. The first step is to collect everything into
 		// pems+lexps: listof-(listof-<>[],[]<> /\ tf)
-		OSExprPem[] pems = new OSExprPem[dnf.getCount()];
-		LiveExprNode[] tfs = new LiveExprNode[dnf.getCount()];
+		final OSExprPem[] pems = new OSExprPem[dnf.getCount()];
+		final LiveExprNode[] tfs = new LiveExprNode[dnf.getCount()];
 		for (int i = 0; i < dnf.getCount(); i++) {
 			// Flatten junctions, because DNF may contain singleton junctions
-			LiveExprNode ln = dnf.getBody(i).flattenSingleJunctions();
-			OSExprPem pem = new OSExprPem();
+			final LiveExprNode ln = dnf.getBody(i).flattenSingleJunctions();
+			final OSExprPem pem = new OSExprPem();
 			pems[i] = pem;
 			if (ln instanceof LNConj) {
-				LNConj lnc2 = (LNConj) ln;
+				final LNConj lnc2 = (LNConj) ln;
 				for (int j = 0; j < lnc2.getCount(); j++) {
 					classifyExpr(lnc2.getBody(j), pem);
 				}
@@ -562,7 +472,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 			if (pem.tfs.size() == 1) {
 				tfs[i] = (LiveExprNode) pem.tfs.elementAt(0);
 			} else if (pem.tfs.size() > 1) {
-				LNConj lnc2 = new LNConj(pem.tfs.size());
+				final LNConj lnc2 = new LNConj(pem.tfs.size());
 				for (int j = 0; j < pem.tfs.size(); j++) {
 					lnc2.addConj((LiveExprNode) pem.tfs.elementAt(j));
 				}
@@ -579,13 +489,13 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		// haven't done any real rearrangement of them, apart from munging
 		// up \/ and /\ above them. tfbin contains the different tf's.
 		// pembin is a vect of vect-of-pems collecting each tf's pems.
-		TBPar tfbin = new TBPar(dnf.getCount());
-		Vect pembin = new Vect(dnf.getCount());
+		final TBPar tfbin = new TBPar(dnf.getCount());
+		final Vect<Vect<OSExprPem>> pembin = new Vect<>(dnf.getCount());
 		for (int i = 0; i < dnf.getCount(); i++) {
 			int found = -1;
-			LiveExprNode tf = tfs[i];
+			final LiveExprNode tf = tfs[i];
 			for (int j = 0; j < tfbin.size() && found == -1; j++) {
-				LiveExprNode tf0 = tfbin.exprAt(j);
+				final LiveExprNode tf0 = tfbin.exprAt(j);
 				if ((tf == null && tf0 == null) || (tf != null && tf0 != null && tf.equals(tf0))) {
 					found = j;
 				}
@@ -593,23 +503,23 @@ public class Liveness implements ToolGlobals, ASTConstants {
 			if (found == -1) {
 				found = tfbin.size();
 				tfbin.addElement(tf);
-				pembin.addElement(new Vect());
+				pembin.addElement(new Vect<OSExprPem>());
 			}
-			((Vect) pembin.elementAt(found)).addElement(pems[i]);
+			((Vect<OSExprPem>) pembin.elementAt(found)).addElement(pems[i]);
 		}
 
 		// We then create an OrderOfSolution for each tf in tfbin.
-		OrderOfSolution[] oss = new OrderOfSolution[tfbin.size()];
+		final OrderOfSolution[] oss = new OrderOfSolution[tfbin.size()];
 		for (int i = 0; i < tfbin.size(); i++) {
-			LiveExprNode tf = tfbin.exprAt(i);
+			final LiveExprNode tf = tfbin.exprAt(i);
 
 			if (tf == null) {
-				oss[i] = new OrderOfSolution(new LNEven[0], tool);
+				oss[i] = new OrderOfSolution(new LNEven[0]);
 			} else {
-				LiveExprNode tf1 = tf.makeBinary();
-				TBPar promises = new TBPar(10);
+				final LiveExprNode tf1 = tf.makeBinary();
+				final TBPar promises = new TBPar(10);
 				tf1.extractPromises(promises);
-				oss[i] = new OrderOfSolution(constructTableau(tf1, 0), new LNEven[promises.size()], tool);
+				oss[i] = new OrderOfSolution(new TBGraph(tf1), new LNEven[promises.size()]);
 				for (int j = 0; j < promises.size(); j++) {
 					oss[i].getPromises()[j] = (LNEven) promises.exprAt(j);
 				}
@@ -617,16 +527,14 @@ public class Liveness implements ToolGlobals, ASTConstants {
 
 			// We lump all the pems into a single checkState and checkAct,
 			// and oss[i].pems will simply be integer lookups into them.
-			Vect stateBin = new Vect();
-			Vect actionBin = new Vect();
-			Vect tfPems = (Vect) pembin.elementAt(i);
+			final Vect<LiveExprNode> stateBin = new Vect<>();
+			final Vect<LiveExprNode> actionBin = new Vect<>();
+			final Vect<OSExprPem> tfPems = (Vect<OSExprPem>) pembin.elementAt(i);
 			oss[i].setPems(new PossibleErrorModel[tfPems.size()]);
 			for (int j = 0; j < tfPems.size(); j++) {
-				OSExprPem pem = (OSExprPem) tfPems.elementAt(j);
-				oss[i].getPems()[j] = new PossibleErrorModel();
-				oss[i].getPems()[j].AEAction = addToBin(pem.AEAction, actionBin);
-				oss[i].getPems()[j].AEState = addToBin(pem.AEState, stateBin);
-				oss[i].getPems()[j].EAAction = addToBin(pem.EAAction, actionBin);
+				final OSExprPem pem = (OSExprPem) tfPems.elementAt(j);
+				oss[i].getPems()[j] = new PossibleErrorModel(addToBin(pem.AEAction, actionBin),
+						addToBin(pem.AEState, stateBin), addToBin(pem.EAAction, actionBin));
 			}
 			// Finally, store the bins with the order of solution.
 			oss[i].setCheckState(new LiveExprNode[stateBin.size()]);
@@ -649,7 +557,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 	 * Given a list of checks, ensures that the checks are in the bin. It
 	 * returns an array of index of the checks in the bin.
 	 */
-	private static int addToBin(LiveExprNode check, Vect bin) {
+	private static int addToBin(LiveExprNode check, Vect<LiveExprNode> bin) {
 		if (check == null) {
 			return -1;
 		}
@@ -667,7 +575,7 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		return idx;
 	}
 
-	private static int[] addToBin(Vect checks, Vect bin) {
+	private static int[] addToBin(Vect<LiveExprNode> checks, Vect<LiveExprNode> bin) {
 		int[] index = new int[checks.size()];
 		for (int i = 0; i < checks.size(); i++) {
 			LiveExprNode check = (LiveExprNode) checks.elementAt(i);
@@ -682,7 +590,14 @@ public class Liveness implements ToolGlobals, ASTConstants {
 	 * tests. This method classifies an expression into <>[]act, []<>act,
 	 * []<>state, temporal formulas (without actions), or erroneous things.
 	 */
+	// TODO Explore the idea to syntactically rewrite an LNActions A into a
+	// ordinary predicate and the next state operator ()A in the tableau.
 	private static void classifyExpr(LiveExprNode ln, OSExprPem pem) {
+		// TLC is clever enough to optimize the case where some temporal formula
+		// can be handled WITHOUT a tableau. In this case, the state graph IS
+		// the behavior graph and thus the overall verification time is reduced.
+		// Additionally, the tableau generation does not support formulas 
+		// containing (nested) LNActions.
 		if (ln instanceof LNEven) {
 			LiveExprNode ln1 = ((LNEven) ln).getBody();
 			if (ln1 instanceof LNAll) {
@@ -710,6 +625,9 @@ public class Liveness implements ToolGlobals, ASTConstants {
 		if (ln.containAction()) {
 			Assert.fail(EC.TLC_LIVE_WRONG_FORMULA_FORMAT);
 		}
+		// If we get here (because of a temporal formula), at tableau is
+		// consequently going to be created. This part corresponds to the
+		// ideas in the MP book.
 		pem.tfs.addElement(ln);
 	}
 
@@ -726,16 +644,16 @@ public class Liveness implements ToolGlobals, ASTConstants {
 	 * PossibleErrorModel and OrderOfSolution.
 	 */
 	private static class OSExprPem {
-		Vect EAAction; // <>[]action's
-		Vect AEState; // []<>state's
-		Vect AEAction; // []<>action's
-		Vect tfs; // other temp formulae with no actions
+		Vect<LiveExprNode> EAAction; // <>[]action's
+		Vect<LiveExprNode> AEState; // []<>state's
+		Vect<LiveExprNode> AEAction; // []<>action's
+		Vect<LiveExprNode> tfs; // other temp formulae with no actions
 
 		public OSExprPem() {
-			this.EAAction = new Vect();
-			this.AEState = new Vect();
-			this.AEAction = new Vect();
-			this.tfs = new Vect();
+			this.EAAction = new Vect<>();
+			this.AEState = new Vect<>();
+			this.AEAction = new Vect<>();
+			this.tfs = new Vect<>();
 		}
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java b/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java
index 5466402f300ba70f4faf0ad69f290186d745f80c..02d93ca04fd0b8b85b7e7e7c4ae244d050589756 100644
--- a/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java
+++ b/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java
@@ -28,20 +28,21 @@ package tlc2.tool.liveness;
 
 import java.io.IOException;
 
+import tlc2.output.EC;
+import tlc2.tool.ITool;
 import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
-import tlc2.util.LongVec;
+import tlc2.util.SetOfStates;
 import tlc2.util.statistics.DummyBucketStatistics;
 import tlc2.util.statistics.IBucketStatistics;
 
 public class NoOpLiveCheck implements ILiveCheck {
 
-	private final Tool tool;
+	private final ITool tool;
 	private final String metadir;
 	private final IBucketStatistics stats;
 
-	public NoOpLiveCheck(Tool tool, String metadir) {
+	public NoOpLiveCheck(ITool tool, String metadir) {
 		this.tool = tool;
 		this.metadir = metadir;
 		this.stats = new DummyBucketStatistics();
@@ -50,33 +51,36 @@ public class NoOpLiveCheck implements ILiveCheck {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#addInitState(tlc2.tool.TLCState, long)
 	 */
-	public void addInitState(TLCState state, long stateFP) {
+	public void addInitState(ITool tool, TLCState state, long stateFP) {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec)
+	 * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates)
 	 */
-	public void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException {
+	public void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#check(boolean)
+	 * @see tlc2.tool.liveness.ILiveCheck#doLiveCheck()
 	 */
-	public boolean check(boolean forceCheck) throws Exception {
-		return true;
+	public boolean doLiveCheck() {
+		return false;
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.ILiveCheck#finalCheck()
-	 */
-	public boolean finalCheck() throws Exception {
-		return true;
+	@Override
+	public int check(ITool tool, boolean forceCheck) throws Exception {
+		return EC.NO_ERROR;
+	}
+
+	@Override
+	public int finalCheck(ITool tool) throws Exception {
+		return EC.NO_ERROR;
 	}
 	
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#checkTrace(tlc2.tool.StateVec)
 	 */
-	public void checkTrace(StateVec trace) throws IOException, InterruptedException {
+	public void checkTrace(ITool tool, StateVec trace) throws IOException, InterruptedException {
 	}
 
 	/* (non-Javadoc)
@@ -89,7 +93,7 @@ public class NoOpLiveCheck implements ILiveCheck {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.liveness.ILiveCheck#getTool()
 	 */
-	public Tool getTool() {
+	public ITool getTool() {
 		return tool;
 	}
 
diff --git a/tlatools/src/tlc2/tool/liveness/NodePtrTable.java b/tlatools/src/tlc2/tool/liveness/NodePtrTable.java
index 7c8a90a87d9eeee3fcdb8b68a7548ae48d6aa8ee..e5a6a1d9368692c613b4b320b00586e62647ac30 100644
--- a/tlatools/src/tlc2/tool/liveness/NodePtrTable.java
+++ b/tlatools/src/tlc2/tool/liveness/NodePtrTable.java
@@ -4,6 +4,12 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.output.EC;
+import tlc2.output.MP;
+
+/**
+ * @see TableauNodePtrTable
+ */
 public class NodePtrTable {
 
 	private int count;
@@ -105,20 +111,56 @@ public class NodePtrTable {
 
 	/* Double the table when the table is full by the threshhold. */
 	private final void grow() {
-		this.length = 2 * this.length + 1;
-		this.thresh = (int) (this.length * 0.75);
-		this.count = 0;
-		long[] oldKeys = this.keys;
-		long[] oldElems = this.elems;
-		this.keys = new long[this.length];
-		this.elems = new long[this.length];
-		for (int i = 0; i < this.length; i++) {
-			this.elems[i] = -1;
-		}
-		for (int i = 0; i < oldElems.length; i++) {
-			long elem = oldElems[i];
-			if (elem != -1) {
-				this.put(oldKeys[i], elem);
+		final int newLength = 2 * this.length + 1;
+		grow(newLength);
+	}
+
+    private final void grow(final int newLength) {
+		try {
+			final long[] oldKeys = this.keys;
+			final long[] oldElems = this.elems;
+			this.keys = new long[newLength];
+			this.elems = new long[newLength];
+			for (int i = 0; i < newLength; i++) {
+				this.elems[i] = -1;
+			}
+			this.count = 0;
+			for (int i = 0; i < oldElems.length; i++) {
+				final long elem = oldElems[i];
+				if (elem != -1) {
+					int loc = ((int) oldKeys[i] & 0x7FFFFFFF) % newLength;
+					while (true) {
+						if (this.elems[loc] == -1) {
+							this.keys[loc] = oldKeys[i];
+							this.elems[loc] = elem;
+							this.count++;
+							break;
+						}
+						if (this.keys[loc] == oldKeys[i]) {
+							this.elems[loc] = elem;
+							break;
+						}
+						loc = (loc + 1) % newLength;
+					}
+				}
+			}
+			this.length = newLength;
+			this.thresh = (int) (newLength * 0.75);
+		} catch (OutOfMemoryError t) {
+			// Handle OOM error locally because grow is on the code path of safety checking
+			// (LiveCheck#addInit/addNext...).
+			System.gc();
+			if (newLength <= this.length + 1) {
+				MP.printError(EC.SYSTEM_OUT_OF_MEMORY, t);
+				System.exit(1);
+			}
+			try {
+				// It doesn't buy us much, but - as fallback - do not grow capacity
+				// exponentially.
+				grow(newLength - (newLength >> 2));
+			} catch (OutOfMemoryError inner) {
+				MP.printError(EC.SYSTEM_OUT_OF_MEMORY, inner);
+				System.exit(1);
 			}
 		}
 	}
diff --git a/tlatools/src/tlc2/tool/liveness/NoopLivenessStateWriter.java b/tlatools/src/tlc2/tool/liveness/NoopLivenessStateWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ad36cd603101b81cc7b00c3e7ee6e000e0de018
--- /dev/null
+++ b/tlatools/src/tlc2/tool/liveness/NoopLivenessStateWriter.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import java.io.IOException;
+
+import tlc2.tool.Action;
+import tlc2.tool.TLCState;
+import tlc2.util.BitVector;
+
+public class NoopLivenessStateWriter implements ILivenessStateWriter {
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState)
+	 */
+	public final void writeState(final TLCState state) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean)
+	 */
+	public final void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visualization) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#close()
+	 */
+	public final void close() {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode)
+	 */
+	public final void writeState(TLCState state, TBGraphNode tableauNode) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.util.BitVector, int, int, boolean)
+	 */
+	public final void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor,
+			TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public final void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor,
+			TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int to, boolean successorStateIsNew, Visualization visulation) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean)
+	 */
+	public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew,
+			Visualization visulation) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.tool.Action)
+	 */
+	public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Action action) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#getDumpFileName()
+	 */
+	public String getDumpFileName() {
+		return "";
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isNoop()
+	 */
+	public boolean isNoop() {
+		return true;
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isDot()
+	 */
+	@Override
+	public boolean isDot() {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#snapshot()
+	 */
+	@Override
+	public void snapshot() throws IOException {
+	}
+}
diff --git a/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java b/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java
index 74b935cd9b0d9ca16de444c098eaec23c492d811..0c0513de92d0ff069a00b1e1e87e0c461619c8da 100644
--- a/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java
+++ b/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java
@@ -7,8 +7,8 @@ package tlc2.tool.liveness;
 
 import java.io.PrintStream;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.BitVector;
 
 /*
@@ -48,20 +48,29 @@ public class OrderOfSolution {
 	 * in the temporal formulas.
 	 */
 	private final TBGraph tableau; // tableau graph
+	
+	/**
+	 * A promise &#966; that a property expressed by a formula will eventually hold.
+	 * 
+	 * @see Page 409ff of Manna & Pnueli
+	 * "Temporal Verification of Reactive Systems: Safety"
+	 * <p>
+	 * @see https://books.google.de/books?id=lfIGCAAAQBAJ&lpg=PR5&ots=_YBX09o5tM
+	 *      &dq=manna%20pnueli%20temporal%20verification%20of%20reactive%
+	 *      20systems%20safety%20doi&pg=PA409
+	 */
 	private final LNEven[] promises; // promises in the tableau
 	private LiveExprNode[] checkState; // state subformula
 	private LiveExprNode[] checkAction; // action subformula
 	private PossibleErrorModel[] pems;
-	private final Tool tool;
 
-	public OrderOfSolution(final LNEven[] livenessEventually, Tool aTool) {
-		this(null, livenessEventually, aTool);
+	public OrderOfSolution(final LNEven[] livenessEventually) {
+		this(null, livenessEventually);
 	}
 
-	public OrderOfSolution(final TBGraph aTableau, final LNEven[] livenessEventually, Tool aTool) {
+	public OrderOfSolution(final TBGraph aTableau, final LNEven[] livenessEventually) {
 		tableau = aTableau;
 		promises = livenessEventually;
-		this.tool = aTool;
 	}
 
 	public final void printPromises(PrintStream ps) {
@@ -124,7 +133,7 @@ public class OrderOfSolution {
 		return checkState;
 	}
 	
-	public boolean[] checkState(final TLCState state) {
+	public boolean[] checkState(ITool tool, final TLCState state) {
 		final boolean[] result = new boolean[checkState.length];
 		for (int i = 0; i < checkState.length; i++) {
 			result[i] = checkState[i].eval(tool, state, null);
@@ -137,7 +146,7 @@ public class OrderOfSolution {
 	}
 
 	// legacy LiveCheck1
-	public boolean[] checkAction(final TLCState state0, final TLCState state1) {
+	public boolean[] checkAction(ITool tool, final TLCState state0, final TLCState state1) {
 		final boolean[] result = new boolean[checkAction.length];
 		for (int i = 0; i < checkAction.length; i++) {
 			result[i] = checkAction[i].eval(tool, state0, state1);
@@ -145,7 +154,7 @@ public class OrderOfSolution {
 		return result;
 	}
 	
-	public BitVector checkAction(final TLCState state0, final TLCState state1, final BitVector result, final int offset) {
+	public BitVector checkAction(ITool tool, final TLCState state0, final TLCState state1, final BitVector result, final int offset) {
 		for (int i = 0; i < checkAction.length; i++) {
 			if (checkAction[i].eval(tool, state0, state1)) {
 				result.set(offset + i);
diff --git a/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java b/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java
index eba04bfdd62248c8407dbac43275b0f64ba6531a..c8310971a2a7561f175212807097dc3345c4cf20 100644
--- a/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java
+++ b/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java
@@ -5,10 +5,59 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.util.BitVector;
+
+/**
+ * A {@link PossibleErrorModel} is technically a lookup table into its
+ * corresponding {@link OrderOfSolution} Eventually Always, Infinitely often
+ * action checks and infinitely often state checks. The
+ * {@link PossibleErrorModel} basically selects the set of checks stored in
+ * {@link OrderOfSolution} that a relevant for this {@link PossibleErrorModel}.
+ * The mapping is done by storing the integer index of the relevant
+ * {@link OrderOfSolution} checks in EAAction, AEState and AEAction.
+ * <p>
+ * The integer index is used in two places: Lookup the set of relevant OOS
+ * checks
+ * <ul>
+ * <li>When the states and transitions (see
+ * {@link GraphNode#addTransition(long, int, int, int, BitVector, int, int)})
+ * are evaluated during "normal" model checking the PEM index selects the set of
+ * relevant OOS checks. The result of each check is stored in a per node
+ * {@link BitVector} index again by the corresponding PEM mapping.</li>
+ * <li>During liveness checking (see {@link LiveWorker#checkSccs()} when the
+ * pre-computed check result is being looked up in the node's {@link BitVector}.
+ * </li>
+ * </ul>
+ * <p>
+ * This class can be seen as a C-like struct (PEM's method are all related to
+ * OOS#toString).
+ * <p>
+ * <p>
+ * Theorie-wise, a {@link PossibleErrorModel} (there are as many PEMs as
+ * disjuncts in the normal form (DNF) produced by
+ * {@link Liveness#processLiveness(tlc2.tool.Tool)}) represents the negation of
+ * the stated liveness properties. It is then applied onto the discovered
+ * strongly-connected-components in {@link LiveWorker#checkComponent()} to check
+ * if the PEM is "P-satisfiable". If it is, it shows that a violation of the
+ * original liveness properties has been found (meaning they can't be
+ * "P-valid"). A counter-example can be created.
+ * <p>
+ * A simplified example:<br>
+ * Suppose Spec has the (trivial) liveness property '&lt;&gt;[](x \in Nat)' (
+ * "eventually the variable x is always a natural number") defined. This will
+ * result in a PEM looking for a state such that x \notin Nat. If there exists
+ * such a behavior (SCC), it would violate the liveness property.
+ */
 public class PossibleErrorModel {
-	int[] EAAction; // <>[]act's (Eventually Always actions) (Strong fairness)
-	int[] AEState; // []<>state's (Infinitely Often states)
-	int[] AEAction; // []<>act's (Infinitely Often actions) (Weak fairness)
+	final int[] EAAction; // <>[]act's (Eventually Always actions) (Strong fairness)
+	final int[] AEState; // []<>state's (Infinitely Often states)
+	final int[] AEAction; // []<>act's (Infinitely Often actions) (Weak fairness)
+	
+	public PossibleErrorModel(int[] aeAction, int[] aeState, int[] eaAction) {
+		this.AEAction = aeAction;
+		this.AEState = aeState;
+		this.EAAction = eaAction;
+	}
 	
 	public final boolean isEmpty() {
 		return (this.EAAction.length == 0 && this.AEState.length == 0 && this.AEAction.length == 0);
diff --git a/tlatools/src/tlc2/tool/liveness/TBGraph.java b/tlatools/src/tlc2/tool/liveness/TBGraph.java
index c5114097b7cc2d9ae62785ff0fe295e6bc0d7436..ecd714ec7fe1744af1ea26ff3aaf1a4837bd6ec4 100644
--- a/tlatools/src/tlc2/tool/liveness/TBGraph.java
+++ b/tlatools/src/tlc2/tool/liveness/TBGraph.java
@@ -7,16 +7,69 @@ package tlc2.tool.liveness;
 
 import tlc2.util.Vect;
 
-public class TBGraph extends Vect {
+@SuppressWarnings("serial")
+public class TBGraph extends Vect<TBGraphNode> {
 	/**
 	 * TBGraph represents the nodes in the tableau graph.
 	 */
 	public final LiveExprNode tf;
 	private int initCnt;
 
-	public TBGraph(LiveExprNode tf) {
+	public TBGraph() {
+		this.tf = null;
+		this.initCnt = 0;
+	}
+
+	/**
+	 * Given a starting TBGraphNode, constructTableau constructs the tableau for
+	 * it. Read MP for details. It returns a list of all the nodes in the
+	 * tableau graph.
+	 */
+	public TBGraph(final LiveExprNode tf) {
 		this.tf = tf;
 		this.initCnt = 0;
+		
+		final TBPar initTerms = new TBPar(1);
+		initTerms.addElement(tf);
+		final TBParVec pars = initTerms.particleClosure();
+
+		for (int i = 0; i < pars.size(); i++) {
+			final TBGraphNode gn = new TBGraphNode(pars.parAt(i));
+			this.addElement(gn);
+		}
+		this.setInitCnt(this.size());
+		// We now repeatedly compute the outlinks of each node:
+		for (int i = 0; i < this.size(); i++) {
+			final TBGraphNode gnSrc = (TBGraphNode) this.elementAt(i);
+			final TBPar imps = gnSrc.getPar().impliedSuccessors();
+			final TBParVec succs = imps.particleClosure();
+			for (int j = 0; j < succs.size(); j++) {
+				final TBPar par = succs.parAt(j);
+				final TBGraphNode gnDst = findOrCreateNode(par);
+				gnSrc.nexts.addElement(gnDst);
+			}
+		}
+		// Assign each node in the tableau an index.
+		for (int i = 0; i < this.size(); i++) {
+			this.getNode(i).setIndex(i);
+		}
+	}
+	
+	/**
+	 * The method findOrCreateNode, given a list of particles, either finds the
+	 * particle in that list, or creates a new one and puts it in the list. If
+	 * it does create a node, then it also sticks that node into allnodes.
+	 */
+	private TBGraphNode findOrCreateNode(final TBPar par) {
+		for (int i = 0; i < this.size(); i++) {
+			final TBGraphNode gn = (TBGraphNode) this.elementAt(i);
+			if (par.equals(gn.getPar())) {
+				return gn;
+			}
+		}
+		final TBGraphNode gn = new TBGraphNode(par);
+		this.addElement(gn);
+		return gn;
 	}
 
 	public TBGraphNode getNode(int idx) {
@@ -30,6 +83,10 @@ public class TBGraph extends Vect {
 	public int getInitCnt() {
 		return this.initCnt;
 	}
+	
+	private boolean isInitNode(TBGraphNode aNode) {
+		return aNode.getIndex() < getInitCnt();
+	}
 
 	public final void toString(StringBuffer sb, String padding) {
 		for (int i = 0; i < this.size(); i++) {
@@ -39,7 +96,7 @@ public class TBGraph extends Vect {
 			tnode.getPar().toString(sb, padding);
 			sb.append(" --> ");
 			for (int j = 0; j < tnode.nexts.size(); j++) {
-				sb.append(tnode.nextAt(j).index + " ");
+				sb.append(tnode.nextAt(j).getIndex() + " ");
 			}
 			sb.append("\n");
 		}
@@ -60,7 +117,8 @@ public class TBGraph extends Vect {
 		sb.append("nodesep = 0.7\n");
 		sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom
 		for(int i = 0; i < size(); i++) {
-			sb.append(getNode(i).toDotViz());
+			final TBGraphNode node = getNode(i);
+			sb.append(node.toDotViz(isInitNode(node)));
 		}
 		sb.append("}");
 		return sb.toString();
diff --git a/tlatools/src/tlc2/tool/liveness/TBGraphNode.java b/tlatools/src/tlc2/tool/liveness/TBGraphNode.java
index 3cd44fa6c3fa300ff509c8bf654ca04e96384471..d3177d8fb3cd5bb16fd728db6f897850d7d1b099 100644
--- a/tlatools/src/tlc2/tool/liveness/TBGraphNode.java
+++ b/tlatools/src/tlc2/tool/liveness/TBGraphNode.java
@@ -5,8 +5,8 @@
 
 package tlc2.tool.liveness;
 
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.util.SetOfLong;
 import tlc2.util.Vect;
 
@@ -19,8 +19,8 @@ public class TBGraphNode {
 	 * if a state-node is consistent with a tableau-node.
 	 */
 	private final TBPar par; // particle
-	public final Vect nexts; // outlinks
-	public int index; // unique id for this node
+	public final Vect<TBGraphNode> nexts; // outlinks
+	private int index; // unique id for this node
 	private final LiveExprNode[] statePreds; // state predicates in the particle
 
 	public static TBGraphNode dummyNode = new TBGraphNode();
@@ -35,7 +35,7 @@ public class TBGraphNode {
 	public TBGraphNode(TBPar par) {
 		this.par = par;
 		this.index = 0;
-		this.nexts = new Vect();
+		this.nexts = new Vect<>();
 		TBPar preds = new TBPar(par.size());
 		for (int i = 0; i < par.size(); i++) {
 			LiveExprNode ln = par.exprAt(i);
@@ -54,6 +54,14 @@ public class TBGraphNode {
 		}
 	}
 
+	public int getIndex() {
+		return index;
+	}
+
+	public void setIndex(final int i) {
+		this.index = i;
+	}
+
 	public final TBPar getPar() {
 		return this.par;
 	}
@@ -76,8 +84,13 @@ public class TBGraphNode {
 		return false;
 	}
 
-	/* Checks if this particle node is consistent with a state. */
-	public boolean isConsistent(TLCState state, Tool tool) {
+	/**
+	 * Checks if this particle node is consistent with the given
+	 * {@link TLCState}. In other words, it checks if {@link TLCState}'s truth
+	 * values according to all state predicates of the tableau node. The state
+	 * predicates are deduced from the particles during tableau construction.
+	 */
+	public boolean isConsistent(TLCState state, ITool tool) {
 		for (int j = 0; j < this.statePreds.length; j++) {
 			if (!this.statePreds[j].eval(tool, state, null)) {
 				return false;
@@ -109,11 +122,14 @@ public class TBGraphNode {
 	/**
 	 * @see TBGraph#toDotViz()
 	 */
-	public String toDotViz() {
+	public String toDotViz(final boolean isInitNode) {
 		final String label = "\"Id: " + this.index + "\n" + par.toDotViz() + "\"";
 		
 		final StringBuffer buf = new StringBuffer(nextSize());
 		buf.append(this.index + " [label=" + label + "]\n"); // nodes label
+		if (isInitNode) {
+			buf.append("[style = filled]\n");
+		}
 		for (int i = 0; i < nextSize(); i++) {
 			final TBGraphNode successor = nextAt(i);
 			buf.append(this.index + " -> " + successor.index);
diff --git a/tlatools/src/tlc2/tool/liveness/TBPar.java b/tlatools/src/tlc2/tool/liveness/TBPar.java
index 0ef57fd1b4c53b54bdf472a5ab80dff408094b88..fffa55ffb19b3af26a67e42a83e533c046979ff6 100644
--- a/tlatools/src/tlc2/tool/liveness/TBPar.java
+++ b/tlatools/src/tlc2/tool/liveness/TBPar.java
@@ -27,7 +27,7 @@ import util.Assert;
  * with p. 43 fig. 0.15), thus it uses the PART-TAB algorithm (p. 456) for the
  * tableau construction.
  */
-public class TBPar extends Vect {
+public class TBPar extends Vect<LiveExprNode> {
 
 	public TBPar(int i) {
 		super(i);
@@ -102,6 +102,105 @@ public class TBPar extends Vect {
 		return res;
 	}
 
+	/**
+	 * The method particleClosure, given a list of terms (initially just a
+	 * single term), returns a list of all particles containing those terms.
+	 * It's a recursive tree search.
+	 */
+	public TBParVec particleClosure() {
+		TBPar positive_closure = this.positiveClosure();
+		Vect<TBTriple> alphas = positive_closure.alphaTriples();
+		Vect<TBTriple> betas = positive_closure.betaTriples();
+		return particleClosure(this, alphas, betas);
+	}
+
+	private TBParVec particleClosure(TBPar terms, Vect alphas, Vect betas) {
+		// if terms is not locally consistent, then terminate.
+		if (!terms.isLocallyConsistent()) {
+			return new TBParVec(0);
+		}
+		// if terms is not alpha-closed, then close it.
+		// first, try alpha expansion. See MP page 403 
+		// figure 5.1. for alpha expansion rules.
+		TBPar terms1 = terms;
+		for (int i = 0; i < terms1.size(); i++) {
+			LiveExprNode ln = terms1.exprAt(i);
+			LiveExprNode kappa1 = null, kappa2 = null;
+			if (ln instanceof LNAll) {
+				// Alpha-Expansion: []p expands to p, ()[]p
+				kappa1 = ((LNAll) ln).getBody();
+				kappa2 = new LNNext(ln);
+			} else if (ln instanceof LNConj) {
+				// Alpha-Expansion: p /\ q expands to p, q
+				kappa1 = ((LNConj) ln).getBody(0);
+				kappa2 = ((LNConj) ln).getBody(1);
+			}
+			if (kappa1 != null) {
+				if (terms1.member(kappa1)) {
+					if (!terms1.member(kappa2)) {
+						terms1 = terms1.append(kappa2);
+					}
+				} else if (terms1.member(kappa2)) {
+					terms1 = terms1.append(kappa1);
+				} else {
+					terms1 = terms1.append(kappa1, kappa2);
+				}
+			}
+		}
+		// second, try alpha^-1 expansion
+		boolean done;
+		do {
+			done = true;
+			for (int i = 0; i < alphas.size(); i++) {
+				TBTriple alpha = (TBTriple) alphas.elementAt(i);
+				if (terms1.member(alpha.getB()) && terms1.member(alpha.getC()) && !terms1.member(alpha.getA())) {
+					terms1.addElement(alpha.getA());
+					done = false;
+				}
+			}
+		} while (!done);
+		// finally, recurse only when locally consistent
+		if ((terms1.size() > terms.size()) && (!terms1.isLocallyConsistent())) {
+			return new TBParVec(0);
+		}
+		return particleClosureBeta(terms1, alphas, betas);
+	}
+
+	private TBParVec particleClosureBeta(TBPar terms, Vect alphas, Vect betas) {
+		// try a beta expansion. See MP page 403 
+		// figure 5.1. for beta expansion rules.
+		for (int i = 0; i < terms.size(); i++) {
+			LiveExprNode ln = terms.exprAt(i);
+			LiveExprNode kappa1 = null, kappa2 = null;
+			if (ln instanceof LNEven) {
+				// Beta-Expansion: <>p expands to p, ()<>p
+				kappa1 = ((LNEven) ln).getBody();
+				kappa2 = new LNNext(ln);
+			} else if (ln instanceof LNDisj) {
+				// Beta-Expansion: p \/ expands to p, q
+				kappa1 = ((LNDisj) ln).getBody(0);
+				kappa2 = ((LNDisj) ln).getBody(1);
+			}
+			if ((kappa1 != null) && !terms.member(kappa1) && !terms.member(kappa2)) {
+				TBParVec ps1 = particleClosure(terms.append(kappa1), alphas, betas);
+				TBParVec ps2 = particleClosure(terms.append(kappa2), alphas, betas);
+				return ps1.union(ps2);
+			}
+		}
+		// try a beta^-1 expansion
+		for (int i = 0; i < betas.size(); i++) {
+			TBTriple beta = (TBTriple) betas.elementAt(i);
+			if ((terms.member(beta.getB()) || terms.member(beta.getC())) && !terms.member(beta.getA())) {
+				return particleClosure(terms.append(beta.getA()), alphas, betas);
+			}
+		}
+		// if there are not any more expansions to do, return the terms
+		// we've got as the only particle in a list of particles.
+		TBParVec res = new TBParVec(1);
+		res.addElement(terms);
+		return res;
+	}
+
 	/**
 	 * The methods alphaTriples and betaTriples, given a positive closure,
 	 * figure out the alpha and beta triples. The way the algorithm works, by
@@ -109,8 +208,8 @@ public class TBPar extends Vect {
 	 * closed. All junctions must have been binarified at this stage by
 	 * makeBinary, otherwise it may give the wrong answer and crash.
 	 */
-	public final Vect alphaTriples() {
-		Vect ts = new Vect();
+	public final Vect<TBTriple> alphaTriples() {
+		Vect<TBTriple> ts = new Vect<>();
 		for (int i = 0; i < this.size(); i++) {
 			LiveExprNode ln = this.exprAt(i);
 			if (ln instanceof LNAll) {
@@ -123,8 +222,8 @@ public class TBPar extends Vect {
 		return ts;
 	}
 
-	public final Vect betaTriples() {
-		Vect ts = new Vect();
+	public final Vect<TBTriple> betaTriples() {
+		Vect<TBTriple> ts = new Vect<>();
 		for (int i = 0; i < this.size(); i++) {
 			LiveExprNode ln = this.exprAt(i);
 			if (ln instanceof LNEven) {
@@ -231,7 +330,14 @@ public class TBPar extends Vect {
 	}
 
 	/**
-	 * This methods returns true iff this particle fulfills the promise.
+	 * This methods returns true iff this particle (TBPar) fulfills the given
+	 * promise.
+	 * <p>
+	 * A particle/atom A is said to fulfill formula &#966; which promises r if either:
+	 * <ul>
+	 * <li>&#966; \notin A</li>
+	 * <li>r \in A</li>
+	 * </ul>
 	 */
 	public final boolean isFulfilling(LNEven promise) {
 		return !this.member(promise) || this.member(promise.getBody());
diff --git a/tlatools/src/tlc2/tool/liveness/TBParVec.java b/tlatools/src/tlc2/tool/liveness/TBParVec.java
index 76856c648c255217940d1d0b51e35564810ed6e7..a0b39d074ea84f10a365941a4c1ab897b949fc55 100644
--- a/tlatools/src/tlc2/tool/liveness/TBParVec.java
+++ b/tlatools/src/tlc2/tool/liveness/TBParVec.java
@@ -7,7 +7,7 @@ package tlc2.tool.liveness;
 
 import tlc2.util.Vect;
 
-public class TBParVec extends Vect {
+public class TBParVec extends Vect<TBPar> {
 
 	public TBParVec(int size) {
 		super(size);
diff --git a/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java b/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java
index 5ba39a9848a92c5558f5e67aac852137ca921852..e3a19defa82283b6fa9c22005a87fb64bc591666 100644
--- a/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java
+++ b/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java
@@ -30,6 +30,7 @@ import java.io.IOException;
 
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.ITool;
 import tlc2.util.LongVec;
 import tlc2.util.MemIntQueue;
 import tlc2.util.statistics.IBucketStatistics;
@@ -69,7 +70,7 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 	 * long as it has been recorded with recordNode().
 	 * <p>
 	 * A node is logically undone when it's an initial state and added via
-	 * {@link LiveCheck#addInitState(tlc2.tool.TLCState, long)} but not yet
+	 * {@link LiveCheck#addInitState(ITool, tlc2.tool.TLCState, long)} but not yet
 	 * added via
 	 * {@link LiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, LongVec)}
 	 * . A second case is when a successor node in the behavior graph is added
@@ -112,11 +113,18 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#putNode(tlc2.tool.liveness.GraphNode, long)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#putNode(tlc2.tool.liveness.GraphNode, long)
 	 */
 	protected void putNode(GraphNode node, long ptr) {
 		this.nodePtrTbl.put(node.stateFP, node.tindex, ptr);
 	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#checkDuplicate(tlc2.tool.liveness.GraphNode)
+	 */
+	protected boolean checkDuplicate(final GraphNode node) {
+		return this.nodePtrTbl.get(node.stateFP, node.tindex) != -1;
+	}
 
 	/**
 	 * Get the graph node. Returns a new GraphNode if the node is not in this.
@@ -156,16 +164,17 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#getLink(long, int)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#getLink(long, int)
 	 */
 	public long getLink(long state, int tidx) {
 		return this.nodePtrTbl.get(state, tidx);
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#putLink(long, int, long)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#putLink(long, int, long)
 	 */
 	public long putLink(long state, int tidx, long link) {
+		assert MAX_PTR <= link && link < MAX_LINK; 
 		int[] node = this.nodePtrTbl.getNodes(state);
 		int cloc = this.nodePtrTbl.getIdx(node, tidx);
 		long oldLink = TableauNodePtrTable.getElem(node, cloc);
@@ -177,7 +186,7 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#setMaxLink(long, int)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#setMaxLink(long, int)
 	 */
 	public void setMaxLink(long state, int tidx) {
 		this.nodePtrTbl.put(state, tidx, MAX_LINK);
@@ -197,7 +206,7 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#makeNodePtrTbl(long)
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#makeNodePtrTbl(long)
 	 */
 	protected void makeNodePtrTbl(final long ptr) throws IOException  {
 		makeNodePtrTbl(ptr, nodePtrTbl);
@@ -256,9 +265,11 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.tool.liveness.DiskGraph#toDotViz()
+	 * @see tlc2.tool.liveness.AbstractDiskGraph#toDotViz(tlc2.tool.liveness.OrderOfSolution)
 	 */
-	public final String toDotViz() {
+	public final String toDotViz(final OrderOfSolution oos) {
+		final int slen = oos.getCheckState().length;
+		final int alen = oos.getCheckAction().length;
 
 		// The following code relies on gnodes not being null, thus safeguard
 		// against accidental invocations.
@@ -274,6 +285,9 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 			sb.append("digraph DiskGraph {\n");
 			sb.append("nodesep = 0.7\n");
 			sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom
+			sb.append(toDotVizLegend(oos));
+			sb.append("subgraph cluster_graph {\n"); 
+	        sb.append("color=\"white\";\n"); // no border.
 			//TODO Reading the file front to end potentially yields node duplicates in the output. Better to create a (temporary) nodeptrtable and traverse it instead.
 			long nodePtr = this.nodeRAF.getFilePointer();
 			long nodePtrPtr = this.nodePtrRAF.getFilePointer();
@@ -284,9 +298,9 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 				int tidx = nodePtrRAF.readInt();
 				long loc = nodePtrRAF.readLongNat();
 				GraphNode gnode = this.getNode(fp, tidx, loc);
-				sb.append(gnode.toDotViz(isInitState(gnode), true));
+				sb.append(gnode.toDotViz(isInitState(gnode), true, slen, alen));
 			}
-			sb.append("}");
+			sb.append("}}");
 			this.nodeRAF.seek(nodePtr);
 			this.nodePtrRAF.seek(nodePtrPtr);
 		} catch (IOException e) {
@@ -380,7 +394,21 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 					continue;
 				}
 				if (nextState == state && nextTidx == tidx) {
-					// Stop BFS (see MemIntQUEUE above), we found a path to state.
+					// Stop BFS (see MemIntQUEUE above), we found a path to
+					// state. The path might not necessarily be the shortest one
+					// possible. A non-optimal path is returned if the BFS
+					// search happens to produce a path <<fp1, fp2, fp3>> first
+					// before it finds <<fp1, fp1, fp2>> (the first two elements
+					// share the same fingerprint and only differ in the
+					// discarded tableau id). The caller LiveWorker#printTrace
+					// collapses contiguous segments (identical fingerprint) in
+					// the path into a single state. E.g. the second example
+					// above will be printed as two states: <<State(fp1),
+					// State(fp2)>>. Hence, it is shorter than <<State(fp1),
+					// State(fp2), State(fp3)>>. Note that the search does *not*
+					// stop after the second node from the end in <<fp1, fp1, fp2>>
+					// because this node - due to its tableau id - does not
+					// correspond to an initial state in reversablePtrTable.
 					return reconstructReversePath(reversablePtrTable, curState, curTidx, nextState, nextTidx);
 				}
 
@@ -503,6 +531,12 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 		return res;
 	}
 	
+	/**
+	 * This implementation extends {@link TableauNodePtrTable} to additionally
+	 * store the tableau index of the predecessor node. It is needed to traverse
+	 * the {@link ReverseTraversableTableauNodePtrTable} backwards once a error
+	 * trace path has been created.
+	 */
 	private class ReverseTraversableTableauNodePtrTable extends TableauNodePtrTable {
 
 		public ReverseTraversableTableauNodePtrTable(final int size) {
@@ -543,9 +577,9 @@ public class TableauDiskGraph extends AbstractDiskGraph {
 		}
 
 		/* (non-Javadoc)
-		 * @see tlc2.tool.liveness.TableauNodePtrTable#addElem(int[], int, long)
+		 * @see tlc2.tool.liveness.TableauNodePtrTable#appendElem(int[], int, long)
 		 */
-		protected int[] addElem(final int[] node, final int tidx, final long elem) {
+		protected int[] appendElem(final int[] node, final int tidx, final long elem) {
 			int len = node.length;
 			int[] newNode = new int[len + getElemLength()];
 			System.arraycopy(node, 0, newNode, 0, len);
diff --git a/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java b/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java
index 1fd7b918351ef7470defea52b8b0347341c75c38..7a5385138aed0e203b419767018d0eb4c983a97a 100644
--- a/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java
+++ b/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java
@@ -26,8 +26,62 @@
 
 package tlc2.tool.liveness;
 
+/**
+ * {@link TableauNodePtrTable} (and its sibling {@link NodePtrTable} for
+ * tableau-less liveness checking) is a - highly optimized - set of all nodes in
+ * the behavior graph {@link TableauDiskGraph}.
+ * <p>
+ * Each node in the behavior graph consists of the pair <<state, tidx>> (where
+ * state is a state's fingerprint) and auxiliary information. The auxiliary
+ * information is:
+ * <ul>
+ * <li>An offset into the second set (@see {@link AbstractDiskGraph#nodeRAF})
+ * which represents the arcs between the nodes (logically outgoing transitions).
+ * Technically this is a pointer location into the second disk file of
+ * {@link TableauDiskGraph}.</li>
+ * <li>The node's link number during Tarjan's SCC search.</li>
+ * <li>A flag if the node is done or not (see {@link TableauNodePtrTable#UNDONE}
+ * below).</li>
+ * <li>A flag that marks a node an initial node.</li>
+ * <li>A flag if the node has been seen before during error trace re-creation
+ * (see {@link LiveWorker#printTrace}.</li>
+ * </ul>
+ * <p>
+ * The last item indicates that this class is used in two scenarios. It's
+ * primary purpose is to be the backing store of the liveness/behavior disc
+ * graph. Additionally though, LiveWorker#printTrace independently instantiates
+ * a new {@link TableauNodePtrTable} to do its work.
+ * <p>
+ * To minimize {@link TableauNodePtrTable}'s space/memory requirements, the
+ * auxiliary information replace each other depending on the phase of liveness
+ * checking.<br>
+ * During model checking (safety checking) the auxiliary information is set to a
+ * pointer location (long) pointing into the arcs set and the high bits of the
+ * long are used to mark nodes as done or undone.<br>
+ * As soon as the SCC search starts, the pointer location is replaced by the SCC
+ * link number.<br>
+ * Once a liveness violation has been detected, the seen flag is set during the
+ * error trace path reconstruction.
+ * <p>
+ * Internally {@link TableauNodePtrTable} hashes the node's fingerprint to a
+ * bucket address. In case of hash collision, open addressing is used.
+ */
 public class TableauNodePtrTable {
 
+	/**
+	 * A node is marked UNDONE if it is:
+	 * <ul>
+	 * <li>An initial node <b>s0</b> and not yet visited again by
+	 * LiveChecker#addNextState(<b>s0</b>)</li>
+	 * <li>A previously unseen successor node <b>t</b> of a node <b>s</b> that
+	 * is added as an outgoing transition of <b>s</b> when <b>s</b> is being
+	 * added via LiveChecker#addNextState(<b>s</b> )</li>
+	 * </ul>
+	 * <p>
+	 * It logically markers the successor node <b>t</b> to be incomplete which
+	 * can only happen during liveness checking of a <b>partial</b> liveness
+	 * graph.
+	 */
 	public static final long UNDONE = 0xFFFFFFFE00000000L;
 	
 	private int count;
@@ -102,10 +156,10 @@ public class TableauNodePtrTable {
 				int cloc = getIdx(node, tidx);
 				if (cloc == -1) {
 					// The list of nodes does not contain the give tableau idx
-					// yet, thus add a new element. Technically, it means we
+					// yet, thus append a new element. Technically, it means we
 					// grow the nodes array by three and insert the tableau idx
 					// and its element.
-					this.nodes[loc] = addElem(node, tidx, elem);
+					this.nodes[loc] = appendElem(node, tidx, elem);
 				} else {
 					// Nodes already contains an entry for the given tableau.
 					// Update its element. The element is either a pointer
@@ -200,6 +254,7 @@ public class TableauNodePtrTable {
 		return node[3] != -2;
 	}
 
+	// Called by addNextState
 	public final int setDone(long k) {
 		if (this.count >= this.thresh) {
 			this.grow();
@@ -235,12 +290,22 @@ public class TableauNodePtrTable {
 		}
 	}
 	
+	/**
+	 * Clears the seen flag of all records set by static setSeen(..) calls
+	 * earlier.
+	 * <p>
+	 * Post-Condition: None of the records is marked seen.
+	 * 
+	 * @see TableauNodePtrTable#setSeen(int[])
+	 * @see TableauNodePtrTable#setSeen(int[], int)
+	 */
 	public final void resetElems() {
+		// Only called when the error trace is being printed. 
 		for (int i = 0; i < this.nodes.length; i++) {
 			int[] node = this.nodes[i];
 			if (node != null) {
 				for (int j = 3; j < node.length; j += getElemLength()) {
-					node[j] &= 0x7FFFFFFF;
+					node[j] &= 0x7FFFFFFF; // Clear the MSB set by setSeen(..)
 				}
 			}
 		}
@@ -288,7 +353,7 @@ public class TableauNodePtrTable {
 		return node;
 	}
 
-	protected int[] addElem(int[] node, int tidx, long elem) {
+	protected int[] appendElem(int[] node, int tidx, long elem) {
 		int len = node.length;
 		int[] newNode = new int[len + getElemLength()];
 		System.arraycopy(node, 0, newNode, 0, len);
@@ -345,47 +410,80 @@ public class TableauNodePtrTable {
 		return node[loc];
 	}
 
+	/*
+	 * Helper methods used by LiveWorker#printTrace(..) only. Note that
+	 * printTrace does not use the TNPT instance of the DiskGraph but its own
+	 * instance only containing a single SCC.
+	 */
+	public static final int END_MARKER = -1;
+	
 	public static int startLoc(int[] node) {
-		return (node.length > 2) ? 2 : -1;
+		return (node.length > 2) ? 2 : END_MARKER;
 	}
 
 	public static int nextLoc(int[] node, int curLoc) {
 		int loc = curLoc + 3;
-		return (loc < node.length) ? loc : -1;
+		return (loc < node.length) ? loc : END_MARKER;
 	}
 
+	/**
+	 * @param nodes
+	 * @return True, iff the record at tloc has been marked seen.
+	 * @see TableauNodePtrTable#setSeen(int[], int)
+	 */
 	public static boolean isSeen(int[] nodes, int tloc) {
 		return getElem(nodes, tloc) < 0;
 	}
 
+	/**
+	 * Marks the record at tloc seen.
+	 * 
+	 * @param nodes
+	 * @see TableauNodePtrTable#setSeen(int[])
+	 * @see TableauNodePtrTable#resetElems()
+	 */
 	public static void setSeen(int[] nodes, int tloc) {
 		long ptr = getElem(nodes, tloc);
-		putElem(nodes, (ptr | 0x8000000000000000L), tloc);
+		putElem(nodes, (ptr | 0x8000000000000000L), tloc); // Set the MSB
 	}
 
 	public static long getPtr(long ptr) {
 		return (ptr & 0x7FFFFFFFFFFFFFFFL);
 	}
 
+	/**
+	 * @see TableauNodePtrTable#setSeen(int[])
+	 * @param nodes
+	 * @return True, iff the record has been marked seen.
+	 */
 	public static boolean isSeen(int[] nodes) {
 		return nodes[3] < 0;
 	}
 
+	/**
+	 * Marks this record seen.
+	 * 
+	 * @param nodes
+	 * @see TableauNodePtrTable#resetElems()
+	 */
 	public static void setSeen(int[] nodes) {
-		nodes[3] |= 0x80000000;
+		nodes[3] |= 0x80000000; // Set the MSB
 	}
 
+	public static final int NO_PARENT = -1;
+	
 	public static int getParent(int[] nodes) {
 		return nodes[4];
 	}
 
 	public static void setParent(int[] nodes, int loc) {
+		assert loc >= NO_PARENT && loc <= AbstractDiskGraph.MAX_PTR;
 		nodes[4] = loc;
 	}
 
   	/*
 	 * The detailed formatter below can be activated in Eclipse's variable view
-	 * by choosing "New detailed formatter" from the MemIntQueue context menu.
+	 * by choosing "New detailed formatter" from the nodePtrTable's context menu.
 	 * Insert "TableauNodePtrTable.DetailedFormatter.toString(this);".
 	 */
   	public static class DetailedFormatter {
@@ -402,8 +500,11 @@ public class TableauNodePtrTable {
   					buf.append(" isDone: " + (node.length == 2 || (node.length > 2 && node[3] != -2)));
   					buf.append("\n");
   					
-  					// A node maintains n records. Each record logically contains information about a node's successor.
-  					// fingerprint
+					// A node maintains n records. Each record logically
+					// contains information - combined with the fingerprint -
+					// about the full tuple <<fp, tidx, loc>>.
+  					// Depending on the state of the record, the loc might
+  					// also be overwritten by the SCC link number. 
   					int j = 2;
   					for (; j < node.length - 1; j+=table.getElemLength()) { // don't miss the ptr at the end
   						buf.append("\t");
@@ -413,7 +514,11 @@ public class TableauNodePtrTable {
   						// element
   						final long elem = getElem(node, j);
   						if (AbstractDiskGraph.isFilePointer(elem)) {
-  							buf.append("  ptr: " + elem);
+  							if (table.isDone(fp)) {
+  								buf.append("  ptr: " + elem);
+  							} else {
+  								buf.append("  ptr: undone");
+  							}
   						} else if (AbstractDiskGraph.MAX_PTR == elem){
   							buf.append(" elem: Init State");
   						} else {
diff --git a/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java b/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java
index 1a8c8c0c481edbfec2458e614b16e686d7d2ab69..f8f56b3d7f5b7026fd2d8492f85507e59ac5b848 100644
--- a/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java
+++ b/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java
@@ -2,12 +2,12 @@
 
 package tlc2.tool.management;
 
-import java.io.IOException;
-
 import javax.management.NotCompliantMBeanException;
 
+import tlc2.TLC;
 import tlc2.TLCGlobals;
 import tlc2.tool.ModelChecker;
+import tlc2.tool.TLCState;
 import tlc2.tool.distributed.management.TLCStatisticsMXBean;
 import tlc2.tool.fp.DiskFPSet;
 
@@ -16,14 +16,18 @@ import tlc2.tool.fp.DiskFPSet;
  */
 public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatisticsMXBean {
 
+	public static final String OBJ_NAME = "tlc2.tool:type=ModelChecker";
+
 	private final ModelChecker modelChecker;
+	private final TLC tlc;
 
-	public ModelCheckerMXWrapper(final ModelChecker aModelChecker)
+	public ModelCheckerMXWrapper(final ModelChecker aModelChecker, final TLC tlc)
 			throws NotCompliantMBeanException {
 		super(TLCStatisticsMXBean.class);
 		this.modelChecker = aModelChecker;
+		this.tlc = tlc;
 		// register all TLCStatisticsMXBeans under the same name
-		registerMBean("tlc2.tool:type=ModelChecker");
+		registerMBean(OBJ_NAME);
 	}
 
 	/* (non-Javadoc)
@@ -51,7 +55,7 @@ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatis
 	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getStateQueueSize()
 	 */
 	public long getStateQueueSize() {
-		return modelChecker.theStateQueue.size();
+		return modelChecker.getStateQueueSize();
 	}
 
 	/* (non-Javadoc)
@@ -72,13 +76,7 @@ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatis
 	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getProgress()
 	 */
 	public int getProgress() {
-		try {
-			return modelChecker.trace.getLevelForReporting();
-		} catch (IOException e) {
-			// The modelchecker trace file might be closed already (e.g. it
-			// gets closed at the end of the modelchecker run)
-			return -1;
-		}
+		return modelChecker.getProgress();
 	}
 
 	/* (non-Javadoc)
@@ -102,4 +100,64 @@ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatis
 		//TODO adapt once Workers can support units of work greater than 1 
 		return 1;
 	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getRuntimeRatio()
+	 */
+	public double getRuntimeRatio() {
+		return modelChecker.getRuntimeRatio();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#liveCheck()
+	 */
+	public void liveCheck() {
+		modelChecker.forceLiveCheck();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getCurrentState()
+	 */
+	public String getCurrentState() {
+		final TLCState state = modelChecker.theStateQueue.sPeek();
+		if (state != null) {
+			return state.toString();
+		}
+		return "N/A";
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getSpecName()
+	 */
+	public String getSpecName() {
+		return tlc.getSpecName();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getModelName()
+	 */
+	public String getModelName() {
+		return tlc.getModelName();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#stop()
+	 */
+	public void stop() {
+		modelChecker.stop();
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#suspend()
+	 */
+	public void suspend() {
+		modelChecker.suspend();
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#resume()
+	 */
+	public void resume() {
+		modelChecker.resume();
+	}
 }
diff --git a/tlatools/src/tlc2/tool/management/StateMonitor.java b/tlatools/src/tlc2/tool/management/StateMonitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..90eb4cd236e827ff46826b1354e739cce54927c1
--- /dev/null
+++ b/tlatools/src/tlc2/tool/management/StateMonitor.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.management;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Scanner;
+
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.VirtualMachineDescriptor;
+
+import tlc2.tool.distributed.management.TLCStatisticsMXBean;
+
+/*
+ * This class does not compile in the Eclipse IDE on Ubuntu 18.04 with JaveSE1.8 linked to the
+ * 8u201-1~webupd8~1 JVM installed from webupd8 and probably fails to compile elsewhere too,
+ * because Eclipse fails to find the com.sun.tools.attach classes.  The fix is to add the JDK's
+ * tools.jar to the Java installation.  On Java 11 this doesn't seem to be an issue.
+ */
+public class StateMonitor {
+
+	private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ 
+
+	public static void main(String[] args) throws IOException, MalformedObjectNameException, InterruptedException, AttachNotSupportedException {
+		int interval = 10; // 10sec interval by default
+		if (args.length == 1) {
+			interval = Integer.valueOf(args[0]);
+		}
+		
+		JMXServiceURL url = null;
+		try {
+			final List<VirtualMachineDescriptor> vmds = com.sun.tools.attach.VirtualMachine.list();
+			vmds.sort(new Comparator<VirtualMachineDescriptor>() {
+				@Override
+				public int compare(VirtualMachineDescriptor o1, VirtualMachineDescriptor o2) {
+					// Sort those vms higher whose display name contains TLC.
+					final boolean c1 = o1.displayName().contains("TLC");
+					final boolean c2 = o2.displayName().contains("TLC");
+					if (c1 ^ c2) {
+						if (c1) {
+							return -1;
+						}
+						return 1;
+					}
+					return 0;
+				}
+			});
+			
+			int index = 1;
+			try (Scanner scanner = new Scanner(System.in)) {
+				rd: while (true) {
+					index = 1;
+					System.out.printf("============\n");
+					for (VirtualMachineDescriptor vmd : vmds) {
+						System.out.printf("[%s]: pid=%s, name=%s\n", index++, vmd.id(), vmd.displayName());
+					}
+					System.out.printf("Please select the number of the Java VM running TLC to connect to:\n");
+					if (scanner.hasNextInt()) {
+						index = scanner.nextInt();
+						
+						// Check index is within bounds.
+						if (index >= 1 && index <= vmds.size()) {
+							break rd;
+						}
+					}
+					System.err.printf("Invalid selection %s\n", index);
+				}
+			}
+			final VirtualMachineDescriptor tlcVMD = vmds.get(index - 1);
+			final VirtualMachine vm = tlcVMD.provider().attachVirtualMachine(tlcVMD);
+			final String address = vm.startLocalManagementAgent();
+			url = new JMXServiceURL(address);
+			System.out.printf("Connecting to TLC running at %s.\n(Hit Ctrl+c to terminate)\n", url);
+		} catch (NumberFormatException nfe) {
+			// If monitored VM has been launched with explicit port, use service url instead
+			// of CAL:
+			// -Dcom.sun.management.jmxremote
+			// -Dcom.sun.management.jmxremote.port=10995
+			// -Dcom.sun.management.jmxremote.ssl=false
+			// -Dcom.sun.management.jmxremote.authenticate=false
+			// => "service:jmx:rmi:///jndi/rmi://localhost:10995/jmxrmi".
+			url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + "/jmxrmi");
+			System.out.printf("Connecting to TLC running at %s.\n(Hit Ctrl+c to terminate)\n", url);
+		}
+		
+		final JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
+		final MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
+		// ObjectName should be same as your MBean name
+		final ObjectName mbeanName = new ObjectName(ModelCheckerMXWrapper.OBJ_NAME);
+
+		// Get MBean proxy instance that will be used to make calls to registered MBean
+		final TLCStatisticsMXBean mbeanProxy = (TLCStatisticsMXBean) MBeanServerInvocationHandler
+				.newProxyInstance(mbeanServerConnection, mbeanName, TLCStatisticsMXBean.class, true);
+
+		while (true) {
+			System.out.printf("############ %s ############\n%s", SDF.format(new Date()), mbeanProxy.getCurrentState());
+			Thread.sleep(interval * 1000L);
+		}
+		
+		// No need to close the connection because JVM itself terminates.
+		//jmxConnector.close();
+	}
+}
diff --git a/tlatools/src/tlc2/tool/management/TLCStandardMBean.java b/tlatools/src/tlc2/tool/management/TLCStandardMBean.java
index 52973c84717de1ca64d2600068978d96767d210b..9c3ab9e8f3ae812ba50ba53403282f9a9c984de6 100644
--- a/tlatools/src/tlc2/tool/management/TLCStandardMBean.java
+++ b/tlatools/src/tlc2/tool/management/TLCStandardMBean.java
@@ -14,6 +14,8 @@ import javax.management.NotCompliantMBeanException;
 import javax.management.ObjectName;
 import javax.management.StandardMBean;
 
+import tlc2.TLCGlobals;
+
 /**
  * @author Markus Alexander Kuppe
  */
@@ -26,6 +28,17 @@ public abstract class TLCStandardMBean extends StandardMBean {
 		super(mbeanInterface);
 	}
 
+	public String getVersion() {
+		return TLCGlobals.versionOfTLC;
+	}
+	
+	public String getRevision() {
+		if (TLCGlobals.getRevision() == null) {
+			return "N/A";
+		}
+		return TLCGlobals.getRevision();
+	}
+
 	/**
 	 * Registers a new MBean with the platform's MBean server after which this MBean will be visible in JMX.
 	 * 
@@ -66,7 +79,9 @@ public abstract class TLCStandardMBean extends StandardMBean {
 	public boolean unregister() {
 		final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
 		try {
-			mbs.unregisterMBean(mxbeanName);
+			if (mbs.isRegistered(mxbeanName)) {
+				mbs.unregisterMBean(mxbeanName);
+			}
 		} catch (MBeanRegistrationException e) {
 			e.printStackTrace();
 			return false;
diff --git a/tlatools/src/tlc2/tool/queue/ByteArrayQueue.java b/tlatools/src/tlc2/tool/queue/ByteArrayQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a5228f25b6ec83b973d9eac0a80e422f06a594f
--- /dev/null
+++ b/tlatools/src/tlc2/tool/queue/ByteArrayQueue.java
@@ -0,0 +1,474 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:18:27 PST by lamport
+//      modified on Tue Feb 13 18:38:24 PST 2001 by yuanyu
+
+package tlc2.tool.queue;
+
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.StateVec;
+import tlc2.tool.TLCState;
+import tlc2.tool.Worker;
+
+/*
+ * This class is identical to StateQueue except that it internally works
+ * on byte[] whereas StateQueue's internal data is TLCStates. In other words,
+ * serialization of TLCState to a byte[] occurs outside the critical section.
+ */
+public abstract class ByteArrayQueue implements IStateQueue {
+	/**
+	 * In model checking, this is the sequence of states waiting to be explored
+	 * further. When the queue is empty, the checking is completed.
+	 */
+	protected long len = 0; // the queue length
+	private int numWaiting = 0; // the number of waiting threads
+	private volatile boolean finish = false; // terminate
+	/**
+	 * Signals {@link Worker} that checkpointing is going happen next.
+	 */
+	private boolean stop = false; // suspend all workers.
+	/**
+	 * Synchronizes between workers and checkpointing. More precisely it is used
+	 * to notify (wake up) the checkpointing thread once the last worker is
+	 * done.
+	 */
+	private Object mu = new Object();
+	
+	
+	private final byte[] toBytes(final TLCState state) {
+		try {
+			final DiskByteArrayQueue.ByteValueOutputStream vos = new DiskByteArrayQueue.ByteValueOutputStream();
+			state.write(vos);
+			return vos.toByteArray();
+		} catch (IOException notExpectedToHappen) {
+			// With ByteArrayOutputStream
+			notExpectedToHappen.printStackTrace();
+		}
+		return null;
+	}
+	
+	private final byte[] toBytes(final TLCState state, final DiskByteArrayQueue.ByteValueOutputStream vos) {
+		try {
+			state.write(vos);
+			return vos.toByteArray();
+		} catch (IOException notExpectedToHappen) {
+			// With ByteArrayOutputStream
+			notExpectedToHappen.printStackTrace();
+		}
+		return null;
+	}
+	
+	private final TLCState toState(final byte[] bytes) {
+		try {
+			final TLCState state = TLCState.Empty.createEmpty();
+			state.read(new DiskByteArrayQueue.ByteValueInputStream(bytes));
+			return state;
+		} catch (IOException notExpectedToHappen) {
+			// With ByteValueInputStream
+			notExpectedToHappen.printStackTrace();
+		}
+		return null;
+	}
+	
+	/* Enqueues the state. It is not thread-safe. */
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#enqueue(tlc2.tool.TLCState)
+	 */
+	@Override
+	public final void enqueue(final TLCState state) {
+		enqueue(toBytes(state));
+	}
+	
+	private final void enqueue(final byte[] state) {
+		this.enqueueInner(state);
+		this.len++;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#dequeue()
+	 */
+	@Override
+	public final TLCState dequeue() {
+		final byte[] bytes = dequeueRaw();
+		if (bytes != null) {
+			return toState(bytes);
+		}
+		return null;
+	}
+	
+	private final byte[] dequeueRaw() {
+		if (isEmpty()) {
+			return null;
+		}
+		final byte[] state = this.dequeueInner();
+		this.len--;
+		return state;
+	}
+
+	/* Enqueues a state. Wake up any waiting thread. */
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#sEnqueue(tlc2.tool.TLCState)
+	 */
+	@Override
+	public final void sEnqueue(final TLCState state) {
+		sEnqueue(toBytes(state));
+	}
+	
+	private final synchronized void sEnqueue(final byte[] state) {
+		this.enqueueInner(state);
+		this.len++;
+		if (this.numWaiting > 0 && !this.stop) {
+			this.notifyAll();
+		}
+	}
+
+	/* Enqueues a list of states. Wake up any waiting thread. */
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#sEnqueue(tlc2.tool.TLCState[])
+	 */
+	@Override
+	public final void sEnqueue(final TLCState[] states) {
+		final byte[][] bytes = new byte[states.length][];
+		for (int i = 0; i < states.length; i++) {
+			bytes[i] = toBytes(states[i]);
+		}
+		sEnqueue(bytes);
+	}
+	
+	private final synchronized void sEnqueue(final byte[][] states) {
+		for (int i = 0; i < states.length; i++) {
+			this.enqueueInner(states[i]);
+		}
+		this.len += states.length;
+		if (this.numWaiting > 0 && !this.stop) {
+			this.notifyAll();
+		}
+	}
+	@Override
+	public final void sEnqueue(final StateVec stateVec) {
+		sEnqueue(stateVec, stateVec.size());
+	}
+
+//	@Override
+	public final void sEnqueue(final StateVec stateVec, int n) {
+		if (n == 0) {
+			return;
+		}
+		
+		final DiskByteArrayQueue.ByteValueOutputStream vos = new DiskByteArrayQueue.ByteValueOutputStream();
+		final byte[][] bytes = new byte[n][];
+		for (int i = 0; i < stateVec.size(); i++) {
+			final TLCState state = stateVec.elementAt(i);
+			if (state != null) {
+				bytes[n - 1] = toBytes(state, vos);
+				n--;
+			}
+		}
+		sEnqueue(bytes);
+	}
+	
+	@Override
+	public final TLCState sPeek() {
+		final byte[] bytes = sPeekRaw();
+		if (bytes != null) {
+			return toState(bytes);
+		}
+		return null;
+	}
+		
+	private final synchronized byte[] sPeekRaw() {
+		if (this.isAvail()) {
+			return this.peekInner();
+		}
+		return null;
+	}
+
+	/* Return the first element in the queue. Wait if empty. */
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#sDequeue()
+	 */
+	@Override
+	public final TLCState sDequeue() {
+		final byte[] bytes = sDequeueRaw();
+		if (bytes != null) {
+			return toState(bytes);
+		}
+		return null;
+	}
+	
+	private final synchronized byte[] sDequeueRaw() {
+		if (this.isAvail()) {
+			final byte[] state = this.dequeueInner();
+			assert state != null : "Null state found on queue";
+			this.len--;
+			return state;
+		}
+		return null;
+	}
+	
+	@Override
+	public final TLCState[] sDequeue(int cnt) {
+		final byte[][] bytes = sDequeueRaw(cnt);
+		if (bytes != null) {
+			final TLCState[] array = new TLCState[cnt];
+			for (int i = 0; i < array.length; i++) {
+				array[i] = toState(bytes[i]);
+			}
+			return array;
+		}
+		return null;
+	}
+
+	private final synchronized byte[][] sDequeueRaw(int cnt) {
+		assert cnt > 0 : "Nonpositive number of states requested.";
+		if (this.isAvail()) {
+			if (cnt > len) {
+				// in this case, casting len to int is safe 
+				cnt = (int) len;
+			}
+			final byte[][] states = new byte[cnt][];
+			int idx;
+			for (idx = 0; idx < cnt && this.len > 0; idx++) {
+				states[idx] = this.dequeueInner();
+				this.len--;
+			}
+			if (idx == cnt) {
+				return states;
+			}
+
+			// cnt >= index, shrink resulting array
+			// dead code due to resetting cnt == len if cnt > len
+			final byte[][] res = new byte[idx][];
+			for (int i = 0; i < idx; i++) {
+				res[i] = states[i];
+			}
+			return res;
+		}
+		return null;
+	}
+
+	/**
+	 * Checks if states are available. If no states are available, the callee
+	 * will be put to sleep until new states are available or another callee
+	 * signals work done. This is determined by the fact, that all other workers
+	 * are waiting for states.
+	 * 
+	 * @return true if states are available in the queue.
+	 */
+	private final boolean isAvail() {
+		/*
+		 * isAvail is only called from within sDequeue() and sDequeue(..) and
+		 * thus is always synchronized on this.
+		 */
+		
+		if (this.finish) {
+			return false;
+		}
+		while (isEmpty() || this.stop) {
+			this.numWaiting++;
+			// the last worker accessing notices that all other workers are
+			// waiting. This indicates that all work is done.
+			if (this.numWaiting >= TLCGlobals.getNumWorkers()) {
+				if (isEmpty()) {
+					this.numWaiting--;
+					return false;
+				}
+				// TODO what happens if control flow exits without ever
+				// notifying the checkpoint (mu.wait()) thread? In case
+				// of distributed TLC, this is the main thread of
+				// TLCServer.
+				synchronized (this.mu) {
+					this.mu.notify();
+				}
+			}
+			try {
+				this.wait();
+			} catch (Exception e) {
+				MP.printError(EC.GENERAL, "making a worker wait for a state from the queue", e);  // LL changed call 7 April 2012
+				System.exit(1);
+			}
+			this.numWaiting--;
+			if (this.finish) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#finishAll()
+	 */
+	@Override
+	public synchronized void finishAll() {
+		this.finish = true;
+		// Notify all other worker threads.
+		this.notifyAll();
+		// Need to wake main thread that waits (mu.wait()) to suspend access to
+		// the squeue (see suspendAll). The main thread might attempt to do its
+		// periodic work (tlc2.tool.ModelChecker.doPeriodicWork()) the moment
+		// all worker threads finish. Since suspendAll assumes the main thread
+		// is woken up (potentially) multiple times from sleeping indefinitely
+		// in the while loop before it finally returns after locking the
+		// StateQueue, we have to live up to this assumption.
+		//
+		// synchronized(this.mu) does not block when the main thread waits on
+		// this.mu in suspend all. We merely have to follow proper thread access.
+		synchronized (this.mu) {
+			// Technically notify() would do.
+			this.mu.notify();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#suspendAll()
+	 */
+	@Override
+	public final boolean suspendAll() {
+		boolean needWait = false;
+		synchronized (this) {
+			if (this.finish) {
+				return false;
+			}
+			this.stop = true;
+			needWait = needsWaiting();
+		}
+		// Wait for all worker threads to stop.
+		while (needWait) {
+			synchronized (this.mu) {
+				try {
+					// finishAll & suspendAll race:
+					//
+					// suspendAll from main is on the heels of finishAll, but
+					// not quite as fast. SuspendAll's synchronized(this.mu)
+					// is executed one clock tick after finishAll's thus waiting
+					// for finishAll() to notify all waiters of this.mu.
+					// With main being the only potential waiter in the system
+					// nobody gets notified and the lock on this released
+					// subsequently.
+					// Now it's suspendAll's turn. It acquires the lock on
+					// this.mu and immediately after goes to wait on it
+					// (this.mu) to be waken by worker threads eventually.
+					// Unfortunately, there are none left in the system. All
+					// have long finishedAll.
+					//
+					// The fix is to check the this.finish variable one more
+					// time directly after suspendAll acquires the lock this.mu,
+					// it checks if it still has to wait for workers. To make
+					// sure it reads the most recent value, it's declared
+					// volatile to establish a happens-before relation (since
+					// Java5's memory model) between workers and main. Otherwise
+					// the VM might decide to let main read a cached value of
+					// false of this.finish.
+					if (this.finish) {
+						return false;
+					}
+					// waiting here assumes that subsequently a worker
+					// is going to wake us up by calling isAvail() or
+					// this.mu.notify*()
+					this.mu.wait();
+				} catch (Exception e) {
+					MP.printError(EC.GENERAL, "waiting for a worker to wake up", e);  // LL changed call 7 April 2012
+					System.exit(1);
+				}
+			}
+			synchronized (this) {
+				if (this.finish) {
+					return false;
+				}
+				needWait = needsWaiting();
+			}
+		}
+		return true;
+	}
+	
+	/**
+	 * @return
+	 */
+	private boolean needsWaiting() {
+		//MAK 04/2012: Commented to fix an EOFException when liveness checking is enabled 
+//		// no need to wait without workers present
+//		if (this.numWaiting < 1) {
+//			return false;
+//		}
+		// if all workers wait at once, it indicates that all work is
+		// done and suspending all workers can happen right away without
+		// waiting.
+		return this.numWaiting < TLCGlobals.getNumWorkers();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#resumeAll()
+	 */
+	@Override
+	public final synchronized void resumeAll() {
+		this.stop = false;
+		this.notifyAll();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#resumeAllStuck()
+	 */
+	@Override
+	public void resumeAllStuck() {
+		// needsWaiting() read might have been incorrect if a TLCWorkerRMI dies
+		// unexpectedly, thus this recovers a potentially stuck checkpointing
+		// run.
+		if (stop) {
+			synchronized (mu) {
+				mu.notifyAll();
+			}
+		}
+		// 
+		if (!stop && !isEmpty() && this.numWaiting > 0) {
+			synchronized (this) {
+				this.notifyAll();
+			}
+		}
+	}
+
+	/* This method returns the size of the state queue. */
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#size()
+	 */
+	@Override
+	public final long size() {
+		return this.len;
+	}
+
+	/**
+	 * @return true iff the inner queue does not contain states
+	 */
+	@Override
+	public boolean isEmpty() {
+		return len < 1;
+	}
+
+	/* This method must be implemented in the subclass. */
+	abstract void enqueueInner(byte[] state);
+
+	/* This method must be implemented in the subclass. */
+	abstract byte[] dequeueInner();
+
+	/* This method must be implemented in the subclass. */
+	abstract byte[] peekInner();
+	
+	/* Checkpoint. */
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#beginChkpt()
+	 */
+	public abstract void beginChkpt() throws IOException;
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#commitChkpt()
+	 */
+	public abstract void commitChkpt() throws IOException;
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#recover()
+	 */
+	public abstract void recover() throws IOException;
+}
diff --git a/tlatools/src/tlc2/tool/queue/DiskByteArrayQueue.java b/tlatools/src/tlc2/tool/queue/DiskByteArrayQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..1deadcf6acaa683f2a63c3e2006bac8dffb0af98
--- /dev/null
+++ b/tlatools/src/tlc2/tool/queue/DiskByteArrayQueue.java
@@ -0,0 +1,945 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at  9:25:48 PST by lamport  
+//      modified on Thu Feb  8 23:32:12 PST 2001 by yuanyu   
+
+package tlc2.tool.queue;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.ValueConstants;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.TupleValue;
+import util.Assert;
+import util.BufferedDataInputStream;
+import util.BufferedDataOutputStream;
+import util.FileUtil;
+import util.IDataInputStream;
+import util.IDataOutputStream;
+import util.UniqueString;
+import util.WrongInvocationException;
+
+/**
+ * A {@link DiskByteArrayQueue} uses the local hard disc as a backing store for
+ * states. An in-memory buffer of size {@link DiskByteArrayQueue}{@link #BufSize}
+ */
+public class DiskByteArrayQueue extends ByteArrayQueue {
+	// TODO dynamic bufsize based on current VM parameters?
+	private final static int BufSize = Integer.getInteger(DiskStateQueue.class.getName() + ".BufSize", 8192);
+
+	/*
+	 * Invariants: I1. Entries in deqBuf are in the indices: [deqIndex,
+	 * deqBuffer.length ) I2. Entries in enqBuf are in the indices: [ 0,
+	 * enqIndex ) I3. 0 <= deqIndex <= deqBuf.length, where deqIndex == 0 =>
+	 * buffer full deqIndex == deqBuf.length => buffer empty I4. 0 <= enqIndex
+	 * <= enqBuf.length, where enqIndex == 0 => buffer empty enqIndex ==
+	 * enqBuf.length => buffer full
+	 */
+
+	/* Fields */
+	private final String filePrefix;
+	protected byte[][] deqBuf, enqBuf;
+	protected int deqIndex, enqIndex;
+	protected ByteArrayPoolReader reader;
+	protected ByteArrayPoolWriter writer;
+	
+	/**
+	 * The SPC takes care of deleting swap files on the lower end of the range
+	 * (loPool, hiPool). It terminates, when the first checkpoint is written at
+	 * which point checkpointing itself takes care of removing obsolete swap
+	 * files.
+	 */
+	protected final StatePoolCleaner cleaner;
+	private int loPool, hiPool, lastLoPool, newLastLoPool;
+	private File loFile;
+	
+	// TESTING ONLY!
+	DiskByteArrayQueue() throws IOException {
+		this(Files.createTempDirectory("DiskByteArrayQueue").toFile().toString());
+	}
+
+	/* Constructors */
+	public DiskByteArrayQueue(String diskdir) {
+		this.deqBuf = new byte[BufSize][];
+		this.enqBuf = new byte[BufSize][];
+		this.deqIndex = BufSize;
+		this.enqIndex = 0;
+		this.loPool = 1;
+		this.hiPool = 0;
+		this.lastLoPool = 0;
+		this.filePrefix = diskdir + FileUtil.separator;
+		File rFile = new File(this.filePrefix + Integer.toString(0));
+		this.reader = new ByteArrayPoolReader(BufSize, rFile);
+		this.reader.setDaemon(true);
+		this.loFile = new File(this.filePrefix + Integer.toString(this.loPool));
+		this.reader.start();
+		this.writer = new ByteArrayPoolWriter(BufSize, this.reader);
+		this.writer.setDaemon(true);
+		this.writer.start();
+		this.cleaner = new StatePoolCleaner();
+		this.cleaner.setDaemon(true);
+		this.cleaner.start();
+	}
+
+	final void enqueueInner(byte[] state) {
+		if (this.enqIndex == this.enqBuf.length) {
+			// enqBuf is full; flush it to disk
+			try {
+				String pstr = Integer.toString(this.hiPool);
+				File file = new File(this.filePrefix + pstr);
+				this.enqBuf = this.writer.doWork(this.enqBuf, file);
+				this.hiPool++;
+				this.enqIndex = 0;
+			} catch (Exception e) {
+				Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES,
+						new String[] { "queue", (e.getMessage() == null) ? e.toString() : e.getMessage() });
+			}
+		}
+		this.enqBuf[this.enqIndex++] = state;
+	}
+
+	final byte[] dequeueInner() {
+		if (this.deqIndex == this.deqBuf.length) {
+			this.fillDeqBuffer();
+		}
+		return this.deqBuf[this.deqIndex++];
+	}
+	
+	byte[] peekInner() {
+		if (this.deqIndex == this.deqBuf.length) {
+			this.fillDeqBuffer();
+		}
+		return this.deqBuf[this.deqIndex];
+	}
+
+	private final void fillDeqBuffer() {
+		try {
+			if (this.loPool + 1 <= this.hiPool) {
+				// We are sure there are disk files.
+				if (this.loPool + 1 >= this.hiPool) {
+					// potential read-write conflict on a file
+					this.writer.ensureWritten();
+				}
+				this.deqBuf = this.reader.doWork(this.deqBuf, this.loFile);
+				this.deqIndex = 0;
+				this.loPool++;
+				String pstr = Integer.toString(this.loPool);
+				this.loFile = new File(this.filePrefix + pstr);
+			} else {
+				// We still need to check if a file is buffered.
+				this.writer.ensureWritten();
+				byte[][] buf = this.reader.getCache(this.deqBuf, this.loFile);
+				if (buf != null) {
+					this.deqBuf = buf;
+					this.deqIndex = 0;
+					this.loPool++;
+					String pstr = Integer.toString(this.loPool);
+					this.loFile = new File(this.filePrefix + pstr);
+				} else {
+					// copy entries from enqBuf to deqBuf.
+					this.deqIndex = this.deqBuf.length - this.enqIndex;
+					System.arraycopy(this.enqBuf, 0, this.deqBuf, this.deqIndex, this.enqIndex);
+					this.enqIndex = 0;
+				}
+			}
+			// Notify the cleaner to do its job unless its waits for more work
+			// to pile up.
+			if ((loPool - lastLoPool) > 100) { //TODO Take BufSize into account. It defines the disc file size.
+				synchronized (this.cleaner) {
+					this.cleaner.deleteUpTo = loPool - 1;
+					this.cleaner.notifyAll();
+				}
+			}
+		} catch (Exception e) {
+			Assert.fail(EC.SYSTEM_ERROR_READING_STATES, new String[] { "queue",
+					(e.getMessage() == null) ? e.toString() : e.getMessage() });
+		}
+	}
+
+	/* Checkpoint. */
+	public final void beginChkpt() throws IOException {
+		synchronized (this.cleaner) {
+			// Checkpointing takes precedence over periodic cleaning
+			// (cleaner would otherwise delete checkpoint files as it know
+			// nothing of checkpoints).
+			this.cleaner.finished = true;
+			this.cleaner.notifyAll();
+		}
+		
+		String filename = this.filePrefix + "queue.tmp";
+	  	final BufferedDataOutputStream vos = new BufferedDataOutputStream(filename);
+		vos.writeLong(this.len);
+		vos.writeInt(this.loPool);
+		vos.writeInt(this.hiPool);
+		vos.writeInt(this.enqIndex);
+		vos.writeInt(this.deqIndex);
+		for (int i = 0; i < this.enqIndex; i++) {
+	  		vos.writeInt(this.enqBuf[i].length);
+	  		vos.write(this.enqBuf[i]);
+		}
+		for (int i = this.deqIndex; i < this.deqBuf.length; i++) {
+	  		vos.writeInt(this.deqBuf[i].length);
+	  		vos.write(this.deqBuf[i]);
+		}
+		vos.close();
+		this.newLastLoPool = this.loPool - 1;
+	}
+
+	public final void commitChkpt() throws IOException {
+		for (int i = this.lastLoPool; i < this.newLastLoPool; i++) {
+			String pstr = Integer.toString(i);
+			File oldPool = new File(this.filePrefix + pstr);
+			if (!oldPool.delete()) {
+				String msg = "DiskStateQueue.commitChkpt: cannot delete " + oldPool;
+				throw new IOException(msg);
+			}
+		}
+		this.lastLoPool = this.newLastLoPool;
+		File oldChkpt = new File(this.filePrefix + "queue.chkpt");
+		File newChkpt = new File(this.filePrefix + "queue.tmp");
+		if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) {
+			String msg = "DiskStateQueue.commitChkpt: cannot delete " + oldChkpt;
+			throw new IOException(msg);
+		}
+	}
+
+	public final void recover() throws IOException {
+		String filename = this.filePrefix + "queue.chkpt";
+	  	final BufferedDataInputStream vis = new BufferedDataInputStream(filename);
+		this.len = vis.readLong();
+		this.loPool = vis.readInt();
+		this.hiPool = vis.readInt();
+		this.enqIndex = vis.readInt();
+		this.deqIndex = vis.readInt();
+		this.lastLoPool = this.loPool - 1;
+
+		for (int i = 0; i < this.enqIndex; i++) {
+			this.enqBuf[i] = new byte[vis.readInt()];
+			vis.read(this.enqBuf[i]);
+		}
+		for (int i = this.deqIndex; i < this.deqBuf.length; i++) {
+			this.deqBuf[i] = new byte[vis.readInt()];
+			vis.read(this.deqBuf[i]);
+		}
+		vis.close();
+		File file = new File(this.filePrefix + Integer.toString(this.lastLoPool));
+		boolean canRead = (this.lastLoPool < this.hiPool);
+		this.reader.restart(file, canRead);
+		String pstr = Integer.toString(this.loPool);
+		this.loFile = new File(this.filePrefix + pstr);
+	}
+
+	public void finishAll() {
+		super.finishAll();
+		synchronized (this.writer) {
+			this.writer.notifyAll();
+		}
+		synchronized (this.reader) {
+			this.reader.setFinished();
+			this.reader.notifyAll();
+		}
+		synchronized (this.cleaner) {
+			this.cleaner.finished = true;
+			this.cleaner.notifyAll();
+		}
+	}
+
+	private class StatePoolCleaner extends Thread {
+
+		private volatile boolean finished = false;
+		public int deleteUpTo;
+		
+		private StatePoolCleaner() {
+			super("RawTLCStatePoolCleaner");
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Thread#run()
+		 */
+		public void run() {
+			try {
+				synchronized (this) {
+					while (!this.finished) {
+						this.wait();
+						if (this.finished) {
+							return;
+						}
+						
+						for (int i = lastLoPool; i < deleteUpTo; i++) {
+							final File oldPoolFile = new File(filePrefix + Integer.toString(i));
+							if (!oldPoolFile.delete()) {
+								// No reason to terminate/kill TLC when the cleanup fails.
+								// Contrary to StatePoolReader/Write, cleanup is optional
+								// functionality whose purpose is to prevent the disc from
+								// filling up. If the cleaner fails, the user can still
+								// manually delete the files.
+								MP.printWarning(EC.SYSTEM_ERROR_CLEANING_POOL, oldPoolFile.getCanonicalPath());
+							}
+						}
+						lastLoPool = deleteUpTo;
+					}
+				}
+			} catch (Exception e) {
+				// Assert.printStack(e);
+				MP.printError(EC.SYSTEM_ERROR_CLEANING_POOL, e.getMessage(), e);
+				System.exit(1);
+			}
+		}
+	}
+	
+	private static final class ByteArrayPoolWriter extends Thread {
+
+	    private byte[][] buf;     
+	    private File poolFile;           // the file to be written
+	    private final ByteArrayPoolReader reader;  // the consumer if not null
+	    
+	  public ByteArrayPoolWriter(int bufSize, ByteArrayPoolReader reader) {
+		  super("RawTLCStatePoolWriter");
+	    this.buf = new byte[bufSize][];
+	    this.poolFile = null;
+	    this.reader = reader;
+	  }
+	  
+	  /*
+	   * This method first completes the preceding write if not started.
+	   * It then notifies this writer to flush enqBuf to file. In practice,
+	   * we expect the preceding write to have been completed. 
+	   */
+	  public final synchronized byte[][] doWork(byte[][] enqBuf, File file)
+	  throws IOException {
+	    if (this.poolFile != null) {
+	  	  final BufferedDataOutputStream vos = new BufferedDataOutputStream(this.poolFile);
+	  	  for (int i = 0; i < this.buf.length; i++) {
+	  		  vos.writeInt(this.buf[i].length);
+	  		  vos.write(this.buf[i]);
+	  	  }
+	      vos.close();
+	    }
+	    byte[][] res = this.buf;
+	    this.buf = enqBuf;
+	    this.poolFile = file;
+	    this.notify();
+	    return res;
+	  }
+
+	  /* Spin waiting for the write to complete.  */
+	  public final void ensureWritten() throws InterruptedException {
+	    synchronized(this) {
+	      while (this.poolFile != null) {
+		this.wait();
+	      }
+	    }
+	  }
+
+	  public final synchronized void beginChkpt(ObjectOutputStream oos)
+	  throws IOException {
+	    boolean hasFile = (this.poolFile == null) ? false : true;
+	    oos.writeBoolean(hasFile);
+	    if (hasFile) {
+	      oos.writeObject(this.poolFile);
+	      for (int i = 0; i < this.buf.length; i++) {
+		oos.writeObject(this.buf[i]);
+	      }
+	    }
+	  }
+
+	  /* Note this method is not synchronized.  */
+	  public final void recover(ObjectInputStream ois) throws IOException {    
+	    boolean hasFile = ois.readBoolean();
+	    if (hasFile) {
+	      try {
+		this.poolFile = (File)ois.readObject();
+		for (int i = 0; i < this.buf.length; i++) {
+		  this.buf[i] = (byte[])ois.readObject();
+		}
+	      }
+	      catch (ClassNotFoundException e) 
+	      {
+	          Assert.fail(EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, e);
+	      }
+	    }
+	    else {
+	      this.poolFile = null;
+	    }
+	  }
+
+	  /**
+	   * Write "buf" to "poolFile". The objects in the queue are written
+	   * using Java's object serialization facilities.
+	   */
+	  public void run() {
+	    try {
+	      synchronized(this) {
+		while (true) {
+		  while (this.poolFile == null) {
+		    this.wait();
+		    // we are done without ever receiving a pool file
+		    if(this.poolFile == null) {
+		    	return;
+		    }
+		  }
+		  final BufferedDataOutputStream vos = new BufferedDataOutputStream(this.poolFile);
+		  for (int i = 0; i < this.buf.length; i++) {
+			  vos.writeInt(this.buf[i].length);
+			  vos.write(this.buf[i]);
+		  }
+		  vos.close();
+		  this.poolFile = null;
+		  this.notify();
+		  if (this.reader != null) this.reader.wakeup();
+		}
+	      }
+	    }
+	    catch (Exception e) {
+	      // Assert.printStack(e);
+	        MP.printError(EC.SYSTEM_ERROR_WRITING_POOL, e.getMessage(), e);
+	      System.exit(1);
+	    }
+	  }
+	}
+	
+	private static final class ByteArrayPoolReader extends Thread {
+
+		  public ByteArrayPoolReader(int bufSize, File file) {
+			  super("RawTLCStatePoolReader");
+		    this.buf = new byte[bufSize][];
+		    this.poolFile = file;
+		    this.isFull = false;
+		    this.canRead = false;
+		  }
+		  
+		  private byte[][] buf;
+		  private File poolFile;      // the file to be read
+		  private boolean isFull;     // true iff the buf is filled
+		  private boolean canRead;    // true iff the file can be read
+		  private boolean finished = false;
+
+		  public final synchronized void wakeup() {
+		    this.canRead = true;
+		    this.notify();
+		  }
+
+		  public final synchronized void restart(File file, boolean canRead) {
+		    this.poolFile = file;
+		    this.isFull = false;
+		    this.canRead = canRead;
+		    this.notify();
+		  }
+		  
+		  /*
+		   * In the most common case, this method expects to see the buffer is
+		   * full, it returns its buffer and notifies this reader to read the
+		   * content of the file.
+		   */
+		  public final synchronized byte[][] doWork(byte[][] deqBuf, File file)
+		  throws IOException, ClassNotFoundException {
+		    if (this.isFull) {
+		      assert this.poolFile == null : EC.SYSTEM_FILE_NULL;
+		      byte[][] res = this.buf;
+		      this.buf = deqBuf;
+		      this.poolFile = file;
+		      this.isFull = false;      // <file, false>
+		      this.canRead = true;
+		      this.notify();
+		      return res;
+		    }
+		    else if (this.poolFile != null) {
+		  	  final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile);
+		  	  for (int i = 0; i < deqBuf.length; i++) {
+		      	  deqBuf[i] = new byte[vis.readInt()];
+		      	  vis.read(deqBuf[i]);
+		  	  }
+		      vis.close();
+		      this.poolFile = file;     // <file, false>
+		      this.canRead = true;
+		      this.notify();
+		      return deqBuf;
+		    }
+		    else {
+		  	  final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile);
+		  	  for (int i = 0; i < deqBuf.length; i++) {
+		  		  deqBuf[i] = new byte[vis.readInt()];
+		      	  vis.read(deqBuf[i]);
+		  	  }
+		      vis.close();              // <null, false>
+		      return deqBuf;
+		    }
+		  }
+
+		  /*
+		   * Returns the cached buffer if filled. Otherwise, returns null.
+		   */
+		  public final synchronized byte[][] getCache(byte[][] deqBuf, File file)
+		  throws IOException, ClassNotFoundException {
+		    if (this.isFull) {
+		      assert this.poolFile == null : EC.SYSTEM_FILE_NULL;
+		      byte[][] res = this.buf;
+		      this.buf = deqBuf;
+		      this.poolFile = file;
+		      this.isFull = false;      // <file, false>
+		      this.canRead = false;
+		      return res;
+		    }
+		    else if (this.poolFile != null && this.canRead) {
+		      // this should seldom occur.
+		  	  final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile);
+		  	  for (int i = 0; i < deqBuf.length; i++) {
+		  		  deqBuf[i] = new byte[vis.readInt()];
+		      	  vis.read(deqBuf[i]);
+		      }
+		      vis.close();
+		      // this.poolFile.delete();
+		      this.poolFile = file;    // <file, false>
+		      this.canRead = false;
+		      return deqBuf;
+		    }
+		    return null;
+		  }
+
+		  public final synchronized void beginChkpt(ObjectOutputStream oos)
+		  throws IOException {
+		    boolean hasFile = this.poolFile != null;
+		    oos.writeBoolean(hasFile);
+		    oos.writeBoolean(this.canRead);
+		    oos.writeBoolean(this.isFull);
+		    if (hasFile) {
+		      oos.writeObject(this.poolFile);
+		    }
+		    if (this.isFull) {
+		      for (int i = 0; i < this.buf.length; i++) {
+			oos.writeObject(this.buf[i]);
+		      }
+		    }
+		  }
+
+		  /* Note that this method is not synchronized. */
+		  public final void recover(ObjectInputStream ois) throws IOException {
+		    boolean hasFile = ois.readBoolean();
+		    this.canRead = ois.readBoolean();
+		    this.isFull = ois.readBoolean();
+		    try {
+		      if (hasFile) {
+			this.poolFile = (File)ois.readObject();
+		      }
+		      if (this.isFull) {
+			for (int i = 0; i < this.buf.length; i++) {
+			  this.buf[i] = (byte[])ois.readObject();
+			}
+		      }
+		    }
+		    catch (ClassNotFoundException e) 
+		    {
+		      Assert.fail(EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, e);
+		    }
+		  }
+		  
+		  /**
+		   * Read the contents of "poolFile" into "buf". The objects in the
+		   * file are read using Java's object serialization facilities.
+		   */
+		  public void run() {
+		    try {
+		      synchronized(this) {
+			while (true) {
+			  while (this.poolFile == null || this.isFull || !this.canRead) {
+			    this.wait();
+			    if(this.finished ) {
+			    	return;
+			    }
+			  }
+			  final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile);
+			  for (int i = 0; i < this.buf.length; i++) {
+		    	  this.buf[i] = new byte[vis.readInt()];
+		    	  vis.read(this.buf[i]);
+			  }
+			  vis.close();
+			  this.poolFile = null;
+			  this.isFull = true;       // <null, true>
+			}
+		      }
+		    }
+		    catch (Exception e) 
+		    {
+		      // Assert.printStack(e);
+		      MP.printError(EC.SYSTEM_ERROR_READING_POOL, e.getMessage(), e);
+		      System.exit(1);
+		    }
+		  }
+		  
+		  public void setFinished() {
+			  finished = true;
+		  }
+	}
+	
+	static final class ByteValueOutputStream implements IValueOutputStream, IDataOutputStream {
+
+		private byte[] bytes;
+		
+		private int idx;
+		
+		public ByteValueOutputStream() {
+			this.bytes = new byte[16]; // TLCState "header" already has 6 bytes.
+			this.idx = 0;
+		}
+		
+	    private void ensureCapacity(int minCap) {
+	        if (minCap - bytes.length > 0) {
+	            int oldCap = bytes.length;
+	            int newCap = oldCap << 1; // double size
+	            if (newCap - minCap < 0) {
+	            	newCap = minCap;
+	            }
+	            bytes = Arrays.copyOf(bytes, newCap);
+	        }
+	    }
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeShort(short)
+		 */
+		@Override
+		public final void writeShort(short s) throws IOException {
+			ensureCapacity(idx + 2);
+	        this.bytes[idx++] = (byte) ((s >>> 8) & 0xff);
+	        this.bytes[idx++] = (byte) (s & 0xff);
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeInt(int)
+		 */
+		@Override
+		public final void writeInt(int i) throws IOException {
+			ensureCapacity(idx + 4);
+	        this.bytes[idx++] = (byte) ((i >>> 24) & 0xff);
+	        this.bytes[idx++] = (byte) ((i >>> 16) & 0xff);
+	        this.bytes[idx++] = (byte) ((i >>> 8) & 0xff);
+	        this.bytes[idx++] = (byte) (i & 0xff);
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeLong(long)
+		 */
+		@Override
+		public final void writeLong(long l) throws IOException {
+			ensureCapacity(idx + 8);
+	        this.bytes[idx++] = (byte) ((l >>> 56) & 0xff);
+	        this.bytes[idx++] = (byte) ((l >>> 48) & 0xff);
+	        this.bytes[idx++] = (byte) ((l >>> 40) & 0xff);
+	        this.bytes[idx++] = (byte) ((l >>> 32) & 0xff);
+	        this.bytes[idx++] = (byte) ((l >>> 24) & 0xff);
+	        this.bytes[idx++] = (byte) ((l >>> 16) & 0xff);
+	        this.bytes[idx++] = (byte) ((l >>> 8) & 0xff);
+	        this.bytes[idx++] = (byte) (l & 0xff);
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#close()
+		 */
+		@Override
+		public final void close() throws IOException {
+			// No-op
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeShortNat(short)
+		 */
+		@Override
+		public final void writeShortNat(short x) throws IOException {
+			if (x > 0x7f) {
+				this.writeShort((short) -x);
+			} else {
+				this.writeByte((byte) x);
+			}
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeNat(int)
+		 */
+		@Override
+		public final void writeNat(int x) throws IOException {
+			if (x > 0x7fff) {
+				this.writeInt(-x);
+			} else {
+				this.writeShort((short) x);
+			}
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeLongNat(long)
+		 */
+		@Override
+		public final void writeLongNat(long x) throws IOException {
+			if (x <= 0x7fffffff) {
+				this.writeInt((int) x);
+			} else {
+				this.writeLong(-x);
+			}
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeByte(byte)
+		 */
+		@Override
+		public final void writeByte(byte b) throws IOException {
+			ensureCapacity(idx + 1);
+			this.bytes[idx++] = b;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#writeBoolean(boolean)
+		 */
+		@Override
+		public final void writeBoolean(boolean bool) throws IOException {
+	        byte b = (bool ? (byte)1 : (byte)0);
+	        this.writeByte(b);
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#getOutputStream()
+		 */
+		@Override
+		public final IDataOutputStream getOutputStream() {
+			return this;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueOutputStream#put(java.lang.Object)
+		 */
+		@Override
+		public final int put(Object obj) {
+			return -1;
+		}
+
+		public final byte[] toByteArray() {
+			final byte[] copyOf = Arrays.copyOf(bytes, idx);
+			idx = 0;
+			return copyOf;
+		}
+
+		/* (non-Javadoc)
+		 * @see util.IDataOutputStream#writeString(java.lang.String)
+		 */
+		@Override
+		public final void writeString(String str) throws IOException {
+			final int length = str.length();
+			ensureCapacity(idx + length);
+			
+			final char[] c = new char[length];
+			str.getChars(0, length, c, 0);
+			
+			for (int i = 0; i < c.length; i++) {
+				this.bytes[idx++] = (byte) c[i];
+			}
+		}
+	}
+	
+	static final class ByteValueInputStream implements ValueConstants, IValueInputStream, IDataInputStream {
+
+		private final byte[] bytes;
+		
+		private int idx = 0;
+
+		public ByteValueInputStream(byte[] bytes) {
+			this.bytes = bytes;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#read()
+		 */
+		@Override
+		public final IValue read() throws IOException {
+			final byte kind = this.readByte();
+
+			switch (kind) {
+			case BOOLVALUE: {
+				return (this.readBoolean()) ? BoolValue.ValTrue : BoolValue.ValFalse;
+			}
+			case INTVALUE: {
+				return IntValue.gen(this.readInt());
+			}
+			case STRINGVALUE: {
+				return StringValue.createFrom(this);
+			}
+			case MODELVALUE: {
+				return ModelValue.mvs[this.readShort()];
+			}
+			case INTERVALVALUE: {
+				return new IntervalValue(this.readInt(), this.readInt());
+			}
+			case RECORDVALUE: {
+				return RecordValue.createFrom(this);
+			}
+			case FCNRCDVALUE: {
+				return FcnRcdValue.createFrom(this);
+			}
+			case SETENUMVALUE: {
+				return SetEnumValue.createFrom(this);
+			}
+			case TUPLEVALUE: {
+				return TupleValue.createFrom(this);
+			}
+			default: {
+				throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind);
+			}
+			}
+		}
+
+		private final boolean readBoolean() throws EOFException, IOException {
+	        return (bytes[idx++] != 0);
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readShort()
+		 */
+		@Override
+		public final int readShort() throws IOException {
+	        return (short) ((bytes[idx++] << 8) | (bytes[idx++] & 0xff));
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readInt()
+		 */
+		@Override
+		public final int readInt() throws IOException {
+	        int res = bytes[idx++];
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        return res;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readLong()
+		 */
+		@Override
+		public final long readLong() throws IOException {
+	        long res = bytes[idx++];
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        res <<= 8; res |= (bytes[idx++] & 0xff);
+	        return res;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#close()
+		 */
+		@Override
+		public final void close() throws IOException {
+			// No-op
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readNat()
+		 */
+		@Override
+		public final int readNat() throws IOException {
+		    int res = this.readShort();
+		    if (res >= 0) return res;
+		    res = (res << 16) | (this.readShort() & 0xFFFF);
+		    return -res;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readShortNat()
+		 */
+		@Override
+		public final short readShortNat() throws IOException {
+			short res = this.readByte();
+			if (res >= 0) return res;
+			return (short) -((res << 8) | (this.readByte() & 0xFF));
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readLongNat()
+		 */
+		@Override
+		public final long readLongNat() throws IOException {
+		    long res = this.readInt();
+		    if (res >= 0) return res;
+		    res = (res << 32) | ((long)this.readInt() & 0xFFFFFFFFL);
+		    return -res;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#readByte()
+		 */
+		@Override
+		public final byte readByte() throws EOFException, IOException {
+			return bytes[idx++];
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#assign(java.lang.Object, int)
+		 */
+		@Override
+		public final void assign(Object obj, int idx) {
+			// No-op
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#getIndex()
+		 */
+		@Override
+		public final int getIndex() {
+			return -1;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#getInputStream()
+		 */
+		@Override
+		public final IDataInputStream getInputStream() {
+			return this;
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.value.IValueInputStream#getValue(int)
+		 */
+		@Override
+		public final UniqueString getValue(int idx) {
+			throw new WrongInvocationException("Not supported");
+		}
+
+		/* (non-Javadoc)
+		 * @see util.IDataInputStream#readString(int)
+		 */
+		@Override
+		public final String readString(int length) throws IOException {
+			final char[] s = new char[length];
+			for (int i = 0; i < s.length; i++) {
+				s[i] = (char) this.bytes[idx++];
+			}
+			return new String(s);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#delete()
+	 */
+	@Override
+	public void delete() {
+		finishAll();
+		new File(this.filePrefix).delete();
+	}
+}
diff --git a/tlatools/src/tlc2/tool/queue/DiskStateQueue.java b/tlatools/src/tlc2/tool/queue/DiskStateQueue.java
index 14b4e6d17099d31559f125bbd43852e49fb735fb..4acf373684100378ffc4d2fa3208ab5dccdeeccb 100644
--- a/tlatools/src/tlc2/tool/queue/DiskStateQueue.java
+++ b/tlatools/src/tlc2/tool/queue/DiskStateQueue.java
@@ -7,8 +7,10 @@ package tlc2.tool.queue;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
 
 import tlc2.output.EC;
+import tlc2.output.MP;
 import tlc2.tool.TLCState;
 import tlc2.util.StatePoolReader;
 import tlc2.util.StatePoolWriter;
@@ -23,7 +25,7 @@ import util.FileUtil;
  */
 public class DiskStateQueue extends StateQueue {
 	// TODO dynamic bufsize based on current VM parameters?
-	private final static int BufSize = Integer.getInteger(DiskStateQueue.class.getName() + ".BufSize", 8192);;
+	private final static int BufSize = Integer.getInteger(DiskStateQueue.class.getName() + ".BufSize", 8192);
 
 	/*
 	 * Invariants: I1. Entries in deqBuf are in the indices: [deqIndex,
@@ -40,9 +42,21 @@ public class DiskStateQueue extends StateQueue {
 	protected int deqIndex, enqIndex;
 	protected StatePoolReader reader;
 	protected StatePoolWriter writer;
+	/**
+	 * The SPC takes care of deleting swap files on the lower end of the range
+	 * (loPool, hiPool). It terminates, when the first checkpoint is written at
+	 * which point checkpointing itself takes care of removing obsolete swap
+	 * files.
+	 */
+	protected final StatePoolCleaner cleaner;
 	private int loPool, hiPool, lastLoPool, newLastLoPool;
 	private File loFile;
 
+	// TESTING ONLY!
+	DiskStateQueue() throws IOException {
+		this(Files.createTempDirectory("DiskStateQueue").toFile().toString());
+	}
+
 	/* Constructors */
 	public DiskStateQueue(String diskdir) {
 		this.deqBuf = new TLCState[BufSize];
@@ -61,6 +75,9 @@ public class DiskStateQueue extends StateQueue {
 		this.writer = new StatePoolWriter(BufSize, this.reader);
 		this.writer.setDaemon(true);
 		this.writer.start();
+		this.cleaner = new StatePoolCleaner();
+		this.cleaner.setDaemon(true);
+		this.cleaner.start();
 	}
 
 	final void enqueueInner(TLCState state) {
@@ -86,6 +103,16 @@ public class DiskStateQueue extends StateQueue {
 		}
 		return this.deqBuf[this.deqIndex++];
 	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.StateQueue#peekInner()
+	 */
+	TLCState peekInner() {
+		if (this.deqIndex == this.deqBuf.length) {
+			this.fillDeqBuffer();
+		}
+		return this.deqBuf[this.deqIndex];
+	}
 
 	private final void fillDeqBuffer() {
 		try {
@@ -117,6 +144,14 @@ public class DiskStateQueue extends StateQueue {
 					this.enqIndex = 0;
 				}
 			}
+			// Notify the cleaner to do its job unless its waits for more work
+			// to pile up.
+			if ((loPool - lastLoPool) > 100) { //TODO Take BufSize into account. It defines the disc file size.
+				synchronized (this.cleaner) {
+					this.cleaner.deleteUpTo = loPool - 1;
+					this.cleaner.notifyAll();
+				}
+			}
 		} catch (Exception e) {
 			Assert.fail(EC.SYSTEM_ERROR_READING_STATES, new String[] { "queue",
 					(e.getMessage() == null) ? e.toString() : e.getMessage() });
@@ -125,6 +160,14 @@ public class DiskStateQueue extends StateQueue {
 
 	/* Checkpoint. */
 	public final void beginChkpt() throws IOException {
+		synchronized (this.cleaner) {
+			// Checkpointing takes precedence over periodic cleaning
+			// (cleaner would otherwise delete checkpoint files as it know
+			// nothing of checkpoints).
+			this.cleaner.finished = true;
+			this.cleaner.notifyAll();
+		}
+		
 		String filename = this.filePrefix + "queue.tmp";
 		ValueOutputStream vos = new ValueOutputStream(filename);
 		vos.writeLongNat(this.len);
@@ -195,6 +238,61 @@ public class DiskStateQueue extends StateQueue {
 			this.reader.setFinished();
 			this.reader.notifyAll();
 		}
+		synchronized (this.cleaner) {
+			this.cleaner.finished = true;
+			this.cleaner.notifyAll();
+		}
 	}
 
+	private class StatePoolCleaner extends Thread {
+
+		private volatile boolean finished = false;
+		public int deleteUpTo;
+		
+		private StatePoolCleaner() {
+			super("TLCStatePoolCleaner");
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Thread#run()
+		 */
+		public void run() {
+			try {
+				synchronized (this) {
+					while (!this.finished) {
+						this.wait();
+						if (this.finished) {
+							return;
+						}
+						
+						for (int i = lastLoPool; i < deleteUpTo; i++) {
+							final File oldPoolFile = new File(filePrefix + Integer.toString(i));
+							if (!oldPoolFile.delete()) {
+								// No reason to terminate/kill TLC when the cleanup fails.
+								// Contrary to StatePoolReader/Write, cleanup is optional
+								// functionality whose purpose is to prevent the disc from
+								// filling up. If the cleaner fails, the user can still
+								// manually delete the files.
+								MP.printWarning(EC.SYSTEM_ERROR_CLEANING_POOL, oldPoolFile.getCanonicalPath());
+							}
+						}
+						lastLoPool = deleteUpTo;
+					}
+				}
+			} catch (Exception e) {
+				// Assert.printStack(e);
+				MP.printError(EC.SYSTEM_ERROR_CLEANING_POOL, e.getMessage(), e);
+				System.exit(1);
+			}
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.queue.IStateQueue#delete()
+	 */
+	@Override
+	public void delete() {
+		finishAll();
+		new File(this.filePrefix).delete();
+	}
 }
diff --git a/tlatools/src/tlc2/tool/queue/IStateQueue.java b/tlatools/src/tlc2/tool/queue/IStateQueue.java
index 88f979698e43c0031c538270a088f7bdc3fcd564..e2ed3d6631ba35136454cac1a5b6cdf3a0fc8742 100644
--- a/tlatools/src/tlc2/tool/queue/IStateQueue.java
+++ b/tlatools/src/tlc2/tool/queue/IStateQueue.java
@@ -2,6 +2,7 @@ package tlc2.tool.queue;
 
 import java.io.IOException;
 
+import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
 import tlc2.tool.Worker;
 
@@ -21,9 +22,16 @@ public interface IStateQueue {
 
 	/* Enqueues a list of states. Wake up any waiting thread. */
 	public abstract void sEnqueue(final TLCState states[]);
-
+	public abstract void sEnqueue(final StateVec stateVec);
+	
 	/* Return the first element in the queue. Wait if empty. */
 	public abstract TLCState sDequeue();
+	
+	/**
+	 * Returns the first element in the queue. Wait if empty. Does not remove the
+	 * element. Can be null and blocks other consumers (sEnqueue and sDequeue).
+	 */
+	public abstract TLCState sPeek();
 
 	/**
 	 * Return (up to) the first count elements in the queue. Wait if empty.
@@ -62,7 +70,7 @@ public interface IStateQueue {
 	 * free when workers behave correctly except for the single case when a
 	 * remote worker dies unexpectedly.
 	 * 
-	 * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=175
+	 * @see Bug #175 in general/bugzilla/index.html
 	 */
 	public abstract void resumeAllStuck();
 
@@ -77,4 +85,10 @@ public interface IStateQueue {
 	public abstract void recover() throws IOException;
 
 	public abstract boolean isEmpty();
+
+	/**
+	 * TESTING ONLY!
+	 * Delete disk files if any.
+	 */
+	abstract void delete() throws IOException;
 }
\ No newline at end of file
diff --git a/tlatools/src/tlc2/tool/queue/MemStateQueue.java b/tlatools/src/tlc2/tool/queue/MemStateQueue.java
index 505fca96e2a56f36e7bde0cb0ab63d8d0fa03e21..b5b36c9b37e78dad1c0b590d22fbe7c6d4b20fed 100644
--- a/tlatools/src/tlc2/tool/queue/MemStateQueue.java
+++ b/tlatools/src/tlc2/tool/queue/MemStateQueue.java
@@ -7,6 +7,7 @@ package tlc2.tool.queue;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
 
 import tlc2.output.EC;
 import tlc2.tool.TLCState;
@@ -23,6 +24,13 @@ public final class MemStateQueue extends StateQueue {
   private int start = 0;
   private String diskdir;
     
+  /**
+   * TESTING ONLY!
+   */
+  MemStateQueue() throws IOException {
+	  this(Files.createTempDirectory("MemStateQueue").toFile().toString());
+  }
+  
   public MemStateQueue(String metadir) {
     this.states = new TLCState[InitialSize];
     this.start = 0;
@@ -31,7 +39,7 @@ public final class MemStateQueue extends StateQueue {
     
   final void enqueueInner(TLCState state) {
 	if (this.len > Integer.MAX_VALUE) {
-        Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, "Amount of states exceeds internal storage");
+        Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[]{"queue", "Amount of states exceeds internal storage"});
 	}
     if (this.len == this.states.length) {
       // grow the array
@@ -60,6 +68,13 @@ public final class MemStateQueue extends StateQueue {
     this.start = (this.start + 1) % this.states.length;
     return res;
   }
+  
+  /* (non-Javadoc)
+   * @see tlc2.tool.queue.StateQueue#peekInner()
+   */
+  final TLCState peekInner() {
+	return this.states[this.start];
+  }
 
   // Checkpoint.
   public final void beginChkpt() throws IOException {
@@ -95,5 +110,4 @@ public final class MemStateQueue extends StateQueue {
     }
     vis.close();
   }
-
 }
diff --git a/tlatools/src/tlc2/tool/queue/StateQueue.java b/tlatools/src/tlc2/tool/queue/StateQueue.java
index 0d151ecadd933e9a1733edabc6912a773a1762fe..e37d9287c8da8d00f2d284c4e1faac9ec7a8fe56 100644
--- a/tlatools/src/tlc2/tool/queue/StateQueue.java
+++ b/tlatools/src/tlc2/tool/queue/StateQueue.java
@@ -10,9 +10,9 @@ import java.io.IOException;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
 import tlc2.tool.Worker;
-import util.Assert;
 
 /**
  * 
@@ -84,6 +84,29 @@ public abstract class StateQueue implements IStateQueue {
 			this.notifyAll();
 		}
 	}
+	
+	public final synchronized void sEnqueue(final StateVec stateVec) {
+		int cnt = 0;
+		for (int j = 0; j < stateVec.size(); j++) {
+			TLCState state = stateVec.elementAt(j);
+			if (state != null) {
+				this.enqueueInner(state);
+				cnt++;
+			}
+		}
+		this.len += cnt;
+		if (this.numWaiting > 0 && !this.stop) {
+			this.notifyAll();
+		}
+	}
+
+
+	public final synchronized TLCState sPeek() {
+		if (this.isAvail()) {
+			return this.peekInner();
+		}
+		return null;
+	}
 
 	/* Return the first element in the queue. Wait if empty. */
 	/* (non-Javadoc)
@@ -93,7 +116,7 @@ public abstract class StateQueue implements IStateQueue {
 		if (this.isAvail()) {
 			final TLCState state = this.dequeueInner();
 			// LL modified error message on 7 April 2012
-			Assert.check(state != null, "Null state found on queue");
+			assert state != null : "Null state found on queue";
 			this.len--;
 			return state;
 		}
@@ -104,7 +127,7 @@ public abstract class StateQueue implements IStateQueue {
 	 * @see tlc2.tool.queue.IStateQueue#sDequeue(int)
 	 */
 	public final synchronized TLCState[] sDequeue(int cnt) {
-		Assert.check(cnt > 0, "Nonpositive number of states requested.");
+		assert cnt > 0 : "Nonpositive number of states requested.";
 		if (this.isAvail()) {
 			if (cnt > len) {
 				// in this case, casting len to int is safe 
@@ -327,6 +350,9 @@ public abstract class StateQueue implements IStateQueue {
 	/* This method must be implemented in the subclass. */
 	abstract TLCState dequeueInner();
 
+	/* This method must be implemented in the subclass. */
+	abstract TLCState peekInner();
+	
 	/* Checkpoint. */
 	/* (non-Javadoc)
 	 * @see tlc2.tool.queue.IStateQueue#beginChkpt()
@@ -342,4 +368,9 @@ public abstract class StateQueue implements IStateQueue {
 	 * @see tlc2.tool.queue.IStateQueue#recover()
 	 */
 	public abstract void recover() throws IOException;
+	
+	@Override
+	public void delete() throws IOException {
+		// no-op
+	}
 }
diff --git a/tlatools/src/tlc2/util/BitVector.java b/tlatools/src/tlc2/util/BitVector.java
index 2f03bc2e0b4d76b7cab91770baf90a637d77c51c..d4230a8494259dbc8f3ce2e3bbd0f2bfa75b07d0 100644
--- a/tlatools/src/tlc2/util/BitVector.java
+++ b/tlatools/src/tlc2/util/BitVector.java
@@ -128,6 +128,40 @@ public class BitVector implements Serializable {
     }
   }
   
+  	/**
+	 * Returns this {@link BitVector} is a bit string with the MSB to the left
+	 * and the LSB to the right.
+	 * 
+	 * @see java.lang.Object#toString()
+	 */
+  public String toString() {
+	  final StringBuffer buf = new StringBuffer(this.word.length * 64);
+	  for (int i = this.word.length * 64; i >= 0; i--) {
+		  if (get(i)) {
+			  buf.append("1");
+		  } else {
+			  buf.append("0");
+		  }
+	  }
+	  buf.append("]");
+	  return buf.toString().replaceAll("^0*","["); // Replace leading zeros with "["
+  }
+  public String toString(int start, int length) {
+	  return toString(start, length, '1', '0');
+  }
+  
+  public String toString(int start, int length, char one, char zero) {
+	  final StringBuffer buf = new StringBuffer(length);
+	  for (int i = 0; i < length; i++) {
+		  if (get(start + i)) {
+			  buf.append(one);
+		  } else {
+			  buf.append(zero);
+		  }
+	  }
+	  return "[" + buf.reverse().toString() + "]";
+  }
+  
 	/**
 	 * @return The number of bits set true
 	 */
@@ -250,5 +284,4 @@ public class BitVector implements Serializable {
       return -1;
     }
   }
-
 }
diff --git a/tlatools/src/tlc2/util/BufferedRandomAccessFile.java b/tlatools/src/tlc2/util/BufferedRandomAccessFile.java
index f9ad6dcba94de5c04f906c81cf07221ba89e1c4e..eaa76c9b50a6fadb0b5003fab0c4414380596144 100644
--- a/tlatools/src/tlc2/util/BufferedRandomAccessFile.java
+++ b/tlatools/src/tlc2/util/BufferedRandomAccessFile.java
@@ -37,8 +37,9 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
     private long maxHi;     // this.lo + this.buff.length
     private boolean hitEOF; // buffer contains last file block?
     private long diskPos;   // disk position
+	private long mark;
     
-    private static Object mu = new Object(); // protects the following fields
+    private static final Object mu = new Object(); // protects the following fields
     private static byte[][] availBuffs = new byte[100][];
     private static int numAvailBuffs = 0;
 
@@ -175,7 +176,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
     }
     
     /* Flush any dirty bytes in the buffer to disk. */
-    private void flushBuffer() throws IOException {
+    private boolean flushBuffer() throws IOException {
         if (this.dirty) {
             // Assert.check(this.curr > this.lo);
             if (this.diskPos != this.lo) super.seek(this.lo);
@@ -183,7 +184,9 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
             super.write(this.buff, 0, len); 
             this.diskPos = this.curr;
             this.dirty = false;
+            return true;
         }
+        return false;
     }
     
     /* Read at most "this.buff.length" bytes into "this.buff",
@@ -248,7 +251,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
 			// seeking inside current buffer -- no read required
 			if (pos < this.curr) {
 				// if seeking backwards, we must flush to maintain V4
-				this.flushBuffer();
+				pageReadNeeded = this.flushBuffer();
 			} else {
 				pageReadNeeded = false;
 			}
@@ -282,9 +285,13 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
             if (this.curr == this.hi) return -1;
         }
         // Assert.check(this.curr < this.hi);
-        byte res = this.buff[(int)(this.curr - this.lo)];
-        this.curr++;
-        return ((int)res) & 0xFF; // convert byte -> int
+        try {
+        	byte res = this.buff[(int)(this.curr - this.lo)];
+        	this.curr++;
+        	return ((int)res) & 0xFF; // convert byte -> int
+        } catch (ArrayIndexOutOfBoundsException e) {
+        	throw new IOException("Read past end of file (increase with setLength)?", e);
+        }
     }
     
     /* overrides RandomAccessFile.read(byte[]) */
@@ -317,6 +324,13 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
       // Assert.check(this.read(b) == size);
       return new BigInteger(b);
     }
+    
+    public final int readShortNat() throws IOException {
+        int res = this.readByte();
+        if (res >= 0) return res;
+        res = (res << 16) | (this.readByte() & 0xff);
+        return -res;
+    }
 
   public final int readNat() throws IOException {
     int res = this.readShort();
@@ -351,9 +365,13 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
             }
         }
         // Assert.check(this.curr < this.hi);
-        this.buff[(int)(this.curr - this.lo)] = (byte)b;
-        this.curr++;
-        this.dirty = true;
+        try {
+        	this.buff[(int)(this.curr - this.lo)] = (byte)b;
+        	this.curr++;
+        	this.dirty = true;
+        } catch (ArrayIndexOutOfBoundsException e) {
+        	throw new IOException("Wrote past end of file (increase with setLength)?", e);
+        }
     }
     
     /* overrides RandomAccessFile.write(byte[]) */
@@ -377,6 +395,16 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
       // Assert.check(b.length <= size);
       this.write(b, 0, size);
     }
+    
+    /* Precondition: x is a non-negative short. */
+    public final void writeShortNat(int x) throws IOException {
+      if (x <= 0x7f) {
+        this.writeByte((short)x);
+      }
+      else {
+        this.writeShort(-x);
+      }
+    }
 
   /* Precondition: x is a non-negative int. */
   public final void writeNat(int x) throws IOException {
@@ -434,6 +462,21 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
     	setLength(0);
     	this.init();
     }
+    
+    public long getMark() {
+    	return this.mark;
+    }
+    
+    public long mark() {
+    	final long oldMark = this.mark; 
+    	this.mark = getFilePointer();
+    	return oldMark;
+    }
+    
+    public void seekAndMark(long pos) throws IOException {
+    	this.mark = pos;
+    	this.seek(pos);
+    }
 
   public static void main(String[] args) throws IOException {
     String name = "xxx";
@@ -449,6 +492,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile {
     System.err.println("len = " + braf.length() + ", pos = " + braf.getFilePointer());
     braf.writeLong(x); braf.writeLong(x); braf.writeLong(x);
     System.err.println("len = " + braf.length() + ", pos = " + braf.getFilePointer());
+    braf.close();
   }
   
 }
diff --git a/tlatools/src/tlc2/util/ByteUtils.java b/tlatools/src/tlc2/util/ByteUtils.java
index c7f3195b462086e50bb03eefd189cfcfbefcdba0..0eaa6b2248c498d8e5d61b47a15424f0be8da1a9 100644
--- a/tlatools/src/tlc2/util/ByteUtils.java
+++ b/tlatools/src/tlc2/util/ByteUtils.java
@@ -68,14 +68,14 @@ public class ByteUtils {
    * The Java Language Specification.
    */
   public static long byteArrayToLong(byte[] b) {
-    long i0 = (b[0] & 0xFF) << 56;
-    long i1 = (b[1] & 0xFF) << 48;
-    long i2 = (b[2] & 0xFF) << 40;
-    long i3 = (b[3] & 0xFF) << 32;
-    long i4 = (b[4] & 0xFF) << 24;
-    long i5 = (b[5] & 0xFF) << 16;
-    long i6 = (b[6] & 0xFF) << 8;
-    long i7 = (b[7] & 0xFF);
+    long i0 = (long) (b[0] & 0xFF) << 56;
+    long i1 = (long) (b[1] & 0xFF) << 48;
+    long i2 = (long) (b[2] & 0xFF) << 40;
+    long i3 = (long) (b[3] & 0xFF) << 32;
+    long i4 = (long) (b[4] & 0xFF) << 24;
+    long i5 = (long) (b[5] & 0xFF) << 16;
+    long i6 = (long) (b[6] & 0xFF) << 8;
+    long i7 = (long) (b[7] & 0xFF);
     return (i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7);
   }
 
diff --git a/tlatools/src/tlc2/util/Combinatorics.java b/tlatools/src/tlc2/util/Combinatorics.java
index 3b55d8e16faffb1e8925e2042f30691ea27c2fb4..e8f24057a5148c4567d7375871a7a8fbe1f95c25 100644
--- a/tlatools/src/tlc2/util/Combinatorics.java
+++ b/tlatools/src/tlc2/util/Combinatorics.java
@@ -10,43 +10,43 @@ import util.Assert;
 public class Combinatorics {
 
   public static final int MAXCHOOSENUM = 62;
-  private static final int CHOOSETABLESIZE = 
-                           (MAXCHOOSENUM-3)*(MAXCHOOSENUM-4)/2+MAXCHOOSENUM-3;
-  private static long[] CHOOSETABLE = new long[CHOOSETABLESIZE];
+	public static final int CHOOSETABLESIZE = (MAXCHOOSENUM - 3) * (MAXCHOOSENUM - 4) / 2 + MAXCHOOSENUM - 3;
+	public static long[] CHOOSETABLE = new long[CHOOSETABLESIZE];
   private static long[] SUMCHOOSETABLE = new long[CHOOSETABLESIZE];
 
   public static long choose(int n, int m) {
+		if (n < 0 || m < 0) {
     Assert.check(((m >= 0) && (n >= 0) && (n >= m)), EC.TLC_CHOOSE_ARGUMENTS_WRONG, "choose");
-    if (m == 0 || m == n)
+		}
+		if (m == 0 || m == n) {
       return (long)1;
-    else if (m == 1 || m == n-1)
+		} else if (m == 1 || m == n - 1) {
       return (long)n;
-    else {
+		} else if (n == 0 || m > n) {
+			// Cannot choose from zero elements or more elements than present.
+			return 0;
+		} else {
       int j = choosePairToInt(n, m);
       if (j < CHOOSETABLESIZE) {
 	return CHOOSETABLE[j];
       }
-      Assert.fail(EC.TLC_CHOOSE_UPPER_BOUND, String.valueOf(MAXCHOOSENUM));
-      return 0;   // make compiler happy
+			return binomial(n, m); // calculate on demand
     }
   }
 
-  public static long sumChoose(int n, int m) 
-  {
+	public static long sumChoose(int n, int m) {
       Assert.check(((m>=0) && (n>=0) && (n>=m)), EC.TLC_CHOOSE_ARGUMENTS_WRONG, "sumChoose");
-      if (m == 0)
+		if (m == 0) {
           return (long)1;
-      else if (m == n)
+		} else if (m == n) {
           return ((long)1 << n);
-      else if (m == 1)
+		} else if (m == 1) {
           return (long)n;
-      else if (m == n-1)
+		} else if (m == n - 1) {
           return ((long)2 << n) - n;
-      else 
-      {
+		} else {
           int j = choosePairToInt(n,m);
-          if (j < CHOOSETABLESIZE) 
-          {
+			if (j < CHOOSETABLESIZE) {
               return SUMCHOOSETABLE[j];
           }
           Assert.fail(EC.TLC_CHOOSE_UPPER_BOUND, String.valueOf(MAXCHOOSENUM));
@@ -55,7 +55,7 @@ public class Combinatorics {
       }
   }
 	     
-  private static int choosePairToInt(int n, int m) {
+	public static int choosePairToInt(int n, int m) {
     return ((n-3)*(n-4))/2 + m -2;
   }
 
@@ -74,8 +74,7 @@ public class Combinatorics {
 	n++;
 	m = 2;
 	sum = 1+n;
-      }
-      else 
+			} else
 	m++;
     }
   }
@@ -122,6 +121,20 @@ public class Combinatorics {
   }
 
   public static BigInteger bigChoose(int n, int m) {
+		if (n < MAXCHOOSENUM && m < MAXCHOOSENUM) {
+			return BigInteger.valueOf(choose(n, m));
+		}
+
+		BigInteger binomial = BigInteger.ONE;
+		for (int i = 1, j = n; i <= m; i++, j--) {
+			final BigInteger bj = BigInteger.valueOf(j);
+			final BigInteger bi = BigInteger.valueOf(i);
+			binomial = binomial.multiply(bj).divide(bi);
+		}
+		return binomial;
+	}
+	
+	public static BigInteger slowBigChoose(int n, int m) {
     BigInteger num = fact(n);
     BigInteger denom = fact(n - m).multiply(fact(m));
 
@@ -135,8 +148,7 @@ public class Combinatorics {
       result = BigInt.BigZero;
       for (int i = 0; i <= m; i++) 
 	result = result.add(bigChoose(n, i));
-    }
-    else {
+		} else {
       result = BigInt.BigOne;
       result = result.shiftLeft(n);
       for (int i = m+1; i <= n; i++) 
@@ -154,6 +166,51 @@ public class Combinatorics {
     return new String(sb);
   }
 
+	// https://blog.plover.com/math/choose.html
+	public static final long binomial(int n, int k) {
+		if (k > n) {
+			return 0;
+		}
+		if (k > n - k) {
+			// Optimize to n choose n - k.
+			k = n - k;
+		}
+
+		long binomial = 1L;
+		for (int i = 1, m = n; i <= k; i++, m--) {
+			binomial = binomial * m / i;
+		}
+		return binomial;
+	}
+
+	public static long[] pascalTableUpTo(final int maxN, final int maxK) {
+		if (maxN > MAXCHOOSENUM) {
+			final long[] ppt = new long[((maxN - MAXCHOOSENUM) * (maxK - 1))];
+			int idx = 0;
+
+			// Initialize first "row" of extension table from existing triangle.
+			int i = MAXCHOOSENUM + 1;
+			for (int j = 2; j <= maxK; j++) {
+				ppt[idx++] = choose(i, j);
+			}
+			// Subsequent rows initialize from previous row.
+			final int k = maxK - 1;
+			for (int j = 1; j < (maxN - MAXCHOOSENUM); j++) {
+				for (int l = 0; l < k; l++) {
+					if (l == 0) {
+						ppt[idx] = i++ + ppt[idx - k];
+					} else {
+						ppt[idx] = ppt[idx - k] + ppt[idx - k - 1];
+					}
+					idx++;
+				}
+			}
+			return ppt;
+		}
+		return new long[0];
+	}
+
+
 // SZ Jul 14, 2009: Dead code. not used.
 //  public static void main(String argv[]) {
 //    int i,j;
diff --git a/tlatools/src/tlc2/util/Context.java b/tlatools/src/tlc2/util/Context.java
index 65a03219eb8417dd838b6f0a1e83b268309f23bf..0b372a5598e2eb108938003f7ccfed17ee7353fc 100644
--- a/tlatools/src/tlc2/util/Context.java
+++ b/tlatools/src/tlc2/util/Context.java
@@ -5,8 +5,26 @@
 
 package tlc2.util;
 
+import java.util.function.Function;
+
 import tla2sany.semantic.SymbolNode;
 
+// Context is used two times:
+// 1) To determine the level boundedness of the expression appearing in the spec
+//    (see Tool#getLevelBounds*). This is done once as part of parsing.
+// 2) To store the variable/name binding at each scope during state exploration.
+//    The hierarchy of levels/scopes is represented as a chain of context instances 
+//    (link-list or associative list). The Empty context is the outer most scope 
+//    (first level).
+// 1) is not relevant performance-wise, but 2) has - provided non-trivial level -
+// a significant performance impact because of excessive lookups. What makes
+// optimizations difficult, is the fact that the context chain is recreated each
+// time a state is generated, as part of the next state relation. An optimization
+// thus has to ensure that it doesn't trade fast - ideally constant - lookups for
+// an equally expensive creation complexity.
+//
+// The contrived spec at the bottom exhibits this problem. Increasing the level,
+// the number of lookups go through the roof.
 public final class Context {
 	/**
 	 * A link list of name and value pairs. When adding <name, value> to the
@@ -18,7 +36,8 @@ public final class Context {
 	private final Context next;
 
 	public final static Context Empty = new Context(null, null, null);
-	public final static Context BaseBranch = new Context(null, null, Empty);
+	
+	private final static Context BaseBranch = new Context(null, null, Empty);
 	
 	private Context(SymbolNode name, Object value, final Context next) {
 		this.name = name;
@@ -26,6 +45,9 @@ public final class Context {
 		this.next = next;
 	}
 
+	// This method is only called within the context of the ENABLED (temporal)
+	// operator. A branching context is specially handled during lookup below
+	// if cutoff is true.
 	public static Context branch(Context base) {
 		if (base == Empty) {
 			// Avoid new instance if the next context in the chain is the Empty
@@ -60,6 +82,28 @@ public final class Context {
 		}
 		return null; // On Empty Context (end of chain), return null value
 	}
+	
+	public final Object lookup(final Function<SymbolNode, Boolean> f) {
+		Context cur = this;
+		while (cur != Empty) {
+			if (f.apply(cur.name)) {
+				return cur.value;
+			}
+			cur = cur.next;
+		}
+		return null;
+	}
+
+	public final SymbolNode lookupName(final Function<SymbolNode, Boolean> f) {
+		Context cur = this;
+		while (cur != Empty) {
+			if (f.apply(cur.name)) {
+				return cur.name;
+			}
+			cur = cur.next;
+		}
+		return null;
+	}
 
 	/**
 	 * @param var
@@ -120,3 +164,43 @@ public final class Context {
 		return sb.toString();
 	}
 }
+/*
+----------------------------- MODULE Scoping -----------------------------
+EXTENDS Naturals
+CONSTANT Limit
+VARIABLE var
+
+A(a) == TRUE
+B(b) == A(b)
+C(c) == B(c)
+D(d) == C(d)
+E(e) == D(e)
+F(f) == E(f)
+G(g) == F(g)
+H(h) == G(h)
+I(i) == H(i)
+J(j) == I(j)
+K(k) == J(k)
+L(l) == K(l)
+M(m) == L(m)
+N(n) == M(n) 
+O(o) == N(o)
+P(p) == O(p)
+Q(q) == P(q)
+R(r) == Q(r) 
+S(s) == R(s)
+T(t) == S(t)
+U(u) == T(u)
+V(v) == U(v)
+W(w) == V(w)
+X(x) == W(x)
+Y(y) == X(y)
+Z(z) == U(z)
+
+Next == /\ var' = var + 1
+        /\ var < 1
+        /\ Z(1)
+
+Spec == var=0 /\ [][Next]_<<var>>
+=============================================================================
+*/
diff --git a/tlatools/src/tlc2/util/DiskIntStack.java b/tlatools/src/tlc2/util/DiskIntStack.java
index 0fdb6874e644e1e3c2e023bf55c3c7905102b362..1d55b7b5d69b296fa24bcd258ef91a3bcf0580e7 100644
--- a/tlatools/src/tlc2/util/DiskIntStack.java
+++ b/tlatools/src/tlc2/util/DiskIntStack.java
@@ -14,12 +14,15 @@ import util.BufferedDataOutputStream;
 import util.FileUtil;
 
 /**
- * Alternative implementation
- * currently not used 
+ * Alternative implementation. Currently not used
+ * 
+ * This implementation is likely to have never worked and should be regarded as
+ * a sketch for a how a concurrent implementation could look like. For the
+ * moment, {@link SynchronousDiskIntStack} does the job just fine. Albeit - as
+ * the name suggests - in a synchronous fashion.
  * 
- * @version $Id$
  */
-public final class DiskIntStack {
+public final class DiskIntStack implements IntStack {
   private final static int BufSize = 16384;
 
   private long size;
@@ -168,4 +171,10 @@ public final class DiskIntStack {
     }
   }
 
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#reset()
+	 */
+	public void reset() {
+		// TODO Auto-generated method stub
+	}
 }
diff --git a/tlatools/src/tlc2/util/DotStateWriter.java b/tlatools/src/tlc2/util/DotStateWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..73d895881a2db4e6c19b06cc447a01f06aa1da18
--- /dev/null
+++ b/tlatools/src/tlc2/util/DotStateWriter.java
@@ -0,0 +1,380 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import tlc2.tool.Action;
+import tlc2.tool.TLCState;
+import util.FileUtil;
+
+/**
+ * Writes the given state in dot notation.
+ * 
+ * @see https://en.wikipedia.org/wiki/DOT_(graph_description_language)
+ * 
+ * 
+ * To ASCII-render a graph (on Debian|Ubuntu) install cpanminus, sudo cpanm Graph::Easy and run:
+ * cat your.dot | graph-easy --from=dot --as_ascii
+ * (https://stackoverflow.com/questions/3211801/graphviz-and-ascii-output)
+ */
+public class DotStateWriter extends StateWriter {
+
+	// The Graphviz color scheme that is used for state transition edge colors. See
+	// https://www.graphviz.org/doc/info/colors.html for more details on color schemes.
+	private static final String dotColorScheme = "paired12";
+
+	// A mapping of action names to their assigned color ids. Since states are fed
+	// into a StateWriter incrementally, one at a time, this table is built up over
+	// time, adding new actions as we find out about them.
+	private final Map<String, Integer> actionToColors = new HashMap<>();
+	
+	// A mapping from ranks to nodes.
+	private final Map<Integer, Set<Long>> rankToNodes = new HashMap<>();
+
+	// Determines whether or not transition edges should be colorized in the state
+	// graph.
+	private final boolean colorize;
+
+	// Determines whether or not transition edges should be labeled with their
+	// action names.
+	private final boolean actionLabels;
+
+	// Used for assigning unique color identifiers to each action type. Incremented
+	// by 1 every time a new color is assigned to an action.
+	private Integer colorGen = 1;
+	
+	// Create a valid fname_snapshot.dot file after a state is written.
+	private final boolean snapshot;
+	
+	public DotStateWriter(final String fname, final String strict) throws IOException {
+		this(fname, strict, false, false, false);
+	}
+	
+	/**
+	 * @param fname
+	 * @param colorize
+	 *            Colorize state transition edges in the DOT state graph.
+	 * @param actionLabels
+	 *            Label transition edges in the state graph with the name of the
+	 *            associated action. Can potentially add a large amount of visual
+	 *            clutter for large graphs with many actions.
+	 * @throws IOException
+	 */
+	public DotStateWriter(final String fname, final boolean colorize, final boolean actionLabels,
+			final boolean snapshot) throws IOException {
+		this(fname, "strict ", colorize, actionLabels, snapshot);
+	}
+	
+	public DotStateWriter(final String fname, final String strict, final boolean colorize, final boolean actionLabels,
+			final boolean snapshot) throws IOException {
+		super(fname);
+		this.colorize = colorize;
+		this.actionLabels = actionLabels;
+		this.snapshot = snapshot;
+		this.writer.append(strict + "digraph DiskGraph {\n"); // strict removes redundant edges
+		// Turned off LR because top to bottom provides better results with GraphViz viewer.
+//		this.writer.append("rankdir=LR;\n"); // Left to right rather than top to bottom
+        
+		// Set the color scheme for transition edges if necessary.
+		if(colorize) {
+			this.writer.append(String.format("edge [colorscheme=\"%s\"]\n", dotColorScheme));	
+		}
+        
+		// Spread out state nodes a bit more.
+        this.writer.append("nodesep=0.35;\n");
+
+		this.writer.append("subgraph cluster_graph {\n"); 
+        this.writer.append("color=\"white\";\n"); // no border.
+		this.writer.flush();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isDot()
+	 */
+	@Override
+	public boolean isDot() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState)
+	 */
+	public synchronized void writeState(final TLCState state) {
+		// Marker the state as an initial state by using a filled style.
+		this.writer.append(Long.toString(state.fingerPrint()));
+		this.writer.append(" [label=\"");
+		this.writer.append(states2dot(state));
+		this.writer.append("\",style = filled]");
+		this.writer.append("\n");
+		
+		maintainRanks(state);
+		
+		if (snapshot) {
+			try {
+				this.snapshot();
+			} catch (IOException e) {
+				// Let's assume this never happens!
+				e.printStackTrace();
+				throw new RuntimeException(e);
+			}
+		}
+	}
+	
+	protected void maintainRanks(final TLCState state) {
+		rankToNodes.computeIfAbsent(state.getLevel(), k -> new HashSet<Long>()).add(state.fingerPrint());
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean)
+	 */
+	public synchronized void writeState(TLCState state, TLCState successor, boolean successorStateIsNew) {
+		writeState(state, successor, successorStateIsNew, Visualization.DEFAULT);
+	}
+	
+    public synchronized void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew, Action action)
+    {
+		writeState(state, successor, null, 0, 0, successorStateIsNew, Visualization.DEFAULT, action);
+    }
+	
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public synchronized void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visualization) {
+		writeState(state, successor, null, 0, 0, successorStateIsNew, visualization, null);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean)
+	 */
+	public synchronized void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew) {
+		writeState(state, successor, actionChecks, from, length, successorStateIsNew, Visualization.DEFAULT, null);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, java.lang.String, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public synchronized void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew,
+			Visualization visualization, Action action) {
+		final String successorsFP = Long.toString(successor.fingerPrint());
+		
+		// Write the transition edge.
+		this.writer.append(Long.toString(state.fingerPrint()));
+		this.writer.append(" -> ");
+		this.writer.append(successorsFP);
+		if (visualization == Visualization.STUTTERING) {
+			this.writer.append(" [style=\"dashed\"];\n");
+		} else {
+			// Add the transition edge label.
+			if(action!=null) {
+				String transitionLabel = this.dotTransitionLabel(state, successor, action);
+				this.writer.append(transitionLabel);	
+			}
+			
+			this.writer.append(";\n");
+			
+			// If the successor is new, print the state's label. Labels are printed
+			// when writeState sees the successor. It does not print the label for
+			// the current state. If it would print the label for the current state,
+			// the init state labels would be printed twice.
+			if (successorStateIsNew) {
+				// Write the successor's label.
+				this.writer.append(successorsFP);
+				this.writer.append(" [label=\"");
+				this.writer.append(states2dot(successor));
+				this.writer.append("\"]");
+				this.writer.append(";\n");
+			}
+		}
+		
+		maintainRanks(state);
+		
+		if (snapshot) {
+			try {
+				this.snapshot();
+			} catch (IOException e) {
+				// Let's assume this never happens!
+				e.printStackTrace();
+				throw new RuntimeException(e);
+			}
+		}
+	}
+	
+	/**
+	 * Given an action, returns the associated color identifier for it. The color
+	 * identifier is just an integer suitable for use in a GraphViz color scheme. This
+	 * method updates the (action -> color) mapping if this action has not been seen
+	 * before for this DotStateWriter instance.
+	 * 
+	 * @param action
+	 * @return the color identifier for the given action
+	 */
+	protected Integer getActionColor(final Action action) {
+		// Return a default color if the given action is null.
+		if (action == null) {
+			return 1;
+		} else {
+			String actionName = action.getName().toString();
+			// If this action has been seen before, retrieve its color.
+			if (actionToColors.containsKey(actionName)) {
+				return actionToColors.get(actionName);
+			}
+			// If this action has not been seen yet, get the next available color
+			// and assign it to this action.
+			else {
+				this.colorGen++;
+				actionToColors.put(actionName, this.colorGen);
+				return this.colorGen;
+			}
+		}
+	}
+	
+	/**
+	 * Creates a DOT label for an edge representing a state transition. 
+	 * 
+	 * @param state the current state of the transition
+	 * @param successor the next state of the transition
+	 * @param action the action that induced the transition
+	 * @return the DOT label for the edge
+	 */
+	protected String dotTransitionLabel(final TLCState state, final TLCState successor, final Action action) {
+	    // Only colorize edges if specified. Default to black otherwise.
+		final String color = colorize ? this.getActionColor(action).toString() : "black" ;
+		
+	    // Only add action label if specified.
+		final String actionName = actionLabels ? action.getName().toString() : "" ;
+		
+		final String labelFmtStr = " [label=\"%s\",color=\"%s\",fontcolor=\"%s\"]";
+		return String.format(labelFmtStr, actionName, color, color);
+	}
+	
+	
+	/**
+	 * Creates a DOT legend that maps actions to their corresponding edge color in the state graph.
+	 * 
+	 * @param name the title of the legend
+	 * @param actions the set of action names that will be included in the legend
+	 * @return
+	 */
+	protected String dotLegend(final String name, final Set<String> actions) {
+		final StringBuilder sb = new StringBuilder();
+		sb.append(String.format("subgraph %s {", "cluster_legend"));
+		sb.append("graph[style=bold];");
+		sb.append("label = \"Next State Actions\" style=\"solid\"\n");
+		sb.append(String.format("node [ labeljust=\"l\",colorscheme=\"%s\",style=filled,shape=record ]\n",
+				dotColorScheme));
+		for (String action : actions) {
+			String str = String.format("%s [label=\"%s\",fillcolor=%d]", action.replaceAll("!", ":"), action,
+					this.actionToColors.get(action));
+			sb.append(str);
+			sb.append("\n");
+		}
+		sb.append("}");
+		return sb.toString();
+	}
+	
+	/**
+	 * Given a TLC state, generate a string representation suitable for a HTML DOT graph label.
+	 */
+	//TODO This cannot handle states with variables such as "active = (0 :> TRUE @@ 1 :> FALSE)". 
+//	protected static String state2html(final TLCState state) {		
+//		final StringBuilder sb = new StringBuilder();
+//		final Map<UniqueString, Value> valMap = state.getVals();
+//
+//		// Generate a string representation of state.
+//		for (UniqueString key : valMap.keySet()) {
+//			final String valString = (key.toString() + " = " + valMap.get(key).toString());
+//			sb.append(valString);
+//			// New line between variables.
+//			sb.append("<br/>");
+//		}
+//		return sb.toString();
+//	}
+
+	protected static String states2dot(final TLCState state) {
+		// Replace "\" with "\\" and """ with "\"".	
+		return state.toString().replace("\\", "\\\\").replace("\"", "\\\"").trim()
+				.replace("\n", "\\n"); // Do not remove remaining (i.e. no danling/leading) "\n". 
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#close()
+	 */
+	public void close() {
+		for (final Set<Long> entry : rankToNodes.values()) {
+			this.writer.append("{rank = same; ");
+			for (final Long l : entry) {
+				this.writer.append(l + ";");
+			}
+			this.writer.append("}\n");
+		}
+		this.writer.append("}\n"); // closes the main subgraph.
+		// We only need the legend if the edges are colored by action and there is more
+		// than a single action.
+		if (colorize && this.actionToColors.size() > 1) {
+			this.writer.append(dotLegend("DotLegend", this.actionToColors.keySet()));
+		}
+		this.writer.append("}");
+		super.close();
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#snapshot()
+	 */
+	@Override
+	public void snapshot() throws IOException {
+		this.writer.flush();
+		
+		final String snapshot = fname.replace(".dot", "_snapshot" + ".dot");
+		FileUtil.copyFile(this.fname, snapshot);
+
+		StringBuffer buf = new StringBuffer();
+		for (final Set<Long> entry : rankToNodes.values()) {
+			buf.append("{rank = same; ");
+			for (final Long l : entry) {
+				buf.append(l + ";");
+			}
+			buf.append("}\n");
+		}
+		buf.append("}\n");
+		// We only need the legend if the edges are colored by action and there is more
+		// than a single action.
+		if (colorize && this.actionToColors.size() > 1) {
+			buf.append(dotLegend("DotLegend", this.actionToColors.keySet()));
+		}
+		buf.append("}");
+
+	    Files.write(Paths.get(snapshot), buf.toString().getBytes(), StandardOpenOption.APPEND);
+	}
+}
diff --git a/tlatools/src/tlc2/util/ExpectInlined.java b/tlatools/src/tlc2/util/ExpectInlined.java
new file mode 100644
index 0000000000000000000000000000000000000000..535c8f2a0bad69cff9203b700e3e47319ce37c6a
--- /dev/null
+++ b/tlatools/src/tlc2/util/ExpectInlined.java
@@ -0,0 +1,13 @@
+package tlc2.util;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface ExpectInlined {
+	
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/util/FP64.java b/tlatools/src/tlc2/util/FP64.java
index 87d3a433857ae3ff9fe1db9a68e85a3c441f118e..b02408439640bd9fe8c4cdd81cf8755bac3cf6ba 100644
--- a/tlatools/src/tlc2/util/FP64.java
+++ b/tlatools/src/tlc2/util/FP64.java
@@ -5,6 +5,7 @@ package tlc2.util;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Random;
 
 /**
  * A 64-bit fingerprint is stored in an instance of the type
@@ -127,7 +128,7 @@ public class FP64 {
      * Extend the fingerprint <code>fp</code> by an
      * integer <code>x</code>.
      */
-    public static long Extend(long fp, int x)
+    public static long ExtendLoop(long fp, int x)
     {
         long[] mod = ByteModTable_7;
 	for (int i = 0; i < 4; i++) {
@@ -138,6 +139,27 @@ public class FP64 {
 	return fp;
     }
 
+    public static long Extend(long fp, int x)
+    {
+      final long[] mod = ByteModTable_7;
+      byte b = (byte)(x & 0xFF);
+	  fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF]));
+  	  x = x >>> 8;
+
+	  b = (byte)(x & 0xFF);
+	  fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF]));
+	  x = x >>> 8;
+
+	  b = (byte)(x & 0xFF);
+	  fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF]));
+	  x = x >>> 8;
+
+	  b = (byte)(x & 0xFF);
+	  fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF]));
+	  
+	  return fp;
+    }
+
     /*
      * Extend the fingerprint <code>fp</code> by a long
      * integer <code>fp1</code>.
@@ -202,7 +224,16 @@ public class FP64 {
     /** Unlikely fingerprint? */
     public static final long Zero = 0L;
 
-    /* This file provides procedures that construct fingerprints of
+    /* Background Reading:
+       - Galois Field (GF) or Finite Field
+       https://en.wikipedia.org/wiki/Finite_field
+       - GHash/MAC
+       https://en.wikipedia.org/wiki/Galois/Counter_Mode
+       - Universal Hashing
+       https://en.wikipedia.org/wiki/Universal_hashing
+       ----
+       
+       This file provides procedures that construct fingerprints of
        strings of bytes via operations in GF[2^64].  GF[64] is represented
        as the set polynomials of degree 64 with coefficients in Z(2),
        modulo an irreducible polynomial P of degree 64.  The computer
@@ -368,13 +399,28 @@ public class FP64 {
     /* This is the table used for computing fingerprints.  The
        ByteModTable could be hardwired.  Note that since we just
        extend a byte at a time, we need just "ByteModeTable[7]". */
-    private static long[] ByteModTable_7;
+    private static final long[] ByteModTable_7 = new long[256];
 
     /* This is the irreducible polynomial used as seed.  */
     private static long IrredPoly;
 
     public static long getIrredPoly() { return IrredPoly; }
   
+	/**
+	 * Initializes {@link FP64#IrredPoly} with a randomly chosen poly from
+	 * {@link FP64#Polys}.
+	 */
+	public static void InitRnd() {
+		Init(new Random().nextInt(Polys.length));
+	}
+    
+	/**
+	 * Initializes {@link FP64#IrredPoly} the first poly in {@link FP64#Polys}.
+	 */
+    public static void Init() {
+    	Init(0);
+    }
+    
     // Initialization code
     public static void Init(int n) {
       Init(Polys[n]);
@@ -398,7 +444,6 @@ public class FP64 {
       }
 
       // Just need the 7th iteration of the ByteModTable initialization code
-      ByteModTable_7 = new long[256];
       for (int j = 0; j <= 255; j++) {
 	long v = Zero;
 	for (int k = 0; k <= 7; k++) {
diff --git a/tlatools/src/tlc2/util/IStateWriter.java b/tlatools/src/tlc2/util/IStateWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..b57111c3cb31b8591f1d8bd804ee56b9abb7d8e6
--- /dev/null
+++ b/tlatools/src/tlc2/util/IStateWriter.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import java.io.IOException;
+
+import tlc2.tool.Action;
+import tlc2.tool.TLCState;
+
+public interface IStateWriter {
+	
+	public enum Visualization {
+		/**
+		 * If successor and the current state are identical and the transition
+		 * is due to stuttering.
+		 */
+		STUTTERING,
+		/**
+		 * No extra visualization hint is given.
+		 */
+		DEFAULT,
+		/**
+		 * A dotted line.
+		 */
+		DOTTED;
+	}
+
+	void writeState(TLCState state);
+
+	void writeState(TLCState state, TLCState successor, boolean successorStateIsNew);
+	
+	void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Action action);
+
+	void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visulation);
+	
+	void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew);
+
+	void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visulation);
+	
+	void close();
+
+	String getDumpFileName();
+
+	boolean isNoop();
+	
+	boolean isDot();
+
+	void snapshot() throws IOException;
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/util/IdThread.java b/tlatools/src/tlc2/util/IdThread.java
index 33a0b8bf959d1803bfb0119005c14a877947c258..6913e2462b6200861446fd4090c27e99c18fb471 100644
--- a/tlatools/src/tlc2/util/IdThread.java
+++ b/tlatools/src/tlc2/util/IdThread.java
@@ -2,18 +2,48 @@
 // Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
 package tlc2.util;
 
+import tlc2.tool.TLCState;
+import tlc2.value.IValue;
+
 /** An <code>IdThread</code> is a <code>Thread</code> with an
     integer identifier. */
 
 public class IdThread extends Thread {
+	private static final ThreadLocal<TLCState> currentState = new ThreadLocal<>();
+	
+	/**
+	 * @return null during the generation of initial states (see
+	 *         tlc2.tool.ModelChecker.doInit(boolean) and a TLCState during the
+	 *         generation of next-states in tlc2.tool.ModelChecker.doNext(TLCState,
+	 *         ObjLongTable, Worker). It corresponds to the predecessor state of the
+	 *         next-state currently being generated by the worker thread.
+	 */
+	public static final TLCState getCurrentState() {
+		return currentState.get();
+	}
+	public static final void setCurrentState(final TLCState state) {
+		currentState.set(state);
+	}
+	public static final TLCState resetCurrentState() {
+		final TLCState tlcState = currentState.get();
+		currentState.remove();
+		return tlcState;
+	}
+	
     private final int id;
-    
+	private IValue[] localValues = new IValue[4];
+   
     /** Create a new thread with ID <code>id</code>. */
     public IdThread(int id) {
         this.id = id;
     }
    
-    /** Return this thread's ID. */
+    public IdThread(Runnable runnable, String name, int id) {
+    	super(runnable, name);
+    	this.id = id;
+	}
+
+	/** Return this thread's ID. */
     // This method was originally called getId, but a later
     // version of Java introduced a getId method into the Thread
     // class which returned a long, and it doesn't allow it to be
@@ -40,4 +70,20 @@ public class IdThread extends Thread {
         Thread th = Thread.currentThread();
         return (th instanceof IdThread) ? ((IdThread)th).id : otherId;
     }
+    
+	public IValue getLocalValue(int idx) {
+		if (idx < this.localValues.length) {
+			return this.localValues[idx];
+		}
+		return null;
+	}
+
+	public void setLocalValue(int idx, IValue val) {
+		if (idx >= this.localValues.length) {
+			IValue[] vals = new IValue[idx + 1];
+			System.arraycopy(this.localValues, 0, vals, 0, this.localValues.length);
+			this.localValues = vals;
+		}
+		this.localValues[idx] = val;
+	}
 }
diff --git a/tlatools/src/tlc2/util/IntStack.java b/tlatools/src/tlc2/util/IntStack.java
new file mode 100644
index 0000000000000000000000000000000000000000..17bb23f92eb305f328927c6f241f747b290a2461
--- /dev/null
+++ b/tlatools/src/tlc2/util/IntStack.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+public interface IntStack {
+
+	/**
+	 * Return the number of items on the stack.
+	 */
+	long size();
+
+	/**
+	 * Push an integer onto the stack.
+	 */
+	void pushInt(final int x);
+
+	/**
+	 * Push a long integer onto the stack.
+	 */
+	void pushLong(final long x);
+
+	/**
+	 * Pop the integer from the top of the stack.
+	 */
+	int popInt();
+
+	/**
+	 * Pop the long integer from the top of the stack.
+	 */
+	long popLong();
+	
+	/**
+	 * Removes all elements from the stack
+	 */
+	void reset();
+
+	// Some implementors support peak operations.
+	default long peakLong() {
+		throw new UnsupportedOperationException("Not implemented");
+	}
+	
+	default int peakInt() {
+		throw new UnsupportedOperationException("Not implemented");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/util/LongVec.java b/tlatools/src/tlc2/util/LongVec.java
index 703e71c6367b50ea162b45d526e73767177f7b48..44706322da05c66ff1f53474b3bebbb3bc28bf6c 100644
--- a/tlatools/src/tlc2/util/LongVec.java
+++ b/tlatools/src/tlc2/util/LongVec.java
@@ -8,14 +8,10 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.util.Arrays;
 
 import util.FileUtil;
 
-/**
- * 
- * 
- * @version $Id$
- */
 public class LongVec implements Cloneable, Serializable {
 	private static final long serialVersionUID = 2406899362740899071L;
 	protected long[] elementData;
@@ -28,6 +24,11 @@ public class LongVec implements Cloneable, Serializable {
 		this.elementData = new long[initialCapacity];
 	}
 
+	private LongVec(final LongVec other) {
+		this.elementCount = other.elementCount;
+		this.elementData = Arrays.copyOfRange(other.elementData, 0, other.elementCount);
+	}
+
 	public final void addElement(long x) {
 		if (this.elementCount == this.elementData.length) {
 			ensureCapacity(this.elementCount + 1);
@@ -132,4 +133,26 @@ public class LongVec implements Cloneable, Serializable {
 		System.err.println(vec1.elementAt(2));
 	}
 
+	private LongVec reverse0() {
+		int left = 0;
+		int right = elementData.length - 1;
+
+		while (left < right) {
+			long temp = elementData[left];
+			elementData[left] = elementData[right];
+			elementData[right] = temp;
+
+			left++;
+			right--;
+		}
+		return this;
+	}
+	
+	public LongVec reverse() {
+		try {
+			return new LongVec(this).reverse0();
+		} catch (Exception e) {
+			throw e;
+		}
+	}
 }
diff --git a/tlatools/src/tlc2/util/MemBasedSet.java b/tlatools/src/tlc2/util/MemBasedSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9b4c119a6fa369cb52727ca2a1b4b2ab25a207f
--- /dev/null
+++ b/tlatools/src/tlc2/util/MemBasedSet.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+public abstract class MemBasedSet {
+
+	protected int size;
+
+	protected int[] elems;
+
+	public MemBasedSet(final int minCapacity) {
+		this.size = 0;
+		this.elems = new int[minCapacity];
+	}
+	
+	protected int[] ensureCapacity(final int minCapacity) {
+		// If the internal storage reaches its capacity, double the size of
+		// the internal storage. This strategy is great for as long as size
+		// is smaller than 2^31. Once size is >= 2^31, doubling it means it
+		// becomes negative resulting in a NegativeArraySizeException. Additionally
+		// if size gets larger and larger, an OutOfMemory exception becomes 
+		// more likely when MemIntStacks memory requirements get doubled.
+		// From the literature and popular implementations (e.g. Java's ArrayList),
+		// a growth factor of 1.5 seems to be practical.
+		final int newSize = (int) ((this.size * 3L) / 2) + 1; // multiply with *long* 3L to stay positive
+		// If newSize is negative (happens when size become too large for
+		// int), just increase the array by MIN_CAPACITY. This obviously
+		// doesn't prevent a NegativeArraySizeException either, but it
+		// postpones it to the moment when the int-sized array will be
+		// completely full. At this point, nothing can be done except using
+		// two MemIntStack instances which requires changes in the places
+		// where MemIntStack is used or swapping MemIntStack to disk (which -
+		// from looking at the ctor's parameters - appears to be the preferred
+		// solution of the original MemIntStack authors).
+		//
+		// Performance obviously goes south the moment the array is 
+		// increased in MIN_CAPACITY steps. It's a trade off between risking
+		// an OutOfMemory exception and performance.
+		return new int[Math.max(newSize, this.size + minCapacity)];
+	}
+
+	/**
+	 * Return the number of items on the stack.
+	 */
+	public final long size() {
+		return this.size;
+	}
+
+	public final boolean hasElements() {
+		return this.size > 0;
+	}
+}
diff --git a/tlatools/src/tlc2/util/MemIntQueue.java b/tlatools/src/tlc2/util/MemIntQueue.java
index ad119d5abca7fc01dfb1acc1db3e05a2a43fd38c..257a9bb0765c36133d8fde777c8b8b8b9b27c5b9 100644
--- a/tlatools/src/tlc2/util/MemIntQueue.java
+++ b/tlatools/src/tlc2/util/MemIntQueue.java
@@ -13,137 +13,143 @@ import util.BufferedDataInputStream;
 import util.BufferedDataOutputStream;
 import util.FileUtil;
 
-/**
- * 
- * @version $Id$
- */
-public final class MemIntQueue {
-  private final static int InitialSize = 4096;
-  private final static int GrowthFactor = 2;
-
-  private int len;
-  private int[] elems;
-  private int start;
-  private String diskdir;
-  private String filename;
-    
-  public MemIntQueue(String metadir, String filename) {
-    this.len = 0;
-    this.elems = new int[InitialSize];
-    this.start = 0;
-    this.diskdir = metadir;
-    this.filename = filename;
-  }
-
-  public final int length() { return this.len; }
-  
-  public final boolean hasElements() { return this.len > 0; }
-  
-  public final void enqueueInt(int elem) {
-    if (this.len == this.elems.length) {
-      // grow the array
-      int newLen = Math.max(1, this.len * GrowthFactor);
-      int[] newElems = new int[newLen];
-      int copyLen = this.elems.length - this.start;
-      System.arraycopy(this.elems, this.start, newElems, 0, copyLen);
-      System.arraycopy(this.elems, 0, newElems, copyLen, this.start);
-      this.elems = newElems;
-      this.start = 0;
-    }
-    int last = (this.start + this.len) % this.elems.length;
-    this.elems[last] = elem;
-    this.len++;
-  }
-
-  public final void enqueueLong(long elem) {
-    this.enqueueInt((int)(elem >>> 32));
-    this.enqueueInt((int)(elem & 0xFFFFFFFFL));
-  }
-    
-  public final int dequeueInt() {
-    // Assert.check(this.len > 0);
-	if (this.len < 1) {
-		throw new NoSuchElementException();
+public final class MemIntQueue extends MemBasedSet {
+	private final static int InitialSize = 4096;
+
+	private int start;
+	private String diskdir;
+	private String filename;
+
+	public MemIntQueue(String metadir, String filename) {
+		this(metadir, filename, InitialSize);
+	}
+
+	public MemIntQueue(String metadir, String filename, final int initialCapacity) {
+		super(initialCapacity);
+		this.start = 0;
+		this.diskdir = metadir;
+		this.filename = filename;
+	}
+
+	public final void enqueueInt(int elem) {
+		if (this.size == this.elems.length) {
+			// grow the array
+			final int[] newElems = ensureCapacity(InitialSize);
+			// copy old content to new array
+			int copyLen = this.elems.length - this.start;
+			System.arraycopy(this.elems, this.start, newElems, 0, copyLen);
+			System.arraycopy(this.elems, 0, newElems, copyLen, this.start);
+			this.elems = newElems;
+			this.start = 0;
+		}
+		int last = (this.start + this.size) % this.elems.length;
+		this.elems[last] = elem;
+		this.size++;
+	}
+
+	public final void enqueueLong(long elem) {
+		this.enqueueInt((int) (elem >>> 32));
+		this.enqueueInt((int) (elem & 0xFFFFFFFFL));
+	}
+
+	public final int dequeueInt() {
+		// Assert.check(this.len > 0);
+		if (this.size < 1) {
+			throw new NoSuchElementException();
+		}
+		int res = this.elems[this.start];
+		this.size--;
+		this.start = (this.start + 1) % this.elems.length;
+		return res;
+	}
+
+	public final long dequeueLong() {
+		long high = this.dequeueInt();
+		long low = this.dequeueInt();
+		return (high << 32) | (low & 0xFFFFFFFFL);
+	}
+
+	public int popInt() {
+		// Assert.check(this.len > 0);
+		if (this.size < 1) {
+			throw new NoSuchElementException();
+		}
+		return this.elems[--this.size];
+	}
+
+	public long popLong() {
+		long low = this.popInt();
+		long high = this.popInt();
+		return (high << 32) | (low & 0xFFFFFFFFL);
+	}
+
+	// Checkpoint.
+	public final void beginChkpt() throws IOException {
+		String tmpName = this.diskdir + FileUtil.separator + this.filename + ".tmp";
+		BufferedDataOutputStream bos = new BufferedDataOutputStream(tmpName);
+		bos.writeInt(this.size);
+		int index = this.start;
+		for (int i = 0; i < this.size; i++) {
+			bos.writeInt(this.elems[index++]);
+			if (index == this.elems.length)
+				index = 0;
+		}
+		bos.close();
+	}
+
+	public final void commitChkpt() throws IOException {
+		String oldName = this.diskdir + FileUtil.separator + this.filename + ".chkpt";
+		File oldChkpt = new File(oldName);
+		String newName = this.diskdir + FileUtil.separator + this.filename + ".tmp";
+		File newChkpt = new File(newName);
+		if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) {
+			throw new IOException("MemStateQueue.commitChkpt: cannot delete " + oldChkpt);
+		}
 	}
-    int res = this.elems[this.start];
-    this.len--;
-    this.start = (this.start + 1) % this.elems.length;
-    return res;
-  }
-
-  public final long dequeueLong() {
-    long high = this.dequeueInt();
-    long low = this.dequeueInt();
-    return (high << 32) | (low & 0xFFFFFFFFL);
-  }
-  
-  // Checkpoint.
-  public final void beginChkpt() throws IOException {
-    String tmpName = this.diskdir + FileUtil.separator + this.filename + ".tmp";
-    BufferedDataOutputStream bos = new BufferedDataOutputStream(tmpName);
-    bos.writeInt(this.len);
-    int index = this.start;
-    for (int i = 0; i < this.len; i++) {
-      bos.writeInt(this.elems[index++]);
-      if (index == this.elems.length) index = 0;
-    }
-    bos.close();
-  }
-
-  public final void commitChkpt() throws IOException {
-    String oldName = this.diskdir + FileUtil.separator + this.filename + ".chkpt";
-    File oldChkpt = new File(oldName);
-    String newName = this.diskdir + FileUtil.separator + this.filename + ".tmp";
-    File newChkpt = new File(newName);
-    if ((oldChkpt.exists() && !oldChkpt.delete()) ||
-	!newChkpt.renameTo(oldChkpt)) {
-      throw new IOException("MemStateQueue.commitChkpt: cannot delete " + oldChkpt);
-    }
-  }
-  
-  public final void recover() throws IOException {
-    String chkptName = this.diskdir + FileUtil.separator + this.filename + ".chkpt";
-    BufferedDataInputStream bis = new BufferedDataInputStream(chkptName);
-    this.len = bis.readInt();
-    for (int i = 0; i < this.len; i++) {
-      this.elems[i] = bis.readInt();
-    }
-    bis.close();
-  }
-
-  	/*
+
+	public final void recover() throws IOException {
+		String chkptName = this.diskdir + FileUtil.separator + this.filename + ".chkpt";
+		BufferedDataInputStream bis = new BufferedDataInputStream(chkptName);
+		this.size = bis.readInt();
+		for (int i = 0; i < this.size; i++) {
+			this.elems[i] = bis.readInt();
+		}
+		bis.close();
+	}
+
+	/*
 	 * The detailed formatter below can be activated in Eclipse's variable view
 	 * by choosing "New detailed formatter" from the MemIntQueue context menu.
 	 * Insert "MemIntQueue.DetailedFormatter.fpAndtidxAndptr(this);".
 	 */
-  	public static class DetailedFormatter {
-  		
-  		// An Eclipse detailed formatter for when this Queue holds pairs of long
-  		// (fp) and int (tableau idx)
-  		public static String fpAndtidx(MemIntQueue aQueue) {
-  			final StringBuffer buf = new StringBuffer(aQueue.len / 3);
-  			for (int i = 0; i < aQueue.len; i+=3) {
-  				final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL);
-  				buf.append("fp: " + fp);
-  				buf.append(" tidx: " + aQueue.elems[i + 2]);
-  				buf.append("\n");
-  			}
-  			return buf.toString();
-  		}
-  		
-  		// An Eclipse detailed formatter for when this Queue holds pairs of long
-  		// (fp), int (tableau idx) and long (disk graph pointer).
-  		public static String fpAndtidxAndptr(MemIntQueue aQueue) {
-  			final StringBuffer buf = new StringBuffer(aQueue.len / 5);
-  			for (int i = 0; i < aQueue.len; i += 5) {
-  				final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL);
-  				buf.append("fp: " + fp);
-  				buf.append(" tidx: " + aQueue.elems[i + 2]);
-  				final long ptr = ((long) aQueue.elems[i + 3] << 32) | ((long) (aQueue.elems[i+ 4] ) & 0xFFFFFFFFL);
-  				buf.append(" ptr: " + ptr);
-  				buf.append("\n");
-  			}
-  			return buf.toString();
-  		}
-  	}
+	public static class DetailedFormatter {
+
+		// An Eclipse detailed formatter for when this Queue holds pairs of long
+		// (fp) and int (tableau idx)
+		public static String fpAndtidx(MemIntQueue aQueue) {
+			final StringBuffer buf = new StringBuffer(aQueue.size / 3);
+			for (int i = 0; i < aQueue.size; i += 3) {
+				final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL);
+				buf.append("fp: " + fp);
+				buf.append(" tidx: " + aQueue.elems[i + 2]);
+				buf.append("\n");
+			}
+			return buf.toString();
+		}
+
+		// An Eclipse detailed formatter for when this Queue holds pairs of long
+		// (fp), int (tableau idx) and long (disk graph pointer).
+		public static String fpAndtidxAndptr(MemIntQueue aQueue) {
+			final StringBuffer buf = new StringBuffer(aQueue.size / 5);
+			for (int i = 0; i < aQueue.size; i += 5) {
+				final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL);
+				buf.append("fp: " + fp);
+				buf.append(" tidx: " + aQueue.elems[i + 2]);
+				final long ptr = ((long) aQueue.elems[i + 3] << 32) | ((long) (aQueue.elems[i + 4]) & 0xFFFFFFFFL);
+				buf.append(" ptr: " + ptr);
+				buf.append("\n");
+			}
+			return buf.toString();
+		}
+	}
 }
diff --git a/tlatools/src/tlc2/util/MemIntStack.java b/tlatools/src/tlc2/util/MemIntStack.java
index e8dae0c97e9b29d63886a947de86a67aee9088e5..ce74548de265fcfa4d66699d3002def008d29808 100644
--- a/tlatools/src/tlc2/util/MemIntStack.java
+++ b/tlatools/src/tlc2/util/MemIntStack.java
@@ -5,66 +5,74 @@
 
 package tlc2.util;
 
-public final class MemIntStack {
-  private int size;
-  private int[] elems;
-  
-  public MemIntStack(String diskdir, String name) {
-    this.size = 0;
-    this.elems = new int[1024];
-  }
+public final class MemIntStack extends MemBasedSet implements IntStack {
+	private static final int MIN_CAPACITY = 1024;
 
-  /* Return the number of items on the stack. */
-  public final int size() { return this.size; }
-  
-  /* Push an integer onto the stack.  */
-  public final synchronized void pushInt(int x) {
-    if (this.size == this.elems.length) {
-      int[] elems1 = new int[2*this.size];
-      System.arraycopy(elems, 0, elems1, 0, this.size);
-      this.elems = elems1;
-    }
-    this.elems[this.size] = x;
-    this.size++;
-  }
-  
-  /* Push a long integer onto the stack.  */
-  public final synchronized void pushLong(long x) {
-    this.pushInt((int)(x & 0xFFFFFFFFL));
-    this.pushInt((int)(x >>> 32));
-  }
+	public MemIntStack(String diskdir, String name) {
+		super(MIN_CAPACITY);
+	}
 
-  /* Pop the integer on top of the stack.  */
-  public final synchronized int popInt() {
-    return this.elems[--this.size];
-  }
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#pushInt(int)
+	 */
+	public final synchronized void pushInt(int x) {
+		if (this.size == this.elems.length) {
+			final int[] newElems = ensureCapacity(MIN_CAPACITY);
+			System.arraycopy(elems, 0, newElems, 0, this.size);
+			this.elems = newElems;
+		}
+		this.elems[this.size] = x;
+		this.size++;
+	}
 
-  public final synchronized int peakInt() {
-	    return peakInt(size - 1);
-  }
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#pushLong(long)
+	 */
+	public final synchronized void pushLong(long x) {
+		this.pushInt((int) (x & 0xFFFFFFFFL));
+		this.pushInt((int) (x >>> 32));
+	}
 
-  public final synchronized int peakInt(int pos) {
-	    return this.elems[pos];
-}
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#popInt()
+	 */
+	public final synchronized int popInt() {
+		return this.elems[--this.size];
+	}
+
+	public final synchronized int peakInt() {
+		return peakInt(size - 1);
+	}
+
+	public final synchronized int peakInt(int pos) {
+		return this.elems[pos];
+	}
 
-  /* Pop the long integer on top of the stack.  */
-  public final synchronized long popLong() {
-    long high = this.popInt();
-    long low = this.popInt();
-    return (high << 32) | (low & 0xFFFFFFFFL);
-  }
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#popLong()
+	 */
+	public final synchronized long popLong() {
+		long high = this.popInt();
+		long low = this.popInt();
+		return (high << 32) | (low & 0xFFFFFFFFL);
+	}
 
-  public final synchronized long peakLong() {
-	    long high = this.peakInt();
-	    long low = this.peakInt();
-	    return (high << 32) | (low & 0xFFFFFFFFL);
-	  }
+	public final synchronized long peakLong() {
+		long high = this.peakInt();
+		long low = this.peakInt();
+		return (high << 32) | (low & 0xFFFFFFFFL);
+	}
 
-  public final synchronized long peakLong(int pos) {
-	    long high = this.peakInt(pos);
-	    long low = this.peakInt(pos + 1);
-	    return (high << 32) | (low & 0xFFFFFFFFL);
-	  }
+	public final synchronized long peakLong(int pos) {
+		long high = this.peakInt(pos + 1);
+		long low = this.peakInt(pos);
+		return (high << 32) | (low & 0xFFFFFFFFL);
+	}
 
-  public final void reset() { this.size = 0; }
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#reset()
+	 */
+	public final void reset() {
+		this.size = 0;
+	}
 }
diff --git a/tlatools/src/tlc2/util/NoopStateWriter.java b/tlatools/src/tlc2/util/NoopStateWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d3dce4cdea66bf803c7b04fc41fd9983d854db1
--- /dev/null
+++ b/tlatools/src/tlc2/util/NoopStateWriter.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import java.io.IOException;
+
+import tlc2.tool.Action;
+import tlc2.tool.TLCState;
+
+public final class NoopStateWriter implements IStateWriter {
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState)
+	 */
+	public final void writeState(final TLCState state) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean)
+	 */
+	public final void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visualization) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#close()
+	 */
+	public final void close() {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean)
+	 */
+	public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew,
+			Visualization visulation) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.tool.Action)
+	 */
+	public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Action action) {
+		// noop
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isNoop()
+	 */
+	public boolean isNoop() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isDot()
+	 */
+	@Override
+	public boolean isDot() {
+		return false;
+	}
+		
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#getDumpFileName()
+	 */
+	public String getDumpFileName() {
+		return "";
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#snapshot()
+	 */
+	@Override
+	public void snapshot() throws IOException {
+	}
+}
diff --git a/tlatools/src/tlc2/util/ObjLongTable.java b/tlatools/src/tlc2/util/ObjLongTable.java
index 7001d7893dcc90844fdf1e960feed93cd8cebd67..0b894d096ac6ea0e3116a5fd316e827ee685795e 100644
--- a/tlatools/src/tlc2/util/ObjLongTable.java
+++ b/tlatools/src/tlc2/util/ObjLongTable.java
@@ -5,38 +5,40 @@
 
 package tlc2.util;
 
-public final class ObjLongTable {
+public final class ObjLongTable<T> {
   private int count;
   private int length;
   private int thresh;
-  private Object[] keys;
+  private T[] keys;
   private long[] elems;
 
+  @SuppressWarnings("unchecked")
   public ObjLongTable(int size) {
-    this.keys = new Object[size];
+    this.keys = (T[]) new Object[size];
     this.elems = new long[size];
     this.count = 0;
     this.length = size;
     this.thresh = this.length / 2;
   }
 
+  @SuppressWarnings("unchecked")
   private final void grow() {
     Object[] oldKeys = this.keys;
     long[] oldElems = this.elems;
     this.count = 0;
     this.length = 2 * this.length + 1;
     this.thresh = this.length / 2;
-    this.keys = new Object[this.length];
+    this.keys = (T[]) new Object[this.length];
     this.elems = new long[this.length];
     for (int i = 0; i < oldKeys.length; i++) {
-      Object key = oldKeys[i];
+      T key = (T) oldKeys[i];
       if (key != null) this.put(key, oldElems[i]);
     }
   }
 
   public final int size() { return this.count; }
 
-  public final int put(Object k, long elem) {
+  public final int put(T k, long elem) {
     if (count >= thresh) this.grow();
     int loc = ((int)k.hashCode() & 0x7FFFFFFF) % this.length;
     while (true) {
@@ -55,7 +57,7 @@ public final class ObjLongTable {
     }
   }
 
-  public final int add(Object k, long elem) {
+  public final int add(T k, long elem) {
     if (count >= thresh) this.grow();
     int loc = ((int)k.hashCode() & 0x7FFFFFFF) % this.length;
     while (true) {
@@ -84,41 +86,46 @@ public final class ObjLongTable {
     }
   }
 
-  public final String[] sortStringKeys() {
-    String[] res = new String[this.count];
-    int idx = -1;
-    for (int i = 0; i < this.length; i++) {
-      String key = (String)this.keys[i];
-      if (key != null) {
-	int j = idx;
-	while (j >= 0) {
-	  if (res[j].compareTo(key) <= 0) break;
-	  res[j+1] = res[j];
-	  j--;
+	/**
+	 * Merges the keys and longs of into this instance. If this instance already
+	 * contains an entry with a given key, the long values will be accumulated.
+	 */
+	public ObjLongTable<T> mergeInto(final ObjLongTable<T> other) {
+		T key;
+		final ObjLongTable<T>.Enumerator<T> keys2 = other.keys();
+		while ((key = keys2.nextElement()) != null) {
+			add(key, other.get(key));
+		}
+		return this;
 	}
-	res[j+1] = key;
-	idx++;
-      }
-    }
-    return res;
-  }
-  
 
-  public final Enumerator keys() { return new Enumerator(); }
+	@SuppressWarnings("unchecked")
+	public T[] toArray(T[] a) {
+		a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), count);
+		ObjLongTable<T>.Enumerator<T> keys2 = keys();
+		T e = null;
+		int i = 0;
+		while((e = keys2.nextElement()) != null) {
+			a[i++] = e;
+		}
+		return a;
+	}
+  
+  public final Enumerator<T> keys() { return new Enumerator<T>(); }
 
-  public final class Enumerator {
+  @SuppressWarnings("hiding")
+  public final class Enumerator<T> {
     int index = 0;
 
-    public final Object nextElement() {
+    @SuppressWarnings("unchecked")
+	public final T nextElement() {
       while (this.index < keys.length) {
 	if (keys[this.index] != null) {
-	  return keys[this.index++];
+	  return (T) keys[this.index++];
 	}
 	this.index++;
       }
       return null;
     }
   }
-    
-
 }
diff --git a/tlatools/src/tlc2/util/SetOfLong.java b/tlatools/src/tlc2/util/SetOfLong.java
index cfc2dd305a8a11086414aa75474d1c5d9714710e..d124389626b9770143ddc01f0c4a780dffa669c1 100644
--- a/tlatools/src/tlc2/util/SetOfLong.java
+++ b/tlatools/src/tlc2/util/SetOfLong.java
@@ -86,7 +86,7 @@ public final class SetOfLong {
 
   public final long sizeof() { return 20 + (8 * this.length); }
 
-  public final double checkFPs() {
+  public final long checkFPs() {
     int cnt = 0;
     for (int i = 0; i < this.length; i++) {
       long x = this.table[i];
@@ -107,7 +107,7 @@ public final class SetOfLong {
       dis = Math.min(dis, this.table[i]-x);
       x = this.table[i];
     }
-    return (1.0/dis);
+    return dis;
   }
 
   public final void beginChkpt(DataOutputStream dos) throws IOException {
diff --git a/tlatools/src/tlc2/util/SetOfStates.java b/tlatools/src/tlc2/util/SetOfStates.java
new file mode 100644
index 0000000000000000000000000000000000000000..ace08cc903306d11d3c36eb1aeb14da44ebdae77
--- /dev/null
+++ b/tlatools/src/tlc2/util/SetOfStates.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+import tlc2.tool.ModelChecker;
+import tlc2.tool.TLCState;
+
+/**
+ * A {@link SetOfStates} is a hash set with open addressing that is intended to
+ * be used in TLC's {@link ModelChecker#getNextStates()} implementation. In this
+ * are the number of {@link TLCState}s generated is relatively small and thus
+ * the likelihood of consecutive ranges in the fingerprint domain low. In turn,
+ * this means that the {@link TLCState}s in {@link SetOfStates} are evenly
+ * distributed assuming the {@link SetOfStates#length} is sufficiently large.
+ */
+public final class SetOfStates {
+
+	private TLCState[] states;
+	private int count;
+	private int length;
+	private int thresh;
+
+	public SetOfStates() {
+		this(16);
+	}
+
+	public SetOfStates(final int size) {
+		this.count = 0;
+		this.length = size;
+		this.thresh = length / 2;
+		this.states = new TLCState[length];
+	}
+
+	public final void clear() {
+		this.count = 0;
+		this.states = new TLCState[length];
+	}
+	
+	private final void grow() {
+		final TLCState[] old = states;
+		this.count = 0;
+		this.length = 2 * this.length + 1;
+		this.thresh = this.length / 2;
+		this.states = new TLCState[this.length];
+		for (int i = 0; i < old.length; i++) {
+			final TLCState s = old[i];
+			// This is where we have to redundantly compute the state's
+			// fingerprint. Thus, try to minimize the number of grow operations.
+			if (s != null) {
+				this.put(s.fingerPrint(), s);
+			}
+		}
+	}
+
+	public final boolean put(final TLCState aState) {
+		return put(aState.fingerPrint(), aState);
+	}
+
+	public final boolean put(final long fingerprint, final TLCState aState) {
+		if (count >= thresh) {
+			this.grow();
+		}
+		int loc = ((int) fingerprint & 0x7FFFFFFF) % this.length;
+		// This loop keep going until either a match or a null bucket is found.
+		while (true) {
+			final TLCState ent = this.states[loc];
+			if (ent == null) {
+				states[loc] = aState;
+				count++;
+				return false;
+			}
+			// Compare with equals here to correctly handle symmetry where two
+			// symmetric states will be hashed to neighboring buckets. The
+			// assumption is that we will end up with only doing a few equality
+			// checks because the primary comparison is still the fingerprint
+			// and that the states[] is sparsely populated.
+			if (aState.equals(ent)) {
+				return true;
+			}
+			loc = (loc + 1) % this.length;
+		}
+	}
+
+	/**
+	 * @return The current capacity of this set. [](capacity > size)
+	 */
+	public final int capacity() {
+		return this.length;
+	}
+	
+	/**
+	 * @return The number of {@link TLCState}s in this set. [](capacity > size)
+	 */
+	public final int size() {
+		return this.count;
+	}
+	
+	/* (non-Javadoc)
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		final StringBuffer buf = new StringBuffer("{");
+		for (int i = 0; i < states.length; i++) {
+			final TLCState tlcState = states[i];
+			if (tlcState != null) {
+				buf.append("<<");
+				buf.append(tlcState.fingerPrint());
+				buf.append(",");
+				final String toStr = tlcState.toString();
+				buf.append(toStr.substring(0, toStr.length() - 1)); // chop off "\n"
+				buf.append(">>,\n");
+			}
+		}
+		buf.append("}");
+		return buf.toString();
+	}
+	
+	/*
+	 * Iterate (avoids creating an iterator object at the price of the mandatory
+	 * resetNext() method).
+	 */
+
+	private int iteratorIndex = 0;
+	
+	public final TLCState next() {
+		TLCState next = null;
+		while ((next = this.states[iteratorIndex++]) == null) {
+			// No-op loop
+		}
+		return next;
+	}
+
+	public void resetNext() {
+		iteratorIndex = 0;
+	}
+}
diff --git a/tlatools/src/tlc2/util/StatePoolReader.java b/tlatools/src/tlc2/util/StatePoolReader.java
index 7690191f2beeb7e847c19bcc795ed125cd2d5bfa..670e3eb1983f22b16f08b9769ff278a4eabc4df6 100644
--- a/tlatools/src/tlc2/util/StatePoolReader.java
+++ b/tlatools/src/tlc2/util/StatePoolReader.java
@@ -23,6 +23,7 @@ public class StatePoolReader extends Thread {
   }
 
   public StatePoolReader(int bufSize, File file) {
+	  super("TLCStatePoolReader");
     this.buf = new TLCState[bufSize];
     this.poolFile = file;
     this.isFull = false;
@@ -55,7 +56,7 @@ public class StatePoolReader extends Thread {
   public final synchronized TLCState[] doWork(TLCState[] deqBuf, File file)
   throws IOException, ClassNotFoundException {
     if (this.isFull) {
-      Assert.check(this.poolFile == null, EC.SYSTEM_FILE_NULL);
+      assert this.poolFile == null : EC.SYSTEM_FILE_NULL;
       TLCState[] res = this.buf;
       this.buf = deqBuf;
       this.poolFile = file;
@@ -93,7 +94,7 @@ public class StatePoolReader extends Thread {
   public final synchronized TLCState[] getCache(TLCState[] deqBuf, File file)
   throws IOException, ClassNotFoundException {
     if (this.isFull) {
-      Assert.check(this.poolFile == null, EC.SYSTEM_FILE_NULL);
+      assert this.poolFile == null : EC.SYSTEM_FILE_NULL;
       TLCState[] res = this.buf;
       this.buf = deqBuf;
       this.poolFile = file;
diff --git a/tlatools/src/tlc2/util/StatePoolWriter.java b/tlatools/src/tlc2/util/StatePoolWriter.java
index 2f4a45a26fae73d0433fc41d770a4ff55a752fa7..b01461bfd405cab8f741586c67517329c79e9d25 100644
--- a/tlatools/src/tlc2/util/StatePoolWriter.java
+++ b/tlatools/src/tlc2/util/StatePoolWriter.java
@@ -28,6 +28,7 @@ public class StatePoolWriter extends Thread {
   }
 
   public StatePoolWriter(int bufSize, StatePoolReader reader) {
+	  super("TLCStatePoolWriter");
     this.buf = new TLCState[bufSize];
     this.poolFile = null;
     this.reader = reader;
diff --git a/tlatools/src/tlc2/util/StateWriter.java b/tlatools/src/tlc2/util/StateWriter.java
index a0021b4f82a1ddd62ff118ef2d7cb098d7652f79..3efc5a0754bf1efc43998448b0e7b2c35aa9c8d2 100644
--- a/tlatools/src/tlc2/util/StateWriter.java
+++ b/tlatools/src/tlc2/util/StateWriter.java
@@ -3,26 +3,52 @@ package tlc2.util;
 import java.io.IOException;
 import java.io.PrintWriter;
 
+import tlc2.tool.Action;
 import tlc2.tool.TLCState;
 import util.FileUtil;
 
 /**
  * State writer 
  * @author Simon Zambrovski
- * @version $Id$
  */
-public class StateWriter
+public class StateWriter implements IStateWriter
 {
-    private PrintWriter writer;
-    private int stateNum;
+    protected final PrintWriter writer;
+    protected int stateNum;
+    protected String fname;
 
     public StateWriter(String fname) throws IOException
     {
-        // SZ Feb 24, 2009: stream creation moved
+        this.fname = fname;
         this.writer = new PrintWriter(FileUtil.newBFOS(fname));
         this.stateNum = 1;
     }
 
+    /* (non-Javadoc)
+     * @see tlc2.util.IStateWriter#getDumpFileName()
+     */
+    public String getDumpFileName() {
+    	return this.fname;
+    }
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isNoop()
+	 */
+	public boolean isNoop() {
+		return false;
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#isDot()
+	 */
+	@Override
+	public boolean isDot() {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState)
+	 */
     public synchronized void writeState(TLCState state)
     {
         this.writer.println("State " + this.stateNum + ":");
@@ -30,8 +56,61 @@ public class StateWriter
         this.stateNum++;
     }
 
-    public final void close()
+    /* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean)
+	 */
+    public synchronized void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew)
+    {
+    	if (successorStateIsNew) {
+    		this.writeState(successor);
+    	}
+    }
+
+    public synchronized void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew, Action action)
+    {
+    	if (successorStateIsNew) {
+    		this.writeState(successor);
+    	}
+    }
+    
+    /* (non-Javadoc)
+     * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization)
+     */
+    public void writeState(TLCState state, TLCState successor, final boolean successorStateIsNew, final Visualization visualization) {
+    	if (successorStateIsNew) {
+    		this.writeState(successor);
+    	}
+    }
+    
+    /* (non-Javadoc)
+     * @see tlc2.util.IStateWriter#close()
+     */
+    public void close()
     {
         this.writer.close();
     }
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean)
+	 */
+	public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) {
+    	if (successorStateIsNew) {
+    		this.writeState(state);
+    	}
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization)
+	 */
+	public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew,
+			Visualization visulation) {
+    	if (successorStateIsNew) {
+    		this.writeState(state);
+    	}
+	}
+
+	@Override
+	public void snapshot() throws IOException {
+		// No-op unless DotStateWriter.
+	}
 }
diff --git a/tlatools/src/tlc2/util/Striped.java b/tlatools/src/tlc2/util/Striped.java
index 7f6ddbdb648b2ebd4a8ef1086156e540b5127216..ba46ff1d6451009d8aaa5e6dad981ce0582a04e3 100644
--- a/tlatools/src/tlc2/util/Striped.java
+++ b/tlatools/src/tlc2/util/Striped.java
@@ -4,7 +4,7 @@ package tlc2.util;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-public class Striped {
+public final class Striped {
 
 	public static Striped readWriteLock(final int lockCnt) {
 		return new Striped(lockCnt);
@@ -19,21 +19,21 @@ public class Striped {
 		}
 	}
 
-	public ReadWriteLock getAt(int lockIndex) {
+	public final ReadWriteLock getAt(int lockIndex) {
 		return this.locks[lockIndex];
 	}
 
-	public int size() {
+	public final int size() {
 		return locks.length;
 	}
 
-	public void releaseAllLocks() {
+	public final void releaseAllLocks() {
 		for (int i = size() - 1; i >= 0; i--) {
 			this.locks[i].writeLock().unlock();
 		}
 	}
 
-	public void acquireAllLocks() {
+	public final void acquireAllLocks() {
 		//TODO find way to do this more efficiently
 		for (int i = 0; i < size(); i++) {
 			this.locks[i].writeLock().lock();
diff --git a/tlatools/src/tlc2/util/SynchronousDiskIntStack.java b/tlatools/src/tlc2/util/SynchronousDiskIntStack.java
new file mode 100644
index 0000000000000000000000000000000000000000..b66120433be9da7b4676c741617549739c626013
--- /dev/null
+++ b/tlatools/src/tlc2/util/SynchronousDiskIntStack.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+import java.io.File;
+
+import tlc2.output.EC;
+import util.Assert;
+import util.BufferedDataInputStream;
+import util.BufferedDataOutputStream;
+import util.FileUtil;
+
+public class SynchronousDiskIntStack implements IntStack {
+
+	public final static int BufSize = 8388608; // ~32mb
+	private final static int BufSizeMax = BufSize << 5; // ~1024mb
+
+	private final int bufSize;
+	private final String filePrefix;
+	
+	private long size = 0L;
+	private int index = 0;
+
+	private int hiPool = 0;
+	
+	private int[] buf;
+
+	public SynchronousDiskIntStack(String diskdir, String name) {
+		this(diskdir, name, BufSize);
+	}
+	
+	public SynchronousDiskIntStack(String diskdir, String name, int capacity) {
+		// Hard-limit capacity to 1gb per page file
+		capacity = Math.min(BufSizeMax, capacity);
+		this.filePrefix = diskdir + FileUtil.separator + name;
+		this.bufSize = capacity;
+		this.buf = new int[capacity];
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#size()
+	 */
+	public long size() {
+		return this.size;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#pushInt(int)
+	 */
+	public void pushInt(int x) {
+		if (this.index == bufSize) {
+			// flush to disk
+			try {
+				final File poolFile = new File(this.filePrefix + Integer.toString(this.hiPool));
+				poolFile.deleteOnExit();
+				final BufferedDataOutputStream bdos = FileUtil.newBdFOS(false, poolFile);
+				final int len = buf.length;
+				for (int i = 0; i < len; i++) {
+					bdos.writeInt(buf[i]);
+				}
+				bdos.close();
+				this.hiPool++;
+				this.index = 0;
+			} catch (Exception e) {
+				Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[] { "stack", e.getMessage() });
+			}
+		}
+		this.buf[this.index++] = x;
+		this.size++;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#pushLong(long)
+	 */
+	public void pushLong(long x) {
+		this.pushInt((int) (x & 0xFFFFFFFFL));
+		this.pushInt((int) (x >>> 32));
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#popInt()
+	 */
+	public int popInt() {
+		if (this.index == 0 && hasPool()) {
+			// fill buffer
+			try {
+				final File poolFile = new File(this.filePrefix + Integer.toString(this.hiPool - 1));
+				final BufferedDataInputStream bdis = FileUtil.newBdFIS(false, poolFile);
+				final int len = buf.length;
+				for (int i = 0; i < len; i++) {
+					buf[i] = bdis.readInt();
+				}
+				bdis.close();
+				this.hiPool--;
+				this.index = len;
+			} catch (Exception e) {
+				Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[] { "stack", e.getMessage() });
+			}
+		}
+		this.size--;
+		return this.buf[--this.index];
+	}
+	
+	private boolean hasPool() {
+		return this.hiPool >= 0;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#popLong()
+	 */
+	public long popLong() {
+		long high = this.popInt();
+		long low = this.popInt();
+		return (high << 32) | (low & 0xFFFFFFFFL);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.IntStack#reset()
+	 */
+	public void reset() {
+		this.size = 0L;
+		this.index = 0;
+
+		this.hiPool = 0;
+	}
+}
diff --git a/tlatools/src/tlc2/util/Vect.java b/tlatools/src/tlc2/util/Vect.java
index 7557aa1bc240af131ab32e8029eb034306f8e1ae..8d4907820ed4d1fbc12c8195a6839c1cfa95d02d 100644
--- a/tlatools/src/tlc2/util/Vect.java
+++ b/tlatools/src/tlc2/util/Vect.java
@@ -9,21 +9,27 @@ import java.util.Enumeration;
 import java.util.NoSuchElementException;
 import java.util.Vector;
 
-public class Vect implements Cloneable, Serializable {
-  private Object[] elementData;
+/**
+ * TODO it's unclear why we've written our own list implementation; we should consider using existing framework code for this;
+ * 		we've also written our own "Vector" class in SANY...
+ */
+@SuppressWarnings("unchecked")
+public class Vect<E> implements Cloneable, Serializable {
+	private static final long serialVersionUID = 1L;
+
+	
+  private E[] elementData;
   private int elementCount;
          
-  static private final Object[] empty = new Object[0];
-
-  final class Enumerator implements Enumeration {
+  final class Enumerator implements Enumeration<E> {
     int index = 0;
 
     public final boolean hasMoreElements () {
       return (this.index < elementCount);
     }
 
-    public final Object nextElement() {
-      return elementData[index++];
+    public final E nextElement() {
+      return (E) elementData[index++];
     }
   }
 
@@ -32,14 +38,14 @@ public class Vect implements Cloneable, Serializable {
   public Vect(int initialCapacity) {
     this.elementCount = 0;
     if (initialCapacity == 0) {
-      this.elementData = empty ;
+      this.elementData = (E[]) new Object[0];
     }
     else {
-      this.elementData = new Object[initialCapacity];
+      this.elementData = (E[]) new Object[initialCapacity];
     }
   }
 
-  public Vect(Vector v) {
+  public Vect(Vector<E> v) {
     this(v.size());
     int sz = v.size();    
     for (int i = 0; i < sz; i++) {
@@ -47,15 +53,15 @@ public class Vect implements Cloneable, Serializable {
     }
   }
 
-  public final void addElement(Object obj) {
+  public final void addElement(E obj) {
     if (this.elementCount == this.elementData.length) {
       this.ensureCapacity(this.elementCount+1);
     }
     this.elementData[this.elementCount++] = obj;
   }
 
-  public final Vect concat(Vect elems) {
-    Vect v = new Vect();
+  public final Vect<E> concat(Vect<E> elems) {
+    Vect<E> v = new Vect<>();
     for (int i = 0; i < this.elementCount; i++) {
       v.addElement(this.elementData[i]);
     }
@@ -68,7 +74,7 @@ public class Vect implements Cloneable, Serializable {
   public int capacity() { return this.elementData.length; }
 
   public Object clone() {
-    Vect v = new Vect(this.elementData.length);
+    Vect<E> v = new Vect<>(this.elementData.length);
     System.arraycopy(this.elementData, 0, v.elementData, 0, this.elementCount);
     v.elementCount = this.elementCount;
     return v;
@@ -82,11 +88,12 @@ public class Vect implements Cloneable, Serializable {
     System.arraycopy(this.elementData, 0, anArray, 0, this.elementCount);
   }
 
-  public final Object elementAt(int index) {
+  public final E elementAt(int index) {
     return this.elementData[index];
   }
 
-  public Enumeration elements() { return new Vect.Enumerator(); }
+  @SuppressWarnings("rawtypes")
+  public Enumeration<E> elements() { return new Vect.Enumerator(); }
 
   public final void ensureCapacity(int minCapacity) { 
     if (this.elementData.length < minCapacity) {
@@ -95,7 +102,7 @@ public class Vect implements Cloneable, Serializable {
 	newCapacity = minCapacity;
       }
       Object oldBuffer[] = elementData;
-      elementData = new Object[newCapacity];
+      elementData = (E[]) new Object[newCapacity];
       System.arraycopy( oldBuffer, 0, elementData, 0, elementCount);
     }
   }
@@ -113,7 +120,7 @@ public class Vect implements Cloneable, Serializable {
     return -1;
   }
 
-  public final void insertElementAt(Object obj, int index) {
+  public final void insertElementAt(E obj, int index) {
     if (elementCount == elementData.length)
       ensureCapacity(elementCount+1);
 
@@ -142,7 +149,7 @@ public class Vect implements Cloneable, Serializable {
     this.elementData[this.elementCount] = null;
   }
   
-  public final void setElementAt(Object obj, int index)	{
+  public final void setElementAt(E obj, int index)	{
     this.elementData[index] = obj;
   }
 
@@ -165,7 +172,7 @@ public class Vect implements Cloneable, Serializable {
     return elem;
   }
 
-  public final void push(Object elem) {
+  public final void push(E elem) {
     this.addElement(elem);
   }
   
@@ -181,7 +188,7 @@ public class Vect implements Cloneable, Serializable {
 
   public final boolean equals(Object obj) {
     if (!(obj instanceof Vect)) return false;
-    Vect v = (Vect)obj;
+    Vect<E> v = (Vect<E>)obj;
     if (v.size() != this.elementCount) return false;
     for (int i = 0; i < this.elementCount; i++) {
       if (!this.elementData[i].equals(v.elementAt(i))) return false;
diff --git a/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java b/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdea80b92ec7b0c3c954fb09f79b9bc2859c9f81
--- /dev/null
+++ b/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util.statistics;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+
+import javax.management.NotCompliantMBeanException;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.management.TLCStandardMBean;
+import tlc2.util.statistics.management.BucketStatisticsMXWrapper;
+
+public abstract class AbstractBucketStatistics implements IBucketStatistics {
+
+	/**
+	 * Human readable statistics name (used in toString())
+	 */
+	protected final String title;
+
+	public AbstractBucketStatistics(String aTitle) {
+		super();
+		this.title = aTitle;
+	}
+
+	/**
+	 * @param aTitle
+	 *            A title for console pretty printing
+	 * @param pkg
+	 *            A package name for this statistics, e.g. tlc2.tool.liveness
+	 *            for stats are from classes in the liveness package.
+	 * @param name
+	 *            The (class) name of the source of the statistics.
+	 */
+	public AbstractBucketStatistics(final String aTitle, final String pkg, final String name) {
+		this(aTitle);
+		try {
+			//TODO unregister somehow
+			new BucketStatisticsMXWrapper(this, name, pkg);
+		} catch (NotCompliantMBeanException e) {
+			// not expected to happen would cause JMX to be broken, hence just log and
+			// continue
+			MP.printWarning(
+					EC.GENERAL,
+					"Failed to create MBean wrapper for BucketStatistics. No statistics/metrics will be avaiable.",
+					e);
+			TLCStandardMBean.getNullTLCStandardMBean();
+		}
+	}
+
+	public String toString() {
+		final StringBuffer buf = new StringBuffer();
+		buf.append("============================%n");
+		buf.append("=" + title + "=%n");
+		buf.append("============================%n");
+		buf.append(String.format("Observations: %d%n", getObservations()));
+		buf.append(String.format("Min: %d%n", getMin()));
+		buf.append(String.format("Max: %d%n", getMax()));
+		buf.append(String.format("Mean: %.2f%n", getMean()));
+		buf.append(String.format("Median: %d%n", getMedian()));
+		buf.append(String.format("Standard deviation: %.2f%n", getStdDev()));
+		buf.append(String.format("75%%: %.2f%n", getPercentile(0.75d)));
+		buf.append(String.format("95%%: %.2f%n", getPercentile(0.95d)));
+		buf.append(String.format("98%%: %.2f%n", getPercentile(0.98d)));
+		buf.append(String.format("99%%: %.2f%n", getPercentile(0.99d)));
+		buf.append(String.format("99.9%%: %.2f%n", getPercentile(0.999d)));
+		buf.append("numEdges/occurrences (log scale)%n");
+		buf.append("--------------------------------%n");
+		final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator();
+		while(iterator.hasNext()) {
+			Entry<Integer, Long> next = iterator.next();
+			long amount = next.getValue();
+			int i = next.getKey();
+			buf.append(String.format("%02d", i));
+			buf.append(":");
+			buf.append(String.format("%02d", amount));
+			buf.append(" ");
+			for (int j = 0; j < Math.log(amount); j++) {
+				buf.append("#");
+			}
+			buf.append("%n");
+		}
+		buf.append("============================");
+		return buf.toString();
+	}
+
+	public int getMedian() {
+		long l = getObservations();
+		if (l <= 0) {
+			return -1;
+		}
+		// skip forward for as many elements as 1/2 observations. The 
+		// corresponding bucket is the median.
+		long sum = 0L;
+		final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator();
+		while(iterator.hasNext()) {
+			final Entry<Integer, Long> next = iterator.next();
+			sum += next.getValue();
+			if (sum > (l / 2)) {
+				return next.getKey();
+			}
+		}
+		// make compiler happy
+		throw new RuntimeException("bug, shoud not get here");
+	}
+
+	public double getMean() {
+		long sum = 0L;
+		// Sum up values and count
+		final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator();
+		while(iterator.hasNext()) {
+			final Entry<Integer, Long> next = iterator.next();
+			final long value = next.getValue();
+			final int i = next.getKey();
+			sum += value * i;
+		}
+		if (getObservations() > 0) {
+			return (sum / (getObservations() * 1.0d));
+		} else {
+			// No mean yet
+			return -1;
+		}
+	}
+
+	public int getMin() {
+		if (getObservations() <= 0) {
+			return -1;
+		}
+		return getSamples().firstKey();
+	}
+
+	public int getMax() {
+		if (getObservations() <= 0) {
+			return -1;
+		}
+		return getSamples().lastKey();
+	}
+
+	public double getStdDev() {
+		final long N = getObservations();
+		if (N <= 0) {
+			return -1.0d;
+		}
+		final double mean = getMean() * 1.0d;
+		double sum = 0.0d;
+		final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator();
+		while(iterator.hasNext()) {
+			Entry<Integer, Long> next = iterator.next();
+			double Xi = next.getKey() * 1.0d;
+			double diff = Xi - mean;
+			sum += (diff * diff) * ((next.getValue() * 1.0d)); // diff^2
+		}
+		double variance = sum / (N * 1.0d);
+		double stdDev = Math.sqrt(variance);
+		return stdDev;
+	}
+
+	public double getPercentile(double quantile) {
+		if (Double.isNaN(quantile)) {
+			throw new IllegalArgumentException("NaN");
+		}
+		final long obsv = getObservations();
+		if (obsv <= 0) {
+			return -1.0;
+		}
+		// adjust values to valid range
+		quantile = Math.min(1.0, quantile);
+		quantile = Math.max(0, quantile);
+		
+		final NavigableMap<Integer, Long> samples = getSamples();
+
+		// calculate the elements position for the
+		// given quantile
+	    final int pos = (int) ((obsv * 1.0d) * quantile);
+		if (pos > obsv) {
+	    	return samples.size();
+	    }
+	    if (pos < 0) {
+	    	return 0;
+	    }
+	    
+	    // advance to the bucket at position
+	    long cnt = 0l;
+		final Iterator<Entry<Integer, Long>> iterator = samples.entrySet().iterator();
+		while(iterator.hasNext()) {
+			Entry<Integer, Long> next = iterator.next();
+			int i  = next.getKey();
+			cnt += next.getValue();
+			if (cnt > pos) {
+				return i;
+			}
+		}
+		return quantile;
+	}
+
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/util/statistics/BucketStatistics.java b/tlatools/src/tlc2/util/statistics/BucketStatistics.java
index f74fe27f021af9589c850b2fc0aa9c3a41af26c0..049930161ef1a2620388570966d9b8b5e33c21fd 100644
--- a/tlatools/src/tlc2/util/statistics/BucketStatistics.java
+++ b/tlatools/src/tlc2/util/statistics/BucketStatistics.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
  *
  * The MIT License (MIT)
  * 
@@ -23,29 +23,21 @@
  * Contributors:
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
-
 package tlc2.util.statistics;
 
-import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentNavigableMap;
-import java.util.concurrent.ConcurrentSkipListMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.management.NotCompliantMBeanException;
+import java.util.NavigableMap;
+import java.util.TreeMap;
 
-import tlc2.output.EC;
-import tlc2.output.MP;
-import tlc2.tool.management.TLCStandardMBean;
-import tlc2.util.statistics.management.BucketStatisticsMXWrapper;
+public class BucketStatistics extends AbstractBucketStatistics implements IBucketStatistics {
 
-public class BucketStatistics implements IBucketStatistics {
-	
 	/**
 	 * The amount of samples seen by this statistics. It's identical
 	 * to the sum of the value of all buckets.
 	 */
-	protected final AtomicLong observations = new AtomicLong(0);
+	private long observations;
 	
 	/**
 	 * Instead of using an ever-growing list of samples, identical
@@ -54,270 +46,66 @@ public class BucketStatistics implements IBucketStatistics {
 	 * seen two times.
 	 * The map is thread safe, so are the values.
 	 */
-	protected final ConcurrentNavigableMap<Integer, AtomicLong> buckets = new ConcurrentSkipListMap<Integer, AtomicLong>();
-
-	/**
-	 * Human readable statistics name (used in toString())
-	 */
-	private final String title;
-
-	/**
-	 * Upper limit for the number of buckets available for sampling. If a sample
-	 * exceeds maximum, it will go into the very last bucket. It serves as an
-	 * "overflow" bucket.
-	 * Useful if samples flatten out to the right and at many buckets with just
-	 * a single sample would be added.
-	 */
-	private final int maximum;
+	private final Map<Integer, Long> buckets;
 
-	// Used for unit testing only
-	BucketStatistics() {
-		this("Historgram");
-	}
-	
-	/**
-	 * @see {@link BucketStatistics#BucketStatistics(String, int)}
-	 */
-	public BucketStatistics(final String aTitle) {
-		this(aTitle, Integer.MAX_VALUE);
-	}
-	
-	/**
-	 * @see {@link BucketStatistics#BucketStatistics(String, int, String, String)}
-	 */
-	public BucketStatistics(final String aTitle, final String pkg, final String name) {
-		this(aTitle, Integer.MAX_VALUE, pkg, name);
-	}
-	
-	/**
-	 * @param aTitle
-	 *            A title for console pretty printing
-	 * @param aMaxmimum
-	 *            see {@link BucketStatistics#maximum}
-	 */
-	public BucketStatistics(final String aTitle, final int aMaxmimum) {
-		this.title = aTitle;
-		this.maximum  = aMaxmimum;
+	public BucketStatistics(String aTitle) {
+		super(aTitle);
+		this.buckets = new HashMap<Integer, Long>();
 	}
 
-	/**
-	 * @param aTitle
-	 *            A title for console pretty printing
-	 * @param aMaxmimum
-	 *            see {@link BucketStatistics#maximum}
-	 * @param pkg
-	 *            A package name for this statistics, e.g. tlc2.tool.liveness
-	 *            for stats are from classes in the liveness package.
-	 * @param name
-	 *            The (class) name of the source of the statistics.
-	 */
-	public BucketStatistics(final String aTitle, final int aMaxmimum, final String pkg, final String name) {
-		this(aTitle, aMaxmimum);
-		
-		TLCStandardMBean graphStatsMXWrapper;
-		try {
-			//TODO unregister somehow
-			graphStatsMXWrapper = new BucketStatisticsMXWrapper(this, name, pkg);
-		} catch (NotCompliantMBeanException e) {
-			// not expected to happen
-			// would cause JMX to be broken, hence just log and continue
-			MP.printWarning(
-					EC.GENERAL,
-					"Failed to create MBean wrapper for BucketStatistics. No statistics/metrics will be avaiable.",
-					e);
-			graphStatsMXWrapper = TLCStandardMBean.getNullTLCStandardMBean();
-		}
+	public BucketStatistics(String aTitle, final String pkg, final String name) {
+		super(aTitle, pkg, name);
+		this.buckets = new HashMap<Integer, Long>();
 	}
-	
+
 	/* (non-Javadoc)
 	 * @see tlc2.util.statistics.IBucketStatistics#addSample(int)
 	 */
-	public void addSample(final int amount) {
+	public void addSample(int amount) {
 		if (amount < 0) {
 			throw new IllegalArgumentException("Negative amount invalid");
 		}
 		
-		// If the amount exceeds the fixed maximum, increment the overflow
-		// bucket. The overflow bucket is the very last bucket. 
-		final int idx = Math.min(maximum, amount);
-		
-		final AtomicLong atomicLong = buckets.get(idx);
-		if(atomicLong == null) {
-			buckets.putIfAbsent(idx, new AtomicLong(1));
+		Long l = buckets.get(amount);
+		if(l == null) {
+			buckets.put(amount, 1L);
 		} else {
-			atomicLong.incrementAndGet();
+			buckets.replace(amount, ++l);
 		}
-		observations.getAndIncrement();
-	}
-	
-	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getObservations()
-	 */
-	public long getObservations() {
-		return observations.get();
-	}
-	
-	/* (non-Javadoc)
-	 * @see java.lang.Object#toString()
-	 */
-	public String toString() {
-		final StringBuffer buf = new StringBuffer();
-		buf.append("============================\n");
-		buf.append("=" + title + "=\n");
-		buf.append("============================\n");
-		buf.append(String.format("Observations: %d\n", observations.get()));
-		buf.append(String.format("Min: %d\n", getMin()));
-		buf.append(String.format("Max: %d\n", getMax()));
-		buf.append(String.format("Mean: %.2f\n", getMean()));
-		buf.append(String.format("Median: %d\n", getMedian()));
-		buf.append(String.format("Standard deviation: %.2f\n", getStdDev()));
-		buf.append(String.format("75%%: %.2f\n", getPercentile(0.75d)));
-		buf.append(String.format("95%%: %.2f\n", getPercentile(0.95d)));
-		buf.append(String.format("98%%: %.2f\n", getPercentile(0.98d)));
-		buf.append(String.format("99%%: %.2f\n", getPercentile(0.99d)));
-		buf.append(String.format("99.9%%: %.2f\n", getPercentile(0.999d)));
-		buf.append("numEdges/occurrences (log scale)\n");
-		buf.append("--------------------------------\n");
-		final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator();
-		while(iterator.hasNext()) {
-			Entry<Integer, AtomicLong> next = iterator.next();
-			long amount = next.getValue().get();
-			int i = next.getKey();
-			buf.append(String.format("%02d", i));
-			buf.append(":");
-			buf.append(String.format("%02d", amount));
-			buf.append(" ");
-			for (int j = 0; j < Math.log(amount); j++) {
-				buf.append("#");
-			}
-			buf.append("\n");
-		}
-		buf.append("============================");
-		return buf.toString();
-	}
-
-	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getMedian()
-	 */
-	public int getMedian() {
-		long l = observations.get();
-		if (l <= 0) {
-			return -1;
-		}
-		// skip forward for as many elements as 1/2 observations. The 
-		// corresponding bucket is the median.
-		long sum = 0L;
-		final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator();
-		while(iterator.hasNext()) {
-			final Entry<Integer, AtomicLong> next = iterator.next();
-			sum += next.getValue().get();
-			if (sum > (l / 2)) {
-				return next.getKey();
-			}
-		}
-		// make compiler happy
-		throw new RuntimeException("bug, shoud not get here");
-	}
-
-	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getMean()
-	 */
-	public double getMean() {
-		long sum = 0L;
-		// Sum up values and count
-		final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator();
-		while(iterator.hasNext()) {
-			final Entry<Integer, AtomicLong> next = iterator.next();
-			final long value = next.getValue().get();
-			final int i = next.getKey();
-			sum += value * i;
-		}
-		if (observations.get() > 0) {
-			return (sum / (observations.get() * 1.0d));
-		} else {
-			// No mean yet
-			return -1;
-		}
-	}
-
-	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getMin()
-	 */
-	public int getMin() {
-		if (observations.get() <= 0) {
-			return -1;
-		}
-		return buckets.firstKey();
+		observations++;
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getMax()
+	 * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations()
 	 */
-	public int getMax() {
-		if (observations.get() <= 0) {
-			return -1;
-		}
-		return buckets.lastKey();
+	public long getObservations() {
+		return observations;
 	}
 
 	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getStdDev()
+	 * @see tlc2.util.statistics.IBucketStatistics#getSamples()
 	 */
-	public double getStdDev() {
-		final long N = observations.get();
-		if (N <= 0) {
-			return -1.0d;
-		}
-		final double mean = getMean() * 1.0d;
-		double sum = 0.0d;
-		final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator();
-		while(iterator.hasNext()) {
-			Entry<Integer, AtomicLong> next = iterator.next();
-			double Xi = next.getKey() * 1.0d;
-			double diff = Xi - mean;
-			sum += (diff * diff) * ((next.getValue().get() * 1.0d)); // diff²
+	public NavigableMap<Integer, Long> getSamples() {
+		final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>();
+		for (Entry<Integer, Long> entry : this.buckets.entrySet()) {
+			res.put(entry.getKey(), entry.getValue());
 		}
-		double variance = sum / (N * 1.0d);
-		double stdDev = Math.sqrt(variance);
-		return stdDev;
+		return res;
 	}
 
-	/* (non-Javadoc)
-	 * @see tlc2.util.statistics.IBucketStatistics#getPercentile(double)
+	/**
+	 * Adds the observations of <code>stat</code> to this {@link BucketStatistics}.
 	 */
-	public double getPercentile(double quantile) {
-		if (Double.isNaN(quantile)) {
-			throw new IllegalArgumentException("NaN");
-		}
-		final long obsv = observations.get();
-		if (obsv <= 0) {
-			return -1.0;
-		}
-		// adjust values to valid range
-		quantile = Math.min(1.0, quantile);
-		quantile = Math.max(0, quantile);
+	public void add(final IBucketStatistics stat) {
+		this.observations += stat.getObservations();
 		
-		// calculate the elements position for the
-		// given quantile
-        final int pos = (int) ((obsv * 1.0d) * quantile);
-        if (pos > obsv) {
-        	return buckets.size();
-        }
-        if (pos < 0) {
-        	return 0;
-        }
-        
-        // advance to the bucket at position
-        long cnt = 0l;
-		final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator();
-		while(iterator.hasNext()) {
-			Entry<Integer, AtomicLong> next = iterator.next();
-			int i  = next.getKey();
-			cnt += next.getValue().get();
-			if (cnt > pos) {
-				return i;
+		for (Entry<Integer, Long> entry : stat.getSamples().entrySet()) {
+			final Long l = this.buckets.get(entry.getKey());
+			if (l == null) {
+				this.buckets.put(entry.getKey(), entry.getValue());
+			} else {
+				this.buckets.replace(entry.getKey(), l + entry.getValue());
 			}
 		}
-		return quantile;
 	}
 }
diff --git a/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java b/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..a69e0eb765a38e21eeb8f83309ed0800322fa13c
--- /dev/null
+++ b/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util.statistics;
+
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAdder;
+
+public class ConcurrentBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics {
+
+	/**
+	 * The amount of samples seen by this statistics. It's identical
+	 * to the sum of the value of all buckets.
+	 */
+	private final LongAdder observations = new LongAdder();
+
+	/**
+	 * Instead of using an ever-growing list of samples, identical
+	 * samples are counted in a bucket. E.g. the sample 5 is stored
+	 * in a bucket with key 5 and a value of 2 if the sample has been
+	 * seen two times.
+	 * The map is thread safe, so are the values.
+	 */
+	private final ConcurrentNavigableMap<Integer, AtomicLong> buckets = new ConcurrentSkipListMap<Integer, AtomicLong>();
+	
+	ConcurrentBucketStatistics() {
+		super("Concurrent Historgram");
+	}
+	
+	/**
+	 * @see {@link BucketStatistics#BucketStatistics(String, int)}
+	 */
+	public ConcurrentBucketStatistics(final String aTitle) {
+		super(aTitle);
+	}
+	
+	/**
+	 * @see {@link BucketStatistics#BucketStatistics(String, int, String, String)}
+	 */
+	public ConcurrentBucketStatistics(final String aTitle, final String pkg, final String name) {
+		super(aTitle, pkg, name);
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#addSample(int)
+	 */
+	public void addSample(final int amount) {
+		if (amount < 0) {
+			throw new IllegalArgumentException("Negative amount invalid");
+		}
+		
+		final AtomicLong atomicLong = buckets.get(amount);
+		if(atomicLong == null) {
+			buckets.putIfAbsent(amount, new AtomicLong(1));
+		} else {
+			atomicLong.incrementAndGet();
+		}
+		observations.increment();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations()
+	 */
+	public long getObservations() {
+		return observations.sum();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#getSamples()
+	 */
+	public NavigableMap<Integer, Long> getSamples() {
+		final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>();
+		for (Entry<Integer, AtomicLong> entry : buckets.entrySet()) {
+			res.put(entry.getKey(), entry.getValue().get());
+		}
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/util/statistics/CounterStatistic.java b/tlatools/src/tlc2/util/statistics/CounterStatistic.java
new file mode 100644
index 0000000000000000000000000000000000000000..39fe31a71138a19d24539c5d09068d7782013b41
--- /dev/null
+++ b/tlatools/src/tlc2/util/statistics/CounterStatistic.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util.statistics;
+
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BooleanSupplier;
+
+public abstract class CounterStatistic {
+	
+	public static CounterStatistic getInstance(final BooleanSupplier s) {
+		if (s.getAsBoolean()) {
+			return new LongAdderCounterStatistic();
+		} else {
+			return new NoopCounterStatistic();
+		}
+	}
+	
+	public abstract void increment();
+	
+	public abstract void add(final long evalCount);
+
+	public abstract long getCount();
+
+	private CounterStatistic() {
+		// private ctor
+	}
+
+	private static class NoopCounterStatistic extends CounterStatistic {
+		
+		@Override
+		public final long getCount() {
+			return 0;
+		}
+		
+		@Override
+		public final void increment() {
+			// noop
+		}
+		
+		@Override
+		public void add(long evalCount) {
+			// noop
+		}
+	}
+	
+	private static class LongAdderCounterStatistic extends CounterStatistic {
+
+		private final LongAdder adder = new LongAdder();
+
+		@Override
+		public final long getCount() {
+			return this.adder.sum();
+		}
+
+		@Override
+		public final void increment() {
+			adder.increment();
+		}
+
+		@Override
+		public void add(long evalCount) {
+			adder.add(evalCount);
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java b/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java
index ddecbf410538c29ef6cbfa4cc85e5eb9ea4469e8..72880929f53ffbc9db5ba8ea8d3d9f5506ae670d 100644
--- a/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java
+++ b/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java
@@ -26,6 +26,9 @@
 
 package tlc2.util.statistics;
 
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
 /**
  * Doesn't really do a thing. Can be instantiated multiple times contrary to
  * BucketStatistics which registers itself via JMX.
@@ -87,4 +90,17 @@ public class DummyBucketStatistics implements IBucketStatistics {
 	public double getPercentile(double quantile) {
 		return 0;
 	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#getSamples()
+	 */
+	public NavigableMap<Integer, Long> getSamples() {
+		return new TreeMap<Integer, Long>();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#addSamples(tlc2.util.statistics.IBucketStatistics)
+	 */
+	public void addSamples(IBucketStatistics stat) {
+	}
 }
diff --git a/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java b/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1743379e705b7c87f39a0d29df682a76115a128
--- /dev/null
+++ b/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util.statistics;
+
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+public class FixedSizedBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics {
+
+	/**
+	 * The amount of samples seen by this statistics. It's identical
+	 * to the sum of the value of all buckets.
+	 */
+	private long observations;
+	
+	/**
+	 * Instead of using an ever-growing list of samples, identical
+	 * samples are counted in a bucket. E.g. the sample 5 is stored
+	 * in a bucket with key 5 and a value of 2 if the sample has been
+	 * seen two times.
+	 * The map is thread safe, so are the values.
+	 */
+	private final long[] buckets;
+	
+	/**
+	 * @param aTitle
+	 *            A title for console pretty printing
+	 * @param aMaxmimum
+	 *            see {@link BucketStatistics#maximum}
+	 */
+	public FixedSizedBucketStatistics(final String aTitle, final int aMaxmimum) {
+		super(aTitle);
+		this.buckets = new long[aMaxmimum];
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#addSample(int)
+	 */
+	public void addSample(int amount) {
+		if (amount < 0) {
+			throw new IllegalArgumentException("Negative amount invalid");
+		}
+		
+		// If the amount exceeds the fixed maximum, increment the overflow
+		// bucket. The overflow bucket is the very last bucket. 
+		final int idx = Math.min(this.buckets.length - 1, amount);
+		
+		this.buckets[idx]++;
+		this.observations++;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations()
+	 */
+	public long getObservations() {
+		return observations;
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#getSamples()
+	 */
+	public NavigableMap<Integer, Long> getSamples() {
+		final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>();
+		for (int i = 0; i < this.buckets.length; i++) {
+			long value = this.buckets[i];
+			if (value > 0) {
+				res.put(i, value);
+			}
+		}
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java b/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6da9197785319d207a01792246e3087d8307326
--- /dev/null
+++ b/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util.statistics;
+
+import java.util.NavigableMap;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.atomic.LongAdder;
+
+public class FixedSizedConcurrentBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics {
+	
+	/**
+	 * The amount of samples seen by this statistics. It's identical
+	 * to the sum of the value of all buckets.
+	 */
+	private final LongAdder observations = new LongAdder();
+	
+	/**
+	 * Instead of using an ever-growing list of samples, identical
+	 * samples are counted in a bucket. E.g. the sample 5 is stored
+	 * in a bucket with key 5 and a value of 2 if the sample has been
+	 * seen two times.
+	 * The map is thread safe, so are the values.
+	 */
+	protected final AtomicLongArray buckets;
+
+	/**
+	 * @param aTitle
+	 *            A title for console pretty printing
+	 * @param aMaxmimum
+	 *            see {@link BucketStatistics#maximum}
+	 */
+	public FixedSizedConcurrentBucketStatistics(final String aTitle, final int aMaxmimum) {
+		super(aTitle);
+		this.buckets = new AtomicLongArray(aMaxmimum);
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#addSample(int)
+	 */
+	public void addSample(final int amount) {
+		if (amount < 0) {
+			throw new IllegalArgumentException("Negative amount invalid");
+		}
+		
+		// If the amount exceeds the fixed maximum, increment the overflow
+		// bucket. The overflow bucket is the very last bucket. 
+		final int idx = Math.min(this.buckets.length() - 1, amount);
+		
+		this.buckets.incrementAndGet(idx);
+		this.observations.increment();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations()
+	 */
+	public long getObservations() {
+		return observations.sum();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.util.statistics.IBucketStatistics#getSamples()
+	 */
+	public NavigableMap<Integer, Long> getSamples() {
+		final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>();
+		for (int i = 0; i < this.buckets.length(); i++) {
+			long value = this.buckets.get(i);
+			if (value > 0) {
+				res.put(i, value);
+			}
+		}
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/util/statistics/IBucketStatistics.java b/tlatools/src/tlc2/util/statistics/IBucketStatistics.java
index 05bda1b1f06ca1144191efe56b3b526da0acc109..490dcecda8235ad32a5a3f8df834bb4651b00957 100644
--- a/tlatools/src/tlc2/util/statistics/IBucketStatistics.java
+++ b/tlatools/src/tlc2/util/statistics/IBucketStatistics.java
@@ -1,10 +1,15 @@
 package tlc2.util.statistics;
 
+import java.util.NavigableMap;
+
+/**
+ * Keeps statistics about any samples added.
+ */
 public interface IBucketStatistics {
 
 	/**
 	 * @param amount
-	 *            Add a sample to the stastics. Allowed range is 0 <= sample <=
+	 *            Add a sample to the statistics. Allowed range is 0 <= sample <=
 	 *            Integer.MAX_VALUE
 	 */
 	void addSample(int amount);
@@ -45,4 +50,5 @@ public interface IBucketStatistics {
 	 */
 	double getPercentile(double quantile);
 
+	NavigableMap<Integer, Long> getSamples();
 }
\ No newline at end of file
diff --git a/tlatools/src/tlc2/value/BoolValue.java b/tlatools/src/tlc2/value/BoolValue.java
deleted file mode 100644
index 3df9f9784c6f1e12077d6a69efe0c38bcd347b7a..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/BoolValue.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:01:16 PST by lamport
-//      modified on Fri Aug 10 15:07:07 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.util.FP64;
-import util.Assert;
-
-public class BoolValue extends Value {
-  public boolean val;   // the boolean
-
-  /* Constructor */
-  public BoolValue(boolean b) { this.val = b; }
-
-  public final byte getKind() { return BOOLVALUE; }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof BoolValue) {
-      int x = this.val ? 1 : 0;
-      int y = ((BoolValue)obj).val ? 1 : 0;
-      return x - y;
-    }
-    if (!(obj instanceof ModelValue)) {
-      Assert.fail("Attempted to compare boolean " + ppr(this.toString()) +
-		  " with non-boolean:\n" + ppr(obj.toString()));
-    }
-    return 1;
-  }
-
-  public final boolean equals(Object obj) {
-    if (obj instanceof BoolValue) {
-      return this.val == ((BoolValue)obj).val;
-    }
-    if (!(obj instanceof ModelValue)) {
-      Assert.fail("Attempted to compare equality of boolean " + ppr(this.toString()) +
-		  " with non-boolean:\n" + ppr(obj.toString()));
-    }
-    return ((ModelValue) obj).modelValueEquals(this) ;
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of the boolean " + ppr(this.toString()));
-    return false;   // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the boolean " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;   // make compiler happy
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT construct to the boolean " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the boolean " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the boolean " +
-		ppr(this.toString()) + ".");
-    return 0;   // make compiler happy
-  }
-
-  public final boolean isNormalized() { return true; }
-
-  public final void normalize() { /*nop*/ }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return ((val instanceof BoolValue) &&
-	    this.val == ((BoolValue)val).val);
-  }
-
-  /* The fingerprint method */
-  public final long fingerPrint(long fp) {
-    fp = FP64.Extend(fp, BOOLVALUE) ;
-    fp = FP64.Extend(fp, (this.val) ? 't' : 'f') ;
-    return fp ;
-  }
-
-  public final Value permute(MVPerm perm) { return this; }
-
-  /* The string representation */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return sb.append((this.val) ? "TRUE" : "FALSE");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/Enumerable.java b/tlatools/src/tlc2/value/Enumerable.java
deleted file mode 100644
index 810f4173b50f7fd813dcd2709cff877f4d0cdcee..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/Enumerable.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:20:54 PST by lamport
-//      modified on Thu Mar 11 21:25:20 PST 1999 by yuanyu
-
-package tlc2.value;
-
-public interface Enumerable {
-
-  public int size();
-  public boolean member(Value elem);
-  public ValueEnumeration elements();
-
-}
-
diff --git a/tlatools/src/tlc2/value/FcnLambdaValue.java b/tlatools/src/tlc2/value/FcnLambdaValue.java
deleted file mode 100644
index 6846a525f33cafd29b969dfa3aaf83d0a1d7aaf8..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/FcnLambdaValue.java
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Wed  4 Jul 2007 at 17:31:23 PST by lamport
-//      modified on Thu Dec  6 21:46:34 PST 2001 by yuanyu
-
-package tlc2.value;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import tla2sany.semantic.FormalParamNode;
-import tla2sany.semantic.SemanticNode;
-import tla2sany.semantic.SymbolNode;
-import tlc2.tool.EvalControl;
-import tlc2.tool.EvalException;
-import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
-import tlc2.util.Context;
-import util.Assert;
-
-public class FcnLambdaValue extends Value implements Applicable {
-  public FcnParams params;       // the function formals
-  public SemanticNode body;      // the function body
-  public ValueExcept[] excepts;  // the EXCEPTs
-  public Tool tool;
-  public Context con;
-  public TLCState state;
-  public TLCState pstate;
-  public int control;
-  public FcnRcdValue fcnRcd;
-
-  /* Constructor */
-  public FcnLambdaValue(FcnParams params, SemanticNode body, Tool tool,
-			Context c, TLCState s0, TLCState s1, int control) {
-    this.params = params;
-    this.body = body;
-    this.excepts = null;
-    this.tool = tool;
-    this.con = c;
-    this.state = s0.copy();  // copy() added 12 Mar 2010 by Yuan Yu. 
-    if (s1 != null) {        // see SetPredValue constructor.
-        this.pstate = s1.copy(); 
-    } else {
-        this.pstate = null;
-    }
-    
-    this.control = control;
-    this.fcnRcd = null;
-  }
-
-  public FcnLambdaValue(FcnLambdaValue fcn) {
-    this.params = fcn.params;
-    this.body = fcn.body;
-    this.excepts = fcn.excepts;
-    this.tool = fcn.tool;
-    this.con = fcn.con;
-    this.state = fcn.state;
-    this.pstate = fcn.pstate;
-    this.control = fcn.control;
-    this.fcnRcd = fcn.fcnRcd;
-  }
-
-  public final byte getKind() { return FCNLAMBDAVALUE; }
-
-  public final void makeRecursive(SymbolNode fname) {
-    this.con = this.con.cons(fname, this);
-    this.control = EvalControl.setKeepLazy(this.control);
-  }
-
-  public final int compareTo(Object obj) {
-    FcnRcdValue fcn = FcnRcdValue.convert(this);
-    return fcn.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    FcnRcdValue fcn = FcnRcdValue.convert(this);
-    return fcn.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of the function " + ppr(this.toString()));
-    return false;   // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the function:\n" + ppr(this.toString()) +
-		"\nis a finite set.");
-    return false;   // make compiler happy
-  }
-
-  /* Apply this function to the arguments given by args.  */  
-  public final Value apply(Value args, int control) throws EvalException {
-    if (this.fcnRcd != null) {
-      return this.fcnRcd.apply(args, control);
-    }
-
-    // First, find all the excepts that match args.
-    Value res = null;
-    int num = 0;
-    ValueExcept[] excepts1 = null;
-    if (this.excepts != null) {
-      int exlen = this.excepts.length;
-      for (int i = exlen-1; i >= 0; i--) {
-	ValueExcept ex = this.excepts[i];
-	Value arg = ex.current();
-	boolean inExcept = true;
-	inExcept = arg.equals(args);
-	if (inExcept) {
-	  if (ex.isLast()) { res = ex.value; break; }
-	  if (excepts1 == null) excepts1 = new ValueExcept[exlen];
-	  excepts1[num++] = new ValueExcept(ex, ex.idx+1);
-	}
-      }
-    }
-
-    // Second, evaluate the function application.
-    if (res == null) {
-      Context c1 = this.con;
-      FormalParamNode[][] formals = this.params.formals;
-      Value[] domains = this.params.domains;
-      boolean[] isTuples = this.params.isTuples;
-      int plen = this.params.length();
-    
-      if (plen == 1) {
-	if (!domains[0].member(args)) {
-	  Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-		      ",\nthe first argument is:\n" + Value.ppr(args.toString()) +
-		      "\nwhich is not in its domain.\n");
-	}
-	if (isTuples[0]) {
-	  FormalParamNode[] ids = formals[0];
-	  TupleValue argVal = TupleValue.convert(args);
-	  if (argVal == null) {
-	    Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-			",\nthe first argument is:\n" + Value.ppr(args.toString()) +
-			"\nwhich does not match its formal parameter.\n");
-	  }
-	  if (argVal.size() != ids.length) return null;
-	  Value[] elems = argVal.elems;
-	  for (int i = 0; i < ids.length; i++) {
-	    c1 = c1.cons(ids[i], elems[i]);
-	  }
-	}
-	else {
-	  c1 = c1.cons(formals[0][0], args);
-	}
-      }
-      else {
-	TupleValue tv = TupleValue.convert(args);
-	if (tv == null) {
-	  Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-		      ",\nthe argument list is:\n" + Value.ppr(args.toString()) +
-		      "\nwhich does not match its formal parameter.\n");
-	}
-	Value[] elems = tv.elems;
-	int argn = 0;
-	for (int i = 0; i < formals.length; i++) {
-	  FormalParamNode[] ids = formals[i];
-	  Value domain = domains[i];	  
-	  if (isTuples[i]) {
-	    if (!domain.member(elems[argn])) {
-	      Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-			  ",\nthe argument number " + (argn+1) + " is:\n" +
-			  Value.ppr(elems[argn].toString()) +
-			  "\nwhich is not in its domain.\n");
-	    }
-	    TupleValue tv1 = TupleValue.convert(elems[argn++]);
-	    if (tv1 == null || tv1.size() != ids.length) {
-	      Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-			  ",\nthe argument number " + argn + " is:\n" +
-			  Value.ppr(elems[argn-1].toString()) +
-			  "\nwhich does not match its formal parameter.\n");
-	    }
-	    Value[] avals = tv1.elems;
-	    for (int j = 0; j < ids.length; j++) {
-	      c1 = c1.cons(ids[j], avals[j]);
-	    }	  
-	  }
-	  else {
-	    for (int j = 0; j < ids.length; j++) {
-	      if (!domain.member(elems[argn])) {
-		Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-			    ",\nthe argument number " + (argn+1) + " is:\n" +
-			    Value.ppr(elems[argn].toString()) + "\nwhich is not in its domain.\n");
-	      }
-	      c1 = c1.cons(ids[j], elems[argn++]);
-	    }
-	  }
-	}
-      }
-      res = this.tool.eval(this.body, c1, this.state, this.pstate, control);
-    }
-    
-    // Finally, apply the matching excepts on the result.
-    if (num == 0) return res;
-    ValueExcept[] excepts2 = new ValueExcept[num];
-    for (int i = 0; i < num; i++) {
-      excepts2[num-1-i] = excepts1[i];
-    }
-    return res.takeExcept(excepts2);
-  }
-
-  /* This one does not seem to be needed anymore.  */
-  public final Value apply(Value[] args, int control) throws EvalException {
-    return this.apply(new TupleValue(args), control);
-  }
-
-  public final Value select(Value arg) {
-    if (this.fcnRcd != null) {
-      return this.fcnRcd.select(arg);
-    }
-
-    // First, find all the excepts that match arg.
-    Value res = null;
-    int num = 0;
-    ValueExcept[] excepts1 = null;
-    if (this.excepts != null) {
-      int exlen = this.excepts.length;
-      for (int i = exlen-1; i >= 0; i--) {
-	ValueExcept ex = this.excepts[i];
-	Value exArg = ex.current();
-	boolean inExcept = exArg.equals(arg);
-	if (inExcept) {
-	  if (ex.isLast()) { res = ex.value; break; }
-	  if (excepts1 == null) excepts1 = new ValueExcept[exlen];
-	  excepts1[num++] = new ValueExcept(ex, ex.idx+1);
-	}
-      }
-    }
-
-    // Second, evaluate the function application.
-    if (res == null) {
-      Context c1 = this.con;
-      FormalParamNode[][] formals = this.params.formals;
-      Value[] domains = this.params.domains;
-      boolean[] isTuples = this.params.isTuples;
-      int plen = this.params.length();
-    
-      if (plen == 1) {
-	if (!domains[0].member(arg)) return null;
-	if (isTuples[0]) {
-	  FormalParamNode[] ids = formals[0];
-	  TupleValue argVal = TupleValue.convert(arg);
-	  /*
-	   * SZA: Changed from argVal.toString() to arg.toString() to prevent a NullPointerException
-	   */
-	  if (argVal == null) {
-	    Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-			",\nthe first argument is:\n" + Value.ppr(arg.toString()) +
-			"\nwhich does not match its formal parameter.\n");
-	  }
-	  if (argVal.size() != ids.length) return null;
-	  Value[] elems = argVal.elems;
-	  for (int i = 0; i < ids.length; i++) {
-	    c1 = c1.cons(ids[i], elems[i]);
-	  }
-	}
-	else {
-	  c1 = c1.cons(formals[0][0], arg);
-	}
-      }
-      else {
-	TupleValue tv = TupleValue.convert(arg);
-	if (tv == null) {
-	  Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-		      ",\nthe argument list is:\n" + Value.ppr(arg.toString()) +
-		      "\nwhich does not match its formal parameter.\n");
-	}
-	Value[] elems = tv.elems;
-	int argn = 0;
-	for (int i = 0; i < formals.length; i++) {
-	  FormalParamNode[] ids = formals[i];
-	  Value domain = domains[i];	  
-	  if (isTuples[i]) {
-	    if (!domain.member(elems[argn])) return null;
-	    TupleValue tv1 = TupleValue.convert(elems[argn++]);
-	    if (tv1 == null) {
-	      Assert.fail("In applying the function\n" + Value.ppr(this.toString()) +
-			  ",\nthe argument number " + argn + " is:\n" +
-			  Value.ppr(elems[argn-1].toString()) +
-			  "\nwhich does not match its formal parameter.\n");
-	    }
-	    if (tv1.size() != ids.length) return null;
-	    Value[] avals = tv1.elems;
-	    for (int j = 0; j < ids.length; j++) {
-	      c1 = c1.cons(ids[j], avals[j]);
-	    }	  
-	  }
-	  else {
-	    for (int j = 0; j < ids.length; j++) {
-	      if (!domain.member(elems[argn])) return null;
-	      c1 = c1.cons(ids[j], elems[argn++]);
-	    }
-	  }
-	}
-      }
-      res = this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
-    }
-    
-    // Finally, apply the matching excepts on the result.
-    if (num == 0) return res;
-    ValueExcept[] excepts2 = new ValueExcept[num];
-    for (int i = 0; i < num; i++) {
-      excepts2[num-1-i] = excepts1[i];
-    }
-    return res.takeExcept(excepts2);
-  }
-  
-  /* This method returns a new function value by taking except. */
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx >= ex.path.length) return ex.value;
-    
-    if (this.fcnRcd != null) {
-      return this.fcnRcd.takeExcept(ex);
-    }
-    FcnLambdaValue fcn = new FcnLambdaValue(this);
-    if (this.excepts == null) {
-      fcn.excepts = new ValueExcept[1];
-      fcn.excepts[0] = ex;
-    }
-    else {
-      int exlen = this.excepts.length;
-      fcn.excepts = new ValueExcept[exlen+1];
-      for (int i = 0; i < exlen; i++) {
-	fcn.excepts[i] = this.excepts[i];
-      }
-      fcn.excepts[exlen] = ex;
-    }
-    return fcn;
-  }
-
-  /* This method returns a new function value by taking excepts. */
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (this.fcnRcd != null) {
-      return this.fcnRcd.takeExcept(exs);
-    }
-    FcnLambdaValue fcn = new FcnLambdaValue(this);
-    int exslen = exs.length;
-    if (exslen != 0) {
-      int i = 0;
-      for (i = exs.length-1; i >= 0; i--) {
-	if (exs[i].idx >= exs[i].path.length) break;
-      }
-      if (i >= 0) {
-	int xlen = exslen-i-1;
-	fcn.excepts = new ValueExcept[xlen];
-	System.arraycopy(exs, i+1, fcn.excepts, 0, xlen);
-      }
-      else if (this.excepts == null) {
-	fcn.excepts = new ValueExcept[exslen];
-	System.arraycopy(exs, 0, fcn.excepts, 0, exslen);
-      }
-      else {
-	int len = this.excepts.length;
-	fcn.excepts = new ValueExcept[len + exslen];
-	System.arraycopy(this.excepts, 0, fcn.excepts, 0, len);
-	System.arraycopy(exs, 0, fcn.excepts, len, exslen);
-      }
-    }
-    return fcn;
-  }
-
-  public final Value getDomain() {
-    if (this.fcnRcd != null) {
-      return this.fcnRcd.getDomain();
-    }
-    int len = this.params.length();
-    if (len == 1) {
-      return this.params.domains[0];
-    }
-    Value[] sets = new Value[len];
-    int dlen = this.params.domains.length;
-    boolean[] isTuples = this.params.isTuples;
-    int idx = 0;
-    for (int i = 0; i < dlen; i++) {
-      FormalParamNode[] formal = this.params.formals[i];
-      Value domain = this.params.domains[i];
-      if (isTuples[i]) {
-	sets[idx++] = domain;
-      }
-      else {
-	for (int j = 0; j < formal.length; j++) {
-	  sets[idx++] = domain;
-	}
-      }
-    }
-    return new SetOfTuplesValue(sets);
-  }
-  
-  public final int size() {
-    if (this.fcnRcd == null) {
-      return this.params.size();
-    }
-    return this.fcnRcd.size();
-  }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() {
-    FcnLambdaValue fcn = new FcnLambdaValue(this);
-    // A bug occured when printing a function whose domain is a Cartesian product because this.fcnRcd 
-    // is null at this point.  On 5 Mar 2012, LL wrapped the following null test around the assignment.
-    if (this.fcnRcd != null) {
-       fcn.fcnRcd = (FcnRcdValue)this.fcnRcd.deepCopy();
-    }
-    return fcn;
-  }
-
-  public final boolean assignable(Value val) {
-    return (val instanceof FcnLambdaValue);
-  }
-
-  private void readObject(ObjectInputStream ois)
-  throws IOException, ClassNotFoundException {
-    this.fcnRcd = (FcnRcdValue)ois.readObject();
-  }
-
-  private void writeObject(ObjectOutputStream oos) throws IOException {
-    FcnRcdValue res = this.toFcnRcd();
-    oos.writeObject(res);
-  }
-  
-  public final boolean isNormalized() {
-    if (this.fcnRcd == null) {
-      return false;
-    }
-    return this.fcnRcd.isNormalized();
-  }
-
-  public final void normalize() {
-    if (this.fcnRcd != null) {
-      this.fcnRcd.normalize();
-    }
-  }
-
-  public final FcnRcdValue toFcnRcd() {
-    if (this.fcnRcd == null) {
-      int sz = this.params.size();
-      FormalParamNode[][] formals = this.params.formals;
-      boolean[] isTuples = this.params.isTuples;
-    
-      Value[] domain = new Value[sz];
-      Value[] values = new Value[sz];
-      int idx = 0;
-      ValueEnumeration Enum = this.params.elements();    
-      Value arg;
-      if (this.params.length() == 1) {
-	while ((arg = Enum.nextElement()) != null) {
-	  domain[idx] = arg;
-	  Context c1 = this.con;
-	  if (isTuples[0]) {
-	    FormalParamNode[] ids = formals[0];
-	    Value[] avals = ((TupleValue)arg).elems;
-	    for (int j = 0; j < ids.length; j++) {
-	      c1 = c1.cons(ids[j], avals[j]);
-	    }
-	  }
-	  else {
-	    c1 = c1.cons(formals[0][0], arg);
-	  }
-	  values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
-	}
-      }
-      else {
-	while ((arg = Enum.nextElement()) != null) {
-	  domain[idx] = arg;
-	  Value[] argList = ((TupleValue)arg).elems;
-	  int argn = 0;
-	  Context c1 = this.con;
-	  for (int i = 0; i < formals.length; i++) {
-	    FormalParamNode[] ids = formals[i];
-	    if (isTuples[i]) {
-	      Value[] avals = ((TupleValue)argList[argn++]).elems;
-	      for (int j = 0; j < ids.length; j++) {
-		c1 = c1.cons(ids[j], avals[j]);
-	      }
-	    }
-	    else {
-	      for (int j = 0; j < ids.length; j++) {
-		c1 = c1.cons(ids[j], argList[argn++]);
-	      }
-	    }
-	  }
-	  values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
-	}
-      }
-      this.fcnRcd = new FcnRcdValue(domain, values, false);
-      if (this.excepts != null) {
-	this.fcnRcd = (FcnRcdValue)fcnRcd.takeExcept(this.excepts);
-      }
-    }
-    return this.fcnRcd;
-  }
-  
-  /* The fingerprint methods.  */
-  public final long fingerPrint(long fp) {
-    FcnRcdValue fcn = FcnRcdValue.convert(this);
-    return fcn.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    FcnRcdValue fcn = FcnRcdValue.convert(this);    
-    return fcn.permute(perm);
-  }
-  
-  /* The string representation of this function.  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    if (expand || this.params == null) {
-      try {
-	Value val = FcnRcdValue.convert(this);
-	return val.toString(sb, offset);
-      }
-      catch (Throwable e) { /*SKIP*/ }
-    }
-    sb.append("[" + this.params.toString());
-    sb.append(" |-> <expression " + this.body + ">]");
-    return sb;
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/FcnRcdValue.java b/tlatools/src/tlc2/value/FcnRcdValue.java
deleted file mode 100644
index f47940cd5531193837de4ec085e21ad7ba1af540..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/FcnRcdValue.java
+++ /dev/null
@@ -1,627 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:07:06 PST by lamport
-//      modified on Fri Aug 10 15:07:25 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import java.util.Arrays;
-
-import tlc2.tool.EvalControl;
-import tlc2.util.FP64;
-import util.Assert;
-
-public class FcnRcdValue extends Value implements Applicable {
-  public Value[] domain;
-  public IntervalValue intv;
-  public Value[] values;
-  private boolean isNorm;
-  private int[] indexTbl;  // speed up function application
-
-  /* Constructor */
-  public FcnRcdValue(Value[] domain, Value[] values, boolean isNorm) {
-    this.domain = domain;
-    this.values = values;
-    this.intv = null;
-    this.isNorm = isNorm;
-    this.indexTbl = null;
-  }
-
-  public FcnRcdValue(IntervalValue intv, Value[] values) {
-    this.intv = intv;
-    this.values = values;
-    this.domain = null;
-    this.isNorm = true;
-    this.indexTbl = null;
-  }
-
-  private FcnRcdValue(FcnRcdValue fcn, Value[] values) {
-    this.domain = fcn.domain;
-    this.intv = fcn.intv;
-    this.values = values;
-    this.isNorm = fcn.isNorm;
-    this.indexTbl = fcn.indexTbl;
-  }
-
-  public final byte getKind() { return FCNRCDVALUE; }
-
-  /* We create an index only when the domain is not very small. */
-  private final void createIndex() {
-    if (this.domain != null && this.domain.length > 10) {
-      int len = this.domain.length * 2 + 1;
-
-      int[] tbl = new int[len];
-      Arrays.fill(tbl, -1);
-
-      synchronized(this) {
-	for (int i = 0; i < this.domain.length; i++) {
-	  int loc = (this.domain[i].hashCode() & 0x7FFFFFFF) % len;
-	  while (tbl[loc] != -1) {
-	    loc = (loc + 1) % len;
-	  }
-	  tbl[loc] = i;
-	}
-      }
-
-      synchronized(this) { this.indexTbl = tbl; }
-    }
-  }
-
-  private final int lookupIndex(Value arg) {
-    int len = this.indexTbl.length;
-    int loc = (arg.hashCode() & 0x7FFFFFFF) % len;
-    while (true) {
-      int idx = this.indexTbl[loc];
-      if (idx == -1) {
-	Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) +
-		    "\nto argument " + ppr(arg.toString()) +
-		    ", which is not in the domain of the function.");
-      }
-      if (this.domain[idx].equals(arg)) {
-	return idx;
-      }
-      loc = (loc + 1) % len;
-    }
-  }
-  
-  public final int compareTo(Object obj) {
-    FcnRcdValue fcn = convert(obj);
-    if (fcn == null) {
-      if (obj instanceof ModelValue) return 1;
-      Assert.fail("Attempted to compare the function " + ppr(this.toString()) +
-		  " with the value:\n" + ppr(obj.toString()));
-    }
-    this.normalize();
-    fcn.normalize();
-
-    int result = this.values.length - fcn.values.length;
-    if (result != 0) return result;
-
-    if (this.intv != null) {
-      if (fcn.intv != null) {
-	result = this.intv.low - fcn.intv.low;
-	if (result != 0) return result;
-	for (int i = 0; i < this.values.length; i++) {
-	  result = this.values[i].compareTo(fcn.values[i]);
-	  if (result != 0) return result;
-	}
-      }
-      else {
-	for (int i = 0; i < fcn.domain.length; i++) {
-	  Value dElem = fcn.domain[i];
-	  if (!(dElem instanceof IntValue)) {
-	    Assert.fail("Attempted to compare integer with non-integer:\n" +
-			ppr(dElem.toString()) + ".");
-	  }
-	  result = this.intv.low + i - ((IntValue)dElem).val;
-	  if (result != 0) return result;
-	  result = this.values[i].compareTo(fcn.values[i]);
-	  if (result != 0) return result;
-	}
-      }
-    }
-    else {
-      if (fcn.intv != null) {
-	for (int i = 0; i < this.domain.length; i++) {
-	  Value dElem = this.domain[i];
-	  if (!(dElem instanceof IntValue)) {
-	    Assert.fail("Attempted to compare integer with non-integer\n" +
-			ppr(dElem.toString()) + ".");
-	  }
-	  result = ((IntValue)dElem).val - (fcn.intv.low + i);
-	  if (result != 0) return result;
-	  result = this.values[i].compareTo(fcn.values[i]);
-	  if (result != 0) return result;
-	}
-      }
-      else {
-	for (int i = 0; i < this.domain.length; i++) {
-	  result = this.domain[i].compareTo(fcn.domain[i]);
-	  if (result != 0) return result;
-	  result = this.values[i].compareTo(fcn.values[i]);
-	  if (result != 0) return result;
-	}
-      }
-    }
-    return 0;
-  }
-
-  public final boolean equals(Object obj) {
-    FcnRcdValue fcn = convert(obj);
-    if (fcn == null) {
-      if (obj instanceof ModelValue) 
-         return ((ModelValue) obj).modelValueEquals(this) ;
-      Assert.fail("Attempted to check equality of the function " + ppr(this.toString()) +
-		  " with the value:\n" + ppr(obj.toString()));
-    }
-    this.normalize();
-    fcn.normalize();
-
-    if (this.intv != null) {
-      if (fcn.intv != null) {
-	if (!this.intv.equals(fcn.intv)) return false;
-	for (int i = 0; i < this.values.length; i++) {
-	  if (!this.values[i].equals(fcn.values[i]))
-	    return false;
-	}
-      }
-      else {
-	if (fcn.domain.length != this.intv.size()) return false;
-	for (int i = 0; i < fcn.domain.length; i++) {
-	  Value dElem = fcn.domain[i];
-	  if (!(dElem instanceof IntValue)) {
-	    Assert.fail("Attempted to compare an integer with non-integer:\n" +
-			ppr(dElem.toString()) + ".");
-	  }
-	  if (((IntValue)dElem).val != (this.intv.low + i) ||
-	      !this.values[i].equals(fcn.values[i])) {
-	    return false;
-	  }
-	}
-      }
-    }
-    else {
-      if (this.values.length != fcn.values.length) return false;
-      if (fcn.intv != null) {
-	for (int i = 0; i < this.domain.length; i++) {
-	  Value dElem = this.domain[i];
-	  if (!(dElem instanceof IntValue)) {
-	    Assert.fail("Attempted to compare an integer with non-integer:\n" +
-			ppr(dElem.toString()) + ".");
-	  }
-	  if (((IntValue)dElem).val != (fcn.intv.low + i) ||
-	      !this.values[i].equals(fcn.values[i])) {
-	    return false;
-	  }
-	}
-      }
-      else {
-	for (int i = 0; i < this.domain.length; i++) {
-	  if (!this.domain[i].equals(fcn.domain[i]) ||
-	      !this.values[i].equals(fcn.values[i])) {
-	    return false;
-	  }
-	}
-      }
-    }
-    return true;
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of the function " + ppr(this.toString()));
-    return false;  // make compiler happy
-  }
-
-  public final boolean isFinite() { return true; }
-
-  public final Value apply(Value arg, int control) {
-    Value result = this.select(arg);
-    if (result == null) {
-      Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) +
-		  "\nto argument " + ppr(arg.toString()) + ", which is" +
-		  " not in the domain of the function.");
-    }
-    return result;
-  }
-
-  /* This one does not seem to be needed anymore.  */
-  public final Value apply(Value[] args, int control) {
-    return this.apply(new TupleValue(args), EvalControl.Clear);
-  }
-
-  public final Value select(Value arg) {
-    if (this.intv != null) {
-      // domain is represented as an integer interval:
-      if (!(arg instanceof IntValue)) {
-	Assert.fail("Attempted to apply function with integer domain to" +
-		    " the non-integer argument " + ppr(arg.toString()));
-      }
-      int idx = ((IntValue)arg).val;
-      if ((idx >= this.intv.low) && (idx <= this.intv.high)) {
-	return this.values[idx - this.intv.low];
-      }
-    }
-    else {
-      // domain is represented as an array of values:
-      if (this.indexTbl != null) {
-	return this.values[this.lookupIndex(arg)];
-      }
-      if (this.isNorm) this.createIndex();
-
-      if (this.indexTbl != null) {
-	return this.values[this.lookupIndex(arg)];
-      }
-      else {
-	int len = this.domain.length;
-	for (int i = 0; i < len; i++) {
-	  if (this.domain[i].equals(arg)) {
-	    return this.values[i];
-	  }
-	}
-      }
-    }
-    return null;
-  }
-
-  public final boolean assign(Value[] args, Value val) {
-    if (this.intv != null) {
-      // domain is represented as an integer interval:
-      if (args.length != 1) {
-	Assert.fail("Wrong number of function arguments.");
-      }
-      if (args[0] instanceof IntValue) {
-	int idx = ((IntValue)args[0]).val;
-	if ((idx >= this.intv.low) && (idx <= this.intv.high)) {
-	  int vIdx = idx - this.intv.low;
-	  if (this.values[vIdx] == ValUndef ||
-	      this.values[vIdx].equals(val)) {
-	    this.values[vIdx] = val;
-	    return true;
-	  }
-	  return false;
-	}
-      }
-    }
-    else {
-      // domain is represented as an array of values:
-      Value argv = new TupleValue(args);
-      int len = this.domain.length;
-      for (int i = 0; i < len; i++) {
-	if (this.domain[i].equals(argv)) {
-	  if (this.values[i] == ValUndef ||
-	      this.values[i].equals(val)) {
-	    this.values[i] = val;
-	    return true;
-	  }
-	  return false;
-	}
-      }
-    }
-    Assert.fail("Function initialization out of domain.");
-    return false;    // make compiler happy
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx >= ex.path.length) return ex.value;
-
-    int flen = this.values.length;
-    Value[] newValues = new Value[flen];
-    for (int i = 0; i < flen; i++) {
-      newValues[i] = this.values[i];
-    }
-    Value arg = ex.path[ex.idx];
-
-    if (this.intv != null) {
-      // domain is represented as an integer interval:
-      if (arg instanceof IntValue) {
-	int idx = ((IntValue)arg).val;
-	if ((idx >= this.intv.low) && (idx <= this.intv.high)) {
-	  int vidx = idx - this.intv.low;
-	  ex.idx++;
-	  newValues[idx] = this.values[vidx].takeExcept(ex);
-	}
-	return new FcnRcdValue(this.intv, newValues);
-      }
-    }
-    else {
-      // domain is represented as an array of values:
-      for (int i = 0; i < flen; i++) {
-	if (arg.equals(this.domain[i])) {
-	  ex.idx++;
-	  newValues[i] = newValues[i].takeExcept(ex);
-	  Value[] newDomain = this.domain;
-	  if (!this.isNorm) {
-	    newDomain = new Value[flen];
-	    for (int j = 0; j < flen; j++) {
-	      newDomain[j] = this.domain[j];
-	    }
-	  }
-	  return new FcnRcdValue(newDomain, newValues, this.isNorm);
-	}
-      }
-    }
-    return this;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    Value res = this;
-    for (int i = 0; i < exs.length; i++) {
-      res = res.takeExcept(exs[i]);
-    }
-    return res;
-  }
-
-  public final Value getDomain() {
-    if (this.intv != null) {
-      return this.intv;
-    }
-    this.normalize();
-    return new SetEnumValue(this.domain, true);
-  }
-
-  public final int size() {
-    this.normalize();
-    return this.values.length;
-  }
-
-  /*
-   * This method converts a value to a function value. It returns
-   * null if the conversion fails.
-   */
-  public static final FcnRcdValue convert(Object val) {
-    switch (((Value)val).getKind()) {
-    case FCNRCDVALUE:
-      {
-	return (FcnRcdValue)val;
-      }
-    case FCNLAMBDAVALUE:
-      {
-	return ((FcnLambdaValue)val).toFcnRcd();
-      }
-    case RECORDVALUE:
-      {
-	RecordValue rcd = (RecordValue)val;
-	rcd.normalize();
-	Value[] dom = new Value[rcd.names.length];
-	for (int i = 0; i < rcd.names.length; i++) {
-	  dom[i] = new StringValue(rcd.names[i]);
-	}
-	return new FcnRcdValue(dom, rcd.values, rcd.isNormalized());
-      }
-    case TUPLEVALUE:
-      {
-	Value[] elems = ((TupleValue)val).elems;
-	IntervalValue intv = new IntervalValue(1, elems.length);
-	return new FcnRcdValue(intv, elems);
-      }
-    default:
-      // return null if not convertable
-      return null;
-    }
-  }
-
-  /* Return true iff this function is in normal form. */
-  public final boolean isNormalized() { return this.isNorm; }
-  
-  /* This method normalizes (destructively) this function. */
-  public final void normalize() {
-    if (!this.isNorm) {
-      // Assert.check(this.domain != null)
-      int dlen = this.domain.length;
-      for (int i = 1; i < dlen; i++) {
-	int cmp = this.domain[0].compareTo(this.domain[i]);
-	if (cmp == 0) {
-	  Assert.fail("The value\n" + this.domain[i] +
-		      "\noccurs multiple times in the function domain.");
-	}
-	else if (cmp > 0) {
-	  Value tv = this.domain[0];
-	  this.domain[0] = this.domain[i];
-	  this.domain[i] = tv;
-	  tv = this.values[0];
-	  this.values[0] = this.values[i];
-	  this.values[i] = tv;
-	}
-      }
-      for (int i = 2; i < dlen; i++) {
-	Value d = this.domain[i];
-	Value v = this.values[i];
-	int j = i;
-	int cmp;
-	while ((cmp = d.compareTo(this.domain[j-1])) < 0) {
-	  this.domain[j] = this.domain[j-1];
-	  this.values[j] = this.values[j-1];
-	  j--;
-	}
-	if (cmp == 0) {
-	  Assert.fail("The value\n" + this.domain[i] +
-		      "\noccurs multiple times in the function domain.");
-	}
-	this.domain[j] = d;
-	this.values[j] = v;
-      }
-      this.isNorm = true;
-    }
-  }
-
-  public final boolean isDefined() {
-    boolean defined = true;
-    if (this.intv == null) {
-      for (int i = 0; i < this.values.length; i++) {
-	defined = defined && this.domain[i].isDefined();
-      }
-    }
-    for (int i = 0; i < this.values.length; i++) {
-      defined = defined && this.values[i].isDefined();
-    }
-    return defined;
-  }
-
-  public final Value deepCopy() {
-    Value[] vals = new Value[this.values.length];
-    for (int i = 0; i < vals.length; i++) {
-      vals[i] = this.values[i].deepCopy();
-    }
-    return new FcnRcdValue(this, vals);
-  }
-
-  public final boolean assignable(Value val) {
-    boolean canAssign = ((val instanceof FcnRcdValue) &&
-			 this.values.length == ((FcnRcdValue)val).values.length);
-    if (!canAssign) return false;
-    FcnRcdValue fcn = (FcnRcdValue)val;
-    for (int i = 0; i < this.values.length; i++) {
-      canAssign = (canAssign &&
-		   this.domain[i].equals(fcn.domain[i]) &&
-		   this.values[i].assignable(fcn.values[i]));
-    }
-    return canAssign;
-  }
-
-  /* The fingerprint method.  */
-  public final long fingerPrint(long fp) {
-    this.normalize();
-    int flen = this.values.length;
-    fp = FP64.Extend(fp, FCNRCDVALUE);
-    fp = FP64.Extend(fp, flen);
-    if (this.intv == null) {
-      for (int i = 0; i < flen; i++) {
-	fp = this.domain[i].fingerPrint(fp);
-	fp = this.values[i].fingerPrint(fp);
-      }
-    }
-    else {
-      for (int i = 0; i < flen; i++) {
-	fp = FP64.Extend(fp, INTVALUE);
-	fp = FP64.Extend(fp, i + this.intv.low);
-	fp = this.values[i].fingerPrint(fp);
-      }
-    }
-    return fp;
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.normalize();
-    int flen = this.domain.length;
-    Value[] vals = new Value[flen];
-
-    boolean vchanged = false;
-    for (int i = 0; i < flen; i++) {
-      vals[i] = this.values[i].permute(perm);
-      vchanged = vchanged || (vals[i] != this.values[i]);
-    }
-
-    if (this.intv == null) {
-      Value[] dom = new Value[flen];
-      boolean dchanged = false;
-      for (int i = 0; i < flen; i++) {
-	dom[i] = this.domain[i].permute(perm);
-	dchanged = dchanged || (dom[i] != this.domain[i]);
-      }
-
-      if (dchanged) {
-	return new FcnRcdValue(dom, vals, false);
-      }
-      else if (vchanged) {
-	return new FcnRcdValue(this.domain, vals, true);
-      }
-    }
-    else {
-      if (vchanged) {
-	return new FcnRcdValue(this.intv, vals);
-      }
-    }
-    return this;
-  }
-
-  private static final boolean isName(String name) {
-    int len = name.length();
-    boolean hasLetter = false;
-
-    for (int idx = 0; idx < len; idx++) {
-      char ch = name.charAt(idx);
-      if (ch == '_') continue;
-      if (!Character.isLetterOrDigit(ch)) return false;
-      hasLetter = hasLetter || Character.isLetter(ch);
-    }
-
-    return hasLetter && (len < 4 || (!name.startsWith("WF_") && !name.startsWith("SF_")));
-  }
-
-  private final boolean isRcd() {
-    if (this.intv != null) return false;
-    for (int i = 0; i < this.domain.length; i++) {
-      Value dval = this.domain[i];
-      boolean isName = ((dval instanceof StringValue) &&
-			isName(((StringValue)dval).val.toString()));
-      if (!isName) return false;
-    }
-    return true;
-  }
-
-  private final boolean isTuple() {
-    if (this.intv != null) {
-      return (this.intv.low == 1);
-    }
-    for (int i = 0; i < this.domain.length; i++) {
-      if (!(this.domain[i] instanceof IntValue)) {
-	return false;
-      }
-    }
-    this.normalize();
-    for (int i = 0; i < this.domain.length; i++) {
-      if (((IntValue)this.domain[i]).val != (i+1)) {
-	return false;
-      }
-    }
-    return true;
-  }
-  
-  /* The string representation of the value.  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    int len = this.values.length;
-    if (len == 0) {
-      sb.append("<< >>");
-    }
-    else if (this.isRcd()) {
-      sb.append("[");
-      sb.append(((StringValue)this.domain[0]).val + " |-> ");
-      sb = this.values[0].toString(sb, offset);
-
-      for (int i = 1; i < len; i++) {
-	sb.append(", ");
-	sb.append(((StringValue)this.domain[i]).val + " |-> ");
-	sb = this.values[i].toString(sb, offset);
-      }
-      sb.append("]");	
-    }
-    else if (this.isTuple()) {
-      // It is actually a sequence:
-      sb = sb.append("<<");
-      sb = this.values[0].toString(sb, offset);
-
-      for (int i = 1; i < len; i++) {
-	sb.append(", ");
-	sb = this.values[i].toString(sb, offset);
-      }
-      sb.append(">>");
-    }
-    else {
-      sb = sb.append("(");
-      sb = this.domain[0].toString(sb, offset);
-      sb.append(" :> ");
-      sb = this.values[0].toString(sb, offset);
-
-      for (int i = 1; i < len; i++) {
-	sb.append(" @@ ");
-	sb = this.domain[i].toString(sb, offset);
-	sb.append(" :> ");
-	sb = this.values[i].toString(sb, offset);
-      }
-      sb.append(")");
-    }
-    return sb;
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/IBoolValue.java b/tlatools/src/tlc2/value/IBoolValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..552782b693c3cd6505cafe1347c33345bf17c786
--- /dev/null
+++ b/tlatools/src/tlc2/value/IBoolValue.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+public interface IBoolValue extends IValue {
+
+	boolean getVal();
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/value/IFcnLambdaValue.java b/tlatools/src/tlc2/value/IFcnLambdaValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..b91d82fef09e099887269b6356d5cc9dfec9864a
--- /dev/null
+++ b/tlatools/src/tlc2/value/IFcnLambdaValue.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.value;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.util.Context;
+
+public interface IFcnLambdaValue {
+
+	SemanticNode getBody();
+
+	IFcnRcdValue getRcd();
+
+	IFcnParams getParams();
+
+	Context getCon();
+
+	boolean hasRcd();
+
+}
diff --git a/tlatools/src/tlc2/value/IFcnParams.java b/tlatools/src/tlc2/value/IFcnParams.java
new file mode 100644
index 0000000000000000000000000000000000000000..40082374a6c1e904da86877165fe615bda73895a
--- /dev/null
+++ b/tlatools/src/tlc2/value/IFcnParams.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+import tla2sany.semantic.FormalParamNode;
+
+public interface IFcnParams {
+
+	int length();
+
+	FormalParamNode[][] getFormals();
+
+	IValue[] getDomains();
+
+	boolean[] isTuples();
+
+}
diff --git a/tlatools/src/tlc2/value/IFcnRcdValue.java b/tlatools/src/tlc2/value/IFcnRcdValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2c3b3bdbfe01b16a05daa2b01d0a1d1f0dc2ecd
--- /dev/null
+++ b/tlatools/src/tlc2/value/IFcnRcdValue.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+public interface IFcnRcdValue {
+
+}
diff --git a/tlatools/src/tlc2/value/IMVPerm.java b/tlatools/src/tlc2/value/IMVPerm.java
new file mode 100644
index 0000000000000000000000000000000000000000..2682c3c1f0e0f972b14a189f6fab121e3e8bfaca
--- /dev/null
+++ b/tlatools/src/tlc2/value/IMVPerm.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+public interface IMVPerm {
+
+	IValue get(IValue value);
+
+	void put(IModelValue dval, IModelValue rval);
+
+	int size();
+
+	IMVPerm compose(IMVPerm elementAt);
+
+}
diff --git a/tlatools/src/tlc2/value/IModelValue.java b/tlatools/src/tlc2/value/IModelValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..61d69866eea38a77654936ae087a75342186ef43
--- /dev/null
+++ b/tlatools/src/tlc2/value/IModelValue.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+public interface IModelValue {
+
+}
diff --git a/tlatools/src/tlc2/value/ITupleValue.java b/tlatools/src/tlc2/value/ITupleValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..6525574b3c5448aa4a7d2d26250a7675be0148e8
--- /dev/null
+++ b/tlatools/src/tlc2/value/ITupleValue.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+public interface ITupleValue extends IValue {
+
+	IValue getElem(int i);
+
+	IValue[] getElems();
+
+	@Override
+	int size();
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/value/IValue.java b/tlatools/src/tlc2/value/IValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..30e7a4addfc2052935bc77e8b80301130db20f94
--- /dev/null
+++ b/tlatools/src/tlc2/value/IValue.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+import java.io.IOException;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.tool.coverage.CostModel;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.StringValue;
+
+public interface IValue extends Comparable<Object> {
+
+	/* This method compares this with val.  */
+	@Override
+	int compareTo(Object val);
+
+	void write(IValueOutputStream vos) throws IOException;
+
+	IValue setCostModel(CostModel cm);
+
+	CostModel getCostModel();
+
+	void setSource(SemanticNode semanticNode);
+
+	SemanticNode getSource();
+
+	boolean hasSource();
+
+	/* MAK 09/17/2019: Introduced to guarantee that Value instances are
+	 * fully initialized when created by the SpecProcessor (as opposed
+	 * to by workers during state space exploration).
+	 */
+	/**
+	 * Fully initialize this instance which includes:
+	 * - deep normalization
+	 * - conversion and caching iff defined by the sub-class
+	 * 
+	 *  No further mutation of this instance should be required
+	 *  for any evaluation whatsoever.
+	 *  
+	 *  Afterwards, isNormalized below returns true (it does not
+	 *  return true for all sub-classes when only deepNormalized
+	 *  is executed)!
+	 *  
+	 *  see comment in UnionValue#deepNormalize too
+	 */
+	default IValue initialize() {
+		this.deepNormalize();
+		// Execute fingerprint code path to internally trigger convertAndCache iff
+		// defined (0L parameter is not relevant)
+		this.fingerPrint(0L);
+		return this;
+	}
+	
+	/**
+	   * This method normalizes (destructively) the representation of
+	   * the value. It is essential for equality comparison.
+	   */
+	boolean isNormalized();
+
+	/* Fully normalize this (composite) value. */
+	void deepNormalize();
+
+	/* This method returns the fingerprint of this value. */
+	long fingerPrint(long fp);
+
+	/**
+	   * This method returns the value permuted by the permutation. It
+	   * returns this if nothing is permuted.
+	   */
+	IValue permute(IMVPerm perm);
+
+	/* This method returns true iff the value is finite. */
+	boolean isFinite();
+
+	/* This method returns the size of the value.  */
+	int size();
+
+	/* This method returns true iff the value is fully defined. */
+	boolean isDefined();
+
+	/* This method makes a real deep copy of this.  */
+	IValue deepCopy();
+
+	/**
+	   * This abstract method returns a string representation of this
+	   * value. Each subclass must provide its own implementation.
+	   */
+	StringBuffer toString(StringBuffer sb, int offset, boolean swallow);
+
+	/* The string representation of this value */
+	String toString();
+
+	String toString(String delim);
+	
+	String toUnquotedString();
+
+	default boolean isAtom() {
+		if (this instanceof ModelValue || this instanceof IntValue || this instanceof StringValue
+				|| this instanceof BoolValue) {
+			return true;
+		}
+		return false;
+	}
+	
+	/**
+	 * @return true if a value mutates as part of normalization or fingerprinting.
+	 */
+	default boolean mutates() {
+		return true;
+	}
+
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/value/IValueInputStream.java b/tlatools/src/tlc2/value/IValueInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c1413f967bc0700713b2025f32ea8d65e35ec36
--- /dev/null
+++ b/tlatools/src/tlc2/value/IValueInputStream.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+import util.IDataInputStream;
+import util.UniqueString;
+
+public interface IValueInputStream {
+
+	IValue read() throws IOException;
+
+	int readShort() throws IOException;
+
+	int readInt() throws IOException;
+
+	long readLong() throws IOException;
+
+	void close() throws IOException;
+
+	int readNat() throws IOException;
+
+	short readShortNat() throws IOException;
+
+	long readLongNat() throws IOException;
+
+	byte readByte() throws EOFException, IOException;
+
+	void assign(Object obj, int idx);
+
+	int getIndex();
+
+	IDataInputStream getInputStream();
+
+	UniqueString getValue(int idx);
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/value/IValueOutputStream.java b/tlatools/src/tlc2/value/IValueOutputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc5cc51cf1f0713ff4101c21b3fe43da817165c6
--- /dev/null
+++ b/tlatools/src/tlc2/value/IValueOutputStream.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+import java.io.IOException;
+
+import util.IDataOutputStream;
+
+public interface IValueOutputStream {
+
+	void writeShort(short x) throws IOException;
+
+	void writeInt(int x) throws IOException;
+
+	void writeLong(long x) throws IOException;
+
+	void close() throws IOException;
+
+	/* Precondition: x is a non-negative short. */
+	void writeShortNat(short x) throws IOException;
+
+	/* Precondition: x is a non-negative int. */
+	void writeNat(int x) throws IOException;
+
+	/* Precondition: x is a non-negative long. */
+	void writeLongNat(long x) throws IOException;
+
+	void writeByte(byte b) throws IOException;
+
+	void writeBoolean(boolean b) throws IOException;
+
+	IDataOutputStream getOutputStream();
+
+	/**
+	 * Check if another TLCState - which is currently also being serialized to the
+	 * same storage (i.e. disk file) - has/contains an identical Value. If yes, do
+	 * not serialize the Value instance again but make this TLCState point to the
+	 * Value instance previously serialized for the other TLCState. In other words,
+	 * this is a custom-tailored compression/de-duplication mechanism for Value
+	 * instances.
+	 * <p>
+	 * This approach only works because both TLCStates are serialized to the same
+	 * storage and thus de-serialized as part of the same operation (same
+	 * Value*Stream instance).
+	 * <p>
+	 * The purpose of this approach appears to be:
+	 * <ul>
+	 * <li>Reduce serialization efforts and storage size</li>
+	 * <li>Reduce the number of Value instances created during de-serialization</li>
+	 * <li>Allow identity comparison on Value instances (AFAICT not used by Value
+	 * explicitly, just UniqueString) to speed up check. Value#equals internally
+	 * likely uses identity comparison as first check.</li>
+	 * </ul>
+	 * <p>
+	 * A disadvantage is the cost of maintaining the internal HandleTable which can
+	 * grow to thousands of elements during serialization/de-serialization (in
+	 * ValueInputStream). Since serialization suspends the DiskStateQueue and thus
+	 * blocks tlc2.tool.Workers from exploring the state space, this might has
+	 * adverse effects.
+	 */
+	int put(Object obj);
+
+}
\ No newline at end of file
diff --git a/tlatools/src/tlc2/value/IntValue.java b/tlatools/src/tlc2/value/IntValue.java
deleted file mode 100644
index cbba09d9eb1cb01f0322138d4d16f58b579c8a34..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/IntValue.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:08:05 PST by lamport
-//      modified on Fri Aug 10 15:07:30 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.util.FP64;
-import util.Assert;
-
-public class IntValue extends Value {
-  private static final IntValue[] cache;
-
-  public int val;
-  
-  private IntValue(int i) { this.val = i; }
-
-  static {
-    cache = new IntValue[10];
-    for (int i = 0; i < cache.length; i++) {
-      cache[i] = new IntValue(i);
-    }
-  }
-
-  public final byte getKind() { return INTVALUE; }
-
-  public static final int nbits(int tmp) {
-    int nb = 0;
-    while(tmp != 0 && tmp != -1) {
-      nb++;
-      tmp >>= 1;
-    }
-    return nb + 1;
-  }
-
-  // the number of bits needed to encode the value of this int
-  public final int nbits() { 
-    return nbits(this.val);
-  }
-
-  public static IntValue gen(int i) {
-    if (i >= 0 && i < cache.length) {
-      return cache[i];
-    }
-    return new IntValue(i);
-  }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof IntValue) {
-      return this.val - ((IntValue)obj).val;
-    }
-    if (!(obj instanceof ModelValue)) {
-      Assert.fail("Attempted to compare integer " + ppr(this.toString()) +
-		  " with non-integer:\n" + ppr(obj.toString()));
-    }
-    return 1;
-  }
-  
-  public final boolean equals(Object obj) {
-    if (obj instanceof IntValue) {
-      return this.val == ((IntValue)obj).val;
-    }
-    if (!(obj instanceof ModelValue)) {
-      Assert.fail("Attempted to check equality of integer " + ppr(this.toString()) +
-		  " with non-integer:\n" + ppr(obj.toString()));
-    }
-    return ((ModelValue) obj).modelValueEquals(this) ;
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of the integer " + ppr(this.toString()));
-    return false;  // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the integer " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;   // make compiler happy
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to appy EXCEPT construct to the integer " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the integer " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the integer " +
-		ppr(this.toString()) + ".");
-    return 0;   // make compiler happy
-  }
-
-  public final boolean isNormalized() { return true; }
-  
-  public final void normalize() { /*nop*/ }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return ((val instanceof IntValue) &&
-	    this.val == ((IntValue)val).val);
-  }
-
-  /* The fingerprint methods */
-  public final long fingerPrint(long fp) {
-    return FP64.Extend(FP64.Extend(fp, INTVALUE), this.val);
-  }
-
-  public final Value permute(MVPerm perm) { return this; }
-
-  /* The string representation. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return sb.append(this.val);
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/IntervalValue.java b/tlatools/src/tlc2/value/IntervalValue.java
deleted file mode 100644
index cece9ce89a480202614391f410cc730e9fb1d6da..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/IntervalValue.java
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:12:59 PST by lamport
-//      modified on Fri Aug 10 15:07:36 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.util.FP64;
-import util.Assert;
-
-public class IntervalValue extends Value
-implements Enumerable, Reducible {
-  public int low, high;   // the integer interval [low, high]
-
-  /* Constructor */
-  public IntervalValue(int low, int high) {
-    this.low = low;
-    this.high = high;
-  }
-
-  public final byte getKind() { return INTERVALVALUE; }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof IntervalValue) {
-      IntervalValue intv = (IntervalValue)obj;
-      int cmp = this.size() - intv.size();
-      if (cmp != 0) return cmp;
-      if (this.size() == 0) return 0;
-      return this.low - intv.low;
-    }
-    // Well, we have to convert them to sets and compare.
-    return SetEnumValue.convert(this).compareTo(obj);
-  }
-
-  public final boolean equals(Object obj) {
-    if (obj instanceof IntervalValue) {
-      IntervalValue intv = (IntervalValue)obj;
-      if (this.size() == 0) return intv.size() == 0;
-      return (this.low == intv.low) && (this.high == intv.high);
-    }
-    // Well, we have to convert them to sets and compare.
-    return SetEnumValue.convert(this).equals(obj);
-  }
-  
-  public final boolean member(Value elem) {
-    if (elem instanceof IntValue) {
-      int x = ((IntValue)elem).val;
-      return (x >= low) && (x <= high);
-    }
-    if (   (this.low <= this.high) 
-         && (   !(elem instanceof ModelValue)
-             || (((ModelValue) elem).type != 0)) ) {
-      Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		  "\nis in the integer interval " + ppr(this.toString()));
-    }
-    return false;
-  }
-
-  public final boolean isFinite() { return true; }
-
-  public final int size() {
-    if (this.high < this.low) return 0;
-    return this.high - this.low + 1;
-  }
-
-  /* Return this - val.  */
-  public final Value diff(Value val) {
-    ValueVec diffElems = new ValueVec();
-    for (int i = this.low; i <= this.high; i++) {
-      Value elem = IntValue.gen(i);      
-      if (!val.member(elem)) diffElems.addElement(elem);
-    }
-    return new SetEnumValue(diffElems, true);
-  }
-
-  /* Return this \cap val. */
-  public final Value cap(Value val) {
-    ValueVec capElems = new ValueVec();
-    for (int i = this.low; i <= this.high; i++) {
-      Value elem = IntValue.gen(i);
-      if (val.member(elem)) capElems.addElement(elem);
-    }
-    return new SetEnumValue(capElems, true);
-  }
-
-  /* Return this \cup val.  */
-  public final Value cup(Value set) {
-    if (this.size() == 0) return set;
-
-    if (set instanceof Reducible) {
-      ValueVec cupElems = new ValueVec();
-      for (int i = this.low; i <= this.high; i++) {
-	cupElems.addElement(IntValue.gen(i));
-      }
-      ValueEnumeration Enum = ((Enumerable)set).elements();
-      Value elem;
-      while ((elem = Enum.nextElement()) != null) {
-	if (!this.member(elem)) cupElems.addElement(elem);
-      }
-      return new SetEnumValue(cupElems, false);
-    }
-    return new SetCupValue(this, set);
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT construct to the interval value " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the interval value " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final boolean isNormalized() { return true; }
-  
-  public final void normalize() { /*nop*/ }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return ((val instanceof IntervalValue) &&
-	    this.high == ((IntervalValue)val).high &&
-	    this.low == ((IntervalValue)val).low);
-  }
-
-  /* The fingerprint method */
-  public final long fingerPrint(long fp) {
-    fp = FP64.Extend(fp, SETENUMVALUE);
-    fp = FP64.Extend(fp, this.size()) ;
-    for (int i = this.low; i <= this.high; i++) {
-      fp = FP64.Extend(fp, INTVALUE);
-      fp = FP64.Extend(fp, i);
-    }
-    return fp;
-  }
-
-  public final Value permute(MVPerm perm) {
-    return this;
-  }
-  
-  /* The string representation */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    if (this.low <= this.high) {
-      return sb.append(this.low).append("..").append(this.high);
-    }
-    return sb.append("{").append("}");
-  }
-
-  public final ValueEnumeration elements() {
-    return new Enumerator();
-  }
-  
-  final class Enumerator implements ValueEnumeration {
-    int index = low;
-
-    public final void reset() { this.index = low; }
-
-    public final Value nextElement() {
-      if (this.index <= high) {
-	return IntValue.gen(this.index++);
-      }
-      return null;
-    }
-    
-  }
-  
-}
diff --git a/tlatools/src/tlc2/value/LazyValue.java b/tlatools/src/tlc2/value/LazyValue.java
deleted file mode 100644
index 453e67ef61fed807c99c6cd23f928e5e7f163135..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/LazyValue.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 15:30:08 PST by lamport
-//      modified on Thu Feb  8 21:23:55 PST 2001 by yuanyu
-
-package tlc2.value;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import tla2sany.semantic.SemanticNode;
-import tlc2.util.Context;
-import util.Assert;
-
-public class LazyValue extends Value {
-  /**
-   * The field val is the result of evaluating expr in context con and
-   * a pair of states.  If val is null, then the value has not been
-   * computed, but when computed, the value can be cached in the field
-   * val. If val is ValUndef, then the value has not been computed,
-   * and when computed, it can not be cached in the field val.
-   */
-
-  public SemanticNode expr;
-  public Context con;
-  public Value val;
-
-  public LazyValue(SemanticNode expr, Context con) {
-    this.expr = expr;
-    this.con = con;
-    this.val = null;
-  }
-
-  public final void setUncachable() { this.val = ValUndef; }
-
-  public final byte getKind() { return LAZYVALUE; }
-
-  public final int compareTo(Object obj) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to compare lazy values.");
-    }
-    return this.val.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to check equality of lazy values.");
-    }
-    return this.val.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to check set membership of lazy values.");
-    }
-    return this.val.member(elem);
-  }
-
-  public final boolean isFinite() {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to check if a lazy value is a finite set.");
-    }
-    return this.val.isFinite();
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value.");
-    }
-    return this.val.takeExcept(ex);
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value.");
-    }
-    return this.val.takeExcept(exs);
-  }
-
-  public final int size() {
-    if (this.val == null || this.val == ValUndef) {
-       Assert.fail("Error(TLC): Attempted to compute size of lazy value.");
-    }
-    return this.val.size();
-  }
-
-  private void readObject(ObjectInputStream ois)
-  throws IOException, ClassNotFoundException {
-    this.val = (Value)ois.readObject();
-  }
-
-  private void writeObject(ObjectOutputStream oos) throws IOException {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to serialize lazy value.");
-    }
-    oos.writeObject(this.val);
-  }
-  
-  /* Nothing to normalize. */
-  public final boolean isNormalized() {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to normalize lazy value.");
-    }
-    return this.val.isNormalized();
-  }
-  
-  public final void normalize() {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to normalize lazy value.");
-    }
-    this.val.normalize();
-  }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() {
-    if (this.val == null || this.val == ValUndef) return this;
-    return this.val.deepCopy();
-  }
-
-  public final boolean assignable(Value val) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to call assignable on lazy value.");
-    }
-    return this.val.assignable(val);
-  }
-
-  /* The fingerprint method */
-  public final long fingerPrint(long fp) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to fingerprint a lazy value.");
-    }
-    return this.val.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    if (this.val == null || this.val == ValUndef) {
-      Assert.fail("Error(TLC): Attempted to apply permutation to lazy value.");
-    }
-    return this.val.permute(perm);
-  }
-
-  /* The string representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    if (this.val == null || this.val == ValUndef) {
-      return sb.append("<LAZY " + this.expr + ">");
-    }
-    return this.val.toString(sb, offset);
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/MVPerm.java b/tlatools/src/tlc2/value/MVPerm.java
deleted file mode 100644
index 1f3b3e24ed6754652f2e0cd0821f366864c80701..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/MVPerm.java
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:42:27 PST by lamport
-//      modified on Thu Nov 16 15:53:30 PST 2000 by yuanyu
-
-package tlc2.value;
-
-import java.util.Enumeration;
-
-import tlc2.util.Vect;
-import util.Assert;
-import util.Set;
-
-public final class MVPerm {
-  private ModelValue[] elems;
-  private int count;
-
-  public MVPerm() {
-    this.elems = new ModelValue[ModelValue.mvs.length];
-    this.count = 0;
-  }
-
-  public final boolean equals(Object obj) {
-    if (obj instanceof MVPerm) {
-      MVPerm perm = (MVPerm)obj;
-      for (int i = 0; i < this.elems.length; i++) {
-	if (this.elems[i] == null) {
-	  if (perm.elems[i] != null) {
-	    return false;
-	  }
-	}
-	else if (!this.elems[i].equals(perm.elems[i])) {
-	  return false;
-	}
-      }
-      return true;
-    }
-    return false;
-  }
-
-  public final int hashCode() {
-    int res = 0;
-    for (int i = 0; i < this.elems.length; i++) {
-      ModelValue mv = this.elems[i];
-      if (mv != null) {
-	res = 31*res + mv.val.hashCode();
-      }
-    }
-    return res;
-  }
-  
-  public final int size() { return this.count; }
-
-  public final ModelValue get(ModelValue k) {
-    return this.elems[k.index];
-  }
-
-  public final void put(ModelValue k, ModelValue elem) {
-    if (!k.equals(elem) && this.elems[k.index] == null) {
-      this.elems[k.index] = elem;
-      this.count++;
-    }
-  }
-
-  public final void put(int i, ModelValue elem) {
-    if (this.elems[i] == null && elem != null) {
-      this.elems[i] = elem;
-      this.count++;
-    }
-  }
-  
-  public final MVPerm compose(MVPerm perm) {
-    MVPerm res = new MVPerm();
-    for (int i = 0; i < this.elems.length; i++) {
-      ModelValue mv = this.elems[i];
-      if (mv == null) {
-	res.put(i, perm.elems[i]);
-      }
-      else {
-	ModelValue mv1 = perm.elems[mv.index];
-	if (mv1 == null) {
-	  res.put(i, mv);
-	}
-	else if (!ModelValue.mvs[i].equals(mv1)) {
-	  res.put(i, mv1);
-	}
-      }
-    }
-    return res;
-  }
-  
-  public static final MVPerm[] permutationSubgroup(ValueEnumeration Enum) {
-    Set perms = new Set(20);
-    Vect permVec = new Vect(20);
-    // Compute the group generators:
-    Value elem;
-    while ((elem = Enum.nextElement()) != null) {
-      FcnRcdValue fcn = FcnRcdValue.convert(elem);
-      if (fcn == null) {
-	Assert.fail("The symmetry operator must specify a set of functions.");
-      }
-      MVPerm perm = new MVPerm();
-      for (int i = 0; i < fcn.domain.length; i++) {
-	Value dval = fcn.domain[i];
-	Value rval = fcn.values[i];
-	if ((dval instanceof ModelValue) && (rval instanceof ModelValue)) {
-	  perm.put((ModelValue)dval, (ModelValue)rval);
-	}
-	else {
-	  Assert.fail("Symmetry function must have model values as domain and range.");
-	}
-      }
-      if (perm.size() > 0 && perms.put(perm) == null) {
-	permVec.addElement(perm);
-      }
-    }
-    // Compute the group generated by the generators:
-    int gsz = permVec.size();
-    int sz0 = 0;
-    while (true) {
-      int sz1 = permVec.size();
-      for (int i = 0; i < gsz; i++) {
-	MVPerm perm1 = (MVPerm)permVec.elementAt(i);
-	for (int j = sz0; j < sz1; j++) {
-	  MVPerm perm = perm1.compose((MVPerm)permVec.elementAt(j));
-	  if (perm.size() > 0 && perms.put(perm) == null) {
-	    permVec.addElement(perm);
-	  }
-	}
-      }
-      if (sz1 == permVec.size()) break;
-      sz0 = sz1;
-    }
-    // Finally, put all the elements in an array ready for use:
-    MVPerm[] res = new MVPerm[permVec.size()];
-    Enumeration permEnum = permVec.elements();
-    for (int i = 0; i < res.length; i++) {
-      res[i] = (MVPerm)permEnum.nextElement();
-    }
-    return res;
-  }
-
-  public final String toString() {
-    StringBuffer sb = new StringBuffer("[");
-    int i = 0;
-    for (i = 0; i < this.elems.length; i++) {
-      if (this.elems[i] != null) {
-	sb.append(ModelValue.mvs[i].toString());
-	sb.append(" -> ");
-	sb.append(this.elems[i].toString());
-	break;
-      }
-    }
-    for (int j = i+1; j < this.elems.length; j++) {
-      if (this.elems[j] != null) {
-	sb.append(", ");
-	sb.append(ModelValue.mvs[j].toString());
-	sb.append(" -> ");
-	sb.append(this.elems[j].toString());
-      }
-    }
-    sb.append("]");    
-    return sb.toString();
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/MethodValue.java b/tlatools/src/tlc2/value/MethodValue.java
deleted file mode 100644
index ae68732b5410e7c4aff2222f04428bc1fa4a2bc8..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/MethodValue.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:00 PST by lamport
-//      modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu
-
-package tlc2.value;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import tlc2.output.EC;
-import tlc2.tool.EvalException;
-import util.Assert;
-import util.WrongInvocationException;
-
-public class MethodValue extends OpValue implements Applicable {
-  public Method md;
-  
-  /* Constructor */
-  public MethodValue(Method md) { this.md = md; }
-
-  public final byte getKind() { return METHODVALUE; }
-
-  public final int compareTo(Object obj) {
-    Assert.fail("Attempted to compare operator " + this.toString() +
-		" with value:\n" + obj == null ? "null" : ppr(obj.toString()));
-    return 0;       // make compiler happy
-  }
-  
-  public final boolean equals(Object obj) {
-    Assert.fail("Attempted to check equality of operator " + this.toString() +
-		" with value:\n" + obj == null ? "null" : ppr(obj.toString()));
-    return false;   // make compiler happy
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : ppr(elem.toString()) +
-		"\nis an element of operator " + this.toString());
-    return false;   // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the operator " + this.toString() +
-		" is a finite set.");
-    return false;   // make compiler happy
-  }
-
-  public final Value apply(Value arg, int control) {
-    throw new WrongInvocationException("It is a TLC bug: Should use the other apply method.");
-  }
-
-  public final Value apply(Value[] args, int control) {
-      Value res = null;
-      try 
-      {
-          res = (Value)this.md.invoke(null, (Object[]) args);
-      } catch (Exception e) 
-      {
-          if (e instanceof InvocationTargetException) 
-          {
-              Throwable targetException = ((InvocationTargetException)e).getTargetException();
-              throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), targetException.getMessage()});
-          } else 
-          {
-              Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()});
-          }
-      }
-      return res;
-  }
-
-  public final Value select(Value arg) {
-      throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select().");
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    Assert.fail("Attempted to appy EXCEPT construct to the operator " +
-		this.toString() + ".");
-    return null;   // make compiler happy
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    Assert.fail("Attempted to apply EXCEPT construct to the operator " +
-		this.toString() + ".");
-    return null;   // make compiler happy
-  }
-
-  public final Value getDomain() {
-    Assert.fail("Attempted to compute the domain of the operator " +
-		this.toString() + ".");
-    return EmptySet;   // make compiler happy
-  }
-  
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the operator " +
-		this.toString() + ".");
-    return 0;   // make compiler happy
-  }
-
-  /* Should never normalize an operator. */
-  public final boolean isNormalized() {
-      throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator.");
-  }
-  
-  public final void normalize() {
-      throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator.");
-  }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-      throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator.");
-  }
-
-  /* String representation of the value.  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return sb.append("<Java Method: " + this.md + ">");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/ModelValue.java b/tlatools/src/tlc2/value/ModelValue.java
deleted file mode 100644
index 38fe2acf4b8106c03cf1f487b954d117a16f73f8..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/ModelValue.java
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:32:25 PST by lamport
-//      modified on Fri Aug 10 15:07:47 PDT 2001 by yuanyu
-
-/***************************************************************************
-* Change to model values made by LL on 23 Feb 2008:                        *
-* ------                                                                   *
-* A model value whose name is "x_str" for any character x and string str   *
-* is said to have TYPE 'x'.  Otherwise, it is said to be untyped.  It is   *
-* an error to test if a typed model value is equal to any value except a   *
-* model value that is either untyped or has the same type.  (As before,    *
-* an untyped model value is unequal to any value except itself.)           *
-*                                                                          *
-* This was implemented by adding a `type' field to a ModelValue object     *
-* and making changes to the following classes:                             *
-*                                                                          *
-*    changed member method                                                 *
-*       module/Integers.java                                               *
-*       module/Naturals.java                                               *
-*       module/Sequences.java                                              *
-*       module/Strings.java                                                *
-*       value/IntervalValue.java  CHECK THIS with mv \notin 1..2           *
-*       value/SetOfFcnsValue.java                                          *
-*       value/SetOfRcdsValue.java                                          *
-*       value/SetOfTuplesValue.java                                        *
-*       value/StringValue.java                                             *
-*    changed equals method                                                 *
-*       value/BoolValue.java                                               *
-*       value/FcnRcdValue.java                                             *
-*       value/IntValue.java                                                *
-*       value/RecordValue.java                                             *
-*       value/SetEnumValue.java                                            *
-***************************************************************************/
-
-package tlc2.value;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-import tlc2.util.FP64;
-import util.Assert;
-import util.UniqueString;
-
-public class ModelValue extends Value {
-    
-    /**
-     * A method to reset the model values
-     * All callers should make sure that the model value class has been initialized
-     */
-    public static void init()
-    {
-       count = 0;
-       mvTable = new Hashtable();
-       mvs = null;
-    }
-
-    /**
-     * Workround to the static usage
-     */
-    static 
-    {
-        init();
-    }
-    
-  private static int count;
-  private static Hashtable mvTable;
-  // SZ Mar 9, 2009: public accessed field, this will cause troubles
-  public static ModelValue[] mvs;
-
-  public UniqueString val;
-  public int index;
-  public char type;  // type = 0 means untyped.
-  
-  /* Constructor */
-  private ModelValue(String val) {
-    // SZ 11.04.2009: changed access method
-    this.val = UniqueString.uniqueStringOf(val);
-    this.index = count++;
-    if (   (val.length() > 2)
-        && (val.charAt(1) == '_')) {
-      this.type = val.charAt(0) ;  
-      }
-     else { this.type = 0 ; } ;
-  }
-
-  /* Make str a new model value, if it is not one yet.  */
-  public static ModelValue make(String str) {
-    ModelValue mv = (ModelValue)mvTable.get(str);
-    if (mv != null) return mv;
-    mv = new ModelValue(str);
-    mvTable.put(str, mv);
-    return mv;
-  }
-
-  /* Collect all the model values defined thus far. */
-  public static void setValues() {
-    mvs = new ModelValue[mvTable.size()];    
-    Enumeration Enum = mvTable.elements();
-    while (Enum.hasMoreElements()) {
-      ModelValue mv = (ModelValue)Enum.nextElement();
-      mvs[mv.index] = mv;
-    }
-  }
-
-  public final byte getKind() { return MODELVALUE; }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof ModelValue) {
-      return this.val.compareTo(((ModelValue)obj).val);
-    }
-    return -1;
-  }
-
-  public final boolean equals(Object obj) {
-    if (this.type == 0) {
-      return (obj instanceof ModelValue &&
-	      this.val.equals(((ModelValue)obj).val));
-     };
-    if (obj instanceof ModelValue) {
-      ModelValue mobj = (ModelValue) obj ;
-      if (   (mobj.type == this.type) 
-          || (mobj.type == 0) ) { 
-        return mobj.val == this.val ;
-        } 
-       else {
-        Assert.fail("Attempted to check equality "
-                    + "of the differently-typed model values "
-                      + ppr(this.toString()) + " and " 
-                      + ppr(mobj.toString()));
-        } ;
-     } ;
-    Assert.fail("Attempted to check equality of typed model value "
-                 + ppr(this.toString()) + " and non-model value\n"
-                 + ppr(obj.toString())) ;
-    return false;   // make compiler happy
-  }
-
-  /*************************************************************************
-  * The following two methods are used used to check if this model value   *
-  * equal to or a member of non-model value obj.  They return false if     *
-  * this model value is untyped and raise an exception if it is typed.     *
-  *************************************************************************/
-  public final boolean modelValueEquals(Object obj){
-    if (this.type != 0) {
-    Assert.fail("Attempted to check equality of the typed model value "
-                 + ppr(this.toString()) + " and the non-model value\n"
-                 + ppr(obj.toString())) ;
-
-     } ;
-    return false ;    
-  }
-
-  public final boolean modelValueMember(Object obj){
-    if (this.type != 0) {
-    Assert.fail("Attempted to check if the typed model value "
-                 + ppr(this.toString()) 
-                 + " is an element of\n"
-                 + ppr(obj.toString())) ;
-
-     } ;
-    return false ;    
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of the model value " + ppr(this.toString()));
-    return false;   // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the model value " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;   // make compiler happy
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT construct to the model value " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-  
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the model value " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the model value " +
-		ppr(this.toString()) + ".");
-    return 0;   // make compiler happy
-  }
-
-  public final boolean isNormalized() { return true; }
-
-  public final void normalize() { /*nop*/ }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return ((val instanceof ModelValue) &&
-	    this.val.equals(((ModelValue)val).val));
-  }
-
-  /* The fingerprint methods */
-  public final long fingerPrint(long fp) {
-    return this.val.fingerPrint(FP64.Extend(fp, MODELVALUE));
-  }
-
-  public final Value permute(MVPerm perm) {
-    Value res = perm.get(this);
-    if (res == null) return this;
-    return res;
-  }
-
-  /* The string representation. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return sb.append(this.val);
-  }
-
-
-}
diff --git a/tlatools/src/tlc2/value/OpLambdaValue.java b/tlatools/src/tlc2/value/OpLambdaValue.java
deleted file mode 100644
index c7b35723637609a71d3fd9c401ff765eba2cced7..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/OpLambdaValue.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport
-//      modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu
-
-package tlc2.value;
-
-import tla2sany.semantic.FormalParamNode;
-import tla2sany.semantic.OpDefNode;
-import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
-import tlc2.util.Context;
-import util.Assert;
-import util.WrongInvocationException;
-
-public class OpLambdaValue extends OpValue implements Applicable {
-  public OpDefNode opDef;       // the operator definition.
-  public Tool tool;
-  public Context con;
-  public TLCState state;
-  public TLCState pstate;
-  
-  /* Constructor */
-  public OpLambdaValue(OpDefNode op, Tool tool,	Context con,
-		       TLCState state, TLCState pstate) {
-    this.opDef = op;
-    this.tool = tool;
-    this.state = state;
-    this.con = con;
-    this.pstate = pstate;
-  }
-
-  public final byte getKind() { return OPLAMBDAVALUE; }
-
-  public final int compareTo(Object obj) {
-    Assert.fail("Attempted to compare operator " + ppr(this.toString()) +
-		" with value:\n" + ppr(obj.toString()));
-    return 0;       // make compiler happy
-  }
-  
-  public final boolean equals(Object obj) {
-    Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) +
-		" with value:\n" + ppr(obj.toString()));
-    return false;   // make compiler happy
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of operator " + ppr(this.toString()));
-    return false;   // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the operator " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;   // make compiler happy
-  }
-
-  public final Value apply(Value arg, int control) {
-      throw new WrongInvocationException("Should use the other apply method.");
-  }
-
-  public final Value apply(Value[] args, int control) {
-    int alen = this.opDef.getArity();
-    if (alen != args.length) {
-      Assert.fail("Applying the operator " + ppr(this.toString()) +
-		  " with wrong number of arguments.");
-    }
-    Context c1 = this.con;
-    FormalParamNode[] formals = this.opDef.getParams();    
-    for (int i = 0; i < alen; i++) {
-      c1 = c1.cons(formals[i], args[i]);
-    }
-    return this.tool.eval(this.opDef.getBody(), c1, this.state, this.pstate,
-			  control);
-  }
-
-  public final Value select(Value arg) {
-      throw new WrongInvocationException("Error(TLC): attempted to call OpLambdaValue.select().");
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    Assert.fail("Attempted to appy EXCEPT construct to the operator " +
-		ppr(this.toString()) + ".");
-    return null;   // make compiler happy
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    Assert.fail("Attempted to apply EXCEPT construct to the operator " +
-		ppr(this.toString()) + ".");
-    return null;   // make compiler happy
-  }
-
-  public final Value getDomain() {
-    Assert.fail("Attempted to compute the domain of the operator " +
-		ppr(this.toString()) + ".");
-    return EmptySet;   // make compiler happy
-  }
-  
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the operator " +
-		ppr(this.toString()) + ".");
-    return 0;   // make compiler happy
-  }
-
-  /* Should never normalize an operator. */
-  public final boolean isNormalized() {
-      throw new WrongInvocationException("Should not normalize an operator.");
-  }
-  
-  public final void normalize() {
-      throw new WrongInvocationException("Should not normalize an operator.");
-  }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-      throw new WrongInvocationException("Should not initialize an operator.");
-  }
-
-  /* String representation of the value.  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    String opName = this.opDef.getName().toString();
-    return sb.append("<Operator ").append(opName).append(">");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/OpRcdValue.java b/tlatools/src/tlc2/value/OpRcdValue.java
deleted file mode 100644
index fe25a594cf148fc35178e8b691bd513f50440c06..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/OpRcdValue.java
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:01 PST by lamport
-//      modified on Sat Nov 13 12:43:44 PST 1999 by yuanyu
-
-package tlc2.value;
-
-import tlc2.util.Vect;
-import util.Assert;
-import util.WrongInvocationException;
-
-public class OpRcdValue extends OpValue implements Applicable {
-  public Vect domain;
-  public Vect values;
-  
-  /* Constructor */
-  public OpRcdValue() {
-    this.domain = new Vect();
-    this.values = new Vect();
-  }
-
-  public OpRcdValue(Vect domain, Vect values) {
-    this.domain = domain;
-    this.values = values;
-  }
-
-  public final byte getKind() { return OPRCDVALUE; }
-
-  public final int compareTo(Object obj) {
-    Assert.fail("Attempted to compare operator " + ppr(this.toString()) +
-		" with value:\n" + ppr(obj.toString()));
-    return 0;         // make compiler happy
-  }
-  
-  public final boolean equals(Object obj) {
-    Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) +
-		" with value:\n" + ppr(obj.toString()));
-    return false;     // make compiler happy
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of operator " + ppr(this.toString()));
-    return false;     // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the operator " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;     // make compiler happy
-  }
-  
-  public final void addLine(Vect vs) {
-    int len = vs.size();
-    Value[] args = new Value[len-2];
-    for (int i = 0; i < len-2; i++) {
-      args[i] = (Value)vs.elementAt(i+1);
-    }
-    this.domain.addElement(args);
-    this.values.addElement(vs.elementAt(len-1));
-  }
-
-  public final Value apply(Value arg, int control) {
-      throw new WrongInvocationException("Should use the other apply method.");
-  }
-
-  public final Value apply(Value[] args, int control) {
-    int sz = this.domain.size();
-    for (int i = 0; i < sz; i++) {
-      Value[] vals = (Value[])this.domain.elementAt(i);
-      if (args.length != vals.length) {
-	Assert.fail("Attempted to apply the operator " + ppr(this.toString()) +
-		    "\nwith wrong number of arguments.");
-      }
-      boolean matched = true;
-      for (int j = 0; j < vals.length; j++) {
-	matched = vals[j].equals(args[j]);
-	if (!matched) break;
-      }
-      if (matched) {
-	return (Value)this.values.elementAt(i);
-      }
-    }
-    // Generate the error message:
-    String msg = "Attempted to apply operator:\n" + ppr(this.toString()) +
-      "\nto arguments (";
-    if (args.length > 0) msg += args[0];
-    for (int i = 1; i < args.length; i++) {
-      msg += ", " + args[i];
-    }
-    Assert.fail(msg +  "), which is undefined.");
-    return null;     // make compiler happy
-  }
-
-  public final Value select(Value arg) {
-    Assert.fail("Attempted to call OpRcdValue.select(). This is a TLC bug.");
-    return null;   // make compiler happy    
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    Assert.fail("Attempted to appy EXCEPT construct to the operator " +
-		ppr(this.toString()) + ".");
-    return null;     // make compiler happy
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    Assert.fail("Attempted to apply EXCEPT construct to the operator " +
-		ppr(this.toString()) + ".");
-    return null;     // make compiler happy
-  }
-
-  public final Value getDomain() {
-    Assert.fail("Attempted to compute the domain of the operator " +
-		ppr(this.toString()) + ".");
-    return EmptySet;   // make compiler happy
-  }
-  
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the operator " +
-		ppr(this.toString()) + ".");
-    return 0;         // make compiler happy
-  }
-
-  /* Should never normalize an operator. */
-  public final boolean isNormalized() {
-      throw new WrongInvocationException("Should not normalize an operator.");
-  }
-  
-  public final void normalize() {
-      throw new WrongInvocationException("Should not normalize an operator.");
-  }
-
-  public final boolean isDefined() {
-    boolean defined = true;
-    for (int i = 0; i < this.values.size(); i++) {
-      defined = defined && ((Value)this.values.elementAt(i)).isDefined();
-    }
-    return defined;
-  }
-  
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-      throw new WrongInvocationException("Should not initialize an operator.");
-  }
-
-  /* Pretty-printing  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    sb.append("{ ");
-    if (this.values.size() != 0) {
-      sb.append("<");
-      Value[] args = (Value[])this.domain.elementAt(0);
-      for (int j = 0; j < args.length; j++) {
-	sb = args[j].toString(sb, offset);
-	sb.append(", ");	
-      }
-      sb = ((Value)this.values.elementAt(0)).toString(sb, offset);
-      sb.append(">");
-    }
-    for (int i = 1; i < this.values.size(); i++) {
-      sb.append(", <");
-      Value[] args = (Value[])this.domain.elementAt(i);
-      for (int j = 0; j < args.length; j++) {
-	sb = args[j].toString(sb, offset);
-	sb.append(", ");	
-      }
-      sb = ((Value)this.values.elementAt(i)).toString(sb, offset);
-      sb.append(">");
-    }
-    return sb.append("}");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/OpValue.java b/tlatools/src/tlc2/value/OpValue.java
deleted file mode 100644
index 1aa0c0a3a31d69e0da656f28e83012e49e4b0e70..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/OpValue.java
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:02 PST by lamport
-//      modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu
-
-package tlc2.value;
-
-public abstract class OpValue extends Value implements Applicable {
-
-}
diff --git a/tlatools/src/tlc2/value/RandomEnumerableValues.java b/tlatools/src/tlc2/value/RandomEnumerableValues.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d7d3f97f5e71369d1328dc961083587fd3556ad
--- /dev/null
+++ b/tlatools/src/tlc2/value/RandomEnumerableValues.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ *   Ian Morris Nieves - added support for fingerprint stack trace
+ ******************************************************************************/
+
+package tlc2.value;
+
+import java.util.Random;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.ModelChecker;
+import tlc2.tool.TLCState;
+import tlc2.util.IdThread;
+
+public abstract class RandomEnumerableValues {
+	
+	/* Randomization for sets */
+		
+	private static long randomSeed; 
+	
+	public static long getSeed() {
+		return randomSeed;
+	}
+	
+	/**
+	 * Initialize Random with the given seed value.
+	 **/
+	public static void setSeed(final long seed) {
+		randomSeed = seed;
+		reset();
+	}
+	
+	/**
+	 * Re-Initialize Random with the recorded seed value.
+	 **/
+	public static void reset() {
+		RANDOMS.remove();
+	}
+
+	public static Random get() {
+		return RANDOMS.get();
+	}
+	
+	private static final ThreadLocal<Random> RANDOMS = new ThreadLocal<Random>() {
+		@Override
+		protected Random initialValue() {
+			if (TLCGlobals.mainChecker != null && ModelChecker.class.equals(TLCGlobals.mainChecker.getClass())) {
+				// In order to recreate the error trace in BFS mode - which essentially
+				// corresponds to rerunning state exploration following a given path - we have
+				// to recreate the same random values too. Otherwise, TLC will fail to recreate
+				// the trace because the recreated TLCState does not match the fingerprint in
+				// the error trace/path. Therefore, we maintain one RNG per IdThread (IWorker)
+				// which - for the initial states - is seeded with enumFractionSeed and - in the
+				// scope of next-state - gets seeded with the fingerprint of the predecessor
+				// state.
+				return new TLCStateRandom(randomSeed);
+			} else {
+				// DFS or Simulation need no special handling because:
+				// - DFS does not re-create the error trace (just prints the current trace).
+				// - Simulation mode is intended to allow users to gather statistics on
+				// behaviors generated with specified probabilities of different transitions.
+				// (For example, to find the expected time for a message to be delivered with
+				// retransmission as a function of the probability of message loss). For this,
+				// it's important that the random choice have no correlation with the state in
+				// which the choice is made.
+				return new DefaultRandom(randomSeed);
+			}
+		}
+
+		@Override
+		public Random get() {
+			// Hook to re-initialize random (no-op for DFS and simulation).
+			return ((EnumerableValueRandom) super.get()).initialize();
+		}
+	};
+		
+	private interface EnumerableValueRandom {
+		Random initialize();
+	}
+
+	@SuppressWarnings("serial")
+	private static final class DefaultRandom extends Random implements EnumerableValueRandom {
+		public DefaultRandom(long randomSeed) {
+			super(randomSeed);
+		}
+
+		@Override
+		public final Random initialize() {
+			// Noop
+			return this;
+		}
+	}
+
+	@SuppressWarnings("serial")
+	private static final class TLCStateRandom extends Random implements EnumerableValueRandom {
+
+		private TLCState state;
+
+		public TLCStateRandom(long randomSeed) {
+			super(randomSeed);
+		}
+
+		private void initializedFor(final TLCState state) {
+			// XOR the state's fingerprint with the initial randomSeed value. This is done
+			// so that two identical states (same fingerprint) of two different
+			// specifications do not produce the same random value.
+			final long seed = state.fingerPrint() ^ randomSeed;
+			this.setSeed(seed);
+			this.state = state;
+		}
+
+		private boolean isInitializedFor(final TLCState another) {
+			return state == another;
+		}
+
+		@Override
+		public Random initialize() {
+			final TLCState state = IdThread.getCurrentState();
+			// state is null during the generation of initial states and non-null in the
+			// scope of the next-state relation (however, state can be an initial state).
+			// Thus, an RNG is seeded with randomSeed during the generation of initial
+			// states and seeded with randomSeed ^ predecessor's fingerprint during the
+			// generation of next states.
+			// Do not re-initialize random for the same TLCState twice to produce two
+			// distinct values with high probability with a next-state such as:
+			// Next == x' = RandomElement(0..2) /\ y' = RandomElement(0..2)
+			// If random was to be re-initialized/re-seeded, RandomElement(0..2) for x' and y'
+			// would be identical values (also see tlc2.tool.RandomElementXandYTest).
+			if (state != null && !isInitializedFor(state)) {
+				initializedFor(state);
+			}
+			return this;
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/value/RecordValue.java b/tlatools/src/tlc2/value/RecordValue.java
deleted file mode 100644
index 3dd1281069177fe6296207d949e2a23908dfcbe0..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/RecordValue.java
+++ /dev/null
@@ -1,328 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:15:47 PST by lamport
-//      modified on Fri Aug 10 15:09:07 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.output.EC;
-import tlc2.output.MP;
-import tlc2.util.FP64;
-import util.Assert;
-import util.UniqueString;
-
-public class RecordValue extends Value implements Applicable {
-  public UniqueString[] names;   // the field names
-  public Value[] values;         // the field values
-  private boolean isNorm;
-  
-  /* Constructor */
-  public RecordValue(UniqueString[] names, Value[] values, boolean isNorm) {
-    this.names = names;
-    this.values = values;
-    this.isNorm = isNorm;
-  }
-
-  public final byte getKind() { return RECORDVALUE; }
-
-  public final int compareTo(Object obj) {
-    RecordValue rcd = convert(obj);
-    if (rcd == null) {
-      if (obj instanceof ModelValue) return 1;
-      Assert.fail("Attempted to compare record:\n" + ppr(this.toString()) +
-		  "\nwith non-record\n" + ppr(obj.toString()));
-    }
-    this.normalize();
-    rcd.normalize();
-    int len = this.names.length;
-    int cmp = len - rcd.names.length;
-    if (cmp == 0) {
-      for (int i = 0; i < len; i++) {
-	cmp = this.names[i].compareTo(rcd.names[i]);
-	if (cmp != 0) break;
-	cmp = this.values[i].compareTo(rcd.values[i]);
-	if (cmp != 0) break;
-      }
-    }
-    return cmp;
-  }
-
-  public final boolean equals(Object obj) {
-    RecordValue rcd = convert(obj);
-    if (rcd == null) {
-      if (obj instanceof ModelValue) 
-         return ((ModelValue) obj).modelValueEquals(this) ;
-      Assert.fail("Attempted to check equality of record:\n" + ppr(this.toString()) +
-		  "\nwith non-record\n" + ppr(obj.toString()));
-    }
-    this.normalize();
-    rcd.normalize();
-    int len = this.names.length;
-    if (len != rcd.names.length) return false;
-    for (int i = 0; i < len; i++) {
-      if ((!(this.names[i].equals(rcd.names[i]))) ||
-	  (!(this.values[i].equals(rcd.values[i]))))
-	return false;
-    }
-    return true;
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if element:\n" + ppr(elem.toString()) +
-                "\nis in the record:\n" + ppr(this.toString()));
-    return false;    // make compiler happy
-  }
-
-  public final boolean isFinite() { return true; }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      int rlen = this.names.length;
-      Value[] newValues = new Value[rlen];
-      Value arcVal = ex.path[ex.idx];
-      if (arcVal instanceof StringValue) {
-	UniqueString arc = ((StringValue)arcVal).val;
-	for (int i = 0; i < rlen; i++) {
-	  if (this.names[i].equals(arc)) {
-	    ex.idx++;
-	    newValues[i] = this.values[i].takeExcept(ex);
-	  }
-	  else {
-	    newValues[i] = this.values[i];
-	  }
-	}
-	UniqueString[] newNames = this.names;
-	if (!this.isNorm) {
-	  newNames = new UniqueString[rlen];
-	  for (int i = 0; i < rlen; i++) {
-	    newNames[i] = this.names[i];
-	  }
-	}
-	return new RecordValue(newNames, newValues, this.isNorm);
-      }
-      else {
-          MP.printWarning(EC.TLC_WRONG_RECORD_FIELD_NAME, new String[]{ppr(arcVal.toString())});
-      }
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    Value res = this;
-    for (int i = 0; i < exs.length; i++) {
-      res = res.takeExcept(exs[i]);
-    }
-    return res;
-  }
-
-  /*
-   * This method converts a value to a function value. It returns
-   * null if the conversion fails.
-   */
-  public static final RecordValue convert(Object val) {
-    if (val instanceof RecordValue) {
-      return (RecordValue)val;
-    }
-    else if (val instanceof FcnRcdValue ||
-	     val instanceof FcnLambdaValue) {
-      FcnRcdValue fcn = FcnRcdValue.convert(val);
-      if (fcn == null || fcn.domain == null) return null;
-      fcn.normalize();
-      UniqueString[] vars = new UniqueString[fcn.domain.length];
-      for (int i = 0; i < fcn.domain.length; i++) {
-	if (!(fcn.domain[i] instanceof StringValue)) {
-	  return null;
-	}
-	vars[i] = ((StringValue)fcn.domain[i]).getVal();
-      }
-      return new RecordValue(vars, fcn.values, fcn.isNormalized());
-    }
-    else if ((val instanceof TupleValue) &&
-	     ((TupleValue)val).size() == 0) {
-      return EmptyRcd;
-    }
-    // return null if not convertable
-    return null;
-  }
-  
-  public final int size() { return this.names.length; }
-
-  public final Value apply(Value arg, int control) {
-    if (!(arg instanceof StringValue)) {
-      Assert.fail("Attempted to apply record to a non-string value " +
-		  ppr(arg.toString()) + ".");
-    }
-    UniqueString name = ((StringValue)arg).getVal();    
-    int rlen = this.names.length;
-    for (int i = 0; i < rlen; i++) {
-      if (name.equals(this.names[i])) {
-	return this.values[i];
-      }
-    }
-    Assert.fail("Attempted to apply the record\n" + ppr(this.toString()) +
-		"\nto nonexistent record field " + name + ".");
-    return null;    // make compiler happy
-  }
-
-  public final Value apply(Value[] args, int control) {
-    if (args.length != 1) {
-      Assert.fail("Attempted to apply record to more than one arguments.");
-    }
-    return this.apply(args[0], control);
-  }
-
-  /* This method returns the named component of the record. */
-  public final Value select(Value arg) {
-    if (!(arg instanceof StringValue)) {
-      Assert.fail("Attempted to apply record to a non-string argument " +
-		  ppr(arg.toString()) + ".");
-    }
-    UniqueString name = ((StringValue)arg).getVal();    
-    int rlen = this.names.length;
-    for (int i = 0; i < rlen; i++) {
-      if (name.equals(this.names[i])) {
-	return this.values[i];
-      }
-    }
-    return null;
-  }
-
-  public final Value getDomain() {
-    Value[] dElems = new Value[this.names.length];
-    for (int i = 0; i < this.names.length; i++) {
-      dElems[i] = new StringValue(this.names[i]);
-    }
-    return new SetEnumValue(dElems, this.isNormalized());
-  }
-
-  public final boolean assign(UniqueString name, Value val) {
-    for (int i = 0; i < this.names.length; i++) {
-      if (name.equals(this.names[i])) {
-	if (this.values[i] == ValUndef ||
-	    this.values[i].equals(val)) {
-	  this.values[i] = val;
-	  return true;
-	}
-	return false;
-      }
-    }
-    Assert.fail("Attempted to assign to nonexistent record field " + name + ".");
-    return false;    // make compiler happy
-  }
-
-  public final boolean isNormalized() { return this.isNorm; }
-  
-  public final void normalize() {
-    if (!this.isNorm) {
-      int len = this.names.length;
-      for (int i = 1; i < len; i++) {
-	int cmp = this.names[0].compareTo(this.names[i]);
-	if (cmp == 0) {
-	  Assert.fail("Field name " + this.names[i] + " occurs multiple times in record.");
-	}
-	else if (cmp > 0) {
-	  UniqueString ts = this.names[0];
-	  this.names[0] = this.names[i];
-	  this.names[i] = ts;
-	  Value tv = this.values[0];
-	  this.values[0] = this.values[i];
-	  this.values[i] = tv;
-	}
-      }
-      for (int i = 2; i < len; i++) {
-	int j = i;
-	UniqueString st = this.names[i];
-	Value val = this.values[i];
-	int cmp;
-	while ((cmp = st.compareTo(this.names[j-1])) < 0) {
-	  this.names[j] = this.names[j-1];
-	  this.values[j] = this.values[j-1];
-	  j--;
-	}
-	if (cmp == 0) {
-	  Assert.fail("Field name " + this.names[i] + " occurs multiple times in record.");
-	}
-	this.names[j] = st;
-	this.values[j] = val;
-      }
-      this.isNorm = true;
-    }
-  }
-
-  public final boolean isDefined() {
-    boolean defined = true;
-    for (int i = 0; i < this.values.length; i++) {
-      defined = defined && this.values[i].isDefined();
-    }
-    return defined;
-  }
-
-  public final Value deepCopy() {
-    Value[] vals = new Value[this.values.length];
-    for (int i = 0; i < this.values.length; i++) {
-      vals[i] = this.values[i].deepCopy();
-    }
-    return new RecordValue(this.names, vals, this.isNorm);
-  }
-
-  public final boolean assignable(Value val) {
-    boolean canAssign = ((val instanceof RecordValue) &&
-			 this.names.length == ((RecordValue)val).names.length);
-    if (!canAssign) return false;
-    for (int i = 0; i < this.values.length; i++) {
-      canAssign = (canAssign &&
-		   this.names[i].equals(((RecordValue)val).names[i]) &&
-		   this.values[i].assignable(((RecordValue)val).values[i]));
-    }
-    return canAssign;
-  }
-  
-  /* The fingerprint methods.  */
-  public final long fingerPrint(long fp) {
-    this.normalize();
-    int rlen = this.names.length;
-    fp = FP64.Extend(fp, FCNRCDVALUE);
-    fp = FP64.Extend(fp, rlen);
-    for (int i = 0; i < rlen; i++) {
-      String str = this.names[i].toString();
-      fp = FP64.Extend(fp, STRINGVALUE);
-      fp = FP64.Extend(fp, str.length());
-      fp = FP64.Extend(fp, str);
-      fp = this.values[i].fingerPrint(fp);
-    }
-    return fp;
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.normalize();
-    int rlen = this.names.length;
-    Value[] vals = new Value[rlen];
-    boolean changed = false;
-    for (int i = 0; i < rlen; i++) {
-      vals[i] = this.values[i].permute(perm);
-      changed = changed || (vals[i] != this.values[i]);
-    }
-    if (changed) {
-      return new RecordValue(this.names, vals, true);
-    }
-    return this;
-  }
-
-  /* The string representation */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    int len = this.names.length;
-
-    sb.append("[");
-    if (len > 0) {
-      sb.append(this.names[0] + " |-> ");
-      sb = this.values[0].toString(sb, offset);
-    }
-    for (int i = 1; i < len; i++) {
-      sb.append(", ");
-      sb.append(this.names[i] + " |-> ");
-      sb = this.values[i].toString(sb, offset);
-    }
-    return sb.append("]");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/SetCapValue.java b/tlatools/src/tlc2/value/SetCapValue.java
deleted file mode 100644
index aa6a1411ba164cf8276e99341a6c19ce5dda58a5..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetCapValue.java
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport
-//      modified on Fri Aug 10 15:09:21 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import util.Assert;
-
-public class SetCapValue extends Value implements Enumerable {
-  public Value set1;
-  public Value set2;
-  protected SetEnumValue capSet;
-  
-  /* Constructor */
-  public SetCapValue(Value set1, Value set2) {
-    this.set1 = set1;
-    this.set2 = set2;
-    this.capSet = null;
-  }
-
-  public final byte getKind() { return SETCAPVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.capSet.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    this.convertAndCache();
-    return this.capSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    return (this.set1.member(elem) && this.set2.member(elem));
-  }
-
-  public final boolean isFinite() {
-    if (!this.set1.isFinite() && !this.set2.isFinite()) {
-      Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite.");
-    }
-    return true;
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    this.convertAndCache();
-    return this.capSet.size();
-  }
-
-  public final boolean isNormalized() {
-    if (this.capSet == null || this.capSet == DummyEnum) {
-      return (this.set1.isNormalized() && this.set2.isNormalized());
-    }
-    return this.capSet.isNormalized();
-  }
-  
-  public final void normalize() {
-    if (this.capSet == null || this.capSet == DummyEnum) {
-      this.set1.normalize();
-      this.set2.normalize();
-    }
-    else {
-      this.capSet.normalize();
-    }
-  }
-
-  public final boolean isDefined() {
-    return this.set1.isDefined() && this.set2.isDefined();
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) { return this.equals(val); }
-
-  /* The fingerprint methods */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.capSet.fingerPrint(fp);
-  }
-  
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.capSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.capSet == null) {
-      this.capSet = SetEnumValue.convert(this);
-    }
-    else if (this.capSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.capSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.capSet == DummyEnum) { this.capSet = val; }
-      }
-    }
-  }
-  
-  /* String representation of this value.  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    try {
-      if (expand) {
-	Value val = SetEnumValue.convert(this);
-	return val.toString(sb, offset);
-      }
-    }
-    catch (Throwable e) { /*SKIP*/ }
-
-    sb = this.set1.toString(sb, offset);
-    sb = sb.append(" \\cap ");
-    sb = this.set2.toString(sb, offset);
-    return sb;
-  }
-
-  public final ValueEnumeration elements() {
-    if (this.capSet == null || this.capSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.capSet.elements();
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    ValueEnumeration enum1;
-    Value set;
-
-    public Enumerator() {
-      if (set1 instanceof Enumerable) {
-	this.enum1 = ((Enumerable)set1).elements();
-	this.set = set2;
-      }
-      else if (set2 instanceof Enumerable) {
-	this.enum1 = ((Enumerable)set2).elements();
-	this.set = set1;
-      }
-      else {
-	Assert.fail("Attempted to enumerate S \\cap T when neither S:\n" +
-		    ppr(set1.toString()) + "\nnor T:\n" + ppr(set2.toString()) +
-		    "\nis enumerable");
-      }
-    }
-
-    public final void reset() { this.enum1.reset(); }
-      
-    public final Value nextElement() {
-      Value elem = this.enum1.nextElement();
-      while (elem != null) {
-	if (this.set.member(elem)) return elem;
-	elem = this.enum1.nextElement();
-      }
-      return null;
-    }
-  }
-  
-}
diff --git a/tlatools/src/tlc2/value/SetCupValue.java b/tlatools/src/tlc2/value/SetCupValue.java
deleted file mode 100644
index 5ae729e2943d1d8fe4cf8219e517b77ac4c57072..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetCupValue.java
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport
-//      modified on Fri Aug 10 15:09:28 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import util.Assert;
-
-public class SetCupValue extends Value implements Enumerable {
-  public Value set1;
-  public Value set2;
-  protected SetEnumValue cupSet;  
-  
-  /* Constructor */
-  public SetCupValue(Value set1, Value set2) {
-    this.set1 = set1;
-    this.set2 = set2;
-    this.cupSet = null;
-  }
-
-  public final byte getKind() { return SETCUPVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.cupSet.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    this.convertAndCache();    
-    return this.cupSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    return this.set1.member(elem) || this.set2.member(elem);
-  }
-
-  public final boolean isFinite() {
-    return this.set1.isFinite() && this.set2.isFinite();
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    this.convertAndCache();    
-    return this.cupSet.size();
-  }
-
-  public final boolean isNormalized() {
-    return (this.cupSet != null &&
-	    this.cupSet != DummyEnum &&
-	    this.cupSet.isNormalized());
-  }
-  
-  public final void normalize() {
-    if (this.cupSet != null && this.cupSet != DummyEnum) {
-      this.cupSet.normalize();
-    }
-  }
-
-  public final boolean isDefined() {
-    return this.set1.isDefined() && this.set2.isDefined();
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  /* The fingerprint methods */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.cupSet.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.cupSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.cupSet == null) {
-      this.cupSet = SetEnumValue.convert(this);
-    }
-    else if (this.cupSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.cupSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.cupSet == DummyEnum) {	this.cupSet = val; }
-      }
-    }
-  }
-  
-  /* String representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    try {
-      if (expand) {
-	Value val = SetEnumValue.convert(this);
-	return val.toString(sb, offset);
-      }
-    }
-    catch (Throwable e) { /*SKIP*/ }
-
-    sb = this.set1.toString(sb, offset);
-    sb = sb.append(" \\cup ");
-    sb = this.set2.toString(sb, offset);
-    return sb;
-  }
-
-  public final ValueEnumeration elements() {
-    if (this.cupSet == null || this.cupSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.cupSet.elements();
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    ValueEnumeration enum1;
-    ValueEnumeration enum2;
-
-    public Enumerator() {
-      if ((set1 instanceof Enumerable) &&
-	  (set2 instanceof Enumerable)) {
-	this.enum1 = ((Enumerable)set1).elements();
-	this.enum2 = ((Enumerable)set2).elements();
-      }
-      else {
-	Assert.fail("Attempted to enumerate S \\cup T when S:\n" +
-		    ppr(set1.toString()) + "\nand T:\n" + ppr(set2.toString()) +
-		    "\nare not both enumerable");
-      }
-    }
-
-    public final void reset() {
-      this.enum1.reset();
-      this.enum2.reset();
-    }
-    
-    public final Value nextElement() {
-      Value elem = this.enum1.nextElement();
-      if (elem != null) return elem;
-      elem = this.enum2.nextElement();
-      return elem;
-    }
-  }
-  
-}
diff --git a/tlatools/src/tlc2/value/SetDiffValue.java b/tlatools/src/tlc2/value/SetDiffValue.java
deleted file mode 100644
index d14230d38d11ec5a9afca099b80e720f6e29699d..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetDiffValue.java
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport
-//      modified on Fri Aug 10 15:09:34 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import util.Assert;
-
-public class SetDiffValue extends Value implements Enumerable {
-  public Value set1;
-  public Value set2;
-  protected SetEnumValue diffSet; 
-  
-  /* Constructor */
-  public SetDiffValue(Value set1, Value set2) {
-    this.set1 = set1;
-    this.set2 = set2;
-    this.diffSet = null;
-  }
-
-  public final byte getKind() { return SETDIFFVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.diffSet.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    this.convertAndCache();
-    return this.diffSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    return (this.set1.member(elem) && !this.set2.member(elem));
-  }
-
-  public final boolean isFinite() {
-    if (this.set1.isFinite()) {
-      return true;
-    }
-    if (!this.set2.isFinite()) {
-      Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite.");
-    }
-    return false;
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    this.convertAndCache();
-    return this.diffSet.size();
-  }
-
-  public final boolean isNormalized() {
-    if (this.diffSet == null || this.diffSet == DummyEnum) {
-      return this.set1.isNormalized();
-    }
-    return this.diffSet.isNormalized();
-  }
-  
-  public final void normalize() {
-    if (this.diffSet == null || this.diffSet == DummyEnum) {
-      this.set1.normalize();
-      this.set2.normalize();
-    }
-    else {
-      this.diffSet.normalize();
-    }
-  }
-
-  public final boolean isDefined() {
-    return this.set1.isDefined() && this.set2.isDefined();
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  /* The fingerprint methods */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.diffSet.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.diffSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.diffSet == null) {
-      this.diffSet = SetEnumValue.convert(this);
-    }
-    else if (this.diffSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.diffSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.diffSet == DummyEnum) { this.diffSet = val; }
-      }
-    }
-  }
-
-  /* The string representation  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    try {
-      if (expand) {
-	Value val = SetEnumValue.convert(this);
-	return val.toString(sb, offset);
-      }
-    }
-    catch (Throwable e) { /*SKIP*/ }
-
-    sb = this.set1.toString(sb, offset);
-    sb = sb.append(" \\ ");
-    sb = this.set2.toString(sb, offset);
-    return sb;
-  }
-
-  public final ValueEnumeration elements() {
-    if (this.diffSet == null || this.diffSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.diffSet.elements();
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    ValueEnumeration enum1;
-
-    public Enumerator() {
-      if (set1 instanceof Enumerable) {
-	this.enum1 = ((Enumerable)set1).elements();
-      }
-      else {
-	Assert.fail("Attempted to enumerate S \\ T when S:\n" +
-		    ppr(set1.toString()) + "\nis not enumerable.");
-      }
-    }
-
-    public final void reset() { this.enum1.reset(); }
-    
-    public final Value nextElement() {
-      Value elem = this.enum1.nextElement();
-      while (elem != null) {
-	if (!set2.member(elem)) return elem;
-	elem = this.enum1.nextElement();
-      }
-      return null;
-    }
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/SetEnumValue.java b/tlatools/src/tlc2/value/SetEnumValue.java
deleted file mode 100644
index 85eecb938558efe9f2aff11998ca339bd9e4d72d..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetEnumValue.java
+++ /dev/null
@@ -1,378 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:16:24 PST by lamport
-//      modified on Mon Aug 20 10:54:08 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.util.FP64;
-import util.Assert;
-
-public class SetEnumValue extends Value
-implements Enumerable, Reducible {
-  public ValueVec elems;         // the elements of the set
-  private boolean isNorm;        // normalized?
-
-  /* Constructor */
-  public SetEnumValue(Value[] elems, boolean isNorm) {
-    this.elems = new ValueVec(elems);
-    this.isNorm = isNorm;
-  }
-
-  public SetEnumValue(ValueVec elems, boolean isNorm) {
-    this.elems = elems;
-    this.isNorm = isNorm;
-  }
-
-  public final byte getKind() { return SETENUMVALUE; }
-
-  public final int compareTo(Object obj) {
-    SetEnumValue set = convert(obj);
-    if (set == null) {
-      if (obj instanceof ModelValue) return 1;
-      Assert.fail("Attempted to compare the set " + ppr(this.toString()) +
-		  " with the value:\n" + ppr(obj.toString()));
-    }
-    this.normalize();
-    set.normalize();
-    int sz = this.elems.size();
-    int cmp = sz - set.elems.size();
-    if (cmp != 0) return cmp;
-    for (int i = 0; i < sz; i++) {
-      cmp = this.elems.elementAt(i).compareTo(set.elems.elementAt(i));
-      if (cmp != 0) return cmp;
-    }
-    return 0;
-  }
-  
-  public final boolean equals(Object obj) {
-    SetEnumValue set = convert(obj);
-    if (set == null) {
-      if (obj instanceof ModelValue)  
-         return ((ModelValue) obj).modelValueEquals(this) ;
-      Assert.fail("Attempted to check equality of the set " + ppr(this.toString()) +
-		  " with the value:\n" + ppr(obj.toString()));
-    }
-    this.normalize();
-    set.normalize();
-    int sz = this.elems.size();
-    if (sz != set.elems.size()) {
-      return false;
-    }
-    for (int i = 0; i < sz; i++) {
-      if (!this.elems.elementAt(i).equals(set.elems.elementAt(i))) {
-	return false;
-      }
-    }
-    return true;
-  }
-
-  public final boolean member(Value elem) {
-    return this.elems.search(elem, this.isNorm);
-  }
-
-  public final boolean isFinite() { return true; }
-  
-  public final Value diff(Value val) {
-    int sz = this.elems.size();
-    ValueVec diffElems = new ValueVec();
-    for (int i = 0; i < sz; i++) {
-      Value elem = this.elems.elementAt(i);
-      if (!val.member(elem)) {
-	diffElems.addElement(elem);
-      }
-    }
-    return new SetEnumValue(diffElems, this.isNormalized());
-  }
-
-  public final Value cap(Value val) {
-    int sz = this.elems.size();
-    ValueVec capElems = new ValueVec();
-    for (int i = 0; i < sz; i++) {
-      Value elem = this.elems.elementAt(i);
-      if (val.member(elem)) {
-	capElems.addElement(elem);
-      }
-    }
-    return new SetEnumValue(capElems, this.isNormalized());
-  }
-
-  public final Value cup(Value set) {
-    int sz = this.elems.size();
-    if (sz == 0) return set;
-
-    if (set instanceof Reducible) {
-      ValueVec cupElems = new ValueVec();
-      for (int i = 0; i < sz; i++) {
-	Value elem = this.elems.elementAt(i);
-	cupElems.addElement(elem);
-      }
-      ValueEnumeration Enum = ((Enumerable)set).elements();
-      Value elem;
-      while ((elem = Enum.nextElement()) != null) {
-	if (!this.member(elem)) cupElems.addElement(elem);
-      }
-      return new SetEnumValue(cupElems, false);
-    }
-    return new SetCupValue(this, set);
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-  
-  public final int size() {
-    this.normalize();
-    return this.elems.size();
-  }
-
-  /* This method normalizes (destructively) this set. */
-  public final boolean isNormalized() { return this.isNorm; }
-  
-  public final void normalize() {
-    if (!this.isNorm) {
-      this.elems.sort(true);   // duplicates eliminated
-      this.isNorm = true;
-    }
-  }
-
-  /* Convert val into a SetEnumValue.  Returns null if not possible. */
-  public static final SetEnumValue convert(Object val) {
-    switch (((Value)val).getKind()) {
-    case SETENUMVALUE:
-      return (SetEnumValue)val;
-    case INTERVALVALUE:
-      {
-	IntervalValue intv = (IntervalValue)val;
-	Value[] vals = new Value[intv.size()];
-	for (int i = 0; i < vals.length; i++) {
-	  vals[i] = IntValue.gen(i + intv.low);
-	}
-	return new SetEnumValue(vals, true);
-      }
-    case SETCAPVALUE:
-      {
-	SetCapValue cap = (SetCapValue)val;
-	if (cap.capSet != null && cap.capSet != DummyEnum) {
-	  return cap.capSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = cap.elements();	
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, cap.isNormalized());
-      }
-    case SETCUPVALUE:
-      {
-	SetCupValue cup = (SetCupValue)val;
-	if (cup.cupSet != null && cup.cupSet != DummyEnum) {
-	  return cup.cupSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = cup.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, false);
-      }
-    case SETDIFFVALUE:
-      {
-	SetDiffValue diff = (SetDiffValue)val;
-	if (diff.diffSet != null && diff.diffSet != DummyEnum) {
-	  return diff.diffSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = diff.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, diff.set1.isNormalized());
-      }
-    case UNIONVALUE:
-      {
-	UnionValue uv = (UnionValue)val;
-	if (uv.realSet != null && uv.realSet != DummyEnum) {
-	  return uv.realSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = uv.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, false);
-      }
-    case SUBSETVALUE:
-      {
-	SubsetValue pset = (SubsetValue)val;
-	if (pset.pset != null && pset.pset != DummyEnum) {
-	  return pset.pset;
-	}
-	ValueVec vals = new ValueVec(pset.size());
-	ValueEnumeration Enum = pset.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, false);
-      }
-    case SETOFRCDSVALUE:
-      {
-	SetOfRcdsValue rcds = (SetOfRcdsValue)val;
-	if (rcds.rcdSet != null && rcds.rcdSet != DummyEnum) {
-	  return rcds.rcdSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = rcds.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, rcds.isNormalized());
-      }
-    case SETOFFCNSVALUE:
-      {
-	SetOfFcnsValue fcns = (SetOfFcnsValue)val;
-	if (fcns.fcnSet != null && fcns.fcnSet != DummyEnum) {
-	  return fcns.fcnSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = fcns.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, fcns.isNormalized());
-      }
-    case SETOFTUPLESVALUE:
-      {
-	SetOfTuplesValue tvs = (SetOfTuplesValue)val;
-	if (tvs.tupleSet != null && tvs.tupleSet != DummyEnum) {
-	  return tvs.tupleSet;
-	}
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = tvs.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, tvs.isNormalized());
-      }
-    case SETPREDVALUE:
-      {
-	SetPredValue sPred = (SetPredValue)val;
-	if (sPred.tool == null) return (SetEnumValue)sPred.inVal;
-	ValueVec vals = new ValueVec();
-	ValueEnumeration Enum = sPred.elements();
-	Value elem;
-	while ((elem = Enum.nextElement()) != null) {
-	  vals.addElement(elem);
-	}
-	return new SetEnumValue(vals, sPred.isNormalized());
-      }
-    default:
-      // null if val can not be converted.
-      return null;
-    }
-  }
-  
-  public final boolean isDefined() {
-    boolean defined = true;
-    int sz = this.elems.size();    
-    for (int i = 0; i < sz; i++) {
-      defined = defined && this.elems.elementAt(i).isDefined();
-    }
-    return defined;
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) { return this.equals(val); }
-
-  /* The fingerprint methods */
-  public final long fingerPrint(long fp) {
-    this.normalize();
-    int sz = this.elems.size();    
-    fp = FP64.Extend(fp, SETENUMVALUE);
-    fp = FP64.Extend(fp, sz);
-    for (int i = 0; i < sz; i++) {
-      Value elem = this.elems.elementAt(i);
-      fp = elem.fingerPrint(fp);
-    }
-    return fp;
-  }
-
-  public final Value permute(MVPerm perm) {
-    int sz = this.elems.size();
-    Value[] vals = new Value[sz];
-    boolean changed = false;
-    for (int i = 0; i < sz; i++) {
-      vals[i] = this.elems.elementAt(i).permute(perm);
-      changed = (changed || vals[i] != this.elems.elementAt(i));
-    }
-    if (changed) {
-      return new SetEnumValue(vals, false);
-    }
-    return this;
-  }
-
-  /* The string representation */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    // If this SetEnumValue object is created by a union, at least one of
-    // whose elements is a Cartesian product, then this can be an unnormalized
-    // set with repeated elements.  It would therefore seem like a good idea to
-    // normalize this object here.  Since this toString method is probably
-    // used only for printing the value, it seems that correcting this should
-    // not do any harm.  Therefore, LL added the following if statement
-    // on 5 Mar 2012.
-    if (!this.isNormalized()) {
-        this.normalize();
-    }
-    
-    int len = this.elems.size();
-    sb = sb.append("{");
-    if (len > 0) {
-      this.elems.elementAt(0).toString(sb, offset);
-    }
-    for (int i = 1; i < len; i++) {
-      sb.append(", ");
-      this.elems.elementAt(i).toString(sb, offset);
-    }
-    sb.append("}");
-    return sb;
-  }
-
-  public final ValueEnumeration elements() { return new Enumerator(); }
-  
-  final class Enumerator implements ValueEnumeration {
-    int index = 0;
-
-    public Enumerator() {
-      normalize();
-    }
-    
-    public final void reset() { this.index = 0; }
-
-    public final Value nextElement() {
-      if (this.index < elems.size()) {
-	return elems.elementAt(this.index++);
-      }
-      return null;
-    }
-  }
-  
-}
diff --git a/tlatools/src/tlc2/value/SetOfFcnsValue.java b/tlatools/src/tlc2/value/SetOfFcnsValue.java
deleted file mode 100644
index 751a59f16aa7a957cf1bca8c3b8212eaad424494..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetOfFcnsValue.java
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:17:11 PST by lamport
-//      modified on Fri Aug 10 15:09:46 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.TLCGlobals;
-import util.Assert;
-
-public class SetOfFcnsValue extends Value implements Enumerable {
-  public Value domain;        /* Function domain  */
-  public Value range;         /* Function range   */
-  protected SetEnumValue fcnSet;
-  
-  /* Constructor */
-  public SetOfFcnsValue(Value domain, Value range) {
-    this.domain = domain;
-    this.range = range;
-    this.fcnSet = null;
-  }
-
-  public final byte getKind() { return SETOFFCNSVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.fcnSet.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    if (obj instanceof SetOfFcnsValue) {
-      SetOfFcnsValue fcns = (SetOfFcnsValue)obj;
-      return (this.domain.equals(fcns.domain) &&
-	      this.range.equals(fcns.range));
-    }
-    this.convertAndCache();
-    return this.fcnSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    FcnRcdValue fcn = FcnRcdValue.convert(elem);
-    if (fcn == null) {
-      if (elem instanceof ModelValue)  
-         return ((ModelValue) elem).modelValueMember(this) ;
-      Assert.fail("Attempted to check if \n" + elem + "\nwhich is not a TLC function" +
-		  " value, is in the set of functions:\n" + ppr(this.toString()));
-    }
-    if (fcn.intv == null) {
-      fcn.normalize();
-      Value fdom = new SetEnumValue(fcn.domain, true);
-      if (this.domain.equals(fdom)) {
-	for (int i = 0; i < fcn.values.length; i++) {
-	  if (!this.range.member(fcn.values[i])) {
-	    return false;
-	  }
-	}
-	return true;
-      }
-    }
-    else {
-      if (fcn.intv.equals(this.domain)) {
-	for (int i = 0; i < fcn.values.length; i++) {
-	  if (!this.range.member(fcn.values[i])) return false;
-	}
-	return true;
-      }
-    }
-    return false;
-  }
-
-  public final boolean isFinite() {
-    return this.domain.isFinite() && this.range.isFinite();
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" +
-		  ppr(this.toString()));
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" +
-		  ppr(this.toString()));
-    }
-    return this;
-  }
-
-  public final int size() {
-    int dsz = this.domain.size();
-    int rsz = this.range.size();
-    long sz = 1;    
-    for (int i = 0; i < dsz; i++) {
-      sz *= rsz;
-      if (sz < -2147483648 || sz > 2147483647) {
-	Assert.fail("Overflow when computing the number of elements in:\n" +
-		    ppr(toString()));
-      }
-    }
-    return (int)sz;
-  }
-
-  public final boolean isNormalized() {
-    if (this.fcnSet == null || this.fcnSet == DummyEnum) { 
-      return this.domain.isNormalized() && this.range.isNormalized();
-    }
-    return this.fcnSet.isNormalized();
-  }
-  
-  public final void normalize() {
-    if (this.fcnSet == null || this.fcnSet == DummyEnum) {
-      this.domain.normalize();
-      this.range.normalize();
-    }
-    else {
-      this.fcnSet.normalize();
-    }
-  }
-
-  public final boolean isDefined() {
-    return this.domain.isDefined() && this.range.isDefined();
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  /* The fingerprint  */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.fcnSet.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.fcnSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.fcnSet == null) {
-      this.fcnSet = SetEnumValue.convert(this);
-    }
-    else if (this.fcnSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.fcnSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.fcnSet == DummyEnum) { this.fcnSet = val; }
-      }
-    }
-  }
-  
-  /* The string representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    boolean unlazy = expand;
-    try {
-      if (unlazy) {
-	int dsz = this.domain.size();
-	int rsz = this.range.size();
-	long sz = 1;    
-	for (int i = 0; i < dsz; i++) {
-	  sz *= rsz;
-	  if (sz < -2147483648 || sz > 2147483647) {
-	    unlazy = false;
-	    break;
-	  }
-	}
-	unlazy = sz < TLCGlobals.enumBound;
-      }
-    }
-    catch (Throwable e) { unlazy = false; }
-
-    if (unlazy) {
-      Value val = SetEnumValue.convert(this);
-      return val.toString(sb, offset);
-    }
-    else {
-      sb.append("[");
-      this.domain.toString(sb, offset);
-      sb.append(" -> ");
-      this.range.toString(sb, offset);
-      sb.append("]");
-      return sb;
-    }
-  }
-    
-  public final ValueEnumeration elements() {
-    if (this.fcnSet == null || this.fcnSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.fcnSet.elements();    
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    private Value[] dom;
-    private ValueEnumeration[] enums;
-    private Value[] currentElems;
-    private boolean isDone;
-    
-    public Enumerator() {
-      this.isDone = false;
-      SetEnumValue domSet = SetEnumValue.convert(domain);
-      if (domSet == null)
-	Assert.fail("Attempted to enumerate a set of the form [D -> R]," +
-		    "but the domain D:\n" + ppr(domain.toString()) +
-		    "\ncannot be enumerated.");
-      domSet.normalize();
-      ValueVec elems = domSet.elems;
-      int sz = elems.size();
-      if (range instanceof Enumerable) {
-	this.dom = new Value[sz];
-	this.enums = new ValueEnumeration[sz];
-	this.currentElems = new Value[sz];
-	// SZ Feb 24, 2009: never read locally
-	// ValueEnumeration enumeration = ((Enumerable)domSet).elements();
-	for (int i = 0; i < sz; i++) {
-	  this.dom[i] = elems.elementAt(i);
-	  this.enums[i] = ((Enumerable)range).elements();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	  if (this.currentElems[i] == null) {
-	    this.enums = null;
-	    this.isDone = true;
-	    break;
-	  }
-	}
-      }
-      else {
-	Assert.fail("Attempted to enumerate a set of the form [D -> R]," +
-		    "but the range R:\n" + ppr(range.toString()) +
-		    "\ncannot be enumerated.");
-      }
-    }
-
-    public final void reset() {
-      if (this.enums != null) {
-	for (int i = 0; i < this.enums.length; i++) {
-	  this.enums[i].reset();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	}
-	this.isDone = false;
-      }
-    }
-    
-    public final Value nextElement() {
-      if (this.isDone) return null;
-      Value[] elems = new Value[this.currentElems.length];
-      if (elems.length == 0) {
-	this.isDone = true;
-      }
-      else {
-	for (int i = 0; i < elems.length; i++) {
-	  elems[i] = this.currentElems[i];
-	}
-	for (int i = elems.length-1; i >= 0; i--) {
-	  this.currentElems[i] = this.enums[i].nextElement();
-	  if (this.currentElems[i] != null) break;
-	  if (i == 0) {
-	    this.isDone = true;
-	    break;
-	  }
-	  this.enums[i].reset();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	}	    
-      }
-      return new FcnRcdValue(this.dom, elems, true);
-    }
-    
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/SetOfRcdsValue.java b/tlatools/src/tlc2/value/SetOfRcdsValue.java
deleted file mode 100644
index eddd82c9491bf3b3c0183a55a0feb29ea7725b9a..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetOfRcdsValue.java
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:18:01 PST by lamport
-//      modified on Fri Aug 10 15:09:53 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.TLCGlobals;
-import tlc2.output.EC;
-import util.Assert;
-import util.UniqueString;
-
-public class SetOfRcdsValue extends Value implements Enumerable {
-  public UniqueString[] names;      // The names of the fields.
-  public Value[] values;            // The values of the fields.
-  protected SetEnumValue rcdSet;
-  
-  /* Constructor */
-  public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm) {
-    this.names = names;
-    this.values = values;
-    this.rcdSet = null;
-    if (!isNorm) {
-      this.sortByNames();
-    }
-  }
-
-  public final byte getKind() { return SETOFRCDSVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.rcdSet.compareTo(obj);    
-  }
-  
-  public final boolean equals(Object obj) {
-    if (obj instanceof SetOfRcdsValue) {
-      SetOfRcdsValue rcds = (SetOfRcdsValue)obj;
-
-      boolean isEmpty1 = this.isEmpty();
-      if (isEmpty1) return rcds.isEmpty();
-      if (rcds.isEmpty()) return isEmpty1;
-      
-      if (this.names.length != rcds.names.length) {
-	return false;
-      }
-      for (int i = 0; i < this.names.length; i++) {
-	if (!this.names[i].equals(rcds.names[i]) ||
-	    !this.values[i].equals(rcds.values[i])) {
-	  return false;
-	}
-      }
-      return true;
-    }
-    this.convertAndCache();
-    return this.rcdSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    RecordValue rcd = RecordValue.convert(elem);
-    if (rcd == null) {
-      if (elem instanceof ModelValue)   
-         return ((ModelValue) elem).modelValueMember(this) ;
-      Assert.fail("Attempted to check if non-record\n" + elem + "\nis in the" +
-		  " set of records:\n" + ppr(this.toString()));
-    }
-    rcd.normalize();
-    if (this.names.length != rcd.names.length) {
-      return false;
-    }
-    for (int i = 0; i < this.names.length; i++) {
-      if ((!this.names[i].equals(rcd.names[i])) ||
-	  (!this.values[i].member(rcd.values[i]))) {
-	return false;
-      }
-    }
-    return true;
-  }
-
-  public final boolean isFinite() {
-    for (int i = 0; i < this.values.length; i++) {
-      if (!this.values[i].isFinite()) return false;
-    }
-    return true;
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set of records:\n" +
-		  ppr(this.toString()));
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set of records:\n" +
-		  ppr(this.toString()));
-    }
-    return this;
-  }
-
-  public final int size() {
-    long sz = 1;
-    for (int i = 0; i < this.values.length; i++) {
-      sz *= this.values[i].size();
-      if (sz < -2147483648 || sz > 2147483647) {
-	Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" +
-		    ppr(this.toString()));
-      }
-    }
-    return (int)sz;
-  }
-
-  public final boolean isNormalized() {
-    if (this.rcdSet == null || this.rcdSet == DummyEnum) {
-      for (int i = 0; i < this.names.length; i++) {
-	if (!this.values[i].isNormalized()) {
-	  return false;
-	}
-      }
-      return true;
-    }
-    return this.rcdSet.isNormalized();
-  }
-
-  public final void normalize() {
-    if (this.rcdSet == null || this.rcdSet == DummyEnum) {
-      for (int i = 0; i < this.names.length; i++) {
-	this.values[i].normalize();
-      }
-    }
-    else {
-      this.rcdSet.normalize();
-    }
-  }
-  
-  private final void sortByNames() {
-    for (int i = 1; i < this.names.length; i++) {
-      int cmp = this.names[0].compareTo(this.names[i]);
-      if (cmp == 0) {
-	Assert.fail("Field name " + this.names[0] + " occurs multiple times" +
-		    " in set of records.");
-      }
-      else if (cmp > 0) {
-	UniqueString ts = this.names[0];
-	this.names[0] = this.names[i];
-	this.names[i] = ts;
-	Value tv = this.values[0];
-	this.values[0] = this.values[i];
-	this.values[i] = tv;
-      }
-    }
-    for (int i = 2; i < this.names.length; i++) {
-      int j = i;
-      UniqueString st = this.names[i];
-      Value val = this.values[i];
-      int cmp;
-      while ((cmp = st.compareTo(this.names[j-1])) < 0) {
-	this.names[j] = this.names[j-1];
-	this.values[j] = this.values[j-1];
-	j--;
-      }
-      if (cmp == 0) {
-	Assert.fail("Field name " + this.names[i] + " occurs multiple times" +
-		    " in set of records.");
-      }
-      this.names[j] = st;
-      this.values[j] = val;
-    }
-  }
-
-  public final boolean isDefined() {
-    boolean isDefined = true;
-    for (int i = 0; i < this.values.length; i++) {
-      isDefined = isDefined && this.values[i].isDefined();
-    }
-    return isDefined;
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-  
-  /* The fingerprint  */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.rcdSet.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.rcdSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.rcdSet == null) {
-      this.rcdSet = SetEnumValue.convert(this);
-    }
-    else if (this.rcdSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.rcdSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.rcdSet == DummyEnum) { this.rcdSet = val; }
-      }
-    }
-  }
-      
-  /* The string representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    boolean unlazy = expand;
-    try {
-      if (unlazy) {
-	long sz = 1;    
-	for (int i = 0; i < this.values.length; i++) {
-	  sz *= this.values[i].size();
-	  if (sz < -2147483648 || sz > 2147483647) {
-	    unlazy = false;
-	    break;
-	  }
-	}
-	unlazy = sz < TLCGlobals.enumBound;
-      }
-    }
-    catch (Throwable e) { unlazy = false; }
-    
-    if (unlazy) {
-      Value val = SetEnumValue.convert(this);
-      return val.toString(sb, offset);
-    }
-    else {
-      sb.append("[");
-      int len = this.names.length;
-      if (len != 0) {
-	sb.append(names[0] + ": ");
-	this.values[0].toString(sb, offset);
-      }
-      for (int i = 1; i < len; i++) {
-	sb.append(", ");
-	sb.append(names[i] + ": ");
-	this.values[i].toString(sb, offset);
-      }
-      sb.append("]");
-      return sb;
-    }
-  }
-
-  public final ValueEnumeration elements() {
-    if (this.rcdSet == null || this.rcdSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.rcdSet.elements();
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    private ValueEnumeration[] enums;
-    private Value[] currentElems;
-    private boolean isDone;
-
-    public Enumerator() {
-      this.enums = new ValueEnumeration[values.length];
-      this.currentElems = new Value[values.length];
-      this.isDone = false;
-      for (int i = 0; i < values.length; i++) {
-	if (values[i] instanceof Enumerable) {
-	  this.enums[i] = ((Enumerable)values[i]).elements();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	  if (this.currentElems[i] == null) {
-	    this.enums = null;
-	    this.isDone = true;
-	    break;
-	  }
-	}
-	else {
-	  Assert.fail("Attempted to enumerate a set of the form [l1 : v1, ..., ln : vn]," +
-		      "\nbut can't enumerate the value of the `" + names[i] + "' field:\n" +
-		      ppr(values[i].toString()));
-	}
-      }
-    }
-
-    public final void reset() {
-      if (this.enums != null) {
-	for (int i = 0; i < this.enums.length; i++) {
-	  this.enums[i].reset();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	}
-	this.isDone = false;
-      }
-    }
-    
-    public final Value nextElement() {
-      if (this.isDone) return null;
-      Value[] elems = new Value[this.currentElems.length];
-      for (int i = 0; i < elems.length; i++) {
-	elems[i] = this.currentElems[i];
-      }
-      for (int i = elems.length-1; i >= 0; i--) {
-	this.currentElems[i] = this.enums[i].nextElement();
-	if (this.currentElems[i] != null) break;
-	if (i == 0) {
-	  this.isDone = true;
-	  break;
-	}
-	this.enums[i].reset();
-	this.currentElems[i] = this.enums[i].nextElement();
-      }
-      return new RecordValue(names, elems, true);
-    }
-    
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/SetOfTuplesValue.java b/tlatools/src/tlc2/value/SetOfTuplesValue.java
deleted file mode 100644
index 3fa97011418a4ad2a7d5d1f72d63b1dfac3f0a7f..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetOfTuplesValue.java
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:18:39 PST by lamport
-//      modified on Fri Aug 10 15:09:59 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.TLCGlobals;
-import util.Assert;
-
-public class SetOfTuplesValue extends Value implements Enumerable {
-  public Value[] sets;
-  protected SetEnumValue tupleSet;
-  
-  /* Constructor */
-  public SetOfTuplesValue(Value[] sets) {
-    this.sets = sets;
-    this.tupleSet = null;
-  }
-
-  public SetOfTuplesValue(Value val) {
-    this.sets = new Value[1];
-    this.sets[0] = val;
-    this.tupleSet = null;
-  }
-
-  public SetOfTuplesValue(Value v1, Value v2) {
-    this.sets = new Value[2];
-    this.sets[0] = v1;
-    this.sets[1] = v2;
-    this.tupleSet = null;
-  }
-
-  public final byte getKind() { return SETOFTUPLESVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.tupleSet.compareTo(obj);    
-  }
-  
-  public final boolean equals(Object obj) {
-    if (obj instanceof SetOfTuplesValue) {
-      SetOfTuplesValue tvs = (SetOfTuplesValue)obj;
-
-      boolean isEmpty1 = this.isEmpty();
-      if (isEmpty1) return tvs.isEmpty();
-      if (tvs.isEmpty()) return isEmpty1;
-
-      if (this.sets.length != tvs.sets.length) {
-	return false;
-      }
-      for (int i = 0; i < this.sets.length; i++) {
-	if (!this.sets[i].equals(tvs.sets[i])) {
-	  return false;
-	}
-      }
-      return true;
-    }
-    this.convertAndCache();
-    return this.tupleSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    TupleValue tv = TupleValue.convert(elem);
-    if (tv == null) {
-      FcnRcdValue fcn = FcnRcdValue.convert(elem);
-      if (fcn == null) {
-	if (elem instanceof ModelValue)    
-          return ((ModelValue) elem).modelValueMember(this) ;
-	Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) +
-		    "\nis in the set of tuples:\n" + ppr(this.toString()));
-      }
-      if (fcn.intv != null) return false;
-      for (int i = 0; i < fcn.domain.length; i++) {
-	if (!(fcn.domain[i] instanceof IntValue)) {
-	  Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) +
-		      "\nis in the set of tuples:\n" + ppr(this.toString()));
-	}
-      }
-      return false;
-    }
-    if (tv.elems.length == this.sets.length) {
-      for (int i = 0; i < this.sets.length; i++) {
-	if (!this.sets[i].member(tv.elems[i]))
-	  return false;
-      }
-      return true;
-    }
-    return false;
-  }
-
-  public final boolean isFinite() {
-    for (int i = 0; i < this.sets.length; i++) {
-      if (!this.sets[i].isFinite()) {
-	return false;
-      }
-    }
-    return true;
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" +
-		  ppr(this.toString()));
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" +
-		  ppr(this.toString()));
-    }
-    return this;
-  }
-
-  public final int size() {
-    long sz = 1;
-    for (int i = 0; i < this.sets.length; i++) {
-      sz *= this.sets[i].size();
-      if (sz < -2147483648 || sz > 2147483647) {
-	Assert.fail("Overflow when computing the number of elements in " +
-		    ppr(this.toString()));
-      }
-    }
-    return (int)sz;
-  }
-
-  public final boolean isNormalized() {
-    if (this.tupleSet == null || this.tupleSet == DummyEnum) {
-      for (int i = 0; i < this.sets.length; i++) {
-	if (!this.sets[i].isNormalized()) {
-	  return false;
-	}
-      }
-      return true;
-    }
-    return this.tupleSet.isNormalized();
-  }
-
-  public final void normalize() {
-    if (this.tupleSet == null || this.tupleSet == DummyEnum) {
-      for (int i = 0; i < this.sets.length; i++) {
-	this.sets[i].normalize();
-      }
-    }
-    else {
-      this.tupleSet.normalize();
-    }
-  }
-
-  public final boolean isDefined() {
-    boolean defined = true;
-    for (int i = 0; i < this.sets.length; i++) {
-      defined = defined && this.sets[i].isDefined();
-    }
-    return defined;
-  }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  /* The fingerprint  */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.tupleSet.fingerPrint(fp);
-  }
-  
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.tupleSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.tupleSet == null) {
-      this.tupleSet = SetEnumValue.convert(this);
-    }
-    else if (this.tupleSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.tupleSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.tupleSet == DummyEnum) { this.tupleSet = val; }
-      }
-    }
-  }
-      
-  /* The string representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    boolean unlazy = expand;
-    try {
-      if (unlazy) {
-	long sz = 1;    
-	for (int i = 0; i < this.sets.length; i++) {
-	  sz *= this.sets[i].size();
-	  if (sz < -2147483648 || sz > 2147483647) {
-	    unlazy = false;
-	    break;
-	  }
-	}
-	unlazy = sz < TLCGlobals.enumBound;
-      }
-    }
-    catch (Throwable e) { unlazy = false; }
-
-    if (unlazy) {
-      Value val = SetEnumValue.convert(this);
-      return val.toString(sb, offset);
-    }
-    else {
-      if (this.sets.length > 0) {
-	sb.append("(");
-	this.sets[0].toString(sb, offset);
-      }
-      for (int i = 1; i < this.sets.length; i++) {
-	sb.append(" X ");
-	this.sets[i].toString(sb, offset);
-      }
-      if (this.sets.length > 0) {
-	sb.append(")");
-      }
-      return sb;
-    }
-  }
-  
-  public final ValueEnumeration elements() {
-    if (this.tupleSet == null || this.tupleSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.tupleSet.elements();
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    private ValueEnumeration[] enums;
-    private Value[] currentElems;
-    private boolean isDone;
-    
-    public Enumerator() {
-      this.enums = new ValueEnumeration[sets.length];
-      this.currentElems = new Value[sets.length];
-      this.isDone = false;
-      for (int i = 0; i < sets.length; i++) {
-	if (sets[i] instanceof Enumerable) {
-	  this.enums[i] = ((Enumerable)sets[i]).elements();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	  if (this.currentElems[i] == null) {
-	    this.enums = null;
-	    this.isDone = true;
-	    break;
-	  }
-	}
-	else {
-	  Assert.fail("Attempted to enumerate a set of the form s1 \\X s2 ... \\X sn," +
-		      "\nbut can't enumerate s" + i + ":\n" + ppr(sets[i].toString()));
-	}
-      }
-    }
-
-    public final void reset() {
-      if (this.enums != null) {
-	for (int i = 0; i < this.enums.length; i++) {
-	  this.enums[i].reset();
-	  this.currentElems[i] = this.enums[i].nextElement();
-	}
-	this.isDone = false;
-      }
-    }
-    
-    public final Value nextElement() {
-      if (this.isDone) return null;
-      Value[] elems = new Value[this.currentElems.length];
-      for (int i = 0; i < elems.length; i++) {
-	elems[i] = this.currentElems[i];
-      }
-      for (int i = elems.length-1; i >= 0; i--) {
-	this.currentElems[i] = this.enums[i].nextElement();
-	if (this.currentElems[i] != null) break;
-	if (i == 0) {
-	  this.isDone = true;
-	  break;
-	}
-	this.enums[i].reset();
-	this.currentElems[i] = this.enums[i].nextElement();
-      }
-      return new TupleValue(elems);
-    }
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/SetPredValue.java b/tlatools/src/tlc2/value/SetPredValue.java
deleted file mode 100644
index 3b1915c0ea302ca0a73edb772e11b7e571756905..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SetPredValue.java
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Thu  5 Jul 2007 at  4:44:23 PST by lamport
-//      modified on Fri Aug 10 15:10:04 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import tla2sany.semantic.FormalParamNode;
-import tla2sany.semantic.SemanticNode;
-import tlc2.tool.EvalException;
-import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
-import tlc2.util.Context;
-import util.Assert;
-
-public class SetPredValue extends Value implements Enumerable {
-  public Object vars;           // FormalParamNode or FormalParamNode[]
-    /***********************************************************************
-    * Was OpDeclNode or OpDeclNode[].                                      *
-    ***********************************************************************/
-  public Value inVal;           // the in value or the real set
-  public SemanticNode pred;     // the predicate
-  public Tool tool;             // null iff inVal is the real set
-  public Context con;
-  public TLCState state;
-  public TLCState pstate;
-  public int control;
-
-  /* Constructor */
-  public SetPredValue(Object vars, Value inVal, SemanticNode pred, Tool tool,
-		      Context con, TLCState s0, TLCState s1, int control) {
-    this.vars = vars;
-    this.inVal = inVal;
-    this.pred = pred;
-    this.tool = tool;
-    this.con = con;
-    this.state = s0.copy();  
-    if (s1 != null) {
-        this.pstate = s1.copy();
-    } else {
-        this.pstate = null;
-    }
-      /**
-       * The two copy()s above were added by YY on 12 Mar 2010 to fix the
-       * following bug: When a lazily evaluated expression is saved, the
-       * state under which it should be evaluated must be saved.  The
-       * s0 and s1 objects with which this method is called can be modified
-       * after the call, so copies must be made.
-       */
-    this.control = control;
-  }
-
-  public final byte getKind() { return SETPREDVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.inVal = SetEnumValue.convert(this);
-    this.tool = null;
-    return this.inVal.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    this.inVal = SetEnumValue.convert(this);
-    this.tool = null;
-    return this.inVal.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    if (this.tool == null) {
-      return this.inVal.member(elem);
-    }
-    try {
-      if (this.inVal.member(elem)) {
-	Context con1 = this.con;
-	if (this.vars instanceof FormalParamNode) {
-	  con1 = con1.cons((FormalParamNode)this.vars, elem);
-	}
-	else {
-	  FormalParamNode[] ids = (FormalParamNode[])this.vars;
-	  TupleValue tv = TupleValue.convert(elem);
-	  if ((tv != null) && (tv.elems.length == ids.length)) {
-	    Value[] vals = ((TupleValue)tv).elems;
-	    for (int i = 0; i < ids.length; i++) {
-	      con1 = con1.cons(ids[i], vals[i]);
-	    }
-	  }
-	  else {
-	    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-			"\nis an element of a set of " + ids.length + "-tuples.");
-	  }
-	}
-	Value res = this.tool.eval(this.pred, con1, this.state, this.pstate, this.control);
-	if (!(res instanceof BoolValue)) {
-	  Assert.fail("The evaluation of predicate " + this.pred +
-		      " yielded non-Boolean value.");
-	}
-	return ((BoolValue)res).val;
-      }
-    }
-    catch (EvalException e) {
-      Assert.fail("Cannot decide if element:\n" + ppr(elem.toString()) +
-		  "\n is element of:\n" + ppr(this.inVal.toString()) +
-		  "\nand satisfies the predicate " + this.pred);
-    }
-    return false;
-  }
-
-  public final boolean isFinite() {
-    if (!(this.inVal.isFinite())) {
-      Assert.fail("Attempted to check if expression of form {x \\in S : p(x)} is a " +
-		  "finite set, but cannot check if S:\n" + ppr(this.inVal.toString()) +
-		  "\nis finite.");
-    }
-    return true;    
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    this.inVal = SetEnumValue.convert(this);
-    this.tool = null;
-    return this.inVal.size();
-  }
-
-  private final void readObject(ObjectInputStream ois)
-  throws IOException, ClassNotFoundException {
-    this.inVal = (Value)ois.readObject();
-    this.tool = null;
-  }
-
-  private final void writeObject(ObjectOutputStream oos) throws IOException {
-    if (this.tool != null) {
-      this.inVal = SetEnumValue.convert(this);
-      this.tool = null;
-    }
-    oos.writeObject(this.inVal);
-  }
-  
-  /* This method normalizes (destructively) this set. */
-  public final boolean isNormalized() {
-    return this.inVal.isNormalized();
-  }
-
-  public final void normalize() { this.inVal.normalize(); }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  /* The fingerprint method */
-  public final long fingerPrint(long fp) {
-    this.inVal = SetEnumValue.convert(this);
-    this.tool = null;
-    return this.inVal.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.inVal = SetEnumValue.convert(this);
-    this.tool = null;
-    return this.inVal.permute(perm);
-  }
-
-  /* The string representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    try {
-      if (expand) {
-	Value val = SetEnumValue.convert(this);
-	return val.toString(sb, offset);
-      }
-    }
-    catch (Throwable e) { /*SKIP*/ }
-
-    sb.append("{");
-    if (this.vars instanceof FormalParamNode) {
-      sb.append(((FormalParamNode)this.vars).getName());
-    }
-    else {
-      FormalParamNode[] ids = (FormalParamNode[])this.vars;
-      if (ids.length != 0) sb.append(ids[0].getName());
-      for (int i = 1; i < ids.length; i++) {
-	sb.append(", " + ids[i].getName());
-      }
-    }
-    sb.append(" \\in " + this.inVal + " : <expression ");
-    sb.append(this.pred + "> }");
-    return sb;
-  }
-  
-  public final ValueEnumeration elements() {
-    if (this.tool == null) {
-      return ((SetEnumValue)this.inVal).elements();
-    }
-    return new Enumerator();
-  }
-  
-  final class Enumerator implements ValueEnumeration {
-    ValueEnumeration Enum;
-
-    public Enumerator() {
-      if (!(inVal instanceof Enumerable)) {
-	Assert.fail("Attempted to enumerate { x \\in S : p(x) } when S:\n" +
-		    ppr(inVal.toString()) + "\nis not enumerable");
-      }
-      this.Enum = ((Enumerable)inVal).elements();
-    }
-    
-    public final void reset() { this.Enum.reset(); }
-
-    public final Value nextElement() {
-      Value elem;
-      while ((elem = this.Enum.nextElement()) != null) {
-	Context con1 = con;
-	if (vars instanceof FormalParamNode) {
-	  con1 = con1.cons((FormalParamNode)vars, elem);
-	}
-	else {
-	  FormalParamNode[] ids = (FormalParamNode[])vars;
-	  TupleValue tv = TupleValue.convert(elem);
-	  if ((tv != null) &&
-	      (((TupleValue)tv).elems.length == ids.length)) {
-	    Value[] vals = ((TupleValue)tv).elems;
-	    for (int i = 0; i < ids.length; i++) {
-	      con1 = con1.cons(ids[i], vals[i]);
-	    }
-	  }
-	  else {
-	    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-			"\nis an element of a set of " + ids.length + "-tuples.");
-	  }
-	}
-	Value res = tool.eval(pred, con1, state, pstate, control);
-	if (!(res instanceof BoolValue)) {
-	  Assert.fail("Evaluating predicate " + pred + " yielded non-Boolean value.");
-	}
-	if (((BoolValue)res).val) return elem;
-      }
-      return null;
-    }
-    
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/StringValue.java b/tlatools/src/tlc2/value/StringValue.java
deleted file mode 100644
index 3e5507a7c20d31b14a14ef4179fb7fcb9ef6b63c..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/StringValue.java
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Sat 23 February 2008 at 10:19:41 PST by lamport
-//      modified on Fri Aug 10 15:06:37 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tlc2.util.FP64;
-import util.Assert;
-import util.UniqueString;
-
-public class StringValue extends Value {
-  public UniqueString val; 
-
-  /* Constructor */
-  public StringValue(String str) {
-    // SZ 11.04.2009: changed the access method to equivalent
-    this.val = UniqueString.uniqueStringOf(str);
-  }
-
-  public StringValue(UniqueString var) {
-    this.val = var;
-  }
-  
-  public final byte getKind() { return STRINGVALUE; }
-
-  public final UniqueString getVal() { return this.val; }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof StringValue) {
-      return this.val.compareTo(((StringValue)obj).val);
-    }
-    if (!(obj instanceof ModelValue)) {
-      Assert.fail("Attempted to compare string " + ppr(this.toString()) +
-		  " with non-string:\n" + ppr(obj.toString()));
-    }
-    return 1;
-  }
-
-  public final boolean equals(Object obj) {
-    if (obj instanceof StringValue) {
-      return this.val.equals(((StringValue)obj).getVal());
-    }
-    if (!(obj instanceof ModelValue)) {
-      Assert.fail("Attempted to check equality of string " + ppr(this.toString()) +
-		  " with non-string:\n" + ppr(obj.toString()));
-    }
-    return ((ModelValue) obj).modelValueEquals(this) ;
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element of the string " + ppr(this.toString()));
-    return false;     // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the string " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;     // make compiler happy
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT construct to the string " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the string " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the string " +
-		ppr(this.toString()) + ".");
-    return 0;       // make compiler happy
-  }
-
-  public final boolean isNormalized() { return true; }
-
-  public final void normalize() { /*SKIP*/ }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return ((val instanceof StringValue) &&
-	    this.equals(val));
-  }
-
-  public final int length() { return this.val.length(); }
-  
-  /* The fingerprint method */
-  public final long fingerPrint(long fp) {
-    fp = FP64.Extend(fp, STRINGVALUE) ;
-    fp = FP64.Extend(fp, this.val.length()) ;
-    fp = FP64.Extend(fp, this.val.toString());
-    return fp;
-  }
-
-  public final Value permute(MVPerm perm) { return this; }
-
-  /*************************************************************************
-  * toString() modified 23 Aug 2007 by LL to call PrintVersion so strings  *
-  * with special characters are printed properly.                          *
-  *************************************************************************/
-  final String PrintVersion(String str) {
-    StringBuffer buf = new StringBuffer(str.length()) ;
-    for (int i = 0 ; i < str.length() ; i++) {
-      switch (str.charAt(i)) {
-        case '\"' :
-          buf.append("\\\"") ;
-          break ;
-        case '\\' :
-          buf.append("\\\\") ;
-          break ;
-        case '\t' :
-          buf.append("\\t") ;
-          break ;
-        case '\n' :
-          buf.append("\\n") ;
-          break ;
-        case '\f' :
-          buf.append("\\f") ;
-          break ;
-        case '\r' :
-          buf.append("\\r") ;
-          break ;
-        default :
-          buf.append(str.charAt(i)) ;
-          break ;
-       } // switch
-     }; // for
-    return buf.toString();
-   }
-
-
-  /* The string representation of the value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return sb.append("\"" + PrintVersion(this.val.toString()) + "\"");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/SubsetValue.java b/tlatools/src/tlc2/value/SubsetValue.java
deleted file mode 100644
index be76f6626238423862fca1e2f2bbd9ff1d56024f..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/SubsetValue.java
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:46:07 PST by lamport
-//      modified on Fri Aug 10 15:10:16 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import java.util.BitSet;
-
-import tlc2.output.EC;
-import util.Assert;
-
-public class SubsetValue extends Value implements Enumerable {
-  public Value set;           // SUBSET set
-  protected SetEnumValue pset;
-
-  /* Constructor */
-  public SubsetValue(Value set) {
-    this.set = set;
-    this.pset = null;
-  }
-
-  public final byte getKind() { return SUBSETVALUE; }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof SubsetValue) {
-      return this.set.compareTo(((SubsetValue)obj).set);
-    }
-    this.convertAndCache();
-    return this.pset.compareTo(obj);    
-  }
-  
-  public final boolean equals(Object obj) {
-    if (obj instanceof SubsetValue) {
-      return this.set.equals(((SubsetValue)obj).set);
-    }
-    this.convertAndCache();
-    return this.pset.equals(obj);
-  }
-
-  public final boolean member(Value val) {
-    if (val instanceof Enumerable) {
-      ValueEnumeration Enum = ((Enumerable)val).elements();
-      Value elem;
-      while ((elem = Enum.nextElement()) != null) {
-	if (!this.set.member(elem)) return false;
-      }
-    }
-    else {
-      Assert.fail("Attempted to check if the non-enumerable value\n" +
-		  ppr(val.toString()) + "\nis element of\n" + ppr(this.toString()));
-    }
-    return true;    
-  }
-
-  public final boolean isFinite() { return this.set.isFinite(); }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    int sz = this.set.size();
-    if (sz >= 31) {
-      Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" +
-		  ppr(this.toString()));
-    }
-    return (1 << sz);
-  }
-
-  public final boolean isNormalized() {
-    return (this.pset != null &&
-	    this.pset != DummyEnum &&
-	    this.pset.isNormalized());
-  }
-  
-  public final void normalize() {
-    if (this.pset == null || this.pset == DummyEnum) {
-      this.set.normalize();
-    }
-    else {
-      this.pset.normalize();
-    }
-  }
-
-  public final boolean isDefined() { return this.set.isDefined(); }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) { return this.equals(val); }
-
-  /* The fingerprint  */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();
-    return this.pset.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.pset.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.pset == null) {
-      this.pset = SetEnumValue.convert(this);
-    }
-    else if (this.pset == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.pset == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.pset == DummyEnum) { this.pset = val; }
-      }
-    }
-  }
-  
-  /* The string representation  */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    boolean unlazy = expand;
-    try {
-      if (unlazy) {
-	unlazy = this.set.size() < 7;
-      }
-    }
-    catch (Throwable e) { unlazy = false; }
-
-    if (unlazy) {
-      Value val = SetEnumValue.convert(this);
-      return val.toString(sb, offset);
-    }
-    else {
-      sb = sb.append("SUBSET ");
-      sb = this.set.toString(sb, offset);
-      return sb;
-    }
-  }
-
-  public final ValueEnumeration elements() {
-    if (this.pset == null || this.pset == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.pset.elements();
-  }
-
-  final class Enumerator implements ValueEnumeration {
-    ValueVec elems;
-    private BitSet descriptor;
-    
-    public Enumerator() {
-      set = SetEnumValue.convert(set);
-      set.normalize();
-      this.elems = ((SetEnumValue)set).elems;
-      this.descriptor = new BitSet(this.elems.size());
-    }
-
-    public final void reset() {
-      this.descriptor = new BitSet(this.elems.size());
-    }
-    
-    public final Value nextElement() {
-      if (this.descriptor == null) return null;
-      ValueVec vals = new ValueVec();
-      int sz = this.elems.size();
-      if (sz == 0) {
-	this.descriptor = null;
-      }
-      else {
-	for (int i = 0; i < sz; i++) {
-	  if (this.descriptor.get(i)) {
-	    vals.addElement(this.elems.elementAt(i));
-	  }
-	}
-	for (int i = 0; i < sz; i++) {
-	  if (this.descriptor.get(i)) {
-	    this.descriptor.clear(i);
-	    if (i >= sz - 1) {
-	      this.descriptor = null;
-	      break;
-	    }
-	  }
-	  else {
-	    this.descriptor.set(i);
-	    break;
-	  }
-	}
-      }
-      return new SetEnumValue(vals, true);
-    }
-    
-  }
-  
-}
diff --git a/tlatools/src/tlc2/value/TupleValue.java b/tlatools/src/tlc2/value/TupleValue.java
deleted file mode 100644
index d385eeaad817079385d1c9f80c6a730d395eb91a..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/TupleValue.java
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport
-//      modified on Fri Aug 10 15:10:22 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import tla2sany.semantic.SymbolNode;
-import tlc2.output.EC;
-import tlc2.output.MP;
-import tlc2.tool.EvalControl;
-import tlc2.util.Context;
-import tlc2.util.FP64;
-import util.Assert;
-
-public class TupleValue extends Value implements Applicable {
-  public Value[] elems;          // the elements of this tuple.
-
-  /* Constructor */
-  public TupleValue(Value[] elems) { this.elems = elems; }
-
-  public TupleValue(Value v) {
-    this.elems = new Value[1];
-    this.elems[0] = v;
-  }
-  
-  public TupleValue(Value v1, Value v2) {
-    this.elems = new Value[2];
-    this.elems[0] = v1;
-    this.elems[1] = v2;
-  }
-  
-  public final byte getKind() { return TUPLEVALUE; }
-
-  public final int compareTo(Object obj) {
-    TupleValue tv = convert(obj);
-    if (tv == null) {
-      // Well, we have to convert this to function and compare.
-      return FcnRcdValue.convert(this).compareTo(obj);
-    }
-    int len = this.elems.length;
-    int cmp = len - tv.elems.length;
-    if (cmp == 0) {
-      for (int i = 0; i < len; i++) {
-	cmp = this.elems[i].compareTo(tv.elems[i]);
-	if (cmp != 0) break;
-      }
-    }
-    return cmp;
-  }
-
-  public final boolean equals(Object obj) {
-    TupleValue tv = convert(obj);
-    if (tv == null) {
-      // Well, we have to convert this to function and compare.
-      return FcnRcdValue.convert(this).equals(obj);
-    }
-    int len = this.elems.length;
-    if (len != tv.elems.length)
-      return false;
-    for (int i = 0; i < len; i++) {
-      if (!this.elems[i].equals(tv.elems[i]))
-	return false;
-    }
-    return true;
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check set membership in a tuple value.");
-    return false;   // make compiler happy
-  }
-
-  public final boolean isFinite() { return true; }
-  
-  public final Value apply(Value arg, int control) {
-    if (!(arg instanceof IntValue)) {
-      Assert.fail("Attempted to apply tuple to a non-integer argument.");
-    }
-    int idx = ((IntValue)arg).val;
-    if (idx <= 0 || idx > this.elems.length) {
-      Assert.fail("Attempted to apply tuple\n" + ppr(this.toString()) +
-		  "\nto integer " + idx + " which is out of domain.");
-    }
-    return this.elems[idx-1];
-  }
-
-  public final Value apply(Value[] args, int control) {
-    if (args.length != 1) {
-      Assert.fail("Attetmpted to apply tuple with wrong number of arguments.");
-    }
-    return this.apply(args[0], EvalControl.Clear);
-  }
-  
-  public final Value select(Value arg) {
-    if (!(arg instanceof IntValue)) {
-      Assert.fail("Attempted to apply tuple to a non-integer argument " +
-		  ppr(arg.toString()) + ".");		  
-    }
-    int idx = ((IntValue)arg).val;
-    if (idx > 0 && idx <= this.elems.length) {
-      return this.elems[idx-1];
-    }
-    return null;
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      int tlen = this.elems.length;
-      Value[] newElems = new Value[tlen];
-      Value arcVal = ex.path[ex.idx];
-      if (arcVal instanceof IntValue) {
-	int idx = ((IntValue)arcVal).val - 1;
-	if (0 <= idx && idx < tlen) {
-	  for (int i = 0; i < tlen; i++) {
-	    newElems[i] = this.elems[i];
-	  }
-	  ex.idx++;
-	  newElems[idx] = this.elems[idx].takeExcept(ex);
-	}
-	return new TupleValue(newElems);
-      }
-      MP.printWarning(EC.TLC_WRONG_TUPLE_FIELD_NAME, new String[]{ppr(arcVal.toString())});
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    Value val = this;
-    for (int i = 0; i < exs.length; i++) {
-      val = val.takeExcept(exs[i]);
-    }
-    return val;
-  }
-
-  public final Value getDomain() {
-    return new IntervalValue(1, this.size());
-  }
-  
-  public final int size() { return this.elems.length; }
-
-  /*
-   * This method converts a value to a tuple value. It returns
-   * null if the conversion fails.
-   */
-  public static TupleValue convert(Object val) {
-    if (val instanceof TupleValue) {
-      return (TupleValue)val;
-    }
-    else if (val instanceof FcnRcdValue) {
-      FcnRcdValue fcn = (FcnRcdValue)val;
-      if (fcn.intv != null) {
-	if (fcn.intv.low != 1) return null;
-	return new TupleValue(fcn.values);
-      }
-      int len = fcn.values.length;
-      Value[] elems = new Value[len];
-      for (int i = 0; i < len; i++) {
-	if (!(fcn.domain[i] instanceof IntValue)) return null;
-	int idx = ((IntValue)fcn.domain[i]).val;
-	if (0 < idx && idx <= len) {
-	  if (elems[idx-1] != null) return null;
-	  elems[idx-1] = fcn.values[i];
-	}
-	else {
-	  return null;
-	}
-      }
-      return new TupleValue(elems);
-    }
-    else if (val instanceof FcnLambdaValue) {
-      FcnLambdaValue fcn = (FcnLambdaValue)val;
-      if (fcn.params.length() != 1) return null;
-      Value dom = fcn.params.domains[0];
-      SymbolNode var = fcn.params.formals[0][0];
-      if (dom instanceof IntervalValue) {
-	IntervalValue intv = (IntervalValue)dom;
-	if (intv.low != 1) return null;
-	Value[] elems = new Value[intv.high];
-	for (int i = 1; i <= intv.high; i++) {
-	  Context c1 = fcn.con.cons(var, IntValue.gen(i));
-	  elems[i-1] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control);
-	}
-	return new TupleValue(elems);
-      }
-      else {
-	SetEnumValue eSet = SetEnumValue.convert(dom);
-	if (eSet == null)
-	  Assert.fail("To convert a function of form [x \\in S |-> f(x)] " +
-		      "to a tuple, the set S must be enumerable.");
-	eSet.normalize();
-	int len = eSet.size();
-	Value[] elems = new Value[len];
-	for (int i = 0; i < len; i++) {
-	  Value argVal = eSet.elems.elementAt(i);
-	  if (!(argVal instanceof IntValue)) return null;
-	  if (((IntValue)argVal).val != i + 1) return null;
-	  Context c1 = fcn.con.cons(var, argVal);
-	  elems[i] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control);
-	}
-	return new TupleValue(elems);	
-      }
-    }
-    else if ((val instanceof RecordValue) &&
-	     ((RecordValue)val).size() == 0) {
-      return EmptyTuple;
-    }
-    return null;
-  }
-
-  /* The normalization of the value. */
-  public final boolean isNormalized() { return true; }
-  
-  public final void normalize() { /*nop*/ }
-
-  public final boolean isDefined() {
-    boolean defined = true;
-    for (int i = 0; i < this.elems.length; i++) {
-      defined = defined && this.elems[i].isDefined();
-    }
-    return defined;
-  }
-
-  public final Value deepCopy() {
-    Value[] vals = new Value[this.elems.length];
-    for (int i = 0; i < this.elems.length; i++) {
-      vals[i] = this.elems[i].deepCopy();
-    }
-    return new TupleValue(vals);
-  }
-
-  public final boolean assignable(Value val) {
-    boolean canAssign = ((val instanceof TupleValue) &&
-			 (this.elems.length == ((TupleValue)val).elems.length));
-    if (!canAssign) return false;
-    for (int i = 0; i < this.elems.length; i++) {
-      canAssign = canAssign && this.elems[i].assignable(((TupleValue)val).elems[i]);
-    }
-    return canAssign;
-  }
-  
-  /* The fingerprint method: tuples are functions. */
-  public final long fingerPrint(long fp) {
-    int len = this.elems.length;
-    fp = FP64.Extend(fp, FCNRCDVALUE);
-    fp = FP64.Extend(fp, len);
-    for (int i = 0; i < len; i++) {
-      fp = FP64.Extend(fp, INTVALUE);
-      fp = FP64.Extend(fp, i+1);
-      fp = this.elems[i].fingerPrint(fp);
-    }
-    return fp;
-  }
-
-  public final Value permute(MVPerm perm) {
-    Value[] vals = new Value[this.elems.length];
-    boolean changed = false;
-    for (int i = 0; i < vals.length; i++) {
-      vals[i] = this.elems[i].permute(perm);
-      changed = changed || (vals[i] != this.elems[i]);
-    }
-    if (changed) {
-      return new TupleValue(vals);
-    }
-    return this;
-  }
-
-  /* The string representation of this value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    sb.append("<<");
-    int len = this.elems.length;
-    if (len > 0) {
-      sb = this.elems[0].toString(sb, offset);
-    }
-    for (int i = 1; i < len; i++) {
-      sb = sb.append(", ");
-      sb = this.elems[i].toString(sb, offset);
-    }
-    sb.append(">>");
-    return sb;
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/UndefValue.java b/tlatools/src/tlc2/value/UndefValue.java
deleted file mode 100644
index 34765bdb0019eb63e66fdc19e18e9e1b5cad9aba..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/UndefValue.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:06 PST by lamport
-//      modified on Tue Aug 15 23:08:23 PDT 2000 by yuanyu
-
-package tlc2.value;
-
-import util.Assert;
-
-public class UndefValue extends Value {
-
-  public UndefValue() { /*SKIP*/ }
-
-  public byte getKind() { return UNDEFVALUE; }
-
-  public final int compareTo(Object obj) {
-    return (obj instanceof UndefValue) ? 0 : 1;
-  }
-  
-  public final boolean equals(Object obj) {
-    return (obj instanceof UndefValue);
-  }
-
-  public final boolean member(Value elem) {
-    Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) +
-		"\nis an element " + ppr(this.toString()));
-    return false;    // make compiler happy
-  }
-
-  public final boolean isFinite() {
-    Assert.fail("Attempted to check if the value " + ppr(this.toString()) +
-		" is a finite set.");
-    return false;    // make compiler happy
-  }
-
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT construct to the value " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-  
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT construct to the value " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the value " +
-		ppr(this.toString()) + ".");
-    return 0;     // make compiler happy
-  }
-
-  public final boolean isNormalized() { return true; }
-
-  public final void normalize() { /*nop*/ }
-
-  public final boolean isDefined() { return false; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) { return true; }
-
-  /* The string representation. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return sb.append("UNDEF");
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/UnionValue.java b/tlatools/src/tlc2/value/UnionValue.java
deleted file mode 100644
index 32e58d10ff5bf2a60845f61c017891ab11f7de91..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/UnionValue.java
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:46:50 PST by lamport
-//      modified on Fri Aug 10 15:10:39 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import util.Assert;
-
-public class UnionValue extends Value implements Enumerable {
-  public Value set;
-  protected SetEnumValue realSet;
-  
-  /* Constructor */
-  public UnionValue(Value set) {
-    this.set = set;
-    this.realSet = null;
-  }
-
-  public byte getKind() { return UNIONVALUE; }
-
-  public final int compareTo(Object obj) {
-    this.convertAndCache();
-    return this.realSet.compareTo(obj);
-  }
-  
-  public final boolean equals(Object obj) {
-    this.convertAndCache();
-    return this.realSet.equals(obj);
-  }
-
-  public final boolean member(Value elem) {
-    if (!(this.set instanceof Enumerable)) {
-      Assert.fail("Attempted to check if:\n " + ppr(elem.toString()) +
-		  "\nis an element of the non-enumerable set:\n " +
-		  ppr(this.toString()));
-    }
-    ValueEnumeration Enum = ((Enumerable)this.set).elements();
-    Value val;
-    while ((val = Enum.nextElement()) != null) {
-      if (val.member(elem)) return true;
-    }
-    return false;
-  }
-
-  public final boolean isFinite() {
-    if (!(this.set instanceof Enumerable)) {
-      Assert.fail("Attempted to check if the nonenumerable set:\n" + ppr(this.toString()) +
-		  "\nis a finite set.");
-    }
-    ValueEnumeration Enum = ((Enumerable)this.set).elements();
-    Value val;
-    while ((val = Enum.nextElement()) != null) {
-      if (!val.isFinite()) return false;
-    }
-    return true;
-  }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the set:\n" + ppr(this.toString()));
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the set:\n " + ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    this.convertAndCache();
-    return this.realSet.size();
-  }
-
-  public final boolean isNormalized() {
-    return (this.realSet != null &&
-	    this.realSet != DummyEnum &&
-	    this.realSet.isNormalized());
-  }    
-  
-  public final void normalize() {
-    if (this.realSet != null && this.realSet != DummyEnum) {
-      this.realSet.normalize();
-    }
-  }
-
-  public final boolean isDefined() { return this.set.isDefined(); }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  public static Value union(Value val) {
-    boolean canCombine = (val instanceof SetEnumValue);
-    if (canCombine) {
-      ValueVec elems = ((SetEnumValue)val).elems;
-      for (int i = 0; i < elems.size(); i++) {
-	canCombine = (canCombine &&
-		      (elems.elementAt(i) instanceof SetEnumValue));
-      }
-      if (canCombine) {
-	ValueVec resElems = new ValueVec();
-	Value result = new SetEnumValue(resElems, false);
-	for (int i = 0; i < elems.size(); i++) {
-	  ValueVec elems1 = ((SetEnumValue)elems.elementAt(i)).elems;
-	  for (int j = 0; j < elems1.size(); j++) {
-	    Value elem = elems1.elementAt(j);
-	    if (!result.member(elem)) resElems.addElement(elem);
-	  }
-	}
-	return result;
-      }
-    }
-    return new UnionValue(val);
-  }
-
-  /* The fingerprint  */
-  public final long fingerPrint(long fp) {
-    this.convertAndCache();    
-    return this.realSet.fingerPrint(fp);
-  }
-
-  public final Value permute(MVPerm perm) {
-    this.convertAndCache();
-    return this.realSet.permute(perm);
-  }
-
-  private final void convertAndCache() {
-    if (this.realSet == null) {
-      this.realSet = SetEnumValue.convert(this);
-    }
-    else if (this.realSet == DummyEnum) {
-      SetEnumValue val = null;
-      synchronized(this) {
-	if (this.realSet == DummyEnum) {
-	  val = SetEnumValue.convert(this);
-	  val.deepNormalize();
-	}
-      }
-      synchronized(this) {
-	if (this.realSet == DummyEnum) { this.realSet = val; }
-      }
-    }
-  }
-  
-  /* String representation of this value. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    if (expand) {
-      Value val = SetEnumValue.convert(this);
-      return val.toString(sb, offset);
-    }
-    else {
-      sb = sb.append("UNION(");
-      sb = this.set.toString(sb, offset);
-      sb.append(")");
-      return sb;
-    }
-  }
-
-  public final ValueEnumeration elements() {
-    if (this.realSet == null || this.realSet == DummyEnum) {
-      return new Enumerator();
-    }
-    return this.realSet.elements();
-  }
-  
-  final class Enumerator implements ValueEnumeration {
-    ValueEnumeration Enum;
-    Value elemSet;
-    ValueEnumeration elemSetEnum;
-    
-    public Enumerator() {
-      if (!(set instanceof Enumerable)) {
-	Assert.fail("Attempted to enumerate the nonenumerable set:\n"+
-		    ppr(this.toString()));
-      }
-      this.Enum = ((Enumerable)set).elements();
-      this.elemSet = this.Enum.nextElement();
-      if (this.elemSet != null) {
-	if (!(this.elemSet instanceof Enumerable)) {
-	  Assert.fail("Attempted to enumerate UNION(s), but some element of s is nonenumerable.");
-	}
-	this.elemSetEnum = ((Enumerable)this.elemSet).elements();
-      }
-    }
-
-    public final void reset() {
-      this.Enum.reset();
-      this.elemSet = this.Enum.nextElement();
-      this.elemSetEnum = ((Enumerable)this.elemSet).elements();
-    }
-
-    public final Value nextElement() {
-      if (this.elemSet == null) return null;
-      Value val = this.elemSetEnum.nextElement();
-      if (val == null) {
-	this.elemSet = this.Enum.nextElement();
-	if (this.elemSet == null) return null;
-	if (!(this.elemSet instanceof Enumerable)) {
-	  Assert.fail("Attempted to enumerate the nonenumerable set:\n" +
-		      ppr(this.elemSet.toString()) +
-		      "\nwhen enumerating:\n" + ppr(this.toString()));
-	}
-	this.elemSetEnum = ((Enumerable)this.elemSet).elements();
-	val = this.nextElement();
-      }
-      return val;
-    }
-    
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/UserValue.java b/tlatools/src/tlc2/value/UserValue.java
deleted file mode 100644
index 0fb75732f5b151e2c22c2b5f168bf6fc00634b7f..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/UserValue.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport
-//      modified on Fri May 11 15:14:04 PDT 2001 by yuanyu
-
-package tlc2.value;
-
-import util.Assert;
-
-public class UserValue extends Value {
-  public UserObj userObj;
-
-  public UserValue(UserObj obj) { this.userObj = obj; }
-
-  public final byte getKind() { return USERVALUE; }
-
-  public final int compareTo(Object obj) {
-    if (obj instanceof UserValue) {
-      return this.userObj.compareTo((Value)obj);
-    }
-    if (!(obj instanceof ModelValue))
-      Assert.fail("Attempted to compare overridden value " + ppr(this.toString()) +
-		  " with non-overridden value:\n" + ppr(obj.toString()));
-    return 1;
-  }
-  
-  public final boolean equals(Object obj) {
-    return (this.compareTo(obj) == 0);
-  }
-
-  public final boolean member(Value val) {
-    return this.userObj.member(val);
-  }
-
-  public final boolean isFinite() { return this.userObj.isFinite(); }
-  
-  public final Value takeExcept(ValueExcept ex) {
-    if (ex.idx < ex.path.length) {
-      Assert.fail("Attempted to apply EXCEPT to the overridden value " +
-		  ppr(this.toString()) + ".");
-    }
-    return ex.value;
-  }
-
-  public final Value takeExcept(ValueExcept[] exs) {
-    if (exs.length != 0) {
-      Assert.fail("Attempted to apply EXCEPT to the overridden value " +
-		  ppr(this.toString()) + ".");
-    }
-    return this;
-  }
-
-  public final int size() {
-    Assert.fail("Attempted to compute the number of elements in the overridden value " +
-		ppr(this.toString()) + ".");
-    return 0;   // make compiler happy
-  }
-
-  /* Nothing to normalize. */
-  public final boolean isNormalized() { return true; }
-  
-  public final void normalize() { /*SKIP*/ }
-
-  public final boolean isDefined() { return true; }
-
-  public final Value deepCopy() { return this; }
-
-  public final boolean assignable(Value val) {
-    return this.equals(val);
-  }
-
-  /* The string representation. */
-  public final StringBuffer toString(StringBuffer sb, int offset) {
-    return this.userObj.toString(sb, offset);
-  }
-
-}
diff --git a/tlatools/src/tlc2/value/Value.java b/tlatools/src/tlc2/value/Value.java
deleted file mode 100644
index 17d48ad4d1b51229f232fff7933eccaa0a12fbd1..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/Value.java
+++ /dev/null
@@ -1,366 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 15:30:13 PST by lamport
-//      modified on Wed Dec  5 23:18:07 PST 2001 by yuanyu
-
-package tlc2.value;
-
-import java.io.Serializable;
-
-import tla2sany.semantic.SemanticNode;
-import tlc2.TLCGlobals;
-import tlc2.pprint.PrettyPrint;
-import tlc2.util.FP64;
-import util.Assert;
-
-public abstract class Value implements ValueConstants, Serializable {
-  /**
-   * For each kind of value, we introduce a subclass of Value.
-   * All the subclasses are given in this value package.
-   */
-
-  /**
-   * This method returns the value kind: an integer that represents
-   * the kind of this value. See the interface ValueConstants.java.
-   */
-  public abstract byte getKind();
-
-  public String getKindString() { return ValueImage[this.getKind()]; }
-
-  /* This method compares this with val.  */
-  public abstract int compareTo(Object val);
-  
-  /* This method returns true iff elem is a member of this. */
-  public abstract boolean member(Value elem);
-
-  /* This method returns a new value after taking the except. */
-  public abstract Value takeExcept(ValueExcept ex);
-
-  /* This method returns a new value after taking the excepts. */
-  public abstract Value takeExcept(ValueExcept[] exs);
-
-  /**
-   * This method normalizes (destructively) the representation of
-   * the value. It is essential for equality comparison.
-   */
-  public abstract boolean isNormalized();
-  public abstract void normalize();
-
-  public final boolean isEmpty()
-  {
-    switch (this.getKind()) {
-    case SETENUMVALUE:
-      {
-        SetEnumValue set = (SetEnumValue)this;
-	return set.elems.size() == 0;
-      }
-    case INTERVALVALUE:
-      {
-        IntervalValue intv = (IntervalValue)this;
-        return intv.size() == 0;
-      }
-    case SETCAPVALUE:
-      {
-	SetCapValue cap = (SetCapValue)this;
-        return cap.elements().nextElement() == null;
-      }
-    case SETCUPVALUE:
-      {
-	SetCupValue cup = (SetCupValue)this;
-        return cup.elements().nextElement() == null;
-      }
-    case SETDIFFVALUE:
-      {
-	SetDiffValue diff = (SetDiffValue)this;
-        return diff.elements().nextElement() == null;
-      }
-    case SETOFFCNSVALUE:
-      {
-	SetOfFcnsValue fcns = (SetOfFcnsValue)this;
-        return fcns.elements().nextElement() == null;
-      }
-    case SETOFRCDSVALUE:
-      {
-	SetOfRcdsValue srv = (SetOfRcdsValue)this;
-        return srv.elements().nextElement() == null;
-      }
-    case SETOFTUPLESVALUE:
-      {
-	SetOfTuplesValue stv = (SetOfTuplesValue)this;
-        return stv.elements().nextElement() == null;
-      }
-    case SUBSETVALUE:
-      {
-        // SUBSET S is never empty.  (It always contains {}.)
-        return false;
-      }
-    case UNIONVALUE:
-      {
-	UnionValue uv = (UnionValue)this;
-        return uv.elements().nextElement() == null;
-      }
-    case SETPREDVALUE:
-      {
-	SetPredValue spv = (SetPredValue)this;
-        return spv.elements().nextElement() == null;
-      }
-    default:
-      Assert.fail("Shouldn't call isEmpty() on value " + Value.ppr(this.toString()));
-      return false;
-    }
-  }
-
-  /* Fully normalize this (composite) value. */
-  public final void deepNormalize() {
-    switch (this.getKind()) {
-    case RECORDVALUE:
-      {
-	RecordValue rcd = (RecordValue)this;
-	for (int i = 0; i < rcd.values.length; i++) {
-	  rcd.values[i].deepNormalize();
-	}
-	rcd.normalize();
-	break;
-      }
-    case FCNRCDVALUE:
-      {
-	FcnRcdValue fcn = (FcnRcdValue)this;
-	for (int i = 0; i < fcn.values.length; i++) {
-	  fcn.values[i].deepNormalize();
-	}
-	fcn.normalize();
-	break;
-      }
-    case SETENUMVALUE:
-      {
-	SetEnumValue set = (SetEnumValue)this;
-	for (int i = 0; i < set.elems.size(); i++) {
-	  set.elems.elementAt(i).deepNormalize();
-	}
-	set.normalize();
-	break;
-      }
-    case TUPLEVALUE:
-      {
-	TupleValue tv = (TupleValue)this;
-	for (int i = 0; i < tv.elems.length; i++) {
-	  tv.elems[i].deepNormalize();
-	}
-	break;
-      }
-    case SETCAPVALUE:
-      {
-	SetCapValue cap = (SetCapValue)this;
-	cap.set1.deepNormalize();
-	cap.set2.deepNormalize();
-	if (cap.capSet == null) {
-	  cap.capSet = DummyEnum;
-	}
-	else if (cap.capSet != DummyEnum) {
-	  cap.capSet.deepNormalize();
-	}
-	break;
-      }
-    case SETCUPVALUE:
-      {
-	SetCupValue cup = (SetCupValue)this;
-	cup.set1.deepNormalize();
-	cup.set2.deepNormalize();
-	if (cup.cupSet == null) {
-	  cup.cupSet = DummyEnum;
-	}
-	else if (cup.cupSet != DummyEnum) {
-	  cup.cupSet.deepNormalize();
-	}
-	break;
-      }
-    case SETDIFFVALUE:
-      {
-	SetDiffValue diff = (SetDiffValue)this;
-	diff.set1.deepNormalize();
-	diff.set2.deepNormalize();
-	if (diff.diffSet == null) {
-	  diff.diffSet = DummyEnum;
-	}
-	else if (diff.diffSet != DummyEnum) {
-	  diff.diffSet.deepNormalize();
-	}
-	break;
-      }
-    case SETOFFCNSVALUE:
-      {
-	SetOfFcnsValue fcns = (SetOfFcnsValue)this;
-	fcns.domain.deepNormalize();
-	fcns.range.deepNormalize();
-	if (fcns.fcnSet == null) {
-	  fcns.fcnSet = DummyEnum;
-	}
-	else if (fcns.fcnSet != DummyEnum) {
-	  fcns.fcnSet.deepNormalize();
-	}
-	break;
-      }
-    case SETOFRCDSVALUE:
-      {
-	SetOfRcdsValue srv = (SetOfRcdsValue)this;
-	for (int i = 0; i < srv.values.length; i++) {
-	  srv.values[i].deepNormalize();
-	}
-	if (srv.rcdSet == null) {
-	  srv.rcdSet = DummyEnum;
-	}
-	else if (srv.rcdSet != DummyEnum) {
-	  srv.rcdSet.deepNormalize();
-	}
-	break;
-      }
-    case SETOFTUPLESVALUE:
-      {
-	SetOfTuplesValue stv = (SetOfTuplesValue)this;
-	for (int i = 0; i < stv.sets.length; i++) {
-	  stv.sets[i].deepNormalize();
-	}
-	if (stv.tupleSet == null) {
-	  stv.tupleSet = DummyEnum;
-	}
-	else if (stv.tupleSet != DummyEnum) {
-	  stv.tupleSet.deepNormalize();
-	}
-	break;
-      }
-    case SUBSETVALUE:
-      {
-	SubsetValue pset = (SubsetValue)this;
-	pset.set.deepNormalize();
-	if (pset.pset == null) {
-	  pset.pset = DummyEnum;
-	}
-	else if (pset.pset != DummyEnum) {
-	  pset.pset.deepNormalize();
-	}
-	break;
-      }
-    case UNIONVALUE:
-      {
-	UnionValue uv = (UnionValue)this;
-	if (uv.realSet == null) {
-	  uv.realSet = DummyEnum;
-	}
-	else if (uv.realSet != DummyEnum) {
-	  uv.realSet.deepNormalize();
-	}
-	break;
-      }
-    case SETPREDVALUE:
-      {
-	SetPredValue spv = (SetPredValue)this;
-	spv.inVal.deepNormalize();
-	break;
-      }
-    case FCNLAMBDAVALUE:
-      {
-	FcnLambdaValue flv = (FcnLambdaValue)this;
-	if (flv.fcnRcd == null) {
-	  if (flv.excepts != null) {
-	    for (int i = 0; i < flv.excepts.length; i++) {
-	      flv.excepts[i].value.deepNormalize();
-	      for (int j = 0; j < flv.excepts[i].path.length; j++) {
-		flv.excepts[i].path[j].deepNormalize();
-	      }
-	    }
-	  }
-	  Value[] paramDoms = flv.params.domains;
-	  for (int i = 0; i < paramDoms.length; i++) {
-	    paramDoms[i].deepNormalize();
-	  }
-	}
-	else {
-	  flv.fcnRcd.deepNormalize();
-	}
-      }
-    default:
-      break;
-    }
-  }
-  
-  /* This method returns the fingerprint of this value. */
-  public long fingerPrint(long fp) {
-    Assert.fail("TLC has found a state in which the value of a variable contains " +
-		Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access
-    return 0;      // make compiler happy
-  }
-
-  /**
-   * This method returns the value permuted by the permutation. It
-   * returns this if nothing is permuted.
-   */
-  public Value permute(MVPerm perm) {
-    Assert.fail("TLC has found a state in which the value of a variable contains " +
-		Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access
-    return null;   // make compiler happy
-  }
-
-  /* This method returns true iff the value is finite. */
-  public abstract boolean isFinite();
-  
-  /* This method returns the size of the value.  */
-  public abstract int size();
-
-  /* This method returns true iff the value is fully defined. */
-  public abstract boolean isDefined();
-
-  /* This method makes a real deep copy of this.  */
-  public abstract Value deepCopy();
-
-  /* This method returns true iff val can be assigned to this. */
-  public abstract boolean assignable(Value val);
-
-  /* This method returns the hash code of this value. */
-  public final int hashCode() {
-    long fp = this.fingerPrint(FP64.New());
-    int high = (int)(fp >> 32);
-    int low = (int)fp;
-    return high ^ low;
-  }
-
-  public static boolean expand = true;
-  
-  /**
-   * This method selects the component of this value. The component is
-   * specified by path.
-   */
-  public final Value select(Value[] path) {
-    Value result = this;
-    for (int i = 0; i < path.length; i++) {
-      if (!(result instanceof Applicable)) {
-	Assert.fail("Attempted to apply EXCEPT construct to the value " +
-		    ppr(result.toString()) + ".");
-      }
-      Value elem = path[i];
-      result = ((Applicable)result).select(elem);
-      if (result == null) return null;
-    }
-    return result;
-  }
-
- 
-  public static Value getValue(SemanticNode expr) {
-    return (Value)expr.getToolObject(TLCGlobals.ToolId);
-  }
-  
-  /**
-   * This abstract method returns a string representation of this
-   * value. Each subclass must provide its own implementation.
-   */
-  public abstract StringBuffer toString(StringBuffer sb, int offset);
-
-  /* The string representation of this value */
-  public final String toString() {
-    StringBuffer sb = new StringBuffer();
-    return this.toString(sb, 0).toString();
-  }
-
-  public static String ppr(String s) {
-    return PrettyPrint.mypp(s, 80) ;
-  }
-}
diff --git a/tlatools/src/tlc2/value/ValueConstants.java b/tlatools/src/tlc2/value/ValueConstants.java
index e67c6f926b8946e04c27e27239e62094209faec1..108c7b08049db3e49bd9afa493ee8b16a9f2ecc6 100644
--- a/tlatools/src/tlc2/value/ValueConstants.java
+++ b/tlatools/src/tlc2/value/ValueConstants.java
@@ -5,80 +5,35 @@
 
 package tlc2.value;
 
-import util.UniqueString;
-
 public interface ValueConstants {
 
   /* Type code for values. */
-  public final byte BOOLVALUE        = 0;
-  public final byte INTVALUE         = BOOLVALUE + 1;
-  public final byte REALVALUE        = INTVALUE + 1;  
-  public final byte STRINGVALUE      = REALVALUE + 1;
-  public final byte RECORDVALUE      = STRINGVALUE + 1;
-  public final byte SETENUMVALUE     = RECORDVALUE + 1;
-  public final byte SETPREDVALUE     = SETENUMVALUE + 1;
-  public final byte TUPLEVALUE       = SETPREDVALUE + 1;
-  public final byte FCNLAMBDAVALUE   = TUPLEVALUE + 1;
-  public final byte FCNRCDVALUE      = FCNLAMBDAVALUE + 1;
-  public final byte OPLAMBDAVALUE    = FCNRCDVALUE + 1;
-  public final byte OPRCDVALUE       = OPLAMBDAVALUE + 1;
-  public final byte METHODVALUE      = OPRCDVALUE + 1;
-  public final byte SETOFFCNSVALUE   = METHODVALUE + 1;
-  public final byte SETOFRCDSVALUE   = SETOFFCNSVALUE + 1;
-  public final byte SETOFTUPLESVALUE = SETOFRCDSVALUE + 1;
-  public final byte SUBSETVALUE      = SETOFTUPLESVALUE + 1;
-  public final byte SETDIFFVALUE     = SUBSETVALUE + 1;
-  public final byte SETCAPVALUE      = SETDIFFVALUE + 1;
-  public final byte SETCUPVALUE      = SETCAPVALUE + 1;
-  public final byte UNIONVALUE       = SETCUPVALUE + 1;
-  public final byte MODELVALUE       = UNIONVALUE + 1;
-  public final byte USERVALUE        = MODELVALUE + 1;
-  public final byte INTERVALVALUE    = USERVALUE + 1;
-  public final byte UNDEFVALUE       = INTERVALVALUE + 1;
-  public final byte LAZYVALUE        = UNDEFVALUE + 1;
-  public final byte DUMMYVALUE       = LAZYVALUE + 1;  
-
-  public final String[] ValueImage = {
-    "a Boolean value",                     // "BoolValue",
-    "an integer",                          // "IntValue",
-    "a real",                              // "RealValue",
-    "a string",                            // "StringValue",
-    "a record",                            // "RecordValue",
-    "a set of the form {e1, ... ,eN}",     // "SetEnumValue",
-    "a set of the form {x \\in S : expr}", // "SetPredValue",
-    "a tuple",                             // "TupleValue",
-    "a function of the form  [x \\in S |-> expr]",           // "FcnLambdaValue",
-    "a function  of the form (d1 :> e1 @@ ... @@ dN :> eN)", // "FcnRcdValue",
-    "an operator",                                // "OpLambdaValue",
-    "a constant operator",                        // "OpRcdValue",
-    "a java method",                              // "MethodValue",    
-    "a set of the form [S -> T]",                 // "SetOfFcnsValue",
-    "a set of the form [d1 : S1, ... , dN : SN]", // "SetOfRcdsValue",
-    "a cartesian product",                        // "SetOfTuplesValue",
-    "a set of the form SUBSET S",                 // "SubsetValue",
-    "a set of the form S \\ T",                   // "SetDiffValue",
-    "a set of the form S \\cap T",                // "SetCapValue",
-    "a set of the form S \\cup T",                // "SetCupValue",
-    "a set of the form UNION  S",                 // "UnionValue",
-    "a model value",                              // "ModelValue",
-    "a special set constant",                     // "UserValue",
-    "a set of the form i..j",                     // "IntervalValue",
-    "an undefined value",                         // "UndefValue",
-    "a value represented in lazy form",           // "LazyValue",
-    "a dummy for not-a-value",                    // "DummyValue",    
-  };
-
-  /* Value constants. */
-  public final BoolValue ValTrue  = new BoolValue(true);
-  public final BoolValue ValFalse = new BoolValue(false);
-  public final IntValue ValZero   = IntValue.gen(0);
-  public final IntValue ValOne    = IntValue.gen(1);
-  public final IntValue ValNegOne = IntValue.gen(-1);
-  public final Value EmptySet = new SetEnumValue(new ValueVec(0), true);
-  public final Value EmptyFcn = new FcnRcdValue(new Value[0], new Value[0], true);
-  public final TupleValue EmptyTuple = new TupleValue(new Value[0]);
-  public final RecordValue EmptyRcd = new RecordValue(new UniqueString[0], new Value[0], true);
-  public final UndefValue ValUndef = new UndefValue();
-  public final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, true);
+  byte BOOLVALUE        = 0;
+  byte INTVALUE         = BOOLVALUE + 1;
+  byte REALVALUE        = INTVALUE + 1;
+  byte STRINGVALUE      = REALVALUE + 1;
+  byte RECORDVALUE      = STRINGVALUE + 1;
+  byte SETENUMVALUE     = RECORDVALUE + 1;
+  byte SETPREDVALUE     = SETENUMVALUE + 1;
+  byte TUPLEVALUE       = SETPREDVALUE + 1;
+  byte FCNLAMBDAVALUE   = TUPLEVALUE + 1;
+  byte FCNRCDVALUE      = FCNLAMBDAVALUE + 1;
+  byte OPLAMBDAVALUE    = FCNRCDVALUE + 1;
+  byte OPRCDVALUE       = OPLAMBDAVALUE + 1;
+  byte METHODVALUE      = OPRCDVALUE + 1;
+  byte SETOFFCNSVALUE   = METHODVALUE + 1;
+  byte SETOFRCDSVALUE   = SETOFFCNSVALUE + 1;
+  byte SETOFTUPLESVALUE = SETOFRCDSVALUE + 1;
+  byte SUBSETVALUE      = SETOFTUPLESVALUE + 1;
+  byte SETDIFFVALUE     = SUBSETVALUE + 1;
+  byte SETCAPVALUE      = SETDIFFVALUE + 1;
+  byte SETCUPVALUE      = SETCAPVALUE + 1;
+  byte UNIONVALUE       = SETCUPVALUE + 1;
+  byte MODELVALUE       = UNIONVALUE + 1;
+  byte USERVALUE        = MODELVALUE + 1;
+  byte INTERVALVALUE    = USERVALUE + 1;
+  byte UNDEFVALUE       = INTERVALVALUE + 1;
+  byte LAZYVALUE        = UNDEFVALUE + 1;
+  byte DUMMYVALUE       = LAZYVALUE + 1;
 
 }
diff --git a/tlatools/src/tlc2/value/ValueEnumeration.java b/tlatools/src/tlc2/value/ValueEnumeration.java
deleted file mode 100644
index efd6dc27e05ccb6f6d3a8df7364553ca69a97bb2..0000000000000000000000000000000000000000
--- a/tlatools/src/tlc2/value/ValueEnumeration.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
-// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
-// Last modified on Mon 30 Apr 2007 at 13:21:10 PST by lamport
-//      modified on Tue Aug 22 11:56:52 PDT 2000 by yuanyu
-
-package tlc2.value;
-
-public interface ValueEnumeration {
-  /* Reset allows repeated use of this enumerator. */
-  public void reset();
-
-  /* Return the next element if there is one. Otherwise return null. */
-  public Value nextElement();
-  
-}
diff --git a/tlatools/src/tlc2/value/ValueInputStream.java b/tlatools/src/tlc2/value/ValueInputStream.java
index 40f00fb926ce10ce7c547e13e607cd1d28e9f844..a2a80d4624be158c50e6b641a3beebb908f02326 100644
--- a/tlatools/src/tlc2/value/ValueInputStream.java
+++ b/tlatools/src/tlc2/value/ValueInputStream.java
@@ -2,166 +2,150 @@
 
 package tlc2.value;
 
+import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
+import java.util.Map;
 
 import tlc2.TLCGlobals;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.ModelValue;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.TupleValue;
 import util.BufferedDataInputStream;
 import util.FileUtil;
+import util.IDataInputStream;
 import util.UniqueString;
 import util.WrongInvocationException;
 
-public final class ValueInputStream implements ValueConstants {
+public final class ValueInputStream implements ValueConstants, IValueInputStream {
 
-  private BufferedDataInputStream dis;
-  private HandleTable handles;
+  private final BufferedDataInputStream dis;
+  private final HandleTable handles;
 
-  public ValueInputStream(File file) throws IOException 
+  public ValueInputStream(File file, final boolean compressed) throws IOException 
   {
       // SZ Feb 24, 2009: FileUtil refactoring
-    this.dis = FileUtil.newBdFIS(TLCGlobals.useGZIP, file);
+    this.dis = FileUtil.newBdFIS(compressed, file);
     this.handles = new HandleTable();
   }
+  
+  public ValueInputStream(File file) throws IOException 
+  {
+	  this(file, TLCGlobals.useGZIP);
+  }
 
   public ValueInputStream(String fname) throws IOException {
       this(new File(fname));
   }
 
-  public final Value read() throws IOException {
-    byte kind = this.dis.readByte();
-
-    switch (kind) {
-    case BOOLVALUE:
-      {
-	boolean b = this.dis.readBoolean();
-	return (b) ? ValTrue : ValFalse;
-      }
-    case INTVALUE:
-      {
-	int x = this.dis.readInt();
-	return IntValue.gen(x);
-      }
-    case STRINGVALUE:
-      {
-	UniqueString str = UniqueString.read(this.dis);
-	Value res = new StringValue(str);
-	int index = this.handles.getIndex();
-	this.handles.assign(res, index);
-	return res;
-      }
-    case MODELVALUE:
-      {
-	int index = this.dis.readShort();
-	return ModelValue.mvs[index];
-      }
-    case INTERVALVALUE:
-      {
-	int low = this.dis.readInt();
-	int hi = this.dis.readInt();
-	return new IntervalValue(low, hi);
-      }
-    case RECORDVALUE:
-      {
-	int index = this.handles.getIndex();
-	boolean isNorm = true;
-	int len = this.dis.readInt();
-	if (len < 0) { len = -len; isNorm = false; }
-	UniqueString[] names = new UniqueString[len];
-	Value[] vals = new Value[len];
-	for (int i = 0; i < len; i++) {
-	  byte kind1 = this.dis.readByte();
-	  if (kind1 == DUMMYVALUE) {
-	    int index1 = this.readNat();
-	    names[i] = (UniqueString)this.handles.getValue(index1);
-	  }
-	  else {
-	    int index1 = this.handles.getIndex();
-	    names[i] = UniqueString.read(this.dis);
-	    this.handles.assign(names[i], index1);
-	  }
-	  vals[i] = this.read();
-	}
-	Value res = new RecordValue(names, vals, isNorm);
-	this.handles.assign(res, index);
-	return res;
-      }
-    case FCNRCDVALUE:
-      {
-	int index = this.handles.getIndex();
-	int len = this.readNat();
-	int info = this.dis.readByte();
-	Value res;
-	Value[] rvals = new Value[len];
-	if (info == 0) {
-	  int low = this.dis.readInt();
-	  int high = this.dis.readInt();
-	  for (int i = 0; i < len; i++) {
-	    rvals[i] = this.read();
-	  }
-	  IntervalValue intv = new IntervalValue(low, high);
-	  res = new FcnRcdValue(intv, rvals);
+	@Override
+	public final IValue read() throws IOException {
+		final byte kind = this.dis.readByte();
+
+		switch (kind) {
+		case BOOLVALUE: {
+			return (this.dis.readBoolean()) ? BoolValue.ValTrue : BoolValue.ValFalse;
+		}
+		case INTVALUE: {
+			return IntValue.gen(this.dis.readInt());
+		}
+		case STRINGVALUE: {
+			return StringValue.createFrom(this);
+		}
+		case MODELVALUE: {
+			return ModelValue.mvs[this.dis.readShort()];
+		}
+		case INTERVALVALUE: {
+			return new IntervalValue(this.dis.readInt(), this.dis.readInt());
+		}
+		case RECORDVALUE: {
+			return RecordValue.createFrom(this);
+		}
+		case FCNRCDVALUE: {
+			return FcnRcdValue.createFrom(this);
+		}
+		case SETENUMVALUE: {
+			return SetEnumValue.createFrom(this);
+		}
+		case TUPLEVALUE: {
+			return TupleValue.createFrom(this);
+		}
+		case DUMMYVALUE: {
+			return (IValue) this.handles.getValue(this.readNat());
+		}
+		default: {
+			throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind);
+		}
+		}
 	}
-	else {
-	  Value[] dvals = new Value[len];
-	  for (int i = 0; i < len; i++) {
-	    dvals[i] = this.read();
-	    rvals[i] = this.read();
-	  }
-	  res = new FcnRcdValue(dvals, rvals, (info == 1));
-	}
-	this.handles.assign(res, index);
-	return res;
-      }
-    case SETENUMVALUE:
-      {
-	int index = this.handles.getIndex();
-	boolean isNorm = true;
-	int len = this.dis.readInt();
-	if (len < 0) { len = -len; isNorm = false; }
-	Value[] elems = new Value[len];
-	for (int i = 0; i < len; i++) {
-	  elems[i] = this.read();
-	}
-	Value res = new SetEnumValue(elems, isNorm);
-	this.handles.assign(res, index);
-	return res;
-      }
-    case TUPLEVALUE:
-      {
-	int index = this.handles.getIndex();
-	int len = this.readNat();
-	Value[] elems = new Value[len];
-	for (int i = 0; i < len; i++) {
-	  elems[i] = this.read();
+	
+	public final IValue read(final Map<String, UniqueString> tbl) throws IOException {
+		final byte kind = this.dis.readByte();
+
+		switch (kind) {
+		case BOOLVALUE: {
+			return (this.dis.readBoolean()) ? BoolValue.ValTrue : BoolValue.ValFalse;
+		}
+		case INTVALUE: {
+			return IntValue.gen(this.dis.readInt());
+		}
+		case STRINGVALUE: {
+			return StringValue.createFrom(this, tbl);
+		}
+		case MODELVALUE: {
+			return ModelValue.mvs[this.dis.readShort()];
+		}
+		case INTERVALVALUE: {
+			return new IntervalValue(this.dis.readInt(), this.dis.readInt());
+		}
+		case RECORDVALUE: {
+			return RecordValue.createFrom(this, tbl);
+		}
+		case FCNRCDVALUE: {
+			return FcnRcdValue.createFrom(this, tbl);
+		}
+		case SETENUMVALUE: {
+			return SetEnumValue.createFrom(this, tbl);
+		}
+		case TUPLEVALUE: {
+			return TupleValue.createFrom(this, tbl);
+		}
+		case DUMMYVALUE: {
+			return (IValue) this.handles.getValue(this.readNat());
+		}
+		default: {
+			throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind);
+		}
+		}
 	}
-	Value res = new TupleValue(elems);
-	this.handles.assign(res, index);
-	return res;
-      }
-    case DUMMYVALUE:
-      {
-	int index = this.readNat();
-	return (Value)this.handles.getValue(index);
-      }
-    default:
-      {
-	throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind);
-      }
-    }      
+ 
+  @Override
+  public final int readShort() throws IOException {
+	    return this.dis.readShort();
   }
 
+  @Override
   public final int readInt() throws IOException {
     return this.dis.readInt();
   }
 
+  @Override
   public final long readLong() throws IOException {
     return this.dis.readLong();
   }
   
+  @Override
   public final void close() throws IOException {
     this.dis.close();
   }
 
+  @Override
   public final int readNat() throws IOException {
     int res = this.dis.readShort();
     if (res >= 0) return res;
@@ -169,6 +153,14 @@ public final class ValueInputStream implements ValueConstants {
     return -res;
   }
   
+  @Override
+  public final short readShortNat() throws IOException {
+	short res = this.dis.readByte();
+	if (res >= 0) return res;
+	return (short) -((res << 8) | (this.dis.readByte() & 0xFF));
+  }
+  
+  @Override
   public final long readLongNat() throws IOException {
     long res = this.dis.readInt();
     if (res >= 0) return res;
@@ -176,6 +168,32 @@ public final class ValueInputStream implements ValueConstants {
     return -res;
   }
 
+	@Override
+	public final byte readByte() throws EOFException, IOException {
+		return this.dis.readByte();
+	}
+
+	@Override
+	public final void assign(final Object obj, final int idx) {
+		this.handles.assign(obj, idx);
+	}
+
+	@Override
+	public final int getIndex() {
+		return handles.getIndex();
+	}
+
+	@Override
+	public final IDataInputStream getInputStream() {
+		return dis;
+	}
+
+	@Override
+	public final UniqueString getValue(int idx) {
+		return (UniqueString) this.handles.getValue(idx);
+	}
+
+  // @see ValueOutputStream#put
   private static class HandleTable {
     private Object[] values;
     private int index;
@@ -201,5 +219,4 @@ public final class ValueInputStream implements ValueConstants {
     final Object getValue(int idx) { return this.values[idx]; }
 
   }
-  
 }
diff --git a/tlatools/src/tlc2/value/ValueOutputStream.java b/tlatools/src/tlc2/value/ValueOutputStream.java
index 0b2bdbaa9378f25c2c58004cc29559d96c5113b5..f4b56b4da9fbfa2f3bc350de0f72d64a9cd74295 100644
--- a/tlatools/src/tlc2/value/ValueOutputStream.java
+++ b/tlatools/src/tlc2/value/ValueOutputStream.java
@@ -11,15 +11,18 @@ import java.util.zip.GZIPOutputStream;
 
 import tlc2.TLCGlobals;
 import util.BufferedDataOutputStream;
-import util.WrongInvocationException;
 
-public final class ValueOutputStream implements ValueConstants {
+public final class ValueOutputStream implements IValueOutputStream {
 
-  private BufferedDataOutputStream dos;
-  private HandleTable handles;
+  private final BufferedDataOutputStream dos;
+  private final HandleTable handles;
 
   public ValueOutputStream(File file) throws IOException {
-    if (TLCGlobals.useGZIP) {
+	  this(file, TLCGlobals.useGZIP);
+  }
+
+  public ValueOutputStream(File file, final boolean compress) throws IOException {
+    if (compress) {
       OutputStream os = new GZIPOutputStream(new FileOutputStream(file));
       this.dos = new BufferedDataOutputStream(os);
     }
@@ -30,240 +33,53 @@ public final class ValueOutputStream implements ValueConstants {
   }
 
   public ValueOutputStream(String fname) throws IOException {
-    if (TLCGlobals.useGZIP) {
-      OutputStream os = new GZIPOutputStream(new FileOutputStream(fname));
-      this.dos = new BufferedDataOutputStream(os);
-    }
-    else {
-      this.dos = new BufferedDataOutputStream(fname);
-    }
-    this.handles = new HandleTable();
+	  this(fname, TLCGlobals.useGZIP);
   }
-
-  public final void write(Value val) throws IOException {
-    switch (val.getKind()) {
-    case BOOLVALUE:
-      {
-	this.dos.writeByte(BOOLVALUE);
-	this.dos.writeBoolean(((BoolValue)val).val);
-	break;
-      }
-    case INTVALUE:
-      {
-	this.dos.writeByte(INTVALUE);
-	dos.writeInt(((IntValue)val).val);
-	break;
-      }
-    case STRINGVALUE:
-      {
-	int index = this.handles.put(val);
-	if (index == -1) {
-	  this.dos.writeByte(STRINGVALUE);
-	  ((StringValue)val).val.write(this.dos);
-	}
-	else {
-	  this.dos.writeByte(DUMMYVALUE);
-	  this.writeNat(index);	  
-	}
-	break;
-      }
-    case MODELVALUE:
-      {
-	this.dos.writeByte(MODELVALUE);
-	this.dos.writeShort((short)((ModelValue)val).index);
-	break;
-      }
-    case INTERVALVALUE:
-      {
-	this.dos.writeByte(INTERVALVALUE);
-	this.dos.writeInt(((IntervalValue)val).low);
-	this.dos.writeInt(((IntervalValue)val).high);
-	break;
-      }
-    case RECORDVALUE:
-      {
-	int index = this.handles.put(val);
-	if (index == -1) {
-	  this.dos.writeByte(RECORDVALUE);
-	  RecordValue rval = (RecordValue)val;
-	  int len = rval.names.length;
-	  this.dos.writeInt((rval.isNormalized()) ? len : -len);
-	  for (int i = 0; i < len; i++) {
-	    int index1 = this.handles.put(rval.names[i]);
-	    if (index1 == -1) {
-	      this.dos.writeByte(STRINGVALUE);
-	      rval.names[i].write(this.dos);
+  
+  public ValueOutputStream(String fname, boolean zip) throws IOException {
+	    if (zip) {
+	      OutputStream os = new GZIPOutputStream(new FileOutputStream(fname));
+	      this.dos = new BufferedDataOutputStream(os);
 	    }
 	    else {
-	      this.dos.writeByte(DUMMYVALUE);
-	      this.writeNat(index1);
-	    }
-	    this.write(rval.values[i]);
-	  }
-	}
-	else {
-	  this.dos.writeByte(DUMMYVALUE);
-	  this.writeNat(index);
-	}
-	break;
-      }
-    case FCNRCDVALUE:
-      {
-	int index = this.handles.put(val);
-	if (index == -1) {
-	  this.dos.writeByte(FCNRCDVALUE);	  
-	  FcnRcdValue fval = (FcnRcdValue)val;
-	  int len = fval.values.length;
-	  this.writeNat(len);
-	  if (fval.intv != null) {
-	    this.dos.writeByte((byte)0);
-	    this.dos.writeInt(fval.intv.low);
-	    this.dos.writeInt(fval.intv.high);
-	    for (int i = 0; i < len; i++) {
-	      this.write(fval.values[i]);
-	    }
-	  }
-	  else {
-	    this.dos.writeByte((fval.isNormalized()) ? (byte)1 : (byte)2);
-	    for (int i = 0; i < len; i++) {
-	      this.write(fval.domain[i]);
-	      this.write(fval.values[i]);
+	      this.dos = new BufferedDataOutputStream(fname);
 	    }
+	    this.handles = new HandleTable();
 	  }
-	}
-	else {
-	  this.dos.writeByte(DUMMYVALUE);
-	  this.writeNat(index);
-	}
-	break;
-      }
-    case SETENUMVALUE:
-      {
-	int index = this.handles.put(val);
-	if (index == -1) {
-	  this.dos.writeByte(SETENUMVALUE);
-	  SetEnumValue sval = (SetEnumValue)val;
-	  int len = sval.elems.size();
-	  this.dos.writeInt((sval.isNormalized()) ? len : -len);
-	  for (int i = 0; i < len; i++) {
-	    this.write(sval.elems.elementAt(i));
-	  }
-	}
-	else {
-	  this.dos.writeByte(DUMMYVALUE);
-	  this.writeNat(index);
-	}
-	break;
-      }
-    case TUPLEVALUE:
-      {
-	int index = this.handles.put(val);
-	if (index == -1) {
-	  this.dos.writeByte(TUPLEVALUE);
-	  TupleValue tval = (TupleValue)val;
-	  int len = tval.elems.length;
-	  this.writeNat(len);
-	  for (int i = 0; i < len; i++) {
-	    this.write(tval.elems[i]);
-	  }
-	}
-	else {
-	  this.dos.writeByte(DUMMYVALUE);
-	  this.writeNat(index);
-	}
-	break;
-      }
-    case SETCAPVALUE:
-      {
-	SetCapValue cap = (SetCapValue)val;
-	// Assert.check(cap.capSet != null);
-	this.write(cap.capSet);
-	break;
-      }
-    case SETCUPVALUE:
-      {
-	SetCupValue cup = (SetCupValue)val;
-	// Assert.check(cup.cupSet != null);
-	this.write(cup.cupSet);
-	break;
-      }
-    case SETDIFFVALUE:
-      {
-	SetDiffValue diff = (SetDiffValue)val;
-	// Assert.check(diff.diffSet != null);
-	this.write(diff.diffSet);
-	break;
-      }
-    case SUBSETVALUE:
-      {
-	SubsetValue pset = (SubsetValue)val;
-	// Assert.check(pset.pset != null);
-	this.write(pset.pset);
-	break;
-      }
-    case UNIONVALUE:
-      {
-	UnionValue uv = (UnionValue)val;
-	// Assert.check(uv.realSet != null);
-	this.write(uv.realSet);
-	break;
-      }
-    case SETOFRCDSVALUE:
-      {
-	SetOfRcdsValue rcds = (SetOfRcdsValue)val;
-	// Assert.check(rcds.rcdSet != null);
-	this.write(rcds.rcdSet);
-	break;
-      }
-    case SETOFFCNSVALUE:
-      {
-	SetOfFcnsValue fcns = (SetOfFcnsValue)val;
-	// Assert.check(fcns.fcnSet != null);
-	this.write(fcns.fcnSet);
-	break;
-      }
-    case SETOFTUPLESVALUE:
-      {
-	SetOfTuplesValue tuples = (SetOfTuplesValue)val;
-	// Assert.check(tuples.tupleSet != null);
-	this.write(tuples.tupleSet);
-	break;
-      }
-    case SETPREDVALUE:
-      {
-	SetPredValue spred = (SetPredValue)val;
-	// Assert.check(spred.tool == null);
-	this.write(spred.inVal);
-	break;
-      }
-    case FCNLAMBDAVALUE:
-      {
-	FcnLambdaValue flambda = (FcnLambdaValue)val;
-	// Assert.check(flambda.fcnRcd != null);
-	this.write(flambda.fcnRcd);
-	break;
-      }
-    default:
-      {
-	throw new WrongInvocationException("ValueOutputStream: Can not pickle the value\n" +
-		    Value.ppr(val.toString()));
-      }
-    }
-  }
 
+  @Override
+  public final void writeShort(short x) throws IOException {
+	  this.dos.writeShort(x);
+  }
+  
+  @Override
   public final void writeInt(int x) throws IOException {
     this.dos.writeInt(x);
   }
 
+  @Override
   public final void writeLong(long x) throws IOException {
     this.dos.writeLong(x);
   }
   
+  @Override
   public final void close() throws IOException {
     this.dos.close();
   }
+  
+  /* Precondition: x is a non-negative short. */
+  @Override
+  public final void writeShortNat(short x) throws IOException {
+    if (x > 0x7f) {
+      this.dos.writeShort((short) -x);
+    }
+    else {
+      this.dos.writeByte((byte)x);
+    }
+  }
 
   /* Precondition: x is a non-negative int. */
+  @Override
   public final void writeNat(int x) throws IOException {
     if (x > 0x7fff) {
       this.dos.writeInt(-x);
@@ -274,6 +90,7 @@ public final class ValueOutputStream implements ValueConstants {
   }
 
   /* Precondition: x is a non-negative long. */
+  @Override
   public final void writeLongNat(long x) throws IOException {
     if (x <= 0x7fffffff) {
       this.dos.writeInt((int)x);
@@ -282,6 +99,53 @@ public final class ValueOutputStream implements ValueConstants {
       this.dos.writeLong(-x);
     }
   }
+	
+	@Override
+	public final void writeByte(final byte b) throws IOException {
+		this.dos.writeByte(b);
+	}
+
+	@Override
+	public final void writeBoolean(final boolean b) throws IOException {
+		this.dos.writeBoolean(b);
+	}
+
+	@Override
+	public final BufferedDataOutputStream getOutputStream() {
+		return dos;
+	}
+
+	/**
+	 * Check if another TLCState - which is currently also being serialized to the
+	 * same storage (i.e. disk file) - has/contains an identical Value. If yes, do
+	 * not serialize the Value instance again but make this TLCState point to the
+	 * Value instance previously serialized for the other TLCState. In other words,
+	 * this is a custom-tailored compression/de-duplication mechanism for Value
+	 * instances.
+	 * <p>
+	 * This approach only works because both TLCStates are serialized to the same
+	 * storage and thus de-serialized as part of the same operation (same
+	 * Value*Stream instance).
+	 * <p>
+	 * The purpose of this approach appears to be:
+	 * <ul>
+	 * <li>Reduce serialization efforts and storage size</li>
+	 * <li>Reduce the number of Value instances created during de-serialization</li>
+	 * <li>Allow identity comparison on Value instances (AFAICT not used by Value
+	 * explicitly, just UniqueString) to speed up check. Value#equals internally
+	 * likely uses identity comparison as first check.</li>
+	 * </ul>
+	 * <p>
+	 * A disadvantage is the cost of maintaining the internal HandleTable which can
+	 * grow to thousands of elements during serialization/de-serialization (in
+	 * ValueInputStream). Since serialization suspends the DiskStateQueue and thus
+	 * blocks tlc2.tool.Workers from exploring the state space, this might has
+	 * adverse effects.
+	 */
+	@Override
+	public final int put(final Object obj) {
+		return this.handles.put(obj);
+	}
   
   private static class HandleTable {
     private int[] spine;
@@ -347,62 +211,4 @@ public final class ValueOutputStream implements ValueConstants {
       }
     }
   }
-
-  public static void main(String[] args) {
-    if (args.length != 1) {
-      System.err.println("Usage: java tlc2.value.ValueOutputStream filename.");
-      System.exit(1);
-    }
-    
-    IntValue[] aa = new IntValue[100];
-    StringValue[] bb = new StringValue[100];
-      
-    for (int i = 0; i < aa.length; i++) {
-      aa[i] = IntValue.gen(88);
-    }
-
-    StringValue sval = new StringValue("ssssssssss");
-    for (int i = 0; i < bb.length; i++) {
-      bb[i] = sval;
-    }
-
-    try {
-      /**
-      BufferedDataOutputStream dos = new BufferedDataOutputStream(args[0]+"_1");
-      for (int i = 0; i < aa.length; i++) {
-	dos.writeInt(88);
-      }
-      dos.close();
-      
-      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(args[0]+"_2"));
-      for (int i = 0; i < bb.length; i++) {
-	oos.writeObject(bb[i]);
-      }
-      oos.close();
-
-      ValueOutputStream vos = new ValueOutputStream(new File(args[0]+"_3"));
-      for (int i = 0; i < bb.length; i++) {
-	vos.write(bb[i]);
-      }
-      vos.close();
-      **/
-
-      ValueOutputStream vos = new ValueOutputStream(new File(args[0]));
-      long x = 1;
-      for (int i = 0; i < 63; i++) {
-	System.err.println("write " + x);
-	vos.writeLongNat(x);
-	x = x * 2;
-      }
-      vos.close();
-
-      ValueInputStream vis = new ValueInputStream(new File(args[0]));
-      for (int i = 0; i < 63; i++) {
-	System.err.println("read " + vis.readLongNat());
-      }
-      vis.close();      
-    }
-    catch (Exception e) { }
-  }
-  
 }
diff --git a/tlatools/src/tlc2/value/Values.java b/tlatools/src/tlc2/value/Values.java
new file mode 100644
index 0000000000000000000000000000000000000000..22c5ba3af1085710be5e5432fa8ba682c14858ec
--- /dev/null
+++ b/tlatools/src/tlc2/value/Values.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+import tlc2.pprint.PrettyPrint;
+
+public abstract class Values {
+
+	public static String ppr(String s) {
+	    return PrettyPrint.mypp(s, 80) ;
+	  }
+
+	public static String ppr(IValue v) {
+		  if (v == null) {
+			  return "null";
+		  }
+		 return PrettyPrint.mypp(v.toString(), 80) ;
+	  }
+}
diff --git a/tlatools/src/tlc2/value/Applicable.java b/tlatools/src/tlc2/value/impl/Applicable.java
similarity index 56%
rename from tlatools/src/tlc2/value/Applicable.java
rename to tlatools/src/tlc2/value/impl/Applicable.java
index 2f784f73a99f03b63de9e593e327e27934db3501..3822610bebdcf9b0e245320b9c09d7c5cefef5ed 100644
--- a/tlatools/src/tlc2/value/Applicable.java
+++ b/tlatools/src/tlc2/value/impl/Applicable.java
@@ -3,15 +3,15 @@
 // Last modified on Mon 30 Apr 2007 at 13:20:42 PST by lamport
 //      modified on Wed Jun  2 00:10:22 PDT 1999 by yuanyu
 
-package tlc2.value;
+package tlc2.value.impl;
 
 import tlc2.tool.EvalException;
 
 public interface Applicable {
   
-  public Value apply(Value[] args, int control) throws EvalException;
-  public Value apply(Value arg, int control) throws EvalException;
-  public Value getDomain() throws EvalException;
-  public Value select(Value arg) throws EvalException;
+  Value apply(Value[] args, int control) throws EvalException;
+  Value apply(Value arg, int control) throws EvalException;
+  Value getDomain() throws EvalException;
+  Value select(Value arg) throws EvalException;
   
 }
diff --git a/tlatools/src/tlc2/value/impl/BoolValue.java b/tlatools/src/tlc2/value/impl/BoolValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6b40dc3bb7d40b15c3e27f4e3a98aee00d8462c
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/BoolValue.java
@@ -0,0 +1,212 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:01:16 PST by lamport
+//      modified on Fri Aug 10 15:07:07 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.tool.FingerprintException;
+import tlc2.util.FP64;
+import tlc2.value.IBoolValue;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class BoolValue extends Value implements IBoolValue {
+  public boolean val;   // the boolean
+  public static final BoolValue ValFalse = new BoolValue(false);
+  /* Value constants. */
+  public static final BoolValue ValTrue  = new BoolValue(true);
+
+  /* Constructor */
+  public BoolValue(boolean b) { this.val = b; }
+
+  @Override
+  public final boolean getVal() {
+	  return val;
+  }
+  
+  @Override
+  public final byte getKind() { return BOOLVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (obj instanceof BoolValue) {
+        int x = this.val ? 1 : 0;
+        int y = ((BoolValue)obj).val ? 1 : 0;
+        return x - y;
+      }
+      if (!(obj instanceof ModelValue)) {
+        Assert.fail("Attempted to compare boolean " + Values.ppr(this.toString()) +
+        " with non-boolean:\n" + Values.ppr(obj.toString()));
+      }
+      return 1;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof BoolValue) {
+        return this.val == ((BoolValue)obj).val;
+      }
+      if (!(obj instanceof ModelValue)) {
+        Assert.fail("Attempted to compare equality of boolean " + Values.ppr(this.toString()) +
+        " with non-boolean:\n" + Values.ppr(obj.toString()));
+      }
+      return ((ModelValue) obj).modelValueEquals(this) ;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of the boolean " + Values.ppr(this.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the boolean " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT construct to the boolean " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the boolean " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the boolean " +
+      Values.ppr(this.toString()) + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public boolean mutates() {
+	  return false;
+  }
+
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*nop*/ return this; }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return ((val instanceof BoolValue) &&
+        this.val == ((BoolValue)val).val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public void write(IValueOutputStream vos) throws IOException {
+		vos.writeByte(BOOLVALUE);
+		vos.writeBoolean(val);
+	}
+
+  /* The fingerprint method */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      fp = FP64.Extend(fp, BOOLVALUE) ;
+      fp = FP64.Extend(fp, (this.val) ? 't' : 'f') ;
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) { return this; }
+
+  /* The string representation */
+  public final StringBuffer toString(StringBuffer sb, int offset) {
+	return toString(sb, offset, true);
+}
+
+/* The string representation */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      return sb.append((this.val) ? "TRUE" : "FALSE");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/CallableValue.java b/tlatools/src/tlc2/value/impl/CallableValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..f047ac80e450fd1d8402f90766a6159e2c59c138
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/CallableValue.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+import tla2sany.semantic.ExprOrOpArgNode;
+import tlc2.output.EC;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Context;
+import tlc2.value.IValue;
+import util.Assert;
+
+public class CallableValue extends EvaluatingValue {
+
+	public CallableValue(Method md, int minLevel) throws IllegalAccessException {
+		super(MethodHandles.publicLookup().unreflect(md).asSpreader(IValue[].class, md.getParameterCount()), md, minLevel);
+	}
+
+	@Override
+	public Value eval(final Tool tool, final ExprOrOpArgNode[] args, final Context c, final TLCState s0,
+			final TLCState s1, final int control, final CostModel cm) {
+		final Value[] argVals = new Value[args.length];
+		// evaluate the operator's arguments:
+		for (int i = 0; i < args.length; i++) {
+			argVals[i] = tool.eval(args[i], c, s0, s1, control, cm);
+		}
+		try {
+			final Callable<?> cl = (Callable<?>) this.mh.invoke(argVals);
+			s1.setCallable(cl);
+		} catch (Throwable e) {
+			Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[] { this.md.toString(), e.getMessage() });
+		}
+		return BoolValue.ValTrue;
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/Enumerable.java b/tlatools/src/tlc2/value/impl/Enumerable.java
new file mode 100644
index 0000000000000000000000000000000000000000..2622b6a8f93c05bee0189c7ab298c99932edee0b
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/Enumerable.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:20:54 PST by lamport
+//      modified on Thu Mar 11 21:25:20 PST 1999 by yuanyu
+
+package tlc2.value.impl;
+
+import tlc2.value.IValue;
+
+public interface Enumerable extends IValue {
+
+	enum Ordering {
+		UNDEFINED,
+		/**
+		 * The normalized order is the order of elements when a Value gets
+		 * fingerprinted (@see {@link Value#fingerPrint(long)}.
+		 */
+		NORMALIZED
+    }
+	
+  @Override
+  int size();
+  boolean member(Value elem);
+  /**
+   * Semantics or Enumerable#elements(Ordering#UNDEFINED) 
+   */
+  ValueEnumeration elements();
+  ValueEnumeration elements(final Ordering ordering);
+  /**
+   * Returns a {@link ValueEnumeration} which returns k 
+   * {@link IValue}s of all {@link IValue}s returned by 
+   * {@link Enumerable#elements()}. In other words, it returns
+   * a randomly chosen subset of all {@link IValue} members of
+   * this {@link Enumerable}.
+   */
+  ValueEnumeration elements(final int k);
+  EnumerableValue getRandomSubset(final int k);
+  Value isSubsetEq(Value other);
+}
+
diff --git a/tlatools/src/tlc2/value/impl/EnumerableValue.java b/tlatools/src/tlc2/value/impl/EnumerableValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..86586301a1e8549df487709e53bd3c1d62ed2e42
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/EnumerableValue.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ *   Ian Morris Nieves - added support for fingerprint stack trace
+ ******************************************************************************/
+
+package tlc2.value.impl;
+
+import java.util.List;
+
+import tlc2.tool.FingerprintException;
+import tlc2.value.RandomEnumerableValues;
+
+public abstract class EnumerableValue extends Value implements Enumerable {
+
+  @Override
+  public Value isSubsetEq(Value other) {
+    try {
+      final ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        if (!other.member(elem)) {
+          return BoolValue.ValFalse;
+        }
+      }
+      return BoolValue.ValTrue;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+  
+	@Override
+	public EnumerableValue getRandomSubset(final int kOutOfN) {
+		// By default, convert all EVs into SetEnumValue and delegate to its
+		// getRandomSubset.
+    	return ((SetEnumValue) this.toSetEnum()).getRandomSubset(kOutOfN);
+	}
+
+	@Override
+	public ValueEnumeration elements(final Ordering ordering) {
+		if (ordering == Ordering.NORMALIZED) {
+			// By default, none of the EnumerableValues supports a ValueEnumeration that
+			// provides normalized ordering. Thus, to traverse the elements in normalized
+			// ordering, any EV type gets converted into a SetEnumValue - this effectively
+			// enumerates the EV and normalizes the new SEV. Traversing the normalized
+			// SEV with a ValueEnumeration returned by Enumerable#elements is guaranteed 
+			// to be in normalized order (@see Ordering.NORMALIZED for what this means).
+			// In case a subclass provides a more efficient ValueEnumeration that guarantees
+			// normalized order, the subclass may override this default method. This is
+			// so far done by SubsetValue.
+			final Value enumerated = this.toSetEnum();
+			if (enumerated != null) {
+				return ((Enumerable) enumerated.normalize()).elements();
+			}
+		}
+		return elements();
+	}
+	
+	@Override
+	public ValueEnumeration elements(final int k) {
+		// The generic implementation collects all n elements of the actual Enumerable
+		// into the temporary variable values. The SubSetEnumerator then randomly
+		// returns up to k elements.
+		// The underlying assuming here is that the size of Enumerable (n) is
+		// very small and this method is not called as part of the next-state relation.
+		// If it gets called on larger sets or as part of the next-state relation,
+		// subclasses can provide a more efficient implementation (e.g. see
+		// IntervalValue and SetEnumValue which return a more efficient subclass
+		// of SubsetEnumerator).
+		final List<Value> values = elements().all();
+		return new SubsetEnumerator(k) {
+			@Override
+			public Value nextElement() {
+				if (!hasNext()) {
+					return null;
+				}
+				return values.get(nextIndex());
+			}
+		};
+  	}
+
+	abstract class SubsetEnumerator implements ValueEnumeration {
+
+		protected final long x;
+		protected final int a;
+		protected final int n;
+		protected final int k;
+		protected int i;
+
+		public SubsetEnumerator(final int k) {
+			this(k, size());	
+		}
+		
+		public SubsetEnumerator(final int k, final int n) {
+			this.n = n;
+			
+			// https://en.wikipedia.org/wiki/Linear_congruential_generator
+			//
+			// x has to be co-prime to n. Since n might or might not be a prime number
+			// - it depends on the actual size of the set - we simply set x to
+			// be a prime number. The prime x has to be larger than n though, since n is
+			// bound by Integer.MAX_VALUE, we simply choose the Mersenne prime
+			// Integer.MAX_VALUE
+			// for x and accept that we fail if n = Integer.MAX_VALUE. To minimize
+			// intermediate results and the rounds of mod in nextIndex, we choose 191 if n
+			// happens to be smaller (very likely).
+			assert n < Integer.MAX_VALUE;
+			this.x = n < 191 ? 191L : 1L * Integer.MAX_VALUE;
+
+			// k out of n elements in the range 0 <= k <= n.
+			if (n > 0) {
+				this.k = k;
+				this.a = RandomEnumerableValues.get().nextInt(n);
+			} else {
+				this.k = 0;
+				this.a = 0; // RANDOM.nextInt(0) causes IllegalArgumentException.
+			}
+		}
+
+		@Override
+		public void reset() {
+			i = 0;
+		}
+
+		public boolean hasNext() {
+			return i < k;
+		}
+
+		/**
+		 * @return A random (uniformly distributed) index in the range [0,n) where n is
+		 *         {@link Enumerable#size()}.
+		 */
+		public int nextIndex() {
+			if (n <= 0) {
+				i++;
+				return 0;
+			}
+			// long x avoids intermediate overflow, final cast to int safe though because of
+			// mod n.
+			final int index = (int) (((x * i++) + a) % n);
+			assert 0 <= index && index < n;
+			return index;
+		}
+
+		@Override
+		public abstract Value nextElement();
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/EvaluatingValue.java b/tlatools/src/tlc2/value/impl/EvaluatingValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..6847405e172d4664f4dca3352536bb49dfdc5d3a
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/EvaluatingValue.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+import tla2sany.semantic.ExprOrOpArgNode;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Context;
+import tlc2.value.IValue;
+import tlc2.value.Values;
+import util.Assert;
+import util.Assert.TLCRuntimeException;
+import util.WrongInvocationException;
+
+public class EvaluatingValue extends OpValue implements Applicable {
+  protected final MethodHandle mh;
+  protected final Method md;
+  protected final int minLevel;
+
+  protected EvaluatingValue(final MethodHandle mh, final Method md, final int minLevel) {
+	  this.mh = mh;
+	  this.md = md;
+	  this.minLevel = minLevel;
+  }
+  
+  /* Constructor */
+	public EvaluatingValue(final Method md, final int minLevel) {
+		this.md = md;
+		this.minLevel = minLevel;
+		try {
+			this.mh = MethodHandles.publicLookup().unreflect(md).asFixedArity();
+		} catch (IllegalAccessException e) {
+			throw new TLCRuntimeException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, MP.getMessage(
+					EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[] { md.toString(), e.getMessage() }));
+		}
+	}
+
+	public Value eval(final Tool tool, final ExprOrOpArgNode[] args, final Context c, final TLCState s0,
+			final TLCState s1, final int control, final CostModel cm) {
+		try {
+			return (Value) this.mh.invoke(tool, args, c, s0, s1, control, cm);
+		} catch (Throwable e) {
+            Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()});
+            return null; // make compiler happy
+		}
+	}
+
+  public final byte getKind() { return METHODVALUE; }
+
+  public final int compareTo(Object obj) {
+    try {
+      Assert.fail("Attempted to compare operator " + this.toString() +
+      " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString()));
+      return 0;       // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      Assert.fail("Attempted to check equality of operator " + this.toString() +
+      " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : Values.ppr(elem.toString()) +
+      "\nis an element of operator " + this.toString());
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the operator " + this.toString() +
+      " is a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value apply(Value arg, int control) {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Should use the other apply method.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value apply(Value[] args, int control) {
+	    try {
+	        throw new WrongInvocationException("It is a TLC bug: Should use the other apply method.");
+	      }
+	      catch (RuntimeException | OutOfMemoryError e) {
+	        if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	        else { throw e; }
+	      }
+  }
+
+  public final Value select(Value arg) {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select().");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      Assert.fail("Attempted to appy EXCEPT construct to the operator " +
+      this.toString() + ".");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Assert.fail("Attempted to apply EXCEPT construct to the operator " +
+      this.toString() + ".");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value getDomain() {
+    try {
+      Assert.fail("Attempted to compute the domain of the operator " +
+      this.toString() + ".");
+      return SetEnumValue.EmptySet;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the operator " +
+      this.toString() + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Should never normalize an operator. */
+  public final boolean isNormalized() {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value normalize() {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean isDefined() { return true; }
+
+  public final IValue deepCopy() { return this; }
+
+  public final boolean assignable(Value val) {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* String representation of the value.  */
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) {
+    try {
+      return sb.append("<Java Method: " + this.md + ">");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public int getMinLevel() {
+	  return minLevel;
+  }
+}
diff --git a/tlatools/src/tlc2/value/impl/FcnLambdaValue.java b/tlatools/src/tlc2/value/impl/FcnLambdaValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..364c0e89130796b6250c7ae7505a23cc5ca72d1e
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/FcnLambdaValue.java
@@ -0,0 +1,811 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Wed  4 Jul 2007 at 17:31:23 PST by lamport
+//      modified on Thu Dec  6 21:46:34 PST 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.SemanticNode;
+import tla2sany.semantic.SymbolNode;
+import tlc2.TLCGlobals;
+import tlc2.tool.EvalControl;
+import tlc2.tool.EvalException;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.ITool;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.IFcnLambdaValue;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.UniqueString;
+
+public class FcnLambdaValue extends Value implements Applicable, IFcnLambdaValue {
+  public final FcnParams params;       // the function formals
+  public final SemanticNode body;      // the function body
+  public ValueExcept[] excepts;  // the EXCEPTs
+  public final ITool tool;
+  public Context con;
+  public final TLCState state;
+  public final TLCState pstate;
+  public int control;
+  public FcnRcdValue fcnRcd;
+
+	/*
+	 * Constructor: E.g. [ s \in {"A", "B", "C"} |-> "foo" ] where s \in {"A", "B",
+	 * "C"} is FcnLambdaValue and body is the expression "foo".
+	 */
+  public FcnLambdaValue(FcnParams params, SemanticNode body, ITool tool,
+      Context c, TLCState s0, TLCState s1, int control) {
+    this.params = params;
+    this.body = body;
+    this.excepts = null;
+    this.tool = tool;
+    this.con = c;
+    this.state = s0.copy();  // copy() added 12 Mar 2010 by Yuan Yu.
+    if (s1 != null) {        // see SetPredValue constructor.
+        this.pstate = s1.copy();
+    } else {
+        this.pstate = null;
+    }
+
+    this.control = control;
+    this.fcnRcd = null;
+  }
+
+  public FcnLambdaValue(FcnParams params, SemanticNode body, ITool tool,
+	      Context c, TLCState s0, TLCState s1, int control, CostModel cm) {
+	  this(params, body, tool, c, s0, s1, control);
+	  this.cm = cm;
+  }
+
+  public FcnLambdaValue(FcnLambdaValue fcn, ITool tool) {
+    this.params = fcn.params;
+    this.body = fcn.body;
+    this.excepts = fcn.excepts;
+    this.tool = tool;
+    this.con = fcn.con;
+    this.state = fcn.state;
+    this.pstate = fcn.pstate;
+    this.control = fcn.control;
+    this.fcnRcd = fcn.fcnRcd;
+  }
+
+  public FcnLambdaValue(FcnLambdaValue fcn) {
+	  this(fcn, fcn.tool);
+  }
+
+  @Override
+  public final byte getKind() { return FCNLAMBDAVALUE; }
+
+  public final void makeRecursive(SymbolNode fname) {
+    try {
+      this.con = this.con.cons(fname, this);
+      this.control = EvalControl.setKeepLazy(this.control);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      FcnRcdValue fcn = (FcnRcdValue) this.toFcnRcd();
+      return fcn.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      FcnRcdValue fcn = (FcnRcdValue) this.toFcnRcd();
+      return fcn.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of the function " + Values.ppr(this.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the function:\n" + Values.ppr(this.toString()) +
+      "\nis a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Apply this function to the arguments given by args.  */
+  @Override
+  public final Value apply(Value args, int control) throws EvalException {
+    try {
+
+      if (this.fcnRcd != null) {
+        return this.fcnRcd.apply(args, control);
+      }
+
+      // First, find all the excepts that match args.
+      Value  res = null;
+      int num = 0;
+      ValueExcept[] excepts1 = null;
+      if (this.excepts != null) {
+        int exlen = this.excepts.length;
+        for (int i = exlen-1; i >= 0; i--) {
+          ValueExcept ex = this.excepts[i];
+          Value  arg = ex.current();
+          boolean inExcept = true;
+          inExcept = arg.equals(args);
+          if (inExcept) {
+            if (ex.isLast()) { res = ex.value; break; }
+            if (excepts1 == null) excepts1 = new ValueExcept[exlen];
+            excepts1[num++] = new ValueExcept(ex, ex.idx+1);
+          }
+        }
+      }
+
+      // Second, evaluate the function application.
+      if (res == null) {
+        Context c1 = this.con;
+        FormalParamNode[][] formals = this.params.formals;
+        Value [] domains = this.params.domains;
+        boolean[] isTuples = this.params.isTuples;
+        int plen = this.params.length();
+
+        if (plen == 1) {
+          if (!domains[0].member(args)) {
+            Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+            ",\nthe first argument is:\n" + Values.ppr(args.toString()) +
+            "\nwhich is not in its domain.\n");
+          }
+          if (isTuples[0]) {
+            FormalParamNode[] ids = formals[0];
+            TupleValue argVal = (TupleValue) args.toTuple();
+            if (argVal == null) {
+              Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+              ",\nthe first argument is:\n" + Values.ppr(args.toString()) +
+              "\nwhich does not match its formal parameter.\n");
+            }
+            if (argVal.size() != ids.length) return null;
+            Value [] elems = argVal.elems;
+            for (int i = 0; i < ids.length; i++) {
+              c1 = c1.cons(ids[i], elems[i]);
+            }
+          }
+          else {
+            c1 = c1.cons(formals[0][0], args);
+          }
+        }
+        else {
+          TupleValue tv = (TupleValue) args.toTuple();
+          if (tv == null) {
+            Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                  ",\nthe argument list is:\n" + Values.ppr(args.toString()) +
+                  "\nwhich does not match its formal parameter.\n");
+          }
+          Value[] elems = tv.elems;
+          int argn = 0;
+          for (int i = 0; i < formals.length; i++) {
+            FormalParamNode[] ids = formals[i];
+            Value  domain = domains[i];
+            if (isTuples[i]) {
+              if (!domain.member(elems[argn])) {
+                Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                ",\nthe argument number " + (argn+1) + " is:\n" +
+                Values.ppr(elems[argn].toString()) +
+                "\nwhich is not in its domain.\n");
+              }
+              TupleValue tv1 = (TupleValue) elems[argn++].toTuple();
+              if (tv1 == null || tv1.size() != ids.length) {
+                Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                ",\nthe argument number " + argn + " is:\n" +
+                Values.ppr(elems[argn-1].toString()) +
+                "\nwhich does not match its formal parameter.\n");
+              }
+              Value [] avals = tv1.elems;
+              for (int j = 0; j < ids.length; j++) {
+                c1 = c1.cons(ids[j], avals[j]);
+              }
+            }
+            else {
+              for (int j = 0; j < ids.length; j++) {
+                if (!domain.member(elems[argn])) {
+                  Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                        ",\nthe argument number " + (argn+1) + " is:\n" +
+                        Values.ppr(elems[argn].toString()) + "\nwhich is not in its domain.\n");
+                }
+                c1 = c1.cons(ids[j], elems[argn++]);
+              }
+            }
+          }
+        }
+        res = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, control);
+      }
+
+      // Finally, apply the matching excepts on the result.
+      if (num == 0) return res;
+      ValueExcept[] excepts2 = new ValueExcept[num];
+      for (int i = 0; i < num; i++) {
+        excepts2[num-1-i] = excepts1[i];
+      }
+      return res.takeExcept(excepts2);
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This one does not seem to be needed anymore.  */
+  @Override
+  public final Value apply(Value[] args, int control) throws EvalException {
+    try {
+      return this.apply(new TupleValue(args), control);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value select(Value arg) {
+    try {
+
+      if (this.fcnRcd != null) {
+        return this.fcnRcd.select(arg);
+      }
+
+      // First, find all the excepts that match arg.
+      Value res = null;
+      int num = 0;
+      ValueExcept[] excepts1 = null;
+      if (this.excepts != null) {
+        int exlen = this.excepts.length;
+        for (int i = exlen-1; i >= 0; i--) {
+          ValueExcept ex = this.excepts[i];
+          Value exArg = ex.current();
+          boolean inExcept = exArg.equals(arg);
+          if (inExcept) {
+            if (ex.isLast()) { res = ex.value; break; }
+            if (excepts1 == null) excepts1 = new ValueExcept[exlen];
+            excepts1[num++] = new ValueExcept(ex, ex.idx+1);
+          }
+        }
+      }
+
+      // Second, evaluate the function application.
+      if (res == null) {
+        Context c1 = this.con;
+        FormalParamNode[][] formals = this.params.formals;
+        Value[] domains = this.params.domains;
+        boolean[] isTuples = this.params.isTuples;
+        int plen = this.params.length();
+
+        if (plen == 1) {
+          if (!domains[0].member(arg)) return null;
+          if (isTuples[0]) {
+            FormalParamNode[] ids = formals[0];
+            TupleValue argVal = (TupleValue) arg.toTuple();
+            /*
+             * SZA: Changed from argVal.toString() to arg.toString() to prevent a NullPointerException
+             */
+            if (argVal == null) {
+              Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+              ",\nthe first argument is:\n" + Values.ppr(arg.toString()) +
+              "\nwhich does not match its formal parameter.\n");
+            }
+            if (argVal.size() != ids.length) return null;
+            Value [] elems = argVal.elems;
+            for (int i = 0; i < ids.length; i++) {
+              c1 = c1.cons(ids[i], elems[i]);
+            }
+          }
+          else {
+            c1 = c1.cons(formals[0][0], arg);
+          }
+        }
+        else {
+          TupleValue tv = (TupleValue) arg.toTuple();
+          if (tv == null) {
+            Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                  ",\nthe argument list is:\n" + Values.ppr(arg.toString()) +
+                  "\nwhich does not match its formal parameter.\n");
+          }
+          Value[] elems = tv.elems;
+          int argn = 0;
+          for (int i = 0; i < formals.length; i++) {
+            FormalParamNode[] ids = formals[i];
+            Value domain = domains[i];
+            if (isTuples[i]) {
+              if (!domain.member(elems[argn])) return null;
+              TupleValue tv1 = (TupleValue) elems[argn++].toTuple();
+              if (tv1 == null) {
+                Assert.fail("In applying the function\n" + Values.ppr(this.toString()) +
+                ",\nthe argument number " + argn + " is:\n" +
+                Values.ppr(elems[argn-1].toString()) +
+                "\nwhich does not match its formal parameter.\n");
+              }
+              if (tv1.size() != ids.length) return null;
+              Value [] avals = tv1.elems;
+              for (int j = 0; j < ids.length; j++) {
+                c1 = c1.cons(ids[j], avals[j]);
+              }
+            }
+            else {
+              for (int j = 0; j < ids.length; j++) {
+                if (!domain.member(elems[argn])) return null;
+                c1 = c1.cons(ids[j], elems[argn++]);
+              }
+            }
+          }
+        }
+        res = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
+      }
+
+      // Finally, apply the matching excepts on the result.
+      if (num == 0) return res;
+      ValueExcept[] excepts2 = new ValueExcept[num];
+      for (int i = 0; i < num; i++) {
+        excepts2[num-1-i] = excepts1[i];
+      }
+      return res.takeExcept(excepts2);
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This method returns a new function value by taking except. */
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+
+      if (ex.idx >= ex.path.length) return ex.value;
+
+      if (this.fcnRcd != null) {
+        return this.fcnRcd.takeExcept(ex);
+      }
+      FcnLambdaValue fcn = new FcnLambdaValue(this);
+      if (this.excepts == null) {
+        fcn.excepts = new ValueExcept[1];
+        fcn.excepts[0] = ex;
+      }
+      else {
+        int exlen = this.excepts.length;
+        fcn.excepts = new ValueExcept[exlen+1];
+        for (int i = 0; i < exlen; i++) {
+          fcn.excepts[i] = this.excepts[i];
+        }
+        fcn.excepts[exlen] = ex;
+      }
+      return fcn;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This method returns a new function value by taking excepts. */
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+
+      if (this.fcnRcd != null) {
+        return this.fcnRcd.takeExcept(exs);
+      }
+      FcnLambdaValue fcn = new FcnLambdaValue(this);
+      int exslen = exs.length;
+      if (exslen != 0) {
+        int i = 0;
+        for (i = exs.length-1; i >= 0; i--) {
+          if (exs[i].idx >= exs[i].path.length) break;
+        }
+        if (i >= 0) {
+          int xlen = exslen-i-1;
+          fcn.excepts = new ValueExcept[xlen];
+          System.arraycopy(exs, i+1, fcn.excepts, 0, xlen);
+        }
+        else if (this.excepts == null) {
+          fcn.excepts = new ValueExcept[exslen];
+          System.arraycopy(exs, 0, fcn.excepts, 0, exslen);
+        }
+        else {
+          int len = this.excepts.length;
+          fcn.excepts = new ValueExcept[len + exslen];
+          System.arraycopy(this.excepts, 0, fcn.excepts, 0, len);
+          System.arraycopy(exs, 0, fcn.excepts, len, exslen);
+        }
+      }
+      return fcn;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+
+      if (this.fcnRcd != null) {
+        return this.fcnRcd.getDomain();
+      }
+      int len = this.params.length();
+      if (len == 1) {
+        return this.params.domains[0];
+      }
+      Value [] sets = new Value [len];
+      int dlen = this.params.domains.length;
+      boolean[] isTuples = this.params.isTuples;
+      int idx = 0;
+      for (int i = 0; i < dlen; i++) {
+        FormalParamNode[] formal = this.params.formals[i];
+        Value  domain = this.params.domains[i];
+        if (isTuples[i]) {
+          sets[idx++] = domain;
+        }
+        else {
+          for (int j = 0; j < formal.length; j++) {
+            sets[idx++] = domain;
+          }
+        }
+      }
+      return new SetOfTuplesValue(sets);
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      if (this.fcnRcd == null) {
+        return this.params.size();
+      }
+      return this.fcnRcd.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() {
+    try {
+      FcnLambdaValue fcn = new FcnLambdaValue(this);
+      // A bug occured when printing a function whose domain is a Cartesian product because this.fcnRcd 
+      // is null at this point.  On 5 Mar 2012, LL wrapped the following null test around the assignment.
+      if (this.fcnRcd != null) {
+        fcn.fcnRcd = (FcnRcdValue)this.fcnRcd.deepCopy();
+      }
+      return fcn;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return (val instanceof FcnLambdaValue);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+    this.fcnRcd = (FcnRcdValue)ois.readObject();
+  }
+
+  private void writeObject(ObjectOutputStream oos) throws IOException {
+    FcnRcdValue res = (FcnRcdValue) this.toFcnRcd();
+    oos.writeObject(res);
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.fcnRcd == null) {
+        return false;
+      }
+      return this.fcnRcd.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.fcnRcd != null) {
+        this.fcnRcd.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+      if (fcnRcd == null) {
+        if (excepts != null) {
+          for (int i = 0; i < excepts.length; i++) {
+            excepts[i].value.deepNormalize();
+            for (int j = 0; j < excepts[i].path.length; j++) {
+        excepts[i].path[j].deepNormalize();
+            }
+          }
+        }
+        IValue[] paramDoms = params.domains;
+        for (int i = 0; i < paramDoms.length; i++) {
+          paramDoms[i].deepNormalize();
+        }
+      }
+      else {
+        fcnRcd.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final Value toTuple() {
+      if (this.params.length() != 1) return null;
+      Value  dom = this.params.domains[0];
+      SymbolNode var = this.params.formals[0][0];
+      if (dom instanceof IntervalValue) {
+        IntervalValue intv = (IntervalValue)dom;
+        if (intv.low != 1) return null;
+        Value [] elems = new Value [intv.high];
+        for (int i = 1; i <= intv.high; i++) {
+          Context c1 = this.con.cons(var, IntValue.gen(i));
+          elems[i-1] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
+        }
+        if (coverage) {cm.incSecondary(elems.length);}
+        return new TupleValue(elems, cm);
+      }
+      else {
+        SetEnumValue eSet = (SetEnumValue) dom.toSetEnum();
+        if (eSet == null)
+          Assert.fail("To convert a function of form [x \\in S |-> f(x)] " +
+                "to a tuple, the set S must be enumerable.");
+        eSet.normalize();
+        int len = eSet.size();
+        Value [] elems = new Value [len];
+        for (int i = 0; i < len; i++) {
+          Value  argVal = eSet.elems.elementAt(i);
+          if (!(argVal instanceof IntValue)) return null;
+          if (((IntValue)argVal).val != i + 1) return null;
+          Context c1 = this.con.cons(var, argVal);
+          elems[i] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
+        }
+        cm.incSecondary(elems.length);
+        return new TupleValue(elems, cm);
+      }
+  }
+
+  @Override
+  public final Value toRcd() {
+      FcnRcdValue fcn = (FcnRcdValue) this.toFcnRcd();
+      if (fcn == null || fcn.domain == null) { return null; }
+      fcn.normalize();
+      UniqueString[] vars = new UniqueString[fcn.domain.length];
+      for (int i = 0; i < fcn.domain.length; i++) {
+        if (!(fcn.domain[i] instanceof StringValue)) {
+          return null;
+        }
+        vars[i] = ((StringValue)fcn.domain[i]).getVal();
+      }
+      if (coverage) {cm.incSecondary(vars.length);}
+      return new RecordValue(vars, fcn.values, fcn.isNormalized(), cm);
+  }
+
+  @Override
+  public final Value toFcnRcd() {
+    try {
+
+      if (this.fcnRcd == null) {
+        int sz = this.params.size();
+        FormalParamNode[][] formals = this.params.formals;
+        boolean[] isTuples = this.params.isTuples;
+
+        Value [] domain = new Value [sz];
+        Value [] values = new Value [sz];
+        int idx = 0;
+        ValueEnumeration Enum = this.params.elements();
+        Value  arg;
+        if (this.params.length() == 1) {
+          while ((arg = Enum.nextElement()) != null) {
+            domain[idx] = arg;
+            Context c1 = this.con;
+            if (isTuples[0]) {
+              FormalParamNode[] ids = formals[0];
+              Value [] avals = ((TupleValue)arg).elems;
+              for (int j = 0; j < ids.length; j++) {
+                c1 = c1.cons(ids[j], avals[j]);
+              }
+            }
+            else {
+              c1 = c1.cons(formals[0][0], arg);
+            }
+            values[idx++] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
+          }
+        }
+        else {
+          while ((arg = Enum.nextElement()) != null) {
+            domain[idx] = arg;
+            Value [] argList = ((TupleValue)arg).elems;
+            int argn = 0;
+            Context c1 = this.con;
+            for (int i = 0; i < formals.length; i++) {
+              FormalParamNode[] ids = formals[i];
+              if (isTuples[i]) {
+                Value [] avals = ((TupleValue)argList[argn++]).elems;
+                for (int j = 0; j < ids.length; j++) {
+                  c1 = c1.cons(ids[j], avals[j]);
+                }
+              }
+              else {
+                for (int j = 0; j < ids.length; j++) {
+                  c1 = c1.cons(ids[j], argList[argn++]);
+                }
+              }
+            }
+            values[idx++] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control);
+          }
+        }
+        if (coverage) {cm.incSecondary(sz);}
+        this.fcnRcd = new FcnRcdValue(domain, values, false, cm);
+        if (this.excepts != null) {
+          this.fcnRcd = (FcnRcdValue)fcnRcd.takeExcept(this.excepts);
+        }
+      }
+      return this.fcnRcd;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(final IValueOutputStream vos) throws IOException {
+		fcnRcd.write(vos);
+	}
+  
+  /* The fingerprint methods.  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      Value  fcn = this.toFcnRcd();
+      return fcn.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      Value  fcn = this.toFcnRcd();
+      return fcn.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation of this function.  */
+  public final StringBuffer toString(StringBuffer sb, int offset) {
+	return toString(sb, offset, true);
+}
+
+/* The string representation of this function.  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      if (TLCGlobals.expand || this.params == null) {
+        try {
+          Value  val = this.toFcnRcd();
+          return val.toString(sb, offset, true);
+        }
+        catch (Throwable e) { /*SKIP*/ }
+      }
+      sb.append("[" + this.params.toString());
+      sb.append(" |-> <expression " + this.body + ">]");
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final SemanticNode getBody() {
+		return body;
+	}
+	
+	@Override
+	public final FcnRcdValue getRcd() {
+		return fcnRcd;
+	}
+
+	@Override
+	public FcnParams getParams() {
+		return params;
+	}
+
+	@Override
+	public Context getCon() {
+		return con;
+	}
+
+	@Override
+	public boolean hasRcd() {
+		return fcnRcd != null;
+	}
+}
diff --git a/tlatools/src/tlc2/value/FcnParams.java b/tlatools/src/tlc2/value/impl/FcnParams.java
similarity index 92%
rename from tlatools/src/tlc2/value/FcnParams.java
rename to tlatools/src/tlc2/value/impl/FcnParams.java
index ab089e771116fd7164dc4ca53afdbeb68d27293f..d91b0d953573e5d7c51243742dfc0f11d7262488 100644
--- a/tlatools/src/tlc2/value/FcnParams.java
+++ b/tlatools/src/tlc2/value/impl/FcnParams.java
@@ -3,13 +3,15 @@
 // Last modified on Wed  4 Jul 2007 at 17:26:45 PST by lamport
 //      modified on Sat Nov 13 11:14:54 PST 1999 by yuanyu
 
-package tlc2.value;
+package tlc2.value.impl;
 
 import tla2sany.semantic.FormalParamNode;
 import tlc2.output.EC;
+import tlc2.value.IFcnParams;
+import tlc2.value.IValue;
 import util.Assert;
 
-public class FcnParams {
+public class FcnParams implements IFcnParams {
   public FormalParamNode[][] formals;  // array of formal params
   public boolean[] isTuples;      // true iff tuple param
   public Value[] domains;         // the bounds of the formals
@@ -25,6 +27,7 @@ public class FcnParams {
     }
   }
 
+  @Override
   public final int length() { return this.argLen; }
   
   public final int size() {
@@ -134,6 +137,7 @@ public class FcnParams {
       }
     }
 
+    @Override
     public final void reset() {
       if (this.enums != null) {
 	for (int i = 0; i < this.enums.length; i++) {
@@ -143,6 +147,7 @@ public class FcnParams {
       }
     }
 
+    @Override
     public final Value nextElement() {
       if (this.enums == null) return null;
       Value[] elems = new Value[argLen];
@@ -164,5 +169,19 @@ public class FcnParams {
     }
         
   }
-  
+
+	@Override
+	public FormalParamNode[][] getFormals() {
+		return formals;
+	}
+
+	@Override
+	public IValue[] getDomains() {
+		return domains;
+	}
+
+	@Override
+	public boolean[] isTuples() {
+		return isTuples;
+	}
 }
diff --git a/tlatools/src/tlc2/value/impl/FcnRcdValue.java b/tlatools/src/tlc2/value/impl/FcnRcdValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..95e8619d9018f70ada980df15339c30ddfe1fe54
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/FcnRcdValue.java
@@ -0,0 +1,900 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:07:06 PST by lamport
+//      modified on Fri Aug 10 15:07:25 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.util.Map;
+
+import tlc2.tool.EvalControl;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.FP64;
+import tlc2.value.IFcnRcdValue;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.ValueInputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.TLAConstants;
+import util.UniqueString;
+
+public class FcnRcdValue extends Value implements Applicable, IFcnRcdValue {
+  public final Value[] domain;
+  public final IntervalValue intv;
+  public final Value[] values;
+  private boolean isNorm;
+  public static final Value EmptyFcn = new FcnRcdValue(new Value[0], new Value[0], true);
+
+  /* Constructor */
+  public FcnRcdValue(Value[] domain, Value[] values, boolean isNorm) {
+    this.domain = domain;
+    this.values = values;
+    this.intv = null;
+    this.isNorm = isNorm;
+  }
+
+  public FcnRcdValue(IntervalValue intv, Value[] values) {
+    this.intv = intv;
+    this.values = values;
+    this.domain = null;
+    this.isNorm = true;
+  }
+
+  public FcnRcdValue(IntervalValue intv, Value[] values, CostModel cm) {
+	  this(intv, values);
+	  this.cm = cm;
+  }
+
+  private FcnRcdValue(FcnRcdValue fcn, Value[] values) {
+    this.domain = fcn.domain;
+    this.intv = fcn.intv;
+    this.values = values;
+    this.isNorm = fcn.isNorm;
+  }
+
+  public FcnRcdValue(ValueVec elems, Value[] values, boolean isNorm) {
+	  this(elems.toArray(), values, isNorm);
+  }
+
+  public FcnRcdValue(ValueVec elems, Value[] values, boolean isNorm, CostModel cm) {
+	  this(elems, values, isNorm);
+	  this.cm = cm;
+  }
+
+  public FcnRcdValue(Value[] domain, Value[] values, boolean isNorm, CostModel cm) {
+	  this(domain, values, isNorm);
+	  this.cm = cm;
+  }
+
+  @Override
+  public final byte getKind() { return FCNRCDVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+
+			final FcnRcdValue fcn = obj instanceof Value ? (FcnRcdValue) ((Value) obj).toFcnRcd() : null;
+			if (fcn == null) {
+				if (obj instanceof ModelValue)
+					return 1;
+				Assert.fail("Attempted to compare the function " + Values.ppr(this.toString()) + " with the value:\n"
+						+ Values.ppr(obj.toString()));
+			}
+			this.normalize();
+			fcn.normalize();
+
+			final int result = this.values.length - fcn.values.length;
+			if (result != 0) {
+				return result;
+			}
+
+			if (this.intv != null) {
+				return compareToInterval(fcn);
+			} else {
+				return compareOtherInterval(fcn);
+			}
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final int compareOtherInterval(final FcnRcdValue fcn) {
+	int result;
+	if (fcn.intv != null) {
+		for (int i = 0; i < this.domain.length; i++) {
+			final Value dElem = this.domain[i];
+			if (!(dElem instanceof IntValue)) {
+				Assert.fail(
+						"Attempted to compare integer with non-integer\n" + Values.ppr(dElem.toString()) + ".");
+			}
+			result = ((IntValue) dElem).val - (fcn.intv.low + i);
+			if (result != 0) {
+				return result;
+			}
+			result = this.values[i].compareTo(fcn.values[i]);
+			if (result != 0) {
+				return result;
+			}
+		}
+	} else {
+		for (int i = 0; i < this.domain.length; i++) {
+			result = this.domain[i].compareTo(fcn.domain[i]);
+			if (result != 0) {
+				return result;
+			}
+			result = this.values[i].compareTo(fcn.values[i]);
+			if (result != 0) {
+				return result;
+			}
+		}
+	}
+	return 0;
+  }
+
+  private final int compareToInterval(final FcnRcdValue fcn) {
+  	int result;
+  	if (fcn.intv != null) {
+  		result = this.intv.low - fcn.intv.low;
+  		if (result != 0) {
+  			return result;
+  		}
+  		for (int i = 0; i < this.values.length; i++) {
+  			result = this.values[i].compareTo(fcn.values[i]);
+  			if (result != 0) {
+  				return result;
+  			}
+  		}
+  	} else {
+  		for (int i = 0; i < fcn.domain.length; i++) {
+  			final Value dElem = fcn.domain[i];
+  			if (!(dElem instanceof IntValue)) {
+  				Assert.fail(
+  						"Attempted to compare integer with non-integer:\n" + Values.ppr(dElem.toString()) + ".");
+  			}
+  			result = this.intv.low + i - ((IntValue) dElem).val;
+  			if (result != 0) {
+  				return result;
+  			}
+  			result = this.values[i].compareTo(fcn.values[i]);
+  			if (result != 0) {
+  				return result;
+  			}
+  		}
+  	}
+  	return 0;
+  }
+  
+  public final boolean equals(Object obj) {
+    try {
+
+      FcnRcdValue fcn = obj instanceof Value ? (FcnRcdValue) ((Value)obj).toFcnRcd() : null;
+      if (fcn == null) {
+        if (obj instanceof ModelValue)
+           return ((ModelValue) obj).modelValueEquals(this) ;
+        Assert.fail("Attempted to check equality of the function " + Values.ppr(this.toString()) +
+        " with the value:\n" + Values.ppr(obj.toString()));
+      }
+      this.normalize();
+      fcn.normalize();
+
+      if (this.intv != null) {
+        if (fcn.intv != null) {
+          if (!this.intv.equals(fcn.intv)) return false;
+          for (int i = 0; i < this.values.length; i++) {
+            if (!this.values[i].equals(fcn.values[i]))
+              return false;
+          }
+        }
+        else {
+          if (fcn.domain.length != this.intv.size()) return false;
+          for (int i = 0; i < fcn.domain.length; i++) {
+            Value dElem = fcn.domain[i];
+            if (!(dElem instanceof IntValue)) {
+              Assert.fail("Attempted to compare an integer with non-integer:\n" +
+              Values.ppr(dElem.toString()) + ".");
+            }
+            if (((IntValue)dElem).val != (this.intv.low + i) ||
+                !this.values[i].equals(fcn.values[i])) {
+              return false;
+            }
+          }
+        }
+      }
+      else {
+        if (this.values.length != fcn.values.length) return false;
+        if (fcn.intv != null) {
+          for (int i = 0; i < this.domain.length; i++) {
+            Value dElem = this.domain[i];
+            if (!(dElem instanceof IntValue)) {
+              Assert.fail("Attempted to compare an integer with non-integer:\n" +
+              Values.ppr(dElem.toString()) + ".");
+            }
+            if (((IntValue)dElem).val != (fcn.intv.low + i) ||
+                !this.values[i].equals(fcn.values[i])) {
+              return false;
+            }
+          }
+        }
+        else {
+          for (int i = 0; i < this.domain.length; i++) {
+            if (!this.domain[i].equals(fcn.domain[i]) ||
+                !this.values[i].equals(fcn.values[i])) {
+              return false;
+            }
+          }
+        }
+      }
+      return true;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of the function " + Values.ppr(this.toString()));
+      return false;  // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() { return true; }
+
+  @Override
+  public final Value apply(Value arg, int control) {
+    try {
+    	Value result = this.select(arg);
+      if (result == null) {
+        Assert.fail("Attempted to apply function:\n" + Values.ppr(this.toString()) +
+        "\nto argument " + Values.ppr(arg.toString()) + ", which is" +
+        " not in the domain of the function.");
+      }
+      return result;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This one does not seem to be needed anymore.  */
+  @Override
+  public final Value apply(Value[] args, int control) {
+    try {
+      return this.apply(new TupleValue(args), EvalControl.Clear);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value select(Value arg) {
+    try {
+
+      if (this.intv != null) {
+        // domain is represented as an integer interval:
+        if (!(arg instanceof IntValue)) {
+          Assert.fail("Attempted to apply function with integer domain to" +
+                " the non-integer argument " + Values.ppr(arg.toString()));
+        }
+        int idx = ((IntValue)arg).val;
+        if ((idx >= this.intv.low) && (idx <= this.intv.high)) {
+          return this.values[idx - this.intv.low];
+        }
+      }
+      else {
+        // domain is represented as an array of values:
+          int len = this.domain.length;
+          for (int i = 0; i < len; i++) {
+            if (this.domain[i].equals(arg)) {
+              return this.values[i];
+            }
+          }
+      }
+      return null;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean assign(Value[] args, Value val) {
+    try {
+
+      if (this.intv != null) {
+        // domain is represented as an integer interval:
+        if (args.length != 1) {
+          Assert.fail("Wrong number of function arguments.");
+        }
+        if (args[0] instanceof IntValue) {
+          int idx = ((IntValue)args[0]).val;
+          if ((idx >= this.intv.low) && (idx <= this.intv.high)) {
+            int vIdx = idx - this.intv.low;
+            if (this.values[vIdx] == UndefValue.ValUndef ||
+                this.values[vIdx].equals(val)) {
+              this.values[vIdx] = val;
+              return true;
+            }
+            return false;
+          }
+        }
+      }
+      else {
+        // domain is represented as an array of values:
+        Value argv = new TupleValue(args);
+        int len = this.domain.length;
+        for (int i = 0; i < len; i++) {
+          if (this.domain[i].equals(argv)) {
+            if (this.values[i] == UndefValue.ValUndef ||
+                this.values[i].equals(val)) {
+              this.values[i] = val;
+              return true;
+            }
+            return false;
+          }
+        }
+      }
+      Assert.fail("Function initialization out of domain.");
+      return false;    // make compiler happy
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+
+      if (ex.idx >= ex.path.length) return ex.value;
+
+      int flen = this.values.length;
+      Value[] newValues = new Value[flen];
+      for (int i = 0; i < flen; i++) {
+        newValues[i] = this.values[i];
+      }
+      Value arg = ex.path[ex.idx];
+
+      if (this.intv != null) {
+        // domain is represented as an integer interval:
+        if (arg instanceof IntValue) {
+          int idx = ((IntValue)arg).val;
+          if ((idx >= this.intv.low) && (idx <= this.intv.high)) {
+            int vidx = idx - this.intv.low;
+            ex.idx++;
+            newValues[idx] = this.values[vidx].takeExcept(ex);
+          }
+          return new FcnRcdValue(this.intv, newValues);
+        }
+      }
+      else {
+        // domain is represented as an array of values:
+        for (int i = 0; i < flen; i++) {
+          if (arg.equals(this.domain[i])) {
+            ex.idx++;
+            newValues[i] = newValues[i].takeExcept(ex);
+            Value[] newDomain = this.domain;
+            if (!this.isNorm) {
+              newDomain = new Value[flen];
+              for (int j = 0; j < flen; j++) {
+                newDomain[j] = this.domain[j];
+              }
+            }
+            return new FcnRcdValue(newDomain, newValues, this.isNorm);
+          }
+        }
+      }
+      return this;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Value res = this;
+      for (int i = 0; i < exs.length; i++) {
+        res = res.takeExcept(exs[i]);
+      }
+      return res;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+      if (this.intv != null) {
+        return this.intv;
+      }
+      this.normalize();
+      return new SetEnumValue(this.domain, true);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /**
+   * Returns the domain of this FunctionRecordValue regardless of its internal
+   * representation as either Value[] or IntervalValue as Value[].
+   */
+  public final Value[] getDomainAsValues() {
+	  if (this.intv != null) {
+		  return this.intv.asValues();
+	  } else {
+          return this.domain;		  
+	  }
+  }
+  
+  @Override
+  public final int size() {
+    try {
+      this.normalize();
+      return this.values.length;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+  
+  /**
+   * {@link #size()} first normalizes, destructively, this instance; for inspections on the size without normalization
+   * 	use this method.
+   * 
+   * @return
+   */
+  public int nonNormalizedSize() {
+	  return values.length;
+  }
+
+  @Override
+  public final Value toTuple() {
+      if (this.intv != null) {
+        if (this.intv.low != 1) return null;
+        return new TupleValue(this.values);
+      }
+      int len = this.values.length;
+      Value[] elems = new Value[len];
+      for (int i = 0; i < len; i++) {
+        if (!(this.domain[i] instanceof IntValue)) return null;
+        int idx = ((IntValue)this.domain[i]).val;
+        if (0 < idx && idx <= len) {
+          if (elems[idx-1] != null) return null;
+          elems[idx-1] = this.values[i];
+        }
+        else {
+          return null;
+        }
+      }
+      if (coverage) {cm.incSecondary(elems.length);}
+      return new TupleValue(elems, cm);
+  }
+
+  @Override
+  public final Value toRcd() {
+      if (this.domain == null) return null;
+      this.normalize();
+      UniqueString[] vars = new UniqueString[this.domain.length];
+      for (int i = 0; i < this.domain.length; i++) {
+        if (!(this.domain[i] instanceof StringValue)) {
+          return null;
+        }
+        vars[i] = ((StringValue)this.domain[i]).getVal();
+      }
+      if (coverage) {cm.incSecondary(this.values.length);}
+      return new RecordValue(vars, this.values, this.isNormalized(), cm);
+  }
+
+  	@Override
+	public final Value toFcnRcd() {
+		return this;
+	}
+  
+  /* Return true iff this function is in normal form. */
+  @Override
+  public final boolean isNormalized() { return this.isNorm; }
+
+  /* This method normalizes (destructively) this function. */
+  @Override
+  public final Value normalize() {
+    try {
+
+      if (!this.isNorm) {
+        // Assert.check(this.domain != null)
+        int dlen = this.domain.length;
+        for (int i = 1; i < dlen; i++) {
+          int cmp = this.domain[0].compareTo(this.domain[i]);
+          if (cmp == 0) {
+            Assert.fail("The value\n" + this.domain[i] +
+                  "\noccurs multiple times in the function domain.");
+          }
+          else if (cmp > 0) {
+        	  Value tv = this.domain[0];
+            this.domain[0] = this.domain[i];
+            this.domain[i] = tv;
+            tv = this.values[0];
+            this.values[0] = this.values[i];
+            this.values[i] = tv;
+          }
+        }
+        for (int i = 2; i < dlen; i++) {
+        	Value d = this.domain[i];
+        	Value v = this.values[i];
+          int j = i;
+          int cmp;
+          while ((cmp = d.compareTo(this.domain[j-1])) < 0) {
+            this.domain[j] = this.domain[j-1];
+            this.values[j] = this.values[j-1];
+            j--;
+          }
+          if (cmp == 0) {
+            Assert.fail("The value\n" + this.domain[i] +
+                  "\noccurs multiple times in the function domain.");
+          }
+          this.domain[j] = d;
+          this.values[j] = v;
+        }
+        this.isNorm = true;
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	  try {
+      for (int i = 0; i < values.length; i++) {
+           values[i].deepNormalize();
+        }
+        normalize();
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+
+      boolean defined = true;
+      if (this.intv == null) {
+        for (int i = 0; i < this.values.length; i++) {
+          defined = defined && this.domain[i].isDefined();
+        }
+      }
+      for (int i = 0; i < this.values.length; i++) {
+        defined = defined && this.values[i].isDefined();
+      }
+      return defined;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() {
+    try {
+    	Value[] vals = new Value[this.values.length];
+      for (int i = 0; i < vals.length; i++) {
+        vals[i] = (Value) this.values[i].deepCopy();
+      }
+      return new FcnRcdValue(this, vals);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      boolean canAssign = ((val instanceof FcnRcdValue) &&
+        this.values.length == ((FcnRcdValue)val).values.length);
+      if (!canAssign) return false;
+      FcnRcdValue fcn = (FcnRcdValue)val;
+      for (int i = 0; i < this.values.length; i++) {
+        canAssign = (canAssign &&
+         this.domain[i].equals(fcn.domain[i]) &&
+         this.values[i].assignable(fcn.values[i]));
+      }
+      return canAssign;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(final IValueOutputStream vos) throws IOException {
+		final int index = vos.put(this);
+		if (index == -1) {
+			vos.writeByte(FCNRCDVALUE);
+			int len = values.length;
+			vos.writeNat(len);
+			if (intv != null) {
+				vos.writeByte((byte) 0);
+				vos.writeInt(intv.low);
+				vos.writeInt(intv.high);
+				for (int i = 0; i < len; i++) {
+					values[i].write(vos);
+				}
+			} else {
+				vos.writeByte((isNormalized()) ? (byte) 1 : (byte) 2);
+				for (int i = 0; i < len; i++) {
+					domain[i].write(vos);
+					values[i].write(vos);
+				}
+			}
+		} else {
+			vos.writeByte(DUMMYVALUE);
+			vos.writeNat(index);
+		}
+	}
+
+  /* The fingerprint method.  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.normalize();
+      int flen = this.values.length;
+      fp = FP64.Extend(fp, FCNRCDVALUE);
+      fp = FP64.Extend(fp, flen);
+      if (this.intv == null) {
+        for (int i = 0; i < flen; i++) {
+          fp = this.domain[i].fingerPrint(fp);
+          fp = this.values[i].fingerPrint(fp);
+        }
+      }
+      else {
+        for (int i = 0; i < flen; i++) {
+          fp = FP64.Extend(fp, INTVALUE);
+          fp = FP64.Extend(fp, i + this.intv.low);
+          fp = this.values[i].fingerPrint(fp);
+        }
+      }
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+
+      this.normalize();
+      int flen = this.domain.length;
+      Value[] vals = new Value[flen];
+
+      boolean vchanged = false;
+      for (int i = 0; i < flen; i++) {
+        vals[i] = (Value) this.values[i].permute(perm);
+        vchanged = vchanged || (vals[i] != this.values[i]);
+      }
+
+      if (this.intv == null) {
+    	  Value[] dom = new Value[flen];
+        boolean dchanged = false;
+        for (int i = 0; i < flen; i++) {
+          dom[i] = (Value) this.domain[i].permute(perm);
+          dchanged = dchanged || (dom[i] != this.domain[i]);
+        }
+
+        if (dchanged) {
+          return new FcnRcdValue(dom, vals, false);
+        }
+        else if (vchanged) {
+          return new FcnRcdValue(this.domain, vals, true);
+        }
+      }
+      else {
+        if (vchanged) {
+          return new FcnRcdValue(this.intv, vals);
+        }
+      }
+      return this;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private static final boolean isName(String name) {
+    int len = name.length();
+    boolean hasLetter = false;
+
+    for (int idx = 0; idx < len; idx++) {
+      char ch = name.charAt(idx);
+      if (ch == '_') continue;
+      if (!Character.isLetterOrDigit(ch)) return false;
+      hasLetter = hasLetter || Character.isLetter(ch);
+    }
+
+    return hasLetter && (len < 4 || (!name.startsWith("WF_") && !name.startsWith("SF_")));
+  }
+
+  private final boolean isRcd() {
+    if (this.intv != null) return false;
+    for (int i = 0; i < this.domain.length; i++) {
+      Value dval = this.domain[i];
+      boolean isName = ((dval instanceof StringValue) &&
+      isName(((StringValue)dval).val.toString()));
+      if (!isName) return false;
+    }
+    return true;
+  }
+
+  private final boolean isTuple() {
+    if (this.intv != null) {
+      return (this.intv.low == 1);
+    }
+    for (int i = 0; i < this.domain.length; i++) {
+      if (!(this.domain[i] instanceof IntValue)) {
+        return false;
+      }
+    }
+    this.normalize();
+    for (int i = 0; i < this.domain.length; i++) {
+      if (((IntValue)this.domain[i]).val != (i+1)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /* The string representation of the value.  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+
+      int len = this.values.length;
+      if (len == 0) {
+        sb.append("<< >>");
+      }
+      else if (this.isRcd()) {
+        sb.append("[");
+        sb.append(((StringValue)this.domain[0]).val + TLAConstants.RECORD_ARROW);
+        sb = this.values[0].toString(sb, offset, swallow);
+
+        for (int i = 1; i < len; i++) {
+          sb.append(", ");
+          sb.append(((StringValue)this.domain[i]).val + TLAConstants.RECORD_ARROW);
+          sb = this.values[i].toString(sb, offset, swallow);
+        }
+        sb.append("]");
+      }
+      else if (this.isTuple()) {
+        // It is actually a sequence:
+        sb = sb.append("<<");
+        sb = this.values[0].toString(sb, offset, swallow);
+
+        for (int i = 1; i < len; i++) {
+          sb.append(", ");
+          sb = this.values[i].toString(sb, offset, swallow);
+        }
+        sb.append(">>");
+      }
+      else {
+        sb = sb.append("(");
+        sb = this.domain[0].toString(sb, offset, swallow);
+        sb.append(" :> ");
+        sb = this.values[0].toString(sb, offset, swallow);
+
+        for (int i = 1; i < len; i++) {
+          sb.append(" @@ ");
+          sb = this.domain[i].toString(sb, offset, swallow);
+          sb.append(" :> ");
+          sb = this.values[i].toString(sb, offset, swallow);
+        }
+        sb.append(")");
+      }
+      return sb;
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	public static IValue createFrom(final IValueInputStream vos) throws IOException {
+		final int index = vos.getIndex();
+		final int len = vos.readNat();
+		final int info = vos.readByte();
+		Value res;
+		final Value[] rvals = new Value[len];
+		if (info == 0) {
+			final int low = vos.readInt();
+			final int high = vos.readInt();
+			for (int i = 0; i < len; i++) {
+				rvals[i] = (Value) vos.read();
+			}
+			final IntervalValue intv = new IntervalValue(low, high);
+			res = new FcnRcdValue(intv, rvals);
+		} else {
+			final Value[] dvals = new Value[len];
+			for (int i = 0; i < len; i++) {
+				dvals[i] = (Value) vos.read();
+				rvals[i] = (Value) vos.read();
+			}
+			res = new FcnRcdValue(dvals, rvals, (info == 1));
+		}
+		vos.assign(res, index);
+		return res;
+	}
+
+	public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException {
+		final int index = vos.getIndex();
+		final int len = vos.readNat();
+		final int info = vos.readByte();
+		Value res;
+		final Value[] rvals = new Value[len];
+		if (info == 0) {
+			final int low = vos.readInt();
+			final int high = vos.readInt();
+			for (int i = 0; i < len; i++) {
+				rvals[i] = (Value) vos.read(tbl);
+			}
+			final IntervalValue intv = new IntervalValue(low, high);
+			res = new FcnRcdValue(intv, rvals);
+		} else {
+			final Value[] dvals = new Value[len];
+			for (int i = 0; i < len; i++) {
+				dvals[i] = (Value) vos.read(tbl);
+				rvals[i] = (Value) vos.read(tbl);
+			}
+			res = new FcnRcdValue(dvals, rvals, (info == 1));
+		}
+		vos.assign(res, index);
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/IntValue.java b/tlatools/src/tlc2/value/impl/IntValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f6fc3212df40c69f3d1d2e5c0a2434ccca48a37
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/IntValue.java
@@ -0,0 +1,236 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:08:05 PST by lamport
+//      modified on Fri Aug 10 15:07:30 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.tool.FingerprintException;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class IntValue extends Value {
+  private static final IntValue[] cache;
+
+  static {
+    cache = new IntValue[10];
+    for (int i = 0; i < cache.length; i++) {
+      cache[i] = new IntValue(i);
+    }
+  }
+
+	public static final IntValue ValNegOne = gen(-1);
+	
+	public static final IntValue ValOne    = gen(1);
+	
+	public static final IntValue ValZero   = gen(0);
+
+  public static final int nbits(int tmp) {
+    int nb = 0;
+    while(tmp != 0 && tmp != -1) {
+      nb++;
+      tmp >>= 1;
+    }
+    return nb + 1;
+  }
+
+  public final int val;
+
+  private IntValue(int i) { this.val = i; }
+
+  @Override
+  public final byte getKind() { return INTVALUE; }
+
+  // the number of bits needed to encode the value of this int
+  public final int nbits() {
+    try {
+      return nbits(this.val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public static IntValue gen(int i) {
+    if (i >= 0 && i < cache.length) {
+      return cache[i];
+    }
+    return new IntValue(i);
+  }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (obj instanceof IntValue) {
+        return Integer.compare(this.val, ((IntValue)obj).val);
+      }
+      if (!(obj instanceof ModelValue)) {
+        Assert.fail("Attempted to compare integer " + Values.ppr(this.toString()) +
+        " with non-integer:\n" + Values.ppr(obj.toString()));
+      }
+      return 1;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof IntValue) {
+        return this.val == ((IntValue)obj).val;
+      }
+      if (!(obj instanceof ModelValue)) {
+        Assert.fail("Attempted to check equality of integer " + Values.ppr(this.toString()) +
+        " with non-integer:\n" + Values.ppr(obj.toString()));
+      }
+      return ((ModelValue) obj).modelValueEquals(this);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of the integer " + Values.ppr(this.toString()));
+      return false;  // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the integer " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to appy EXCEPT construct to the integer " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the integer " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the integer " +
+      Values.ppr(this.toString()) + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public boolean mutates() {
+	  // finalized after construction.
+	  return true;
+  }
+
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*nop*/return this; }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return ((val instanceof IntValue) &&
+        this.val == ((IntValue)val).val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public void write(IValueOutputStream vos) throws IOException {
+		vos.writeByte(INTVALUE);
+		vos.writeInt(val);
+	}
+
+  /* The fingerprint methods */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      return FP64.Extend(FP64.Extend(fp, INTVALUE), this.val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) { return this; }
+
+  /* The string representation. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) {
+    try {
+      return sb.append(this.val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/IntervalValue.java b/tlatools/src/tlc2/value/impl/IntervalValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6b47041fa28d3baa84b87fd5633c9cbeb083f34
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/IntervalValue.java
@@ -0,0 +1,383 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:12:59 PST by lamport
+//      modified on Fri Aug 10 15:07:36 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.tool.FingerprintException;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class IntervalValue extends EnumerableValue
+implements Enumerable, Reducible {
+  public int low, high;   // the integer interval [low, high]
+
+  /* Constructor */
+  public IntervalValue(int low, int high) {
+    this.low = low;
+    this.high = high;
+  }
+
+  @Override
+  public final byte getKind() { return INTERVALVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+        if (obj instanceof IntervalValue) {
+				IntervalValue intv = (IntervalValue) obj;
+		        int cmp = this.size() - intv.size();
+		        if (cmp != 0) {
+					return cmp;
+				}
+				if (this.size() == 0) {
+					// empty intervals are equal, regardless of the low value
+					return 0;
+				}
+                return Integer.compare(this.low, intv.low);
+			}
+      // Well, we have to convert them to sets and compare.
+      return this.toSetEnum().compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof IntervalValue) {
+        IntervalValue intv = (IntervalValue)obj;
+        if (this.size() == 0) return intv.size() == 0;
+        return (this.low == intv.low) && (this.high == intv.high);
+      }
+      // Well, we have to convert them to sets and compare.
+      return this.toSetEnum().equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      if (elem instanceof IntValue) {
+        int x = ((IntValue)elem).val;
+        return (x >= low) && (x <= high);
+      }
+      if (   (this.low <= this.high)
+           && (   !(elem instanceof ModelValue)
+               || (((ModelValue) elem).type != 0)) ) {
+        Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+        "\nis in the integer interval " + Values.ppr(this.toString()));
+      }
+      return false;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public Value isSubsetEq(Value other) {
+    try {
+      if (other instanceof IntervalValue) {
+        final IntervalValue iv = (IntervalValue) other;
+        if (iv.low <= low && iv.high >= high) {
+          return BoolValue.ValTrue;
+        }
+      }
+      return super.isSubsetEq(other);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() { return true; }
+
+  @Override
+  public final int size() {
+    try {
+		if (this.high < this.low) {
+			return 0;
+		}
+		try {
+			return Math.addExact(Math.subtractExact(this.high, this.low), 1);
+		} catch (ArithmeticException e) {
+			Assert.fail("Size of interval value exceeds the maximum representable size (32bits): "
+			      + Values.ppr(this.toString()) + ".");
+			return 0; // unreachable, but it satisfies the compiler
+		}
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	/**
+	 * @return Converts this IntervalValue instance into a Value[]. This can be seen
+	 *         as the inverse to the performance optimization that the IntervalValue
+	 *         actually is.
+	 */
+	final Value[] asValues() {
+		final Value[] values = new Value[size()];
+		for (int i = 0; i < size(); i++) {
+			values[i] = IntValue.gen(this.low + i);
+		}
+		return values;
+	}
+  
+  /* Return this - val.  */
+  @Override
+  public final Value diff(Value val) {
+    try {
+      ValueVec diffElems = new ValueVec();
+      for (int i = this.low; i <= this.high; i++) {
+    	  Value elem = IntValue.gen(i);
+        if (!val.member(elem)) diffElems.addElement(elem);
+      }
+      return new SetEnumValue(diffElems, true, cm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Return this \cap val. */
+  @Override
+  public final Value cap(Value val) {
+    try {
+      ValueVec capElems = new ValueVec();
+      for (int i = this.low; i <= this.high; i++) {
+    	  Value elem = IntValue.gen(i);
+        if (val.member(elem)) capElems.addElement(elem);
+      }
+      return new SetEnumValue(capElems, true, cm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Return this \cup val.  */
+  @Override
+  public final Value cup(Value set) {
+    try {
+      if (this.size() == 0) return set;
+
+      if (set instanceof Reducible) {
+        ValueVec cupElems = new ValueVec();
+        for (int i = this.low; i <= this.high; i++) {
+          cupElems.addElement(IntValue.gen(i));
+        }
+        ValueEnumeration Enum = ((Enumerable)set).elements();
+        Value elem;
+        while ((elem = Enum.nextElement()) != null) {
+          if (!this.member(elem)) cupElems.addElement(elem);
+        }
+        return new SetEnumValue(cupElems, false, cm);
+      }
+      return new SetCupValue(this, set, cm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT construct to the interval value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the interval value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*nop*/return this; }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return ((val instanceof IntervalValue) &&
+        this.high == ((IntervalValue)val).high &&
+        this.low == ((IntervalValue)val).low);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public void write(final IValueOutputStream vos) throws IOException {
+		vos.writeByte(INTERVALVALUE);
+		vos.writeInt(low);
+		vos.writeInt(high);
+	}
+
+  /* The fingerprint method */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      fp = FP64.Extend(fp, SETENUMVALUE);
+      fp = FP64.Extend(fp, this.size()) ;
+      for (int i = this.low; i <= this.high; i++) {
+        fp = FP64.Extend(fp, INTVALUE);
+        fp = FP64.Extend(fp, i);
+      }
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public boolean mutates() {
+	  return false;
+  }
+  
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    return this;
+  }
+
+  @Override
+  public Value toSetEnum() {
+	  Value[] vals = new Value[size()];
+      for (int i = 0; i < vals.length; i++) {
+        vals[i] = IntValue.gen(i + this.low);
+      }
+      if (coverage) {cm.incSecondary(vals.length);}
+      return new SetEnumValue(vals, true, cm);
+  }
+
+  /* The string representation */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, final boolean ignored) {
+    try {
+      if (this.low <= this.high) {
+        return sb.append(this.low).append("..").append(this.high);
+      }
+      return sb.append("{").append("}");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+    @Override
+	public EnumerableValue getRandomSubset(final int kOutOfN) {
+    	final ValueVec vec = new ValueVec(kOutOfN);
+    	
+    	final ValueEnumeration ve = elements(kOutOfN);
+    	
+    	Value v = null;
+    	while ((v = ve.nextElement()) != null) {
+    		vec.addElement(v);
+    	}
+    	return new SetEnumValue(vec, false, cm);
+	}
+
+	public Value elementAt(final int idx) {
+		if (0 <= idx && idx < size()) {
+			return IntValue.gen(low + idx);
+		}
+		Assert.fail(
+				"Attempted to retrieve out-of-bounds element from the interval value " + Values.ppr(this.toString()) + ".");
+        return null; // make compiler happy
+	}
+    
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      return new Enumerator();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    int index = low;
+
+    @Override
+    public final void reset() { this.index = low; }
+
+    @Override
+    public final Value nextElement() {
+      if (this.index <= high) {
+    	  if (coverage) { cm.incSecondary(); }
+        return IntValue.gen(this.index++);
+      }
+      return null;
+    }
+
+  }
+  
+	@Override
+	public ValueEnumeration elements(final int kOutOfN) {
+		return new EnumerableValue.SubsetEnumerator(kOutOfN) {
+			@Override
+			public Value nextElement() {
+				if (!hasNext()) {
+					return null;
+				}
+				return IntValue.gen(low + nextIndex());
+			}
+		};
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/LazyValue.java b/tlatools/src/tlc2/value/impl/LazyValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b1eba3978c9fecdc5ca4e5c9b064a343762c3
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/LazyValue.java
@@ -0,0 +1,295 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 15:30:08 PST by lamport
+//      modified on Thu Feb  8 21:23:55 PST 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import util.Assert;
+import util.ToolIO;
+
+public class LazyValue extends Value {
+	/**
+	 * Allow to completely disable LazyValue by passing a VM property/system
+	 * property to the Java VM with "-Dtlc2.value.LazyValue.off=true". This is meant
+	 * for debug purposes only and can be removed at any time. This is not API.
+	 * 
+	 * This property was added 01/12/2018 by Markus Kuppe in the process of fixing a
+	 * bug where TLC generates and incorrect set of states with certain statements.
+	 * More details can be found at https://github.com/tlaplus/tlaplus/issues/113.
+	 */
+	public static final boolean LAZYEVAL_OFF = Boolean.getBoolean(tlc2.value.impl.LazyValue.class.getName() + ".off");
+	
+	static {
+		// Indicate if LazyValue will be disabled in this TLC run.
+		if (LAZYEVAL_OFF) {
+			ToolIO.out.println("LazyValue is disabled.");
+		}
+	}
+  /**
+   * The field val is the result of evaluating expr in context con and
+   * a pair of states.  If val is null, then the value has not been
+   * computed, but when computed, the value can be cached in the field
+   * val. If val is ValUndef, then the value has not been computed,
+   * and when computed, it can not be cached in the field val.
+   */
+
+  public SemanticNode expr;
+  public Context con;
+  private Value val;
+
+  public LazyValue(SemanticNode expr, Context con, final CostModel cm) {
+	  this(expr, con, true, coverage ? cm.get(expr) : cm);
+  }
+
+  public LazyValue(SemanticNode expr, Context con, final boolean cachable, final CostModel cm) {
+    this.expr = expr;
+    this.con = con;
+    this.cm = coverage ? cm.get(expr) : cm;
+    this.val = null;
+    // See comment on cachable's meager performance in Tool.java on line 1408.
+    // See other note about a bug that surfaced with LazyValue in Tool.java on line ~1385.
+    if (LAZYEVAL_OFF || !cachable) {
+    	this.val = UndefValue.ValUndef;
+    }
+  }
+
+  public final boolean isUncachable() { return this.val == UndefValue.ValUndef; }
+
+  public final void setValue(final Value aValue) {
+	  assert !isUncachable();
+	  this.val = aValue;
+  }
+
+  public final Value getValue() {
+	  // cache hit on (this.val != null && !isUncachable)
+      // cache miss on (this.val == null)
+	  return this.val;
+  }
+ 
+  @Override
+  public final byte getKind() { return LAZYVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to compare lazy values.");
+      }
+      return this.val.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to check equality of lazy values.");
+      }
+      return this.val.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to check set membership of lazy values.");
+      }
+      return this.val.member(elem);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to check if a lazy value is a finite set.");
+      }
+      return this.val.isFinite();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value.");
+      }
+      return this.val.takeExcept(ex);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value.");
+      }
+      return this.val.takeExcept(exs);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+         Assert.fail("Error(TLC): Attempted to compute size of lazy value.");
+      }
+      return this.val.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+    this.val = (Value)ois.readObject();
+  }
+
+  private void writeObject(ObjectOutputStream oos) throws IOException {
+    if (this.val == null || this.val == UndefValue.ValUndef) {
+      Assert.fail("Error(TLC): Attempted to serialize lazy value.");
+    }
+    oos.writeObject(this.val);
+  }
+
+  /* Nothing to normalize. */
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to normalize lazy value.");
+      }
+      return this.val.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to normalize lazy value.");
+      }
+      this.val.normalize();
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) return this;
+      return this.val.deepCopy();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to call assignable on lazy value.");
+      }
+      return this.val.assignable(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The fingerprint method */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to fingerprint a lazy value.");
+      }
+      return this.val.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        Assert.fail("Error(TLC): Attempted to apply permutation to lazy value.");
+      }
+      return this.val.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation of the value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      if (this.val == null || this.val == UndefValue.ValUndef) {
+        return sb.append("<LAZY " + this.expr + ">");
+      }
+      return this.val.toString(sb, offset, swallow);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/MVPerm.java b/tlatools/src/tlc2/value/impl/MVPerm.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac04e48c0ce83dac470fd0b5ab802b78e1bb2bee
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/MVPerm.java
@@ -0,0 +1,140 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:42:27 PST by lamport
+//      modified on Thu Nov 16 15:53:30 PST 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import tlc2.value.IMVPerm;
+import tlc2.value.IModelValue;
+import tlc2.value.IValue;
+
+public final class MVPerm implements IMVPerm {
+  private final ModelValue[] elems;
+  private int count;
+
+  MVPerm() {
+    this.elems = new ModelValue[ModelValue.mvs.length];
+    this.count = 0;
+  }
+
+  public final boolean equals(Object obj) {
+    if (obj instanceof MVPerm) {
+      MVPerm perm = (MVPerm)obj;
+      for (int i = 0; i < this.elems.length; i++) {
+	if (this.elems[i] == null) {
+	  if (perm.elems[i] != null) {
+	    return false;
+	  }
+	}
+	else if (!this.elems[i].equals(perm.elems[i])) {
+	  return false;
+	}
+      }
+      return true;
+    }
+    return false;
+  }
+
+  public final int hashCode() {
+    int res = 0;
+    for (int i = 0; i < this.elems.length; i++) {
+      ModelValue mv = this.elems[i];
+      if (mv != null) {
+	res = 31*res + mv.val.hashCode();
+      }
+    }
+    return res;
+  }
+  
+  @Override
+  public final int size() { return this.count; }
+
+  @Override
+  public final IValue get(IValue k) {
+    return this.elems[((ModelValue) k).index];
+  }
+
+  @Override
+  public final void put(IModelValue m1, IModelValue m2) {
+	  ModelValue k = (ModelValue) m1;
+	  ModelValue elem = (ModelValue) m2;
+    if (!k.equals(elem) && this.elems[k.index] == null) {
+      this.elems[k.index] = elem;
+      this.count++;
+    }
+  }
+
+  private final void put(int i, ModelValue elem) {
+    if (this.elems[i] == null && elem != null) {
+      this.elems[i] = elem;
+      this.count++;
+    }
+  }
+  
+  @Override
+  public final IMVPerm compose(IMVPerm perm) {
+	  MVPerm res = new MVPerm();
+    for (int i = 0; i < this.elems.length; i++) {
+      ModelValue mv = this.elems[i];
+      if (mv == null) {
+	res.put(i, ((MVPerm) perm).elems[i]);
+      }
+      else {
+	ModelValue mv1 = ((MVPerm) perm).elems[mv.index];
+	if (mv1 == null) {
+	  res.put(i, mv);
+	}
+	else if (!ModelValue.mvs[i].equals(mv1)) {
+	  res.put(i, mv1);
+	}
+      }
+    }
+    return res;
+  }
+  
+  /**
+   * Consider caching if this method is used frequently; currently it is used once per instance per
+   * 	execution of TLC, during initial state setup / expansion.
+   * 
+   * @return a {@code List} of all {@link ModelValue} instances held by this permutation.
+   */
+  public List<ModelValue> getAllModelValues() {
+	  final List<ModelValue> values = new ArrayList<>();
+	  
+	  for (int i = 0; i < elems.length; i++) {
+		  if (elems[i] != null) {
+			  values.add(elems[i]);
+		  }
+	  }
+	  
+	  return values;
+  }
+
+  public final String toString() {
+    StringBuffer sb = new StringBuffer("[");
+    int i = 0;
+    for (i = 0; i < this.elems.length; i++) {
+      if (this.elems[i] != null) {
+	sb.append(ModelValue.mvs[i].toString());
+	sb.append(" -> ");
+	sb.append(this.elems[i].toString());
+	break;
+      }
+    }
+    for (int j = i+1; j < this.elems.length; j++) {
+      if (this.elems[j] != null) {
+	sb.append(", ");
+	sb.append(ModelValue.mvs[j].toString());
+	sb.append(" -> ");
+	sb.append(this.elems[j].toString());
+      }
+    }
+    sb.append("]");    
+    return sb.toString();
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/MVPerms.java b/tlatools/src/tlc2/value/impl/MVPerms.java
new file mode 100644
index 0000000000000000000000000000000000000000..52b8fa7d30d93aa02f8e52288bea8526e78a0192
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/MVPerms.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:42:27 PST by lamport
+//      modified on Thu Nov 16 15:53:30 PST 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import java.util.Enumeration;
+
+import tlc2.util.Vect;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import util.Assert;
+import util.Set;
+
+public abstract class MVPerms {
+  
+  public static final IMVPerm[] permutationSubgroup(final Enumerable enumerable) {
+    final ValueEnumeration Enum = enumerable.elements();
+	final int sz = enumerable.size() - 1;
+    final Set perms = new Set(sz);
+    final Vect<IMVPerm> permVec = new Vect<>(sz);
+    // Compute the group generators:
+    Value elem;
+    while ((elem = Enum.nextElement()) != null) {
+      final FcnRcdValue fcn = (FcnRcdValue) elem.toFcnRcd();
+      if (fcn == null) {
+	Assert.fail("The symmetry operator must specify a set of functions.");
+      }
+      final IMVPerm perm = new MVPerm();
+      for (int i = 0; i < fcn.domain.length; i++) {
+	final IValue dval = fcn.domain[i];
+	final IValue rval = fcn.values[i];
+	if ((dval instanceof ModelValue) && (rval instanceof ModelValue)) {
+	  perm.put((ModelValue)dval, (ModelValue)rval);
+	}
+	else {
+	  Assert.fail("Symmetry function must have model values as domain and range.");
+	}
+      }
+      if (perm.size() > 0 && perms.put(perm) == null) {
+	permVec.addElement(perm);
+      }
+    }
+    // Compute the group generated by the generators:
+    final int gsz = permVec.size();
+    int sz0 = 0;
+    while (true) {
+      final int sz1 = permVec.size();
+      for (int i = 0; i < gsz; i++) {
+	final IMVPerm perm1 = (IMVPerm)permVec.elementAt(i);
+	for (int j = sz0; j < sz1; j++) {
+		IMVPerm perm = perm1.compose((IMVPerm)permVec.elementAt(j));
+	  if (perm.size() > 0 && perms.put(perm) == null) {
+	    permVec.addElement(perm);
+	  }
+	}
+      }
+      if (sz1 == permVec.size()) { break; }
+      sz0 = sz1;
+    }
+    // Finally, put all the elements in an array ready for use:
+    final IMVPerm[] res = new IMVPerm[permVec.size()];
+    final Enumeration<IMVPerm> permEnum = permVec.elements();
+    for (int i = 0; i < res.length; i++) {
+      res[i] = permEnum.nextElement();
+    }
+    return res;
+  }
+}
diff --git a/tlatools/src/tlc2/value/impl/MethodValue.java b/tlatools/src/tlc2/value/impl/MethodValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..2674782629d468ae96332aa9dca3fe7baaf8e2ee
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/MethodValue.java
@@ -0,0 +1,305 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:00 PST by lamport
+//      modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.EvalControl;
+import tlc2.tool.EvalException;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.impl.Tool;
+import tlc2.value.IValue;
+import tlc2.value.Values;
+import util.Assert;
+import util.Assert.TLCRuntimeException;
+import util.WrongInvocationException;
+
+public class MethodValue extends OpValue implements Applicable {
+
+	public static Value get(final Method md) {
+		return get(md, 0);
+	}
+	
+	public static Value get(final Method md, int minLevel) {
+		final MethodValue mv = new MethodValue(md, minLevel);
+		// Eagerly evaluate the constant operator if possible (zero arity) to only
+		// evaluate once at startup and not during state exploration.
+		final int acnt = md.getParameterTypes().length;
+    	final boolean isConstant = (acnt == 0) && Modifier.isFinal(md.getModifiers());
+    	return isConstant ? mv.apply(Tool.EmptyArgs, EvalControl.Clear) : mv;
+	}
+	
+  private final MethodHandle mh;
+  private final Method md;
+  private final int minLevel;
+
+  /* Constructor */
+	private MethodValue(final Method md, final int minLevel) {
+		this.md = md;
+		this.minLevel = minLevel;
+		try {
+			final int parameterCount = this.md.getParameterCount();
+			if (parameterCount > 0) {
+				// With more than one argument, we want to setup the method handle to use a
+				// spreader which essentially converts the Value[] into something that is
+				// accepted by the method handle. Without a spreader, passing a Value[] to
+				// MethodHandle#invoke does not work. Instead one has to use MethodHandle#invokeWithArguments.
+				// MethodHandle#invokeWithArguments internally creates a spreader on the fly
+				// which turns out to be costly (for the spec MongoRepl of the performance
+				// tests it resulted in a 20% performance drop).
+				this.mh = MethodHandles.publicLookup().unreflect(md).asSpreader(IValue[].class, parameterCount);
+			} else {
+				this.mh = MethodHandles.publicLookup().unreflect(md); 
+			}
+		} catch (IllegalAccessException e) {
+			throw new TLCRuntimeException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, MP.getMessage(
+					EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[] { md.toString(), e.getMessage() }));
+		}
+	}
+
+  @Override
+  public final byte getKind() { return METHODVALUE; }
+
+  @Override
+  public final IValue initialize() {
+	  this.deepNormalize();
+	  // Do not call fingerprint as a MethodValue has no fingerprint.
+	  return this;
+  }
+  
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      Assert.fail("Attempted to compare operator " + this.toString() +
+      " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString()));
+      return 0;       // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      Assert.fail("Attempted to check equality of operator " + this.toString() +
+      " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : Values.ppr(elem.toString()) +
+      "\nis an element of operator " + this.toString());
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the operator " + this.toString() +
+      " is a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value arg, int control) {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Should use the other apply method.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value[] args, int control) {
+    try {
+      Value res = null;
+      try
+      {
+    	  if (args.length == 0) {
+    		  res = (Value) this.mh.invokeExact();
+    	  } else {
+    		  res = (Value) this.mh.invoke(args);
+    	  }
+      } catch (Throwable e)
+      {
+          if (e instanceof InvocationTargetException)
+          {
+              Throwable targetException = ((InvocationTargetException)e).getTargetException();
+              throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), targetException.getMessage()});
+          } else if (e instanceof NullPointerException) {
+              throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()});
+          } else if (e instanceof EvalException) {
+        	  // Do not wrap an EvalException below.
+        	  throw (EvalException) e;
+          } else
+          {
+              String message = e.getMessage();
+              if (message == null) {
+				  // Try to pass some information along (i.e. the full stack-trace) in cases where
+				  // message is null.
+		          final StringWriter sw = new StringWriter();
+            	  e.printStackTrace(new PrintWriter(sw));
+            	  message = sw.toString();
+              }
+			Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), message});
+          }
+      }
+      return res;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value select(Value arg) {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select().");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      Assert.fail("Attempted to appy EXCEPT construct to the operator " +
+      this.toString() + ".");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Assert.fail("Attempted to apply EXCEPT construct to the operator " +
+      this.toString() + ".");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+      Assert.fail("Attempted to compute the domain of the operator " +
+      this.toString() + ".");
+      return SetEnumValue.EmptySet;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the operator " +
+      this.toString() + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Should never normalize an operator. */
+  @Override
+  public final boolean isNormalized() {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* String representation of the value.  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) {
+    try {
+      return sb.append("<Java Method: " + this.md + ">");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+  
+  public final int getMinLevel() {
+	  return minLevel;
+  }
+}
diff --git a/tlatools/src/tlc2/value/impl/ModelValue.java b/tlatools/src/tlc2/value/impl/ModelValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..1651468acdf8356f15e6d19aa6aecaece64b3277
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/ModelValue.java
@@ -0,0 +1,359 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:32:25 PST by lamport
+//      modified on Fri Aug 10 15:07:47 PDT 2001 by yuanyu
+
+/***************************************************************************
+* Change to model values made by LL on 23 Feb 2008:                        *
+* ------                                                                   *
+* A model value whose name is "x_str" for any character x and string str   *
+* is said to have TYPE 'x'.  Otherwise, it is said to be untyped.  It is   *
+* an error to test if a typed model value is equal to any value except a   *
+* model value that is either untyped or has the same type.  (As before,    *
+* an untyped model value is unequal to any value except itself.)           *
+*                                                                          *
+* This was implemented by adding a `type' field to a ModelValue object     *
+* and making changes to the following classes:                             *
+*                                                                          *
+*    changed member method                                                 *
+*       module/Integers.java                                               *
+*       module/Naturals.java                                               *
+*       module/Sequences.java                                              *
+*       module/Strings.java                                                *
+*       value/IntervalValue.java  CHECK THIS with mv \notin 1..2           *
+*       value/SetOfFcnsValue.java                                          *
+*       value/SetOfRcdsValue.java                                          *
+*       value/SetOfTuplesValue.java                                        *
+*       value/StringValue.java                                             *
+*    changed equals method                                                 *
+*       value/BoolValue.java                                               *
+*       value/FcnRcdValue.java                                             *
+*       value/IntValue.java                                                *
+*       value/RecordValue.java                                             *
+*       value/SetEnumValue.java                                            *
+***************************************************************************/
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import tlc2.tool.FingerprintException;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IModelValue;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.UniqueString;
+
+public class ModelValue extends Value implements IModelValue {
+
+    /**
+     * A method to reset the model values
+     * All callers should make sure that the model value class has been initialized
+     */
+    public static void init()
+    {
+       count = 0;
+       mvTable = new Hashtable();
+       mvs = null;
+    }
+
+    /**
+     * Workround to the static usage
+     */
+    static
+    {
+        init();
+    }
+
+  private static int count;
+  private static Hashtable mvTable;
+  // SZ Mar 9, 2009: public accessed field, this will cause troubles
+  public static ModelValue[] mvs;
+
+  public UniqueString val;
+  public int index;
+  public char type;  // type = 0 means untyped.
+
+  /* Constructor */
+  private ModelValue(String val) {
+    // SZ 11.04.2009: changed access method
+    this.val = UniqueString.uniqueStringOf(val);
+    this.index = count++;
+    if (   (val.length() > 2)
+        && (val.charAt(1) == '_')) {
+      this.type = val.charAt(0) ;
+      }
+     else { this.type = 0 ; }
+  }
+
+  /* Make str a new model value, if it is not one yet.  */
+  public static Value make(String str) {
+    ModelValue mv = (ModelValue)mvTable.get(str);
+    if (mv != null) return mv;
+    mv = new ModelValue(str);
+    mvTable.put(str, mv);
+    return mv;
+  }
+
+  /* Collect all the model values defined thus far. */
+  public static void setValues() {
+    mvs = new ModelValue[mvTable.size()];
+    Enumeration Enum = mvTable.elements();
+    while (Enum.hasMoreElements()) {
+      ModelValue mv = (ModelValue)Enum.nextElement();
+      mvs[mv.index] = mv;
+    }
+  }
+
+  @Override
+  public final byte getKind() { return MODELVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (obj instanceof ModelValue) {
+        return this.val.compareTo(((ModelValue)obj).val);
+      }
+      return -1;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (this.type == 0) {
+        return (obj instanceof ModelValue &&
+          this.val.equals(((ModelValue)obj).val));
+       }
+      if (obj instanceof ModelValue) {
+        ModelValue mobj = (ModelValue) obj ;
+        if (   (mobj.type == this.type)
+            || (mobj.type == 0) ) {
+          return mobj.val == this.val ;
+          }
+         else {
+          Assert.fail("Attempted to check equality "
+                      + "of the differently-typed model values "
+                        + Values.ppr(this.toString()) + " and "
+                        + Values.ppr(mobj.toString()));
+          }
+      }
+      Assert.fail("Attempted to check equality of typed model value "
+                   + Values.ppr(this.toString()) + " and non-model value\n"
+                   + Values.ppr(obj.toString())) ;
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /*************************************************************************
+  * The following two methods are used used to check if this model value   *
+  * equal to or a member of non-model value obj.  They return false if     *
+  * this model value is untyped and raise an exception if it is typed.     *
+  *************************************************************************/
+  public final boolean modelValueEquals(Object obj){
+    try {
+      if (this.type != 0) {
+      Assert.fail("Attempted to check equality of the typed model value "
+                   + Values.ppr(this.toString()) + " and the non-model value\n"
+                   + Values.ppr(obj.toString())) ;
+
+       }
+      return false ;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean modelValueMember(Object obj){
+    try {
+      if (this.type != 0) {
+      Assert.fail("Attempted to check if the typed model value "
+                   + Values.ppr(this.toString())
+                   + " is an element of\n"
+                   + Values.ppr(obj.toString())) ;
+
+       }
+      return false ;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of the model value " + Values.ppr(this.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the model value " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT construct to the model value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the model value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the model value " +
+      Values.ppr(this.toString()) + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public boolean mutates() {
+	  return false;
+  }
+
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*nop*/return this; }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return ((val instanceof ModelValue) &&
+        this.val.equals(((ModelValue)val).val));
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public void write(IValueOutputStream vos) throws IOException {
+		vos.writeByte(MODELVALUE);
+		vos.writeShort((short) index);
+	}
+
+  /* The fingerprint methods */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      return this.val.fingerPrint(FP64.Extend(fp, MODELVALUE));
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      IValue res = perm.get(this);
+      if (res == null) return this;
+      return res;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) {
+    try {
+      return sb.append(this.val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	// The presence of this field is justified by a small number of ModelValue
+	// instances being created overall.
+	private Object data;
+
+	@Override
+	public boolean hasData() {
+		return data != null;
+	}
+
+	@Override
+	public Object getData() {
+		return data;
+	}
+
+	@Override
+	public Object setData(final Object obj) {
+		this.data = obj;
+		return obj;
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/OpLambdaValue.java b/tlatools/src/tlc2/value/impl/OpLambdaValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..24c4d13bf63fadc8845da1ae0e3660894cbb7a53
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/OpLambdaValue.java
@@ -0,0 +1,251 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport
+//      modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.OpDefNode;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.ITool;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.IValue;
+import tlc2.value.Values;
+import util.Assert;
+import util.WrongInvocationException;
+
+public class OpLambdaValue extends OpValue implements Applicable {
+  public final OpDefNode opDef;       // the operator definition.
+  public final ITool tool;
+  public final Context con;
+  public final TLCState state;
+  public final TLCState pstate;
+
+  /* Constructor */
+  public OpLambdaValue(OpDefNode op, ITool tool,	Context con,
+           TLCState state, TLCState pstate) {
+    this.opDef = op;
+    this.tool = tool;
+    this.state = state;
+    this.con = con;
+    this.pstate = pstate;
+  }
+
+  public OpLambdaValue(OpDefNode op, ITool tool,	Context con,
+          TLCState state, TLCState pstate, CostModel cm) {
+	this(op, tool, con, state, pstate);
+	this.cm = cm;
+  }
+  
+  public OpLambdaValue(OpLambdaValue other, ITool tool) {
+	  this(other.opDef, tool, other.con, other.state, other.pstate);
+  }
+
+  @Override
+  public final byte getKind() { return OPLAMBDAVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      Assert.fail("Attempted to compare operator " + Values.ppr(this.toString()) +
+      " with value:\n" + Values.ppr(obj.toString()));
+      return 0;       // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      Assert.fail("Attempted to check equality of operator " + Values.ppr(this.toString()) +
+      " with value:\n" + Values.ppr(obj.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of operator " + Values.ppr(this.toString()));
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the operator " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value arg, int control) {
+    try {
+      throw new WrongInvocationException("Should use the other apply method.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value[] args, int control) {
+    try {
+      int alen = this.opDef.getArity();
+      if (alen != args.length) {
+        Assert.fail("Applying the operator " + Values.ppr(this.toString()) +
+        " with wrong number of arguments.");
+      }
+      Context c1 = this.con;
+      FormalParamNode[] formals = this.opDef.getParams();
+      for (int i = 0; i < alen; i++) {
+        c1 = c1.cons(formals[i], args[i]);
+      }
+      return (Value) this.tool.eval(this.opDef.getBody(), c1, this.state, this.pstate,
+          control);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value select(Value arg) {
+    try {
+      throw new WrongInvocationException("Error(TLC): attempted to call OpLambdaValue.select().");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      Assert.fail("Attempted to appy EXCEPT construct to the operator " +
+      Values.ppr(this.toString()) + ".");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Assert.fail("Attempted to apply EXCEPT construct to the operator " +
+      Values.ppr(this.toString()) + ".");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+      Assert.fail("Attempted to compute the domain of the operator " +
+      Values.ppr(this.toString()) + ".");
+      return SetEnumValue.EmptySet;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the operator " +
+      Values.ppr(this.toString()) + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Should never normalize an operator. */
+  @Override
+  public final boolean isNormalized() {
+    try {
+      throw new WrongInvocationException("Should not normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      throw new WrongInvocationException("Should not normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      throw new WrongInvocationException("Should not initialize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* String representation of the value.  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) {
+    try {
+      String opName = this.opDef.getName().toString();
+      return sb.append("<Operator ").append(opName).append(">");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/OpRcdValue.java b/tlatools/src/tlc2/value/impl/OpRcdValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..132de719487848f99a2735e1428aaf048f945958
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/OpRcdValue.java
@@ -0,0 +1,296 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:01 PST by lamport
+//      modified on Sat Nov 13 12:43:44 PST 1999 by yuanyu
+
+package tlc2.value.impl;
+
+import tlc2.tool.FingerprintException;
+import tlc2.util.Vect;
+import tlc2.value.IValue;
+import tlc2.value.Values;
+import util.Assert;
+import util.WrongInvocationException;
+
+public class OpRcdValue extends OpValue implements Applicable {
+  public Vect domain;
+  public Vect values;
+
+  /* Constructor */
+  public OpRcdValue() {
+    this.domain = new Vect();
+    this.values = new Vect();
+  }
+
+  public OpRcdValue(Vect domain, Vect values) {
+    this.domain = domain;
+    this.values = values;
+  }
+
+  @Override
+  public final byte getKind() { return OPRCDVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      Assert.fail("Attempted to compare operator " + Values.ppr(this.toString()) +
+      " with value:\n" + Values.ppr(obj.toString()));
+      return 0;         // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      Assert.fail("Attempted to check equality of operator " + Values.ppr(this.toString()) +
+      " with value:\n" + Values.ppr(obj.toString()));
+      return false;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of operator " + Values.ppr(this.toString()));
+      return false;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the operator " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final void addLine(Vect vs) {
+    try {
+      int len = vs.size();
+      Value[] args = new Value[len-2];
+      for (int i = 0; i < len-2; i++) {
+        args[i] = (Value)vs.elementAt(i+1);
+      }
+      this.domain.addElement(args);
+      this.values.addElement(vs.elementAt(len-1));
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value arg, int control) {
+    try {
+      throw new WrongInvocationException("Should use the other apply method.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value[] args, int control) {
+    try {
+      int sz = this.domain.size();
+      for (int i = 0; i < sz; i++) {
+        Value[] vals = (Value[])this.domain.elementAt(i);
+        if (args.length != vals.length) {
+          Assert.fail("Attempted to apply the operator " + Values.ppr(this.toString()) +
+          "\nwith wrong number of arguments.");
+        }
+        boolean matched = true;
+        for (int j = 0; j < vals.length; j++) {
+          matched = vals[j].equals(args[j]);
+          if (!matched) break;
+        }
+        if (matched) {
+          return (Value)this.values.elementAt(i);
+        }
+      }
+      // Generate the error message:
+      String msg = "Attempted to apply operator:\n" + Values.ppr(this.toString()) +
+        "\nto arguments (";
+      if (args.length > 0) msg += args[0];
+      for (int i = 1; i < args.length; i++) {
+        msg += ", " + args[i];
+      }
+      Assert.fail(msg +  "), which is undefined.");
+      return null;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value select(Value arg) {
+    try {
+      Assert.fail("Attempted to call OpRcdValue.select(). This is a TLC bug.");
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      Assert.fail("Attempted to appy EXCEPT construct to the operator " +
+      Values.ppr(this.toString()) + ".");
+      return null;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Assert.fail("Attempted to apply EXCEPT construct to the operator " +
+      Values.ppr(this.toString()) + ".");
+      return null;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+      Assert.fail("Attempted to compute the domain of the operator " +
+      Values.ppr(this.toString()) + ".");
+      return SetEnumValue.EmptySet;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the operator " +
+      Values.ppr(this.toString()) + ".");
+      return 0;         // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Should never normalize an operator. */
+  @Override
+  public final boolean isNormalized() {
+    try {
+      throw new WrongInvocationException("Should not normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      throw new WrongInvocationException("Should not normalize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      boolean defined = true;
+      for (int i = 0; i < this.values.size(); i++) {
+        defined = defined && ((Value)this.values.elementAt(i)).isDefined();
+      }
+      return defined;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      throw new WrongInvocationException("Should not initialize an operator.");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Pretty-printing  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      sb.append("{ ");
+      if (this.values.size() != 0) {
+        sb.append("<");
+        Value[] args = (Value[])this.domain.elementAt(0);
+        for (int j = 0; j < args.length; j++) {
+          sb = args[j].toString(sb, offset, swallow);
+          sb.append(", ");
+        }
+        sb = ((Value)this.values.elementAt(0)).toString(sb, offset, swallow);
+        sb.append(">");
+      }
+      for (int i = 1; i < this.values.size(); i++) {
+        sb.append(", <");
+        Value[] args = (Value[])this.domain.elementAt(i);
+        for (int j = 0; j < args.length; j++) {
+          sb = args[j].toString(sb, offset, swallow);
+          sb.append(", ");
+        }
+        sb = ((Value)this.values.elementAt(i)).toString(sb, offset, swallow);
+        sb.append(">");
+      }
+      return sb.append("}");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/OpValue.java b/tlatools/src/tlc2/value/impl/OpValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..1281b1ae02dfcf0710e500da1fc483871d735e74
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/OpValue.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:21:02 PST by lamport
+//      modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import tla2sany.semantic.ExprOrOpArgNode;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Context;
+
+public abstract class OpValue extends Value implements Applicable {
+
+	// Allow sub-classes to override.
+	public Value eval(final Tool tool, final ExprOrOpArgNode[] args, final Context c, final TLCState s0,
+			final TLCState s1, final int control, final CostModel cm) {
+		final Value[] argVals = new Value[args.length];
+		// evaluate the operator's arguments:
+		for (int i = 0; i < args.length; i++) {
+			argVals[i] = tool.eval(args[i], c, s0, s1, control, cm);
+		}
+		// evaluate the operator:
+		return this.apply(argVals, control);
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/RecordValue.java b/tlatools/src/tlc2/value/impl/RecordValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..18279c7558fc97045cee77a0a37f0691ade1ff92
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/RecordValue.java
@@ -0,0 +1,607 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:15:47 PST by lamport
+//      modified on Fri Aug 10 15:09:07 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+
+import tla2sany.semantic.OpDeclNode;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.ValueInputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.TLAConstants;
+import util.UniqueString;
+
+public class RecordValue extends Value implements Applicable {
+  public final UniqueString[] names;   // the field names
+  public final Value[] values;         // the field values
+  private boolean isNorm;
+public static final RecordValue EmptyRcd = new RecordValue(new UniqueString[0], new Value[0], true);
+
+  /* Constructor */
+  public RecordValue(UniqueString[] names, Value[] values, boolean isNorm) {
+    this.names = names;
+    this.values = values;
+    this.isNorm = isNorm;
+  }
+
+  public RecordValue(UniqueString[] names, Value[] values, boolean isNorm, CostModel cm) {
+	  this(names, values, isNorm);
+	  this.cm = cm;
+  }
+  
+  public RecordValue(UniqueString name, Value v, boolean isNorm) {
+	  this(new UniqueString[] {name}, new Value[] {v}, isNorm);
+  }
+  
+  public RecordValue(UniqueString name, Value v) {
+	  this(new UniqueString[] {name}, new Value[] {v}, false);
+  }
+
+  public RecordValue(final TLCState state) {
+		final OpDeclNode[] vars = state.getVars();
+		
+		this.names = new UniqueString[vars.length];
+		this.values = new Value[vars.length];
+
+		for (int i = 0; i < vars.length; i++) {
+			this.names[i] = vars[i].getName();
+			this.values[i] = (Value) state.lookup(this.names[i]); 
+		}
+
+		this.isNorm = false;
+  }
+
+  @Override
+  public final byte getKind() { return RECORDVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      RecordValue rcd = obj instanceof Value ? (RecordValue) ((Value)obj).toRcd() : null;
+      if (rcd == null) {
+        if (obj instanceof ModelValue) return 1;
+        Assert.fail("Attempted to compare record:\n" + Values.ppr(this.toString()) +
+        "\nwith non-record\n" + Values.ppr(obj.toString()));
+      }
+      this.normalize();
+      rcd.normalize();
+      int len = this.names.length;
+      int cmp = len - rcd.names.length;
+      if (cmp == 0) {
+        for (int i = 0; i < len; i++) {
+          cmp = this.names[i].compareTo(rcd.names[i]);
+          if (cmp != 0) break;
+          cmp = this.values[i].compareTo(rcd.values[i]);
+          if (cmp != 0) break;
+        }
+      }
+      return cmp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      RecordValue rcd = obj instanceof Value ? (RecordValue) ((Value)obj).toRcd() : null;
+      if (rcd == null) {
+        if (obj instanceof ModelValue)
+           return ((ModelValue) obj).modelValueEquals(this) ;
+        Assert.fail("Attempted to check equality of record:\n" + Values.ppr(this.toString()) +
+        "\nwith non-record\n" + Values.ppr(obj.toString()));
+      }
+      this.normalize();
+      rcd.normalize();
+      int len = this.names.length;
+      if (len != rcd.names.length) return false;
+      for (int i = 0; i < len; i++) {
+        if ((!(this.names[i].equals(rcd.names[i]))) ||
+          (!(this.values[i].equals(rcd.values[i]))))
+          return false;
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if element:\n" + Values.ppr(elem.toString()) +
+                  "\nis in the record:\n" + Values.ppr(this.toString()));
+      return false;    // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() { return true; }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        int rlen = this.names.length;
+        Value[] newValues = new Value[rlen];
+        Value arcVal = ex.path[ex.idx];
+        if (arcVal instanceof StringValue) {
+          UniqueString arc = ((StringValue)arcVal).val;
+          for (int i = 0; i < rlen; i++) {
+            if (this.names[i].equals(arc)) {
+              ex.idx++;
+              newValues[i] = this.values[i].takeExcept(ex);
+            }
+            else {
+              newValues[i] = this.values[i];
+            }
+          }
+          UniqueString[] newNames = this.names;
+          if (!this.isNorm) {
+            newNames = new UniqueString[rlen];
+            for (int i = 0; i < rlen; i++) {
+              newNames[i] = this.names[i];
+            }
+          }
+          return new RecordValue(newNames, newValues, this.isNorm);
+        }
+        else {
+            MP.printWarning(EC.TLC_WRONG_RECORD_FIELD_NAME, new String[]{Values.ppr(arcVal.toString())});
+        }
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Value res = this;
+      for (int i = 0; i < exs.length; i++) {
+        res = res.takeExcept(exs[i]);
+      }
+      return res;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value toRcd() {
+	  return this;
+  }
+  
+  @Override
+  public final Value toTuple() {
+	  return size() == 0 ? TupleValue.EmptyTuple : super.toTuple();
+  }
+
+  @Override
+	public final Value toFcnRcd() {
+        this.normalize();
+        Value[] dom = new Value[this.names.length];
+        for (int i = 0; i < this.names.length; i++) {
+          dom[i] = new StringValue(this.names[i], cm);
+        }
+        if (coverage) {cm.incSecondary(dom.length);}
+        return new FcnRcdValue(dom, this.values, this.isNormalized(), cm);
+	}
+
+  @Override
+  public final int size() {
+    try {
+      return this.names.length;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value arg, int control) {
+    try {
+      if (!(arg instanceof StringValue)) {
+        Assert.fail("Attempted to apply record to a non-string value " +
+        Values.ppr(arg.toString()) + ".");
+      }
+      UniqueString name = ((StringValue)arg).getVal();
+      int rlen = this.names.length;
+      for (int i = 0; i < rlen; i++) {
+        if (name.equals(this.names[i])) {
+          return this.values[i];
+        }
+      }
+      Assert.fail("Attempted to apply the record\n" + Values.ppr(this.toString()) +
+      "\nto nonexistent record field " + name + ".");
+      return null;    // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value[] args, int control) {
+    try {
+      if (args.length != 1) {
+        Assert.fail("Attempted to apply record to more than one arguments.");
+      }
+      return this.apply(args[0], control);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This method returns the named component of the record. */
+  @Override
+  public final Value select(Value arg) {
+    try {
+      if (!(arg instanceof StringValue)) {
+        Assert.fail("Attempted to apply record to a non-string argument " +
+        Values.ppr(arg.toString()) + ".");
+      }
+      UniqueString name = ((StringValue)arg).getVal();
+      int rlen = this.names.length;
+      for (int i = 0; i < rlen; i++) {
+        if (name.equals(this.names[i])) {
+          return this.values[i];
+        }
+      }
+      return null;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+    	Value[] dElems = new Value[this.names.length];
+      for (int i = 0; i < this.names.length; i++) {
+        dElems[i] = new StringValue(this.names[i]);
+      }
+      return new SetEnumValue(dElems, this.isNormalized());
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean assign(UniqueString name, Value val) {
+    try {
+      for (int i = 0; i < this.names.length; i++) {
+        if (name.equals(this.names[i])) {
+          if (this.values[i] == UndefValue.ValUndef ||
+              this.values[i].equals(val)) {
+            this.values[i] = val;
+            return true;
+          }
+          return false;
+        }
+      }
+      Assert.fail("Attempted to assign to nonexistent record field " + name + ".");
+      return false;    // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() { return this.isNorm; }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (!this.isNorm) {
+        int len = this.names.length;
+        for (int i = 1; i < len; i++) {
+          int cmp = this.names[0].compareTo(this.names[i]);
+          if (cmp == 0) {
+            Assert.fail("Field name " + this.names[i] + " occurs multiple times in record.");
+          }
+          else if (cmp > 0) {
+            UniqueString ts = this.names[0];
+            this.names[0] = this.names[i];
+            this.names[i] = ts;
+            Value tv = this.values[0];
+            this.values[0] = this.values[i];
+            this.values[i] = tv;
+          }
+        }
+        for (int i = 2; i < len; i++) {
+          int j = i;
+          UniqueString st = this.names[i];
+          Value val = this.values[i];
+          int cmp;
+          while ((cmp = st.compareTo(this.names[j-1])) < 0) {
+            this.names[j] = this.names[j-1];
+            this.values[j] = this.values[j-1];
+            j--;
+          }
+          if (cmp == 0) {
+            Assert.fail("Field name " + this.names[i] + " occurs multiple times in record.");
+          }
+          this.names[j] = st;
+          this.values[j] = val;
+        }
+        this.isNorm = true;
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	  try {
+      for (int i = 0; i < values.length; i++) {
+          values[i].deepNormalize();
+        }
+        normalize();
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      boolean defined = true;
+      for (int i = 0; i < this.values.length; i++) {
+        defined = defined && this.values[i].isDefined();
+      }
+      return defined;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() {
+    try {
+    	Value[] vals = new Value[this.values.length];
+      for (int i = 0; i < this.values.length; i++) {
+        vals[i] = (Value) this.values[i].deepCopy();
+      }
+      // Following code modified 16 June 2015 by adding Arrays.copyOf to fix
+      // the following bug that seems to have manifested itself only in TLC.Print and
+      // TLC.PrintT: Calling normalize on the original modifies the
+      // order of the names array in the deepCopy (and vice-versa) without doing the
+      // corresponding modification on the values array. Thus, the names are
+      // copied too to prevent any modification/normalization done to the
+      // original to appear in the deepCopy.
+      return new RecordValue(Arrays.copyOf(this.names, this.names.length), vals, this.isNorm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      boolean canAssign = ((val instanceof RecordValue) &&
+        this.names.length == ((RecordValue)val).names.length);
+      if (!canAssign) return false;
+      for (int i = 0; i < this.values.length; i++) {
+        canAssign = (canAssign &&
+         this.names[i].equals(((RecordValue)val).names[i]) &&
+         this.values[i].assignable(((RecordValue)val).values[i]));
+      }
+      return canAssign;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(final IValueOutputStream vos) throws IOException {
+		final int index = vos.put(this);
+		if (index == -1) {
+			vos.writeByte(RECORDVALUE);
+			final int len = names.length;
+			vos.writeInt((isNormalized()) ? len : -len);
+			for (int i = 0; i < len; i++) {
+				final int index1 = vos.put(names[i]);
+				if (index1 == -1) {
+					vos.writeByte(STRINGVALUE);
+					names[i].write(vos.getOutputStream());
+				} else {
+					vos.writeByte(DUMMYVALUE);
+					vos.writeNat(index1);
+				}
+				values[i].write(vos);
+			}
+		} else {
+			vos.writeByte(DUMMYVALUE);
+			vos.writeNat(index);
+		}
+	}
+
+  /* The fingerprint methods.  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.normalize();
+      int rlen = this.names.length;
+      fp = FP64.Extend(fp, FCNRCDVALUE);
+      fp = FP64.Extend(fp, rlen);
+      for (int i = 0; i < rlen; i++) {
+        String str = this.names[i].toString();
+        fp = FP64.Extend(fp, STRINGVALUE);
+        fp = FP64.Extend(fp, str.length());
+        fp = FP64.Extend(fp, str);
+        fp = this.values[i].fingerPrint(fp);
+      }
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.normalize();
+      int rlen = this.names.length;
+      Value[] vals = new Value[rlen];
+      boolean changed = false;
+      for (int i = 0; i < rlen; i++) {
+        vals[i] = (Value) this.values[i].permute(perm);
+        changed = changed || (vals[i] != this.values[i]);
+      }
+      if (changed) {
+        return new RecordValue(this.names, vals, true);
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      int len = this.names.length;
+
+      sb.append("[");
+      if (len > 0) {
+        sb.append(this.names[0] + TLAConstants.RECORD_ARROW);
+        sb = this.values[0].toString(sb, offset, swallow);
+      }
+      for (int i = 1; i < len; i++) {
+        sb.append(", ");
+        sb.append(this.names[i] + TLAConstants.RECORD_ARROW);
+        sb = this.values[i].toString(sb, offset, swallow);
+      }
+      return sb.append("]");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	public static IValue createFrom(final IValueInputStream vos) throws EOFException, IOException {
+		final int index = vos.getIndex();
+		boolean isNorm = true;
+		int len = vos.readInt();
+		if (len < 0) {
+			len = -len;
+			isNorm = false;
+		}
+		final UniqueString[] names = new UniqueString[len];
+		final Value[] vals = new Value[len];
+		for (int i = 0; i < len; i++) {
+			final byte kind1 = vos.readByte();
+			if (kind1 == DUMMYVALUE) {
+				final int index1 = vos.readNat();
+				names[i] = vos.getValue(index1);
+			} else {
+				final int index1 = vos.getIndex();
+				names[i] = UniqueString.read(vos.getInputStream());
+				vos.assign(names[i], index1);
+			}
+			vals[i] = (Value) vos.read();
+		}
+		final Value res = new RecordValue(names, vals, isNorm);
+		vos.assign(res, index);
+		return res;
+	}
+
+	public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws EOFException, IOException {
+		final int index = vos.getIndex();
+		boolean isNorm = true;
+		int len = vos.readInt();
+		if (len < 0) {
+			len = -len;
+			isNorm = false;
+		}
+		final UniqueString[] names = new UniqueString[len];
+		final Value[] vals = new Value[len];
+		for (int i = 0; i < len; i++) {
+			final byte kind1 = vos.readByte();
+			if (kind1 == DUMMYVALUE) {
+				final int index1 = vos.readNat();
+				names[i] = vos.getValue(index1);
+			} else {
+				final int index1 = vos.getIndex();
+				names[i] = UniqueString.read(vos.getInputStream(), tbl);
+				vos.assign(names[i], index1);
+			}
+			vals[i] = (Value) vos.read(tbl);
+		}
+		final Value res = new RecordValue(names, vals, isNorm);
+		vos.assign(res, index);
+		return res;
+	}
+
+	public TLCState toState() {
+		final TLCState state = TLCState.Empty.createEmpty();
+		final OpDeclNode[] vars = state.getVars();
+		for (int i = 0; i < vars.length; i++) {
+			final UniqueString name = vars[i].getName();
+			int rlen = this.names.length;
+			for (int j = 0; j < rlen; j++) {
+				if (name.equals(this.names[j])) {
+					state.bind(name, this.values[j]);
+				}
+			}
+		}
+		return state;
+	}
+}
diff --git a/tlatools/src/tlc2/value/Reducible.java b/tlatools/src/tlc2/value/impl/Reducible.java
similarity index 58%
rename from tlatools/src/tlc2/value/Reducible.java
rename to tlatools/src/tlc2/value/impl/Reducible.java
index 492894ca289fa4b5fdde0204c8a5a0eda936a2be..d9183d8d90e642e6c951d0fc2742a97a1a538f7a 100644
--- a/tlatools/src/tlc2/value/Reducible.java
+++ b/tlatools/src/tlc2/value/impl/Reducible.java
@@ -3,16 +3,16 @@
 // Last modified on Mon 30 Apr 2007 at 13:21:02 PST by lamport
 //      modified on Tue Mar 23 00:37:35 PST 1999 by yuanyu
 
-package tlc2.value;
+package tlc2.value.impl;
 
 public interface Reducible {
 
-  public int size();
-  public boolean member(Value elem);
-  public Value diff(Value val);
-  public Value cap(Value val);
-  public Value cup(Value val);
+  int size();
+  boolean member(Value elem);
+  Value diff(Value val);
+  Value cap(Value val);
+  Value cup(Value val);
 
-  public ValueEnumeration elements();
+  ValueEnumeration elements();
 }
 
diff --git a/tlatools/src/tlc2/value/impl/SetCapValue.java b/tlatools/src/tlc2/value/impl/SetCapValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed59231e3c0031b5d1b989dda90d1abe5d0c3756
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetCapValue.java
@@ -0,0 +1,332 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport
+//      modified on Fri Aug 10 15:09:21 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SetCapValue extends EnumerableValue implements Enumerable {
+  public final Value set1;
+  public final Value set2;
+  protected SetEnumValue capSet;
+
+  /* Constructor */
+  public SetCapValue(Value set1, Value set2) {
+    this.set1 = set1;
+    this.set2 = set2;
+    this.capSet = null;
+  }
+
+  @Override
+  public final byte getKind() { return SETCAPVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.capSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.capSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      return (this.set1.member(elem) && this.set2.member(elem));
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      if (!this.set1.isFinite() && !this.set2.isFinite()) {
+        Assert.fail("Attempted to check if the set " + Values.ppr(this.toString()) + "is finite.");
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      this.convertAndCache();
+      return this.capSet.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.capSet == null || this.capSet == SetEnumValue.DummyEnum) {
+        return (this.set1.isNormalized() && this.set2.isNormalized());
+      }
+      return this.capSet.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.capSet == null || this.capSet == SetEnumValue.DummyEnum) {
+        this.set1.normalize();
+        this.set2.normalize();
+      }
+      else {
+        this.capSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      return this.set1.isDefined() && this.set2.isDefined();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public void write(final IValueOutputStream vos) throws IOException {
+		capSet.write(vos);
+	}
+
+  /* The fingerprint methods */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.capSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.capSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.capSet == null) {
+      this.capSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.capSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.capSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.capSet == SetEnumValue.DummyEnum) { this.capSet = val; }
+      }
+    }
+  }
+  
+  @Override
+  public final void deepNormalize() {
+	    try {
+      set1.deepNormalize();
+      set2.deepNormalize();
+      if (capSet == null) {
+        capSet = SetEnumValue.DummyEnum;
+      }
+      else if (capSet != SetEnumValue.DummyEnum) {
+        capSet.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+	  
+  @Override
+  public final Value toSetEnum() {
+      if (this.capSet != null && this.capSet != SetEnumValue.DummyEnum) {
+        return this.capSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();	
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, this.isNormalized(), cm);
+  }
+
+  /* String representation of this value.  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      try {
+        if (TLCGlobals.expand) {
+          Value val = this.toSetEnum();
+          return val.toString(sb, offset, swallow);
+        }
+      }
+      catch (Throwable e) { if (!swallow) throw e; }
+
+      sb = this.set1.toString(sb, offset, swallow);
+      sb = sb.append(" \\cap ");
+      sb = this.set2.toString(sb, offset, swallow);
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.capSet == null || this.capSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.capSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    ValueEnumeration enum1;
+    Value set;
+
+    public Enumerator() {
+      if (set1 instanceof Enumerable) {
+        this.enum1 = ((Enumerable)set1).elements();
+        this.set = set2;
+      }
+      else if (set2 instanceof Enumerable) {
+        this.enum1 = ((Enumerable)set2).elements();
+        this.set = set1;
+      }
+      else {
+        Assert.fail("Attempted to enumerate S \\cap T when neither S:\n" +
+              Values.ppr(set1.toString()) + "\nnor T:\n" + Values.ppr(set2.toString()) +
+              "\nis enumerable");
+      }
+    }
+
+    @Override
+    public final void reset() { this.enum1.reset(); }
+
+    @Override
+    public final Value nextElement() {
+    	Value elem = this.enum1.nextElement();
+      while (elem != null) {
+    	  if (coverage) { cm.incSecondary(); }
+        if (this.set.member(elem)) return elem;
+        elem = this.enum1.nextElement();
+      }
+      return null;
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/SetCupValue.java b/tlatools/src/tlc2/value/impl/SetCupValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..15a183ef9d68651291d2dc8161380c4018f75e0e
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetCupValue.java
@@ -0,0 +1,329 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport
+//      modified on Fri Aug 10 15:09:28 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SetCupValue extends EnumerableValue implements Enumerable {
+  public final Value set1;
+  public final Value set2;
+  protected SetEnumValue cupSet;
+
+  /* Constructor */
+  public SetCupValue(Value set1, Value set2) {
+    this.set1 = set1;
+    this.set2 = set2;
+    this.cupSet = null;
+  }
+
+  public SetCupValue(Value set1, Value set2, CostModel cm) {
+	this(set1, set2);
+	this.cm = cm;
+  }
+
+  @Override
+  public final byte getKind() { return SETCUPVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.cupSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.cupSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      return this.set1.member(elem) || this.set2.member(elem);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      return this.set1.isFinite() && this.set2.isFinite();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      this.convertAndCache();
+      return this.cupSet.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      return (this.cupSet != null &&
+        this.cupSet != SetEnumValue.DummyEnum &&
+        this.cupSet.isNormalized());
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.cupSet != null && this.cupSet != SetEnumValue.DummyEnum) {
+        this.cupSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+      set1.deepNormalize();
+      set2.deepNormalize();
+      if (cupSet == null) {
+        cupSet = SetEnumValue.DummyEnum;
+      }
+      else if (cupSet != SetEnumValue.DummyEnum) {
+        cupSet.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+  
+  @Override
+  public final boolean isDefined() {
+    try {
+      return this.set1.isDefined() && this.set2.isDefined();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(final IValueOutputStream vos) throws IOException {
+		cupSet.write(vos);
+	}
+
+  /* The fingerprint methods */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.cupSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.cupSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.cupSet == null) {
+      this.cupSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.cupSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.cupSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.cupSet == SetEnumValue.DummyEnum) {	this.cupSet = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.cupSet != null && this.cupSet != SetEnumValue.DummyEnum) {
+        return this.cupSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, false, cm);
+  }
+
+  /* String representation of the value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      try {
+        if (TLCGlobals.expand) {
+          Value val = this.toSetEnum();
+          return val.toString(sb, offset, swallow);
+        }
+      }
+      catch (Throwable e) { if (!swallow) throw e; }
+
+      sb = this.set1.toString(sb, offset, swallow);
+      sb = sb.append(" \\cup ");
+      sb = this.set2.toString(sb, offset, swallow);
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.cupSet == null || this.cupSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.cupSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    ValueEnumeration enum1;
+    ValueEnumeration enum2;
+
+    public Enumerator() {
+      if ((set1 instanceof Enumerable) &&
+          (set2 instanceof Enumerable)) {
+        this.enum1 = ((Enumerable)set1).elements();
+        this.enum2 = ((Enumerable)set2).elements();
+      }
+      else {
+        Assert.fail("Attempted to enumerate S \\cup T when S:\n" +
+              Values.ppr(set1.toString()) + "\nand T:\n" + Values.ppr(set2.toString()) +
+              "\nare not both enumerable");
+      }
+    }
+
+    @Override
+    public final void reset() {
+      this.enum1.reset();
+      this.enum2.reset();
+    }
+
+    @Override
+    public final Value nextElement() {
+  	  if (coverage) { cm.incSecondary(); }
+  	Value elem = this.enum1.nextElement();
+      if (elem != null) return elem;
+      elem = this.enum2.nextElement();
+      return elem;
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/SetDiffValue.java b/tlatools/src/tlc2/value/impl/SetDiffValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..0778ce08b88f5f6b4af985386b8294955a606211
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetDiffValue.java
@@ -0,0 +1,329 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport
+//      modified on Fri Aug 10 15:09:34 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SetDiffValue extends EnumerableValue implements Enumerable {
+  public final Value set1;
+  public final Value set2;
+  protected SetEnumValue diffSet;
+
+  /* Constructor */
+  public SetDiffValue(Value set1, Value set2) {
+    this.set1 = set1;
+    this.set2 = set2;
+    this.diffSet = null;
+  }
+
+  @Override
+  public final byte getKind() { return SETDIFFVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.diffSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.diffSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      return (this.set1.member(elem) && !this.set2.member(elem));
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      if (this.set1.isFinite()) {
+        return true;
+      }
+      if (!this.set2.isFinite()) {
+        Assert.fail("Attempted to check if the set " + Values.ppr(this.toString()) + "is finite.");
+      }
+      return false;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      this.convertAndCache();
+      return this.diffSet.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.diffSet == null || this.diffSet == SetEnumValue.DummyEnum) {
+        return this.set1.isNormalized();
+      }
+      return this.diffSet.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.diffSet == null || this.diffSet == SetEnumValue.DummyEnum) {
+        this.set1.normalize();
+        this.set2.normalize();
+      }
+      else {
+        this.diffSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+     set1.deepNormalize();
+      set2.deepNormalize();
+      if (diffSet == null) {
+        diffSet = SetEnumValue.DummyEnum;
+      }
+      else if (diffSet != SetEnumValue.DummyEnum) {
+        diffSet.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      return this.set1.isDefined() && this.set2.isDefined();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(final IValueOutputStream vos) throws IOException {
+		diffSet.write(vos);
+	}
+
+  /* The fingerprint methods */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.diffSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.diffSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.diffSet == null) {
+      this.diffSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.diffSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.diffSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.diffSet == SetEnumValue.DummyEnum) { this.diffSet = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.diffSet != null && this.diffSet != SetEnumValue.DummyEnum) {
+        return this.diffSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, this.set1.isNormalized(), cm);
+  }
+
+  /* The string representation  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      try {
+        if (TLCGlobals.expand) {
+          Value val = this.toSetEnum();
+          return val.toString(sb, offset, swallow);
+        }
+      }
+      catch (Throwable e) { if (!swallow) throw e; }
+
+      sb = this.set1.toString(sb, offset, swallow);
+      sb = sb.append(" \\ ");
+      sb = this.set2.toString(sb, offset, swallow);
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.diffSet == null || this.diffSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.diffSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    ValueEnumeration enum1;
+
+    public Enumerator() {
+      if (set1 instanceof Enumerable) {
+        this.enum1 = ((Enumerable)set1).elements();
+      }
+      else {
+        Assert.fail("Attempted to enumerate S \\ T when S:\n" +
+              Values.ppr(set1.toString()) + "\nis not enumerable.");
+      }
+    }
+
+    @Override
+    public final void reset() { this.enum1.reset(); }
+
+    @Override
+    public final Value nextElement() {
+    	Value elem = this.enum1.nextElement();
+      while (elem != null) {
+    	  if (coverage) { cm.incSecondary(); }
+        if (!set2.member(elem)) return elem;
+        elem = this.enum1.nextElement();
+      }
+      return null;
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/SetEnumValue.java b/tlatools/src/tlc2/value/impl/SetEnumValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d0419ddeb2969bbcb1d88799a1de6d58f775c71
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetEnumValue.java
@@ -0,0 +1,522 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:16:24 PST by lamport
+//      modified on Mon Aug 20 10:54:08 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.util.Map;
+
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.ValueInputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.UniqueString;
+
+@SuppressWarnings("serial")
+public class SetEnumValue extends EnumerableValue
+implements Enumerable, Reducible {
+  public ValueVec elems;         // the elements of the set
+  private boolean isNorm;        // normalized?
+public static final SetEnumValue EmptySet = new SetEnumValue(new ValueVec(0), true);
+public static final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, true);
+
+  /* Constructor */
+  public SetEnumValue(Value[] elems, boolean isNorm) {
+	  this(new ValueVec(elems), isNorm);
+  }
+
+  public SetEnumValue(Value[] vals, boolean isNorm, CostModel cm) {
+	  this(vals, isNorm);
+	  this.cm = cm;
+  }
+
+  public SetEnumValue(ValueVec elems, boolean isNorm) {
+    this.elems = elems;
+    this.isNorm = isNorm;
+  }
+
+  public SetEnumValue(ValueVec elems, boolean isNorm, final CostModel cm) {
+	  this(elems, isNorm);
+	  this.cm = cm;
+  }
+
+  public SetEnumValue() {
+	  this(new ValueVec(0), true);
+  }
+  
+  public SetEnumValue(final Value elem) {
+	  this(new Value[] {elem}, true); // single element is normalized by definition.
+  }
+  
+  public SetEnumValue(CostModel cm) {
+	  this();
+	  this.cm = cm;
+  }
+  
+  // See IValue#isAtom except that this is for sets of atoms.
+  public final boolean isSetOfAtoms() {
+      final int len = this.elems.size();
+      for (int i = 0; i < len; i++) {
+    	  final Value v = this.elems.elementAt(i);
+    	  if (v instanceof SetEnumValue) {
+    		  // Sets of sets of sets... of atoms.
+    		  final SetEnumValue sev = (SetEnumValue) v;
+    		  if (!sev.isSetOfAtoms()) {
+    			  return false;
+    		  }
+    	  } else if (!v.isAtom()) {
+    		  return false;
+    	  }
+      }
+      return true;
+  }
+
+  @Override
+  public final byte getKind() { return SETENUMVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      SetEnumValue set = obj instanceof Value ? (SetEnumValue) ((Value)obj).toSetEnum() : null;
+      if (set == null) {
+        if (obj instanceof ModelValue) return 1;
+        Assert.fail("Attempted to compare the set " + Values.ppr(this.toString()) +
+        " with the value:\n" + Values.ppr(obj.toString()));
+      }
+      this.normalize();
+      set.normalize();
+      int sz = this.elems.size();
+      int cmp = sz - set.elems.size();
+      if (cmp != 0) return cmp;
+      for (int i = 0; i < sz; i++) {
+        cmp = this.elems.elementAt(i).compareTo(set.elems.elementAt(i));
+        if (cmp != 0) return cmp;
+      }
+      return 0;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      SetEnumValue set = obj instanceof Value ? (SetEnumValue) ((Value)obj).toSetEnum() : null;
+      if (set == null) {
+        if (obj instanceof ModelValue)
+           return ((ModelValue) obj).modelValueEquals(this) ;
+        Assert.fail("Attempted to check equality of the set " + Values.ppr(this.toString()) +
+        " with the value:\n" + Values.ppr(obj.toString()));
+      }
+      this.normalize();
+      set.normalize();
+      int sz = this.elems.size();
+      if (sz != set.elems.size()) {
+        return false;
+      }
+      for (int i = 0; i < sz; i++) {
+        if (!this.elems.elementAt(i).equals(set.elems.elementAt(i))) {
+          return false;
+        }
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      return this.elems.search(elem, this.isNorm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() { return true; }
+
+  @Override
+  public final Value diff(Value val) {
+    try {
+      int sz = this.elems.size();
+      ValueVec diffElems = new ValueVec();
+      for (int i = 0; i < sz; i++) {
+    	  Value elem = this.elems.elementAt(i);
+        if (!val.member(elem)) {
+          diffElems.addElement(elem);
+        }
+      }
+      return new SetEnumValue(diffElems, this.isNormalized(), cm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value cap(Value val) {
+    try {
+      int sz = this.elems.size();
+      ValueVec capElems = new ValueVec();
+      for (int i = 0; i < sz; i++) {
+    	  Value elem = this.elems.elementAt(i);
+        if (val.member(elem)) {
+          capElems.addElement(elem);
+        }
+      }
+      return new SetEnumValue(capElems, this.isNormalized(), cm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value cup(Value set) {
+    try {
+      int sz = this.elems.size();
+      if (sz == 0) return set;
+
+      if (set instanceof Reducible) {
+        ValueVec cupElems = new ValueVec();
+        for (int i = 0; i < sz; i++) {
+        	Value elem = this.elems.elementAt(i);
+          cupElems.addElement(elem);
+        }
+        ValueEnumeration Enum = ((Enumerable)set).elements();
+        Value elem;
+        while ((elem = Enum.nextElement()) != null) {
+          if (!this.member(elem)) cupElems.addElement(elem);
+        }
+        return new SetEnumValue(cupElems, false);
+      }
+      return new SetCupValue(this, set, cm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      this.normalize();
+      return this.elems.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This method normalizes (destructively) this set. */
+  @Override
+  public final boolean isNormalized() { return this.isNorm; }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (!this.isNorm) {
+        this.elems.sort(true);   // duplicates eliminated
+        this.isNorm = true;
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+  
+  @Override
+  public final void deepNormalize() {
+	    try {
+      for (int i = 0; i < elems.size(); i++) {
+          elems.elementAt(i).deepNormalize();
+        }
+        normalize();
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+	  return this;
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      boolean defined = true;
+      int sz = this.elems.size();
+      for (int i = 0; i < sz; i++) {
+        defined = defined && this.elems.elementAt(i).isDefined();
+      }
+      return defined;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(IValueOutputStream vos) throws IOException {
+		final int index = vos.put(this);
+		if (index == -1) {
+			vos.writeByte(SETENUMVALUE);
+			final int len = elems.size();
+			vos.writeInt((isNormalized()) ? len : -len);
+			for (int i = 0; i < len; i++) {
+				elems.elementAt(i).write(vos);
+			}
+		} else {
+			vos.writeByte(DUMMYVALUE);
+			vos.writeNat(index);
+		}
+	}
+
+  /* The fingerprint methods */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.normalize();
+      int sz = this.elems.size();
+      fp = FP64.Extend(fp, SETENUMVALUE);
+      fp = FP64.Extend(fp, sz);
+      for (int i = 0; i < sz; i++) {
+        Value elem = this.elems.elementAt(i);
+        fp = elem.fingerPrint(fp);
+      }
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      int sz = this.elems.size();
+      Value[] vals = new Value[sz];
+      boolean changed = false;
+      for (int i = 0; i < sz; i++) {
+        vals[i] = (Value) this.elems.elementAt(i).permute(perm);
+        changed = (changed || vals[i] != this.elems.elementAt(i));
+      }
+      if (changed) {
+        return new SetEnumValue(vals, false);
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      // If this SetEnumValue object is created by a union, at least one of
+      // whose elements is a Cartesian product, then this can be an unnormalized
+      // set with repeated elements.  It would therefore seem like a good idea to
+      // normalize this object here.  Since this toString method is probably
+      // used only for printing the value, it seems that correcting this should
+      // not do any harm.  Therefore, LL added the following if statement
+      // on 5 Mar 2012.
+      // Beware:
+      // normalize() mutates a SetEnumValue's state. Thus calling toString() 
+      // on a SetEnumValue mutates its state. By convention, toString methods
+      // generally do not mutate an instance's state (side-effect free) and
+      // and are thus safe to be called. Failing to adhere to this convention
+      // can lead to subtle bugs. E.g. think of a programmer who inspects an
+      // instance with a debugger unconsciously mutating the instance's state.
+      if (!this.isNormalized()) {
+          this.normalize();
+      }
+
+      int len = this.elems.size();
+      sb = sb.append("{");
+      if (len > 0) {
+        this.elems.elementAt(0).toString(sb, offset, swallow);
+      }
+      for (int i = 1; i < len; i++) {
+        sb.append(", ");
+        this.elems.elementAt(i).toString(sb, offset, swallow);
+      }
+      sb.append("}");
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final Value randomElement() {
+     int sz = size();
+     int index = (int) Math.floor(RandomEnumerableValues.get().nextDouble() * sz);
+     return this.elems.elementAt(index);
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      return new Enumerator();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    int index = 0;
+
+    public Enumerator() {
+      normalize();
+    }
+
+    @Override
+    public final void reset() { this.index = 0; }
+
+    @Override
+    public final Value nextElement() {
+    	if (coverage) { cm.incSecondary(); }
+      if (this.index < elems.size()) {
+        return elems.elementAt(this.index++);
+      }
+      return null;
+    }
+  }
+
+    @Override
+	public EnumerableValue getRandomSubset(final int kOutOfN) {
+    	final ValueVec vec = new ValueVec(kOutOfN);
+    	
+    	final ValueEnumeration ve = elements(kOutOfN);
+    	
+    	Value v = null;
+    	while ((v = ve.nextElement()) != null) {
+    		vec.addElement(v);
+    	}
+    	return new SetEnumValue(vec, false, cm);
+	}
+
+	@Override
+	public ValueEnumeration elements(final int k) {
+		normalize();
+		return new EnumerableValue.SubsetEnumerator(k) {
+			@Override
+			public Value nextElement() {
+				if (!hasNext()) {
+					return null;
+				}
+				return elems.elementAt(nextIndex());
+			}
+		};
+	}
+
+	public static IValue createFrom(final IValueInputStream vos) throws IOException {
+		final int index = vos.getIndex();
+		boolean isNorm = true;
+		int len = vos.readInt();
+		if (len < 0) {
+			len = -len;
+			isNorm = false;
+		}
+		final Value[] elems = new Value[len];
+		for (int i = 0; i < len; i++) {
+			elems[i] = (Value) vos.read();
+		}
+		final Value res = new SetEnumValue(elems, isNorm);
+		vos.assign(res, index);
+		return res;
+	}
+
+	public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException {
+		final int index = vos.getIndex();
+		boolean isNorm = true;
+		int len = vos.readInt();
+		if (len < 0) {
+			len = -len;
+			isNorm = false;
+		}
+		final Value[] elems = new Value[len];
+		for (int i = 0; i < len; i++) {
+			elems[i] = (Value) vos.read(tbl);
+		}
+		final Value res = new SetEnumValue(elems, isNorm);
+		vos.assign(res, index);
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/SetOfFcnsOrRcdsValue.java b/tlatools/src/tlc2/value/impl/SetOfFcnsOrRcdsValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a04b08e989a43bf17988f3ab4e42b1b7cf7d7f0
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetOfFcnsOrRcdsValue.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import java.math.BigInteger;
+
+import tlc2.value.RandomEnumerableValues;
+
+public abstract class SetOfFcnsOrRcdsValue extends EnumerableValue {
+
+	@Override
+	public EnumerableValue getRandomSubset(final int kOutOfN) {
+		final ValueVec vec = new ValueVec(kOutOfN);
+
+		final ValueEnumeration ve = elements(kOutOfN);
+
+		Value v = null;
+		while ((v = ve.nextElement()) != null) {
+			vec.addElement(v);
+		}
+    	
+		// Assert no duplicates. For large sets we assume kOutOfN < size() to avoid
+		// calling size() which then throws an assertion exception anyway.
+		assert (needBigInteger() ? vec.sort(true).size() == kOutOfN
+				: vec.sort(true).size() == Math.min(kOutOfN, size()));
+
+		if (coverage) {cm.incSecondary(vec.size());}
+    	return new SetEnumValue(vec, false, cm);
+	}
+
+	@Override
+	public ValueEnumeration elements(final int k) {
+		if (needBigInteger()) {
+			return getBigSubsetEnumerator(k);
+		} else {
+			return getSubsetEnumerator(k, size());
+		}
+	}
+
+	protected abstract BigIntegerSubsetEnumerator getBigSubsetEnumerator(int k);
+
+	protected abstract SubsetEnumerator getSubsetEnumerator(int k, int n);
+
+	protected abstract boolean needBigInteger();
+
+	abstract class SubsetEnumerator extends EnumerableValue.SubsetEnumerator {
+		public SubsetEnumerator(final int k, final int n) {
+			super(k, n);
+		}
+
+		@Override
+		public Value nextElement() {
+			if (!hasNext()) {
+				return null;
+			}
+			return elementAt(nextIndex());
+		}
+
+		protected abstract Value elementAt(int nextIndex);
+	}
+
+	abstract class BigIntegerSubsetEnumerator implements ValueEnumeration {
+
+		protected final BigInteger x;
+		protected final BigInteger a;
+
+		protected final int k;
+		
+		protected BigInteger sz;
+		protected int i;
+
+		public BigIntegerSubsetEnumerator(final int k) {
+			this.k = k;
+			this.i = 0;
+
+			this.a = BigInteger.valueOf(Math.abs(RandomEnumerableValues.get().nextLong()));
+
+			// http://primes.utm.edu/lists/2small/0bit.html
+			// (2^63 - 25)
+			this.x = BigInteger.valueOf(Long.MAX_VALUE - 24L);
+		}
+
+		private BigInteger nextIndex() {
+			// ((x * i) + a) % sz
+			final BigInteger bi = BigInteger.valueOf(this.i++);
+			final BigInteger multiply = this.x.multiply(bi);
+			return multiply.add(this.a).mod(this.sz);
+		}
+
+		@Override
+		public void reset() {
+			this.i = 0;
+		}
+
+		private boolean hasNext() {
+			return this.i < this.k;
+		}
+
+		@Override
+		public Value nextElement() {
+			if (!hasNext()) {
+				return null;
+			}
+			return elementAt(nextIndex());
+		}
+
+		protected abstract Value elementAt(BigInteger nextIndex);
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/SetOfFcnsValue.java b/tlatools/src/tlc2/value/impl/SetOfFcnsValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..4498dd72263af7438cc1c2e10e25d38bb64d46fb
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetOfFcnsValue.java
@@ -0,0 +1,539 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:17:11 PST by lamport
+//      modified on Fri Aug 10 15:09:46 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SetOfFcnsValue extends SetOfFcnsOrRcdsValue implements Enumerable {
+  public final Value domain;        /* Function domain  */
+  public final Value range;         /* Function range   */
+  protected SetEnumValue fcnSet;
+
+  /* Constructor */
+  public SetOfFcnsValue(Value domain, Value range) {
+    this.domain = domain;
+    this.range = range;
+    this.fcnSet = null;
+  }
+
+  public SetOfFcnsValue(Value domain, Value range, CostModel cm) {
+	  this(domain, range);
+	  this.cm = cm;
+  }
+
+  @Override
+  public final byte getKind() { return SETOFFCNSVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.fcnSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof SetOfFcnsValue) {
+        SetOfFcnsValue fcns = (SetOfFcnsValue)obj;
+        return (this.domain.equals(fcns.domain) &&
+          this.range.equals(fcns.range));
+      }
+      this.convertAndCache();
+      return this.fcnSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      FcnRcdValue fcn = (FcnRcdValue) elem.toFcnRcd();
+      if (fcn == null) {
+        if (elem instanceof ModelValue)
+           return ((ModelValue) elem).modelValueMember(this) ;
+        Assert.fail("Attempted to check if \n" + elem + "\nwhich is not a TLC function" +
+        " value, is in the set of functions:\n" + Values.ppr(this.toString()));
+      }
+      if (fcn.intv == null) {
+        fcn.normalize();
+        Value fdom = new SetEnumValue(fcn.domain, true);
+        if (this.domain.equals(fdom)) {
+          for (int i = 0; i < fcn.values.length; i++) {
+            if (!this.range.member(fcn.values[i])) {
+              return false;
+            }
+          }
+          return true;
+        }
+      }
+      else {
+        if (fcn.intv.equals(this.domain)) {
+          for (int i = 0; i < fcn.values.length; i++) {
+            if (!this.range.member(fcn.values[i])) return false;
+          }
+          return true;
+        }
+      }
+      return false;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      return this.domain.isFinite() && this.range.isFinite();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" +
+        Values.ppr(this.toString()));
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" +
+        Values.ppr(this.toString()));
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      int dsz = this.domain.size();
+      int rsz = this.range.size();
+      long sz = 1;
+      for (int i = 0; i < dsz; i++) {
+        sz *= rsz;
+        if (sz < -2147483648 || sz > 2147483647) {
+          Assert.fail("Overflow when computing the number of elements in:\n" +
+                Values.ppr(toString()));
+        }
+      }
+      return (int)sz;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	protected boolean needBigInteger() {
+		final int rsz = this.range.size();
+		final int dsz = this.domain.size();
+		long sz = 1;
+		for (int i = 0; i < dsz; i++) {
+			sz *= rsz;
+			if (sz < -2147483648 || sz > 2147483647) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.fcnSet == null || this.fcnSet == SetEnumValue.DummyEnum) {
+        return this.domain.isNormalized() && this.range.isNormalized();
+      }
+      return this.fcnSet.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.fcnSet == null || this.fcnSet == SetEnumValue.DummyEnum) {
+        this.domain.normalize();
+        this.range.normalize();
+      }
+      else {
+        this.fcnSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+      domain.deepNormalize();
+      range.deepNormalize();
+      if (fcnSet == null) {
+        fcnSet = SetEnumValue.DummyEnum;
+      }
+      else if (fcnSet != SetEnumValue.DummyEnum) {
+        fcnSet.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      return this.domain.isDefined() && this.range.isDefined();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The fingerprint  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.fcnSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.fcnSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.fcnSet == null) {
+      this.fcnSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.fcnSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.fcnSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.fcnSet == SetEnumValue.DummyEnum) { this.fcnSet = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.fcnSet != null && this.fcnSet != SetEnumValue.DummyEnum) {
+        return this.fcnSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, this.isNormalized(), cm);
+  }
+
+  @Override
+  public final void write(final IValueOutputStream vos) throws IOException {
+	  fcnSet.write(vos);
+  }
+
+  /* The string representation of the value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      boolean unlazy = TLCGlobals.expand;
+      try {
+        if (unlazy) {
+          int dsz = this.domain.size();
+          int rsz = this.range.size();
+          long sz = 1;
+          for (int i = 0; i < dsz; i++) {
+            sz *= rsz;
+            if (sz < -2147483648 || sz > 2147483647) {
+              unlazy = false;
+              break;
+            }
+          }
+          unlazy = sz < TLCGlobals.enumBound;
+        }
+      }
+      catch (Throwable e) { if (swallow) unlazy = false; else throw e; }
+
+      if (unlazy) {
+        Value val = this.toSetEnum();
+        return val.toString(sb, offset, swallow);
+      }
+      else {
+        sb.append("[");
+        this.domain.toString(sb, offset, swallow);
+        sb.append(" -> ");
+        this.range.toString(sb, offset, swallow);
+        sb.append("]");
+        return sb;
+      }
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.fcnSet == null || this.fcnSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.fcnSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    private Value[] dom;
+    private ValueEnumeration[] enums;
+    private Value[] currentElems;
+    private boolean isDone;
+
+    public Enumerator() {
+      this.isDone = false;
+      SetEnumValue domSet = (SetEnumValue) domain.toSetEnum();
+      if (domSet == null)
+        Assert.fail("Attempted to enumerate a set of the form [D -> R]," +
+              "but the domain D:\n" + Values.ppr(domain.toString()) +
+              "\ncannot be enumerated.");
+      domSet.normalize();
+      ValueVec elems = domSet.elems;
+      int sz = elems.size();
+      if (range instanceof Enumerable) {
+        this.dom = new Value[sz];
+        this.enums = new ValueEnumeration[sz];
+        this.currentElems = new Value[sz];
+        // SZ Feb 24, 2009: never read locally
+        // ValueEnumeration enumeration = ((Enumerable)domSet).elements();
+        for (int i = 0; i < sz; i++) {
+          this.dom[i] = elems.elementAt(i);
+          this.enums[i] = ((Enumerable)range).elements();
+          this.currentElems[i] = this.enums[i].nextElement();
+          if (this.currentElems[i] == null) {
+            this.enums = null;
+            this.isDone = true;
+            break;
+          }
+        }
+      }
+      else {
+        Assert.fail("Attempted to enumerate a set of the form [D -> R]," +
+              "but the range R:\n" + Values.ppr(range.toString()) +
+              "\ncannot be enumerated.");
+      }
+    }
+
+    @Override
+    public final void reset() {
+      if (this.enums != null) {
+        for (int i = 0; i < this.enums.length; i++) {
+          this.enums[i].reset();
+          this.currentElems[i] = this.enums[i].nextElement();
+        }
+        this.isDone = false;
+      }
+    }
+
+		@Override
+        public final Value nextElement() {
+			if (this.isDone) {
+				return null;
+			}
+			if (this.currentElems.length == 0) {
+		    	  if (coverage) { cm.incSecondary(); }
+				this.isDone = true;
+				return new FcnRcdValue(this.dom, new Value[this.currentElems.length], true, cm);
+			} else {
+				// Take and store a snapshot of currentElems as the element to return for
+				// this invocation of nextElement().
+				final Value[] elems = new Value[this.currentElems.length];
+				System.arraycopy(this.currentElems, 0, elems, 0, this.currentElems.length);
+
+				// Eagerly generate the next element which is going to be returned the upon next
+				// invocation of nextElement().
+		    	  if (coverage) { cm.incSecondary(this.currentElems.length); }
+				for (int i = this.currentElems.length - 1; i >= 0; i--) {
+					this.currentElems[i] = this.enums[i].nextElement();
+					if (this.currentElems[i] != null) {
+						break;
+					}
+					if (i == 0) {
+						this.isDone = true;
+						break;
+					}
+					this.enums[i].reset();
+					this.currentElems[i] = this.enums[i].nextElement();
+				}
+				
+				return new FcnRcdValue(this.dom, elems, true, cm);
+			}
+		}
+
+  }
+	
+	@Override
+	protected tlc2.value.impl.SetOfFcnsOrRcdsValue.SubsetEnumerator getSubsetEnumerator(int k, int n) {
+		return new SubsetEnumerator(k, n);
+	}
+
+	class SubsetEnumerator extends SetOfFcnsOrRcdsValue.SubsetEnumerator {
+		private final SetEnumValue domSet;
+		private final SetEnumValue rangeSet;
+		private final int mod;
+		
+		SubsetEnumerator(final int k, final int n) {
+			super(k, n);
+			domSet = (SetEnumValue) domain.toSetEnum();
+			domSet.normalize();
+
+			rangeSet = (SetEnumValue) range.toSetEnum();
+
+			mod = range.size();
+		}
+
+		@Override
+		protected Value elementAt(final int idx) {
+			assert 0 <= idx && idx < size();
+
+			final Value[] range = new Value[domSet.size()];
+
+			for (int i = 0; i < domSet.size(); i++) {
+				final int elementAt = (int) (Math.floor(idx / Math.pow(mod, i)) % mod);
+				range[range.length - 1 - i] = rangeSet.elems.elementAt(elementAt);
+			}
+
+			return new FcnRcdValue(domSet.elems, range, true);
+		}
+	}
+
+	@Override
+	protected tlc2.value.impl.SetOfFcnsOrRcdsValue.BigIntegerSubsetEnumerator getBigSubsetEnumerator(int k) {
+		return new BigIntegerSubsetEnumerator(k);
+	}
+	
+	class BigIntegerSubsetEnumerator extends SetOfFcnsOrRcdsValue.BigIntegerSubsetEnumerator {
+		
+		private final SetEnumValue domSet;
+		private final SetEnumValue rangeSet;
+		private final BigInteger bMod;
+		private final int mod;
+
+		public BigIntegerSubsetEnumerator(final int k) {
+			super(k);
+			this.domSet = (SetEnumValue) domain.toSetEnum();
+			this.domSet.normalize();
+			
+			this.rangeSet = (SetEnumValue) range.toSetEnum();
+			this.mod = range.size();
+			this.bMod = BigInteger.valueOf(mod);
+
+			this.sz = bMod.pow(domSet.size());
+		}
+
+		@Override
+		protected Value elementAt(final BigInteger idx) {
+			final Value[] range = new Value[domSet.size()];
+
+			for (int i = 0; i < domSet.size(); i++) {
+				final long scale = (long) Math.pow(mod, i);
+				final BigInteger bScale = BigInteger.valueOf(scale);
+				// idx2 is the index in the range (0,range.size^domset.size] 
+				final BigInteger idx2 = idx.divide(bScale);
+				final int elementAt = idx2.mod(bMod).intValueExact();
+				range[range.length - 1 - i] = rangeSet.elems.elementAt(elementAt);
+			}
+
+			return new FcnRcdValue(domSet.elems, range, true);
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/SetOfRcdsValue.java b/tlatools/src/tlc2/value/impl/SetOfRcdsValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..8cbff7cbc8b27c210b4b647cd42d92d7684e5c0b
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetOfRcdsValue.java
@@ -0,0 +1,581 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:18:01 PST by lamport
+//      modified on Fri Aug 10 15:09:53 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import tlc2.TLCGlobals;
+import tlc2.output.EC;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.UniqueString;
+
+public class SetOfRcdsValue extends SetOfFcnsOrRcdsValue implements Enumerable {
+  public final UniqueString[] names;      // The names of the fields.
+  public final Value[] values;            // The values of the fields.
+  protected SetEnumValue rcdSet;
+
+  /* Constructor */
+  public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm) {
+	  assert names.length == values.length; // see tlc2.tool.Tool.evalAppl(OpApplNode, Context, TLCState, TLCState, int) case for OPCODE_sor
+    this.names = names;
+    this.values = values;
+    this.rcdSet = null;
+    if (!isNorm) {
+      this.sortByNames();
+    }
+  }
+
+  public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm, CostModel cm) {
+	  this(names, values, isNorm);
+	  this.cm = cm;
+  }
+
+  @Override
+  public final byte getKind() { return SETOFRCDSVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.rcdSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof SetOfRcdsValue) {
+        SetOfRcdsValue rcds = (SetOfRcdsValue)obj;
+
+        boolean isEmpty1 = this.isEmpty();
+        if (isEmpty1) { return rcds.isEmpty(); }
+        if (rcds.isEmpty()) { return isEmpty1; }
+
+        if (this.names.length != rcds.names.length) {
+          return false;
+        }
+        for (int i = 0; i < this.names.length; i++) {
+          if (!this.names[i].equals(rcds.names[i]) ||
+              !this.values[i].equals(rcds.values[i])) {
+            return false;
+          }
+        }
+        return true;
+      }
+      this.convertAndCache();
+      return this.rcdSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      RecordValue rcd = (RecordValue) elem.toRcd();
+      if (rcd == null) {
+        if (elem instanceof ModelValue)
+           return ((ModelValue) elem).modelValueMember(this) ;
+        Assert.fail("Attempted to check if non-record\n" + elem + "\nis in the" +
+        " set of records:\n" + Values.ppr(this.toString()));
+      }
+      rcd.normalize();
+      if (this.names.length != rcd.names.length) {
+        return false;
+      }
+      for (int i = 0; i < this.names.length; i++) {
+        if ((!this.names[i].equals(rcd.names[i])) ||
+          (!this.values[i].member(rcd.values[i]))) {
+          return false;
+        }
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      for (int i = 0; i < this.values.length; i++) {
+        if (!this.values[i].isFinite()) return false;
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set of records:\n" +
+        Values.ppr(this.toString()));
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set of records:\n" +
+        Values.ppr(this.toString()));
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      long sz = 1;
+      for (int i = 0; i < this.values.length; i++) {
+        sz *= this.values[i].size();
+        if (sz < -2147483648 || sz > 2147483647) {
+          Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" +
+                Values.ppr(this.toString()));
+        }
+      }
+      return (int)sz;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	protected boolean needBigInteger() {
+		long sz = 1;
+		for (int i = 0; i < values.length; i++) {
+			sz *= values[i].size();
+			if (sz < -2147483648 || sz > 2147483647) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.rcdSet == null || this.rcdSet == SetEnumValue.DummyEnum) {
+        for (int i = 0; i < this.names.length; i++) {
+          if (!this.values[i].isNormalized()) {
+            return false;
+          }
+        }
+        return true;
+      }
+      return this.rcdSet.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.rcdSet == null || this.rcdSet == SetEnumValue.DummyEnum) {
+        for (int i = 0; i < this.names.length; i++) {
+          this.values[i].normalize();
+        }
+      }
+      else {
+        this.rcdSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+      for (int i = 0; i < values.length; i++) {
+          values[i].deepNormalize();
+        }
+        if (rcdSet == null) {
+          rcdSet = SetEnumValue.DummyEnum;
+        }
+        else if (rcdSet != SetEnumValue.DummyEnum) {
+          rcdSet.deepNormalize();
+        }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  private final void sortByNames() {
+    for (int i = 1; i < this.names.length; i++) {
+      int cmp = this.names[0].compareTo(this.names[i]);
+      if (cmp == 0) {
+        Assert.fail("Field name " + this.names[0] + " occurs multiple times" +
+              " in set of records.");
+      }
+      else if (cmp > 0) {
+        UniqueString ts = this.names[0];
+        this.names[0] = this.names[i];
+        this.names[i] = ts;
+        Value tv = this.values[0];
+        this.values[0] = this.values[i];
+        this.values[i] = tv;
+      }
+    }
+    for (int i = 2; i < this.names.length; i++) {
+      int j = i;
+      UniqueString st = this.names[i];
+      Value val = this.values[i];
+      int cmp;
+      while ((cmp = st.compareTo(this.names[j-1])) < 0) {
+        this.names[j] = this.names[j-1];
+        this.values[j] = this.values[j-1];
+        j--;
+      }
+      if (cmp == 0) {
+        Assert.fail("Field name " + this.names[i] + " occurs multiple times" +
+              " in set of records.");
+      }
+      this.names[j] = st;
+      this.values[j] = val;
+    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      boolean isDefined = true;
+      for (int i = 0; i < this.values.length; i++) {
+        isDefined = isDefined && this.values[i].isDefined();
+      }
+      return isDefined;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The fingerprint  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.rcdSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.rcdSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.rcdSet == null) {
+      this.rcdSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.rcdSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.rcdSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.rcdSet == SetEnumValue.DummyEnum) { this.rcdSet = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.rcdSet != null && this.rcdSet != SetEnumValue.DummyEnum) {
+        return this.rcdSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, this.isNormalized(), cm);
+  }
+
+  @Override
+  public final void write(final IValueOutputStream vos) throws IOException {
+	  rcdSet.write(vos);
+  }
+
+  /* The string representation of the value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      boolean unlazy = TLCGlobals.expand;
+      try {
+        if (unlazy) {
+          long sz = 1;
+          for (int i = 0; i < this.values.length; i++) {
+            sz *= this.values[i].size();
+            if (sz < -2147483648 || sz > 2147483647) {
+              unlazy = false;
+              break;
+            }
+          }
+          unlazy = sz < TLCGlobals.enumBound;
+        }
+      }
+      catch (Throwable e) { if (swallow) unlazy = false; else throw e; }
+
+      if (unlazy) {
+        Value val = this.toSetEnum();
+        return val.toString(sb, offset, swallow);
+      }
+      else {
+        sb.append("[");
+        int len = this.names.length;
+        if (len != 0) {
+          sb.append(names[0] + ": ");
+          this.values[0].toString(sb, offset, swallow);
+        }
+        for (int i = 1; i < len; i++) {
+          sb.append(", ");
+          sb.append(names[i] + ": ");
+          this.values[i].toString(sb, offset, swallow);
+        }
+        sb.append("]");
+        return sb;
+      }
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.rcdSet == null || this.rcdSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.rcdSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    private ValueEnumeration[] enums;
+    private Value[] currentElems;
+    private boolean isDone;
+
+    public Enumerator() {
+      this.enums = new ValueEnumeration[values.length];
+      this.currentElems = new Value[values.length];
+      this.isDone = false;
+      for (int i = 0; i < values.length; i++) {
+        if (values[i] instanceof Enumerable) {
+          this.enums[i] = ((Enumerable)values[i]).elements();
+          this.currentElems[i] = this.enums[i].nextElement();
+          if (this.currentElems[i] == null) {
+            this.enums = null;
+            this.isDone = true;
+            break;
+          }
+        }
+        else {
+          Assert.fail("Attempted to enumerate a set of the form [l1 : v1, ..., ln : vn]," +
+                "\nbut can't enumerate the value of the `" + names[i] + "' field:\n" +
+                Values.ppr(values[i].toString()));
+        }
+      }
+    }
+
+    @Override
+    public final void reset() {
+      if (this.enums != null) {
+        for (int i = 0; i < this.enums.length; i++) {
+          this.enums[i].reset();
+          this.currentElems[i] = this.enums[i].nextElement();
+        }
+        this.isDone = false;
+      }
+    }
+
+    @Override
+    public final Value nextElement() {
+      if (this.isDone) return null;
+      Value[] elems = new Value[this.currentElems.length];
+     if (coverage) { cm.incSecondary(elems.length); }
+      for (int i = 0; i < elems.length; i++) {
+        elems[i] = this.currentElems[i];
+      }
+      for (int i = elems.length-1; i >= 0; i--) {
+        this.currentElems[i] = this.enums[i].nextElement();
+        if (this.currentElems[i] != null) break;
+        if (i == 0) {
+          this.isDone = true;
+          break;
+        }
+        this.enums[i].reset();
+        this.currentElems[i] = this.enums[i].nextElement();
+      }
+      return new RecordValue(names, elems, true, cm);
+    }
+
+  }
+
+	@Override
+	protected tlc2.value.impl.SetOfFcnsOrRcdsValue.SubsetEnumerator getSubsetEnumerator(int k, int n) {
+		return new SubsetEnumerator(k, n);
+	}
+	
+	class SubsetEnumerator extends SetOfFcnsOrRcdsValue.SubsetEnumerator {
+		
+		private final SetEnumValue[] convert;
+		private final int[] rescaleBy;
+
+		SubsetEnumerator(final int k, final int n) {
+			super(k, n);
+			
+			convert = new SetEnumValue[values.length];
+			rescaleBy = new int[values.length];
+			
+			int numElems = 1; // 1 to avoid div by zero in elementAt
+			for (int i = values.length - 1; i >= 0; i--) {
+				convert[i] = (SetEnumValue) values[i].toSetEnum();
+				rescaleBy[i] = numElems;
+				numElems *= convert[i].elems.size();
+			}
+		}
+
+		@Override
+        protected RecordValue elementAt(final int idx) {
+			assert 0 <= idx && idx < size();
+			
+			final Value[] val = new Value[names.length];
+			for (int i = 0; i < val.length; i++) {
+				final SetEnumValue sev = convert[i];
+				final int mod = sev.elems.size();
+				
+				final int rescaledIdx = (int) Math.floor(idx / rescaleBy[i]);
+				final int elementAt = rescaledIdx % mod;
+				
+				val[i] = sev.elems.elementAt(elementAt);
+			}
+			return new RecordValue(names, val, false, cm);
+		}
+	}
+	
+	@Override
+	protected BigIntegerSubsetEnumerator getBigSubsetEnumerator(int k) {
+		return new BigIntegerSubsetEnumerator(k);
+	}
+	
+	class BigIntegerSubsetEnumerator extends SetOfFcnsOrRcdsValue.BigIntegerSubsetEnumerator {
+		
+		private final SetEnumValue[] convert;
+		private final BigInteger[] rescaleBy;
+		
+		public BigIntegerSubsetEnumerator(final int k) {
+			super(k);
+			
+			convert = new SetEnumValue[values.length];
+			rescaleBy = new BigInteger[values.length];
+			
+			BigInteger numElems = BigInteger.ONE; // 1 to avoid div by zero in elementAt
+			for (int i = values.length - 1; i >= 0; i--) {
+				convert[i] = (SetEnumValue) values[i].toSetEnum();
+				rescaleBy[i] = numElems;
+				numElems = numElems.multiply(BigInteger.valueOf(convert[i].elems.size()));
+			}
+			
+			// The size of the (enumerated) SetOfFcnsOrRcdsValue needs BigInteger.
+			this.sz = numElems;
+		}
+
+		@Override
+		protected Value elementAt(final BigInteger idx) {
+			final Value[] val = new Value[names.length];
+			for (int i = 0; i < val.length; i++) {
+				final SetEnumValue sev = convert[i];
+				final int mod = sev.elems.size();
+
+				final BigInteger d = idx.divide(rescaleBy[i]);
+				final BigInteger m = d.mod(BigInteger.valueOf(mod));
+				final int elementAt = m.intValueExact();
+
+				val[i] = sev.elems.elementAt(elementAt);
+			}
+			return new RecordValue(names, val, false, cm);
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/SetOfTuplesValue.java b/tlatools/src/tlc2/value/impl/SetOfTuplesValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a73ac078f3fe833c7fd228c95fae65fa741aeac
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetOfTuplesValue.java
@@ -0,0 +1,456 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:18:39 PST by lamport
+//      modified on Fri Aug 10 15:09:59 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SetOfTuplesValue extends EnumerableValue implements Enumerable {
+  public final Value[] sets;
+  protected SetEnumValue tupleSet;
+
+  /* Constructor */
+  public SetOfTuplesValue(Value[] sets) {
+    this.sets = sets;
+    this.tupleSet = null;
+  }
+  public SetOfTuplesValue(Value[] set, CostModel cm) {
+	  this(set);
+	  this.cm = cm;
+  }
+
+  public SetOfTuplesValue(Value val) {
+	  this(new Value[1]);
+    this.sets[0] = val;
+  }
+
+  public SetOfTuplesValue(Value v1, Value v2) {
+	  this(new Value[2]);
+    this.sets[0] = v1;
+    this.sets[1] = v2;
+  }
+
+  @Override
+  public final byte getKind() { return SETOFTUPLESVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.tupleSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof SetOfTuplesValue) {
+        SetOfTuplesValue tvs = (SetOfTuplesValue)obj;
+
+        boolean isEmpty1 = this.isEmpty();
+        if (isEmpty1) return tvs.isEmpty();
+        if (tvs.isEmpty()) return isEmpty1;
+
+        if (this.sets.length != tvs.sets.length) {
+          return false;
+        }
+        for (int i = 0; i < this.sets.length; i++) {
+          if (!this.sets[i].equals(tvs.sets[i])) {
+            return false;
+          }
+        }
+        return true;
+      }
+      this.convertAndCache();
+      return this.tupleSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      TupleValue tv = (TupleValue) elem.toTuple();
+      if (tv == null) {
+        FcnRcdValue fcn = (FcnRcdValue) elem.toFcnRcd();
+        if (fcn == null) {
+          if (elem instanceof ModelValue)
+            return ((ModelValue) elem).modelValueMember(this) ;
+          Assert.fail("Attempted to check if non-tuple\n" + Values.ppr(elem.toString()) +
+                "\nis in the set of tuples:\n" + Values.ppr(this.toString()));
+        }
+        if (fcn.intv != null) return false;
+        for (int i = 0; i < fcn.domain.length; i++) {
+          if (!(fcn.domain[i] instanceof IntValue)) {
+            Assert.fail("Attempted to check if non-tuple\n" + Values.ppr(elem.toString()) +
+                  "\nis in the set of tuples:\n" + Values.ppr(this.toString()));
+          }
+        }
+        return false;
+      }
+      if (tv.elems.length == this.sets.length) {
+        for (int i = 0; i < this.sets.length; i++) {
+          if (!this.sets[i].member(tv.elems[i]))
+            return false;
+        }
+        return true;
+      }
+      return false;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      for (int i = 0; i < this.sets.length; i++) {
+        if (!this.sets[i].isFinite()) {
+          return false;
+        }
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" +
+        Values.ppr(this.toString()));
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" +
+        Values.ppr(this.toString()));
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      long sz = 1;
+      for (int i = 0; i < this.sets.length; i++) {
+        sz *= this.sets[i].size();
+        if (sz < -2147483648 || sz > 2147483647) {
+          Assert.fail("Overflow when computing the number of elements in " +
+                Values.ppr(this.toString()));
+        }
+      }
+      return (int)sz;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      if (this.tupleSet == null || this.tupleSet == SetEnumValue.DummyEnum) {
+        for (int i = 0; i < this.sets.length; i++) {
+          if (!this.sets[i].isNormalized()) {
+            return false;
+          }
+        }
+        return true;
+      }
+      return this.tupleSet.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.tupleSet == null || this.tupleSet == SetEnumValue.DummyEnum) {
+        for (int i = 0; i < this.sets.length; i++) {
+          this.sets[i].normalize();
+        }
+      }
+      else {
+        this.tupleSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+      for (int i = 0; i < sets.length; i++) {
+          sets[i].deepNormalize();
+        }
+        if (tupleSet == null) {
+          tupleSet = SetEnumValue.DummyEnum;
+        }
+        else if (tupleSet != SetEnumValue.DummyEnum) {
+          tupleSet.deepNormalize();
+        }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      boolean defined = true;
+      for (int i = 0; i < this.sets.length; i++) {
+        defined = defined && this.sets[i].isDefined();
+      }
+      return defined;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The fingerprint  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.tupleSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.tupleSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.tupleSet == null) {
+      this.tupleSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.tupleSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.tupleSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.tupleSet == SetEnumValue.DummyEnum) { this.tupleSet = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.tupleSet != null && this.tupleSet != SetEnumValue.DummyEnum) {
+        return this.tupleSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, this.isNormalized(), cm);
+  }
+
+  @Override
+  public final void write(final IValueOutputStream vos) throws IOException {
+	  tupleSet.write(vos);
+  }
+
+  /* The string representation of the value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      boolean unlazy = TLCGlobals.expand;
+      try {
+        if (unlazy) {
+          long sz = 1;
+          for (int i = 0; i < this.sets.length; i++) {
+            sz *= this.sets[i].size();
+            if (sz < -2147483648 || sz > 2147483647) {
+              unlazy = false;
+              break;
+            }
+          }
+          unlazy = sz < TLCGlobals.enumBound;
+        }
+      }
+      catch (Throwable e) { if (swallow) unlazy = false; else throw e; }
+
+      if (unlazy) {
+        Value val = this.toSetEnum();
+        return val.toString(sb, offset, swallow);
+      }
+      else {
+        if (this.sets.length > 0) {
+          sb.append("(");
+          this.sets[0].toString(sb, offset, swallow);
+        }
+        for (int i = 1; i < this.sets.length; i++) {
+          sb.append(" \\X ");
+          this.sets[i].toString(sb, offset, swallow);
+        }
+        if (this.sets.length > 0) {
+          sb.append(")");
+        }
+        return sb;
+      }
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.tupleSet == null || this.tupleSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.tupleSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    private ValueEnumeration[] enums;
+    private Value[] currentElems;
+    private boolean isDone;
+
+    public Enumerator() {
+      this.enums = new ValueEnumeration[sets.length];
+      this.currentElems = new Value[sets.length];
+      this.isDone = false;
+      for (int i = 0; i < sets.length; i++) {
+        if (sets[i] instanceof Enumerable) {
+          this.enums[i] = ((Enumerable)sets[i]).elements();
+          this.currentElems[i] = this.enums[i].nextElement();
+          if (this.currentElems[i] == null) {
+            this.enums = null;
+            this.isDone = true;
+            break;
+          }
+        }
+        else {
+          Assert.fail("Attempted to enumerate a set of the form s1 \\X s2 ... \\X sn," +
+                "\nbut can't enumerate s" + i + ":\n" + Values.ppr(sets[i].toString()));
+        }
+      }
+    }
+
+    @Override
+    public final void reset() {
+      if (this.enums != null) {
+        for (int i = 0; i < this.enums.length; i++) {
+          this.enums[i].reset();
+          this.currentElems[i] = this.enums[i].nextElement();
+        }
+        this.isDone = false;
+      }
+    }
+
+    @Override
+    public final Value nextElement() {
+      if (this.isDone) return null;
+      Value[] elems = new Value[this.currentElems.length];
+	  if (coverage) { cm.incSecondary(elems.length); }
+      for (int i = 0; i < elems.length; i++) {
+        elems[i] = this.currentElems[i];
+      }
+      for (int i = elems.length-1; i >= 0; i--) {
+        this.currentElems[i] = this.enums[i].nextElement();
+        if (this.currentElems[i] != null) break;
+        if (i == 0) {
+          this.isDone = true;
+          break;
+        }
+        this.enums[i].reset();
+        this.currentElems[i] = this.enums[i].nextElement();
+      }
+      return new TupleValue(elems, cm);
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/SetPredValue.java b/tlatools/src/tlc2/value/impl/SetPredValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3ac2bfa8d01838f753c67e5f419e623259fa1ee
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SetPredValue.java
@@ -0,0 +1,423 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Thu  5 Jul 2007 at  4:44:23 PST by lamport
+//      modified on Fri Aug 10 15:10:04 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import tla2sany.semantic.FormalParamNode;
+import tla2sany.semantic.SemanticNode;
+import tlc2.TLCGlobals;
+import tlc2.tool.EvalException;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.ITool;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Context;
+import tlc2.value.IBoolValue;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SetPredValue extends EnumerableValue implements Enumerable {
+  public final Object vars;           // FormalParamNode or FormalParamNode[]
+    /***********************************************************************
+    * Was OpDeclNode or OpDeclNode[].                                      *
+    ***********************************************************************/
+  public Value inVal;           // the in value or the real set
+  public final SemanticNode pred;     // the predicate
+  public final ITool tool;             // null iff inVal is the real set
+  /**
+   * true after inVal has been converted to a SetEnumValue.  I assume this
+   * implies (inVal instanceof SetEnumValue) too but the serialization
+   * might interfere.
+   * MAK 07/18/2019
+   */
+  private boolean converted = false; 
+  public final Context con;
+  public final TLCState state;
+  public final TLCState pstate;
+  public final int control;
+
+  /* Constructor */
+  public SetPredValue(Object vars, Value  inVal, SemanticNode pred, ITool tool,
+          Context con, TLCState s0, TLCState s1, int control) {
+    this.vars = vars;
+    this.inVal = inVal;
+    this.pred = pred;
+    this.tool = tool;
+    this.con = con;
+    this.state = s0.copy();
+    if (s1 != null) {
+        this.pstate = s1.copy();
+    } else {
+        this.pstate = null;
+    }
+      /**
+       * The two copy()s above were added by YY on 12 Mar 2010 to fix the
+       * following bug: When a lazily evaluated expression is saved, the
+       * state under which it should be evaluated must be saved.  The
+       * s0 and s1 objects with which this method is called can be modified
+       * after the call, so copies must be made.
+       */
+    this.control = control;
+  }
+
+  public SetPredValue(Object vars, Value  inVal, SemanticNode pred, ITool tool,
+          Context con, TLCState s0, TLCState s1, int control, CostModel cm) {
+	  this(vars, inVal, pred, tool, con, s0, s1, control);
+	  this.cm = cm;
+  }
+  
+	public SetPredValue(SetPredValue other, ITool tool) {
+		this(other.vars, other.inVal, other.pred, tool, other.con, other.state, other.pstate, other.control, other.cm);
+	}
+
+  @Override
+  public final byte getKind() { return SETPREDVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.inVal = this.toSetEnum();
+      this.converted = true;
+      return this.inVal.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      this.inVal = this.toSetEnum();
+      this.converted = true;
+      return this.inVal.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      if (this.converted) {
+        return this.inVal.member(elem);
+      }
+      try {
+        if (this.inVal.member(elem)) {
+          Context con1 = this.con;
+          if (this.vars instanceof FormalParamNode) {
+            con1 = con1.cons((FormalParamNode)this.vars, elem);
+          }
+          else {
+            FormalParamNode[] ids = (FormalParamNode[])this.vars;
+            TupleValue tv = (TupleValue) elem.toTuple();
+            if ((tv != null) && (tv.elems.length == ids.length)) {
+              Value [] vals = ((TupleValue)tv).elems;
+              for (int i = 0; i < ids.length; i++) {
+                con1 = con1.cons(ids[i], vals[i]);
+              }
+            }
+            else {
+              Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+              "\nis an element of a set of " + ids.length + "-tuples.");
+            }
+          }
+          Value  res = (Value) this.tool.eval(this.pred, con1, this.state, this.pstate, this.control);
+          if (!(res instanceof IBoolValue)) {
+            Assert.fail("The evaluation of predicate " + this.pred +
+                  " yielded non-Boolean value.");
+          }
+          return ((BoolValue)res).val;
+        }
+      }
+      catch (EvalException e) {
+        Assert.fail("Cannot decide if element:\n" + Values.ppr(elem.toString()) +
+        "\n is element of:\n" + Values.ppr(this.inVal.toString()) +
+        "\nand satisfies the predicate " + this.pred);
+      }
+      return false;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      if (!(this.inVal.isFinite())) {
+        Assert.fail("Attempted to check if expression of form {x \\in S : p(x)} is a " +
+        "finite set, but cannot check if S:\n" + Values.ppr(this.inVal.toString()) +
+        "\nis finite.");
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      this.inVal = this.toSetEnum();
+      this.converted = true;
+      return this.inVal.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+    this.inVal = (Value )ois.readObject();
+    this.converted = true;
+  }
+
+  private final void writeObject(ObjectOutputStream oos) throws IOException {
+    if (!this.converted) {
+      this.inVal = this.toSetEnum();
+      this.converted = true;
+    }
+    oos.writeObject(this.inVal);
+  }
+
+  /* This method normalizes (destructively) this set. */
+  @Override
+  public final boolean isNormalized() {
+    try {
+      return this.inVal.isNormalized();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      this.inVal.normalize();
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	  try {
+      inVal.deepNormalize();
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The fingerprint method */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.inVal = this.toSetEnum();
+      this.converted = true;
+      return this.inVal.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.inVal = this.toSetEnum();
+      this.converted = true;
+      return this.inVal.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public Value toSetEnum() {
+      if (this.converted) {
+    	  return (SetEnumValue) this.inVal;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value  elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, this.isNormalized(), cm);
+  }
+
+  @Override
+  public void write(final IValueOutputStream vos) throws IOException {
+	  inVal.write(vos);
+  }
+
+  /* The string representation of the value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      try {
+        if (TLCGlobals.expand) {
+          Value  val = this.toSetEnum();
+          return val.toString(sb, offset, swallow);
+        }
+      }
+      catch (Throwable e) { if (!swallow) throw e; }
+
+      sb.append("{");
+      if (this.vars instanceof FormalParamNode) {
+        sb.append(((FormalParamNode)this.vars).getName());
+      }
+      else {
+        FormalParamNode[] ids = (FormalParamNode[])this.vars;
+        if (ids.length != 0) sb.append(ids[0].getName());
+        for (int i = 1; i < ids.length; i++) {
+          sb.append(", " + ids[i].getName());
+        }
+      }
+      sb.append(" \\in " + this.inVal + " : <expression ");
+      sb.append(this.pred + "> }");
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.converted) {
+        return ((SetEnumValue)this.inVal).elements();
+      }
+      return new Enumerator();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    ValueEnumeration Enum;
+
+    public Enumerator() {
+      if (!(inVal instanceof Enumerable)) {
+        Assert.fail("Attempted to enumerate { x \\in S : p(x) } when S:\n" +
+              Values.ppr(inVal.toString()) + "\nis not enumerable");
+      }
+      this.Enum = ((Enumerable)inVal).elements();
+    }
+
+    @Override
+    public final void reset() { this.Enum.reset(); }
+
+    @Override
+    public final Value nextElement() {
+    	Value  elem;
+      while ((elem = this.Enum.nextElement()) != null) {
+    	  if (coverage) { cm.incSecondary(); }
+        Context con1 = con;
+        if (vars instanceof FormalParamNode) {
+          con1 = con1.cons((FormalParamNode)vars, elem);
+        }
+        else {
+          FormalParamNode[] ids = (FormalParamNode[])vars;
+          TupleValue tv = (TupleValue) elem.toTuple();
+          if ((tv != null) &&
+              (((TupleValue)tv).elems.length == ids.length)) {
+            Value [] vals = ((TupleValue)tv).elems;
+            for (int i = 0; i < ids.length; i++) {
+              con1 = con1.cons(ids[i], vals[i]);
+            }
+          }
+          else {
+            Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+            "\nis an element of a set of " + ids.length + "-tuples.");
+          }
+        }
+        Value  res = (Value) tool.eval(pred, con1, state, pstate, control, cm);
+        if (!(res instanceof IBoolValue)) {
+          Assert.fail("Evaluating predicate " + pred + " yielded non-Boolean value.");
+        }
+        if (((BoolValue)res).val) return elem;
+      }
+      return null;
+    }
+
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/StringValue.java b/tlatools/src/tlc2/value/impl/StringValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e3e203210b58749e2dcdd9d2466cdb83c255590
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/StringValue.java
@@ -0,0 +1,304 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Sat 23 February 2008 at 10:19:41 PST by lamport
+//      modified on Fri Aug 10 15:06:37 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.util.Map;
+
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.UniqueString;
+
+public class StringValue extends Value {
+  public final UniqueString val;
+
+  /* Constructor */
+  public StringValue(String str) {
+    // SZ 11.04.2009: changed the access method to equivalent
+    this.val = UniqueString.uniqueStringOf(str);
+  }
+
+  public StringValue(UniqueString var) {
+    this.val = var;
+  }
+
+  public StringValue(UniqueString var, CostModel cm) {
+	  this(var);
+	  this.cm = cm;
+  }
+
+  @Override
+  public final byte getKind() { return STRINGVALUE; }
+
+  public final UniqueString getVal() { return this.val; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (obj instanceof StringValue) {
+        return this.val.compareTo(((StringValue)obj).val);
+      }
+      if (!(obj instanceof ModelValue)) {
+        Assert.fail("Attempted to compare string " + Values.ppr(this.toString()) +
+        " with non-string:\n" + Values.ppr(obj.toString()));
+      }
+      return 1;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof StringValue) {
+        return this.val.equals(((StringValue)obj).getVal());
+      }
+      if (!(obj instanceof ModelValue)) {
+        Assert.fail("Attempted to check equality of string " + Values.ppr(this.toString()) +
+        " with non-string:\n" + Values.ppr(obj.toString()));
+      }
+      return ((ModelValue) obj).modelValueEquals(this) ;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element of the string " + Values.ppr(this.toString()));
+      return false;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the string " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT construct to the string " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the string " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the string " +
+      Values.ppr(this.toString()) + ".");
+      return 0;       // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public boolean mutates() {
+	  // finalized after construction.
+	  return true;
+  }
+  
+  @Override
+  public final Value toTuple() {
+		final String s = val.toString();
+		Value[] vals = new Value[s.length()];
+		for (int i = 0; i < s.length(); i++) {
+			vals[i] = new StringValue(Character.toString(s.charAt(i)));
+		}
+		return new TupleValue(vals);
+  }
+
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*SKIP*/return this; }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return ((val instanceof StringValue) &&
+        this.equals(val));
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final int length() {
+    try {
+      return this.val.length();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public void write(IValueOutputStream vos) throws IOException {
+		final int index = vos.put(this);
+		if (index == -1) {
+			vos.writeByte(STRINGVALUE);
+			val.write(vos.getOutputStream());
+		} else {
+			vos.writeByte(DUMMYVALUE);
+			vos.writeNat(index);
+		}
+	}
+
+  /* The fingerprint method */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      fp = FP64.Extend(fp, STRINGVALUE) ;
+      fp = FP64.Extend(fp, this.val.length()) ;
+      fp = FP64.Extend(fp, this.val.toString());
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) { return this; }
+
+  /*************************************************************************
+  * toString() modified 23 Aug 2007 by LL to call PrintVersion so strings  *
+  * with special characters are printed properly.                          *
+  *************************************************************************/
+  final String PrintVersion(String str) {
+    try {
+      StringBuffer buf = new StringBuffer(str.length()) ;
+      for (int i = 0 ; i < str.length() ; i++) {
+        switch (str.charAt(i)) {
+          case '\"' :
+            buf.append("\\\"") ;
+            break ;
+          case '\\' :
+            buf.append("\\\\") ;
+            break ;
+          case '\t' :
+            buf.append("\\t") ;
+            break ;
+          case '\n' :
+            buf.append("\\n") ;
+            break ;
+          case '\f' :
+            buf.append("\\f") ;
+            break ;
+          case '\r' :
+            buf.append("\\r") ;
+            break ;
+          default :
+            buf.append(str.charAt(i)) ;
+            break ;
+         } // switch
+       }// for
+      return buf.toString();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+   }
+
+
+  /* The string representation of the value. */
+  @Override
+  public StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      return sb.append("\"" + PrintVersion(this.val.toString()) + "\"");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Same as toString. */
+  @Override
+  public final String toUnquotedString() {
+	  return PrintVersion(this.val.toString());
+  }
+
+	public static IValue createFrom(final IValueInputStream vos) throws IOException {
+		final UniqueString str = UniqueString.read(vos.getInputStream());
+		final IValue res = new StringValue(str);
+		final int index = vos.getIndex();
+		vos.assign(res, index);
+		return res;
+	}
+	
+	public static IValue createFrom(final IValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException {
+		final UniqueString str = UniqueString.read(vos.getInputStream(), tbl);
+		final IValue res = new StringValue(str);
+		final int index = vos.getIndex();
+		vos.assign(res, index);
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/SubsetValue.java b/tlatools/src/tlc2/value/impl/SubsetValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3e3d1776a155673ec5feffbca08468520903666
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SubsetValue.java
@@ -0,0 +1,869 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:46:07 PST by lamport
+//      modified on Fri Aug 10 15:10:16 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.TreeMap;
+
+import tlc2.TLCGlobals;
+import tlc2.output.EC;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.Combinatorics;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.Values;
+import util.Assert;
+
+public class SubsetValue extends EnumerableValue implements Enumerable {
+  public Value  set;           // SUBSET set
+  protected SetEnumValue pset;
+
+  /* Constructor */
+  public SubsetValue(Value  set) {
+    this.set = set;
+    this.pset = null;
+  }
+
+  public SubsetValue(Value  set, CostModel cm) {
+	  this(set);
+	  this.cm = cm;
+  }
+
+  @Override
+  public final byte getKind() { return SUBSETVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (obj instanceof SubsetValue) {
+        return this.set.compareTo(((SubsetValue)obj).set);
+      }
+      this.convertAndCache();
+      return this.pset.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      if (obj instanceof SubsetValue) {
+        return this.set.equals(((SubsetValue)obj).set);
+      }
+      this.convertAndCache();
+      return this.pset.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value val) {
+    try {
+      if (val instanceof Enumerable) {
+        ValueEnumeration Enum = ((Enumerable)val).elements();
+        Value  elem;
+        while ((elem = Enum.nextElement()) != null) {
+          if (!this.set.member(elem)) {
+        	  return false;
+          }
+        }
+      }
+      else {
+        Assert.fail("Attempted to check if the non-enumerable value\n" +
+        Values.ppr(val.toString()) + "\nis element of\n" + Values.ppr(this.toString()));
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public Value isSubsetEq(Value other) {
+    try {
+      // Reduce (SUBSET A \subseteq SUBSET B) to (A \subseteq B) to avoid
+      // exponential blowup inherent in generating the power set.
+      if (other instanceof SubsetValue && this.set instanceof Enumerable) {
+        final SubsetValue sv = (SubsetValue) other;
+        return ((Enumerable) this.set).isSubsetEq(sv.set);
+      }
+      return super.isSubsetEq(other);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      return this.set.isFinite();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      int sz = this.set.size();
+      if (sz >= 31) {
+        Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" +
+        Values.ppr(this.toString()));
+      }
+      return (1 << sz);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      return (this.pset != null &&
+        this.pset != SetEnumValue.DummyEnum &&
+        this.pset.isNormalized());
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.pset == null || this.pset == SetEnumValue.DummyEnum) {
+        this.set.normalize();
+      }
+      else {
+        this.pset.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+      set.deepNormalize();
+      if (pset == null) {
+        pset = SetEnumValue.DummyEnum;
+      }
+      else if (pset != SetEnumValue.DummyEnum) {
+        pset.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      return this.set.isDefined();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(final IValueOutputStream vos) throws IOException {
+		pset.write(vos);
+	}
+
+  /* The fingerprint  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.pset.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.pset.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.pset == null) {
+      this.pset = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.pset == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.pset == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.pset == SetEnumValue.DummyEnum) { this.pset = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.pset != null && this.pset != SetEnumValue.DummyEnum) {
+        return this.pset;
+      }
+      ValueVec vals = new ValueVec(this.size());
+      ValueEnumeration Enum = this.elements();
+      Value  elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      // For as long as pset.elements() (SubsetValue#elements)
+      // internally calls SubsetValue#elementsNormalized, the
+      // result SetEnumValue here is indeed normalized.
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, true, cm);
+  }
+
+  /* The string representation  */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      boolean unlazy = TLCGlobals.expand;
+      try {
+        if (unlazy) {
+          unlazy = this.set.size() < 7;
+        }
+      }
+      catch (Throwable e) { if (swallow) unlazy = false; else throw e; }
+
+      if (unlazy) {
+        Value  val = this.toSetEnum();
+        return val.toString(sb, offset, swallow);
+      }
+      else {
+        sb = sb.append("SUBSET ");
+        sb = this.set.toString(sb, offset, swallow);
+        return sb;
+      }
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }  
+  
+  	public Unrank getUnrank(final int kSubset) {
+		// Convert outer set only once.
+		final SetEnumValue convert = (SetEnumValue) set.toSetEnum();
+		convert.normalize();
+		final ValueVec elems = convert.elems;
+
+		return new Unrank(kSubset, Combinatorics.bigSumChoose(elems.size(), kSubset).longValueExact(),
+				Combinatorics.pascalTableUpTo(elems.size(), kSubset), elems, kSubset);
+  	}
+ 
+	public Enumerable getRandomSetOfSubsets(final int numOfSubsetsRequested, final int maxLengthOfSubsets) {
+		// Convert outer set only once.
+		final SetEnumValue convert = (SetEnumValue) set.toSetEnum();
+		convert.normalize();
+		final ValueVec elems = convert.elems;
+		final int size = elems.size();
+
+		// Calculate the sums of the rows of the Pascal Triangle up to
+		// maxLengthOfSubsets.
+		final long[] kss = new long[maxLengthOfSubsets + 1];
+		kss[0] = 1L;
+		// Sum the elems.size()'s row of the Pascal Triangle up to maxLengthOfSubsets.
+		// This corresponds to Combinatorics.bigSumChoose except that we also keep the
+		// intermediate results.
+		BigInteger sum = BigInteger.ONE; // 1 for k=0
+		for (int i = 1; i <= maxLengthOfSubsets; i++) {
+			kss[i] = Combinatorics.bigChoose(size, i).longValueExact();
+			sum = sum.add(BigInteger.valueOf(kss[i]));
+		}
+		assert sum.equals(Combinatorics.bigSumChoose(size, maxLengthOfSubsets));
+
+		// Extend existing Pascal Triangle by a table for the k's 2..maxLengthOfSubset
+		// for all n up to |S| (if needed, otherwise long[0]).
+		final long[] ppt = Combinatorics.pascalTableUpTo(size, maxLengthOfSubsets);
+
+		final ValueVec vec = new ValueVec(numOfSubsetsRequested);
+		for (int rank = 0; rank < kss.length; rank++) {
+			final BigDecimal divide = BigDecimal.valueOf(kss[rank]).divide(new BigDecimal(sum), 32,
+					BigDecimal.ROUND_HALF_DOWN);
+			// Small bias towards smaller/shorter k-Subsets because 0 gets rounded up to 1.
+			final long n = divide.multiply(BigDecimal.valueOf(numOfSubsetsRequested)).max(BigDecimal.ONE).toBigInteger()
+					.longValueExact();
+
+			// The last one (kSubsetSizes.length - 1) is generates the outstanding
+			// number of subsets (will be close to its calculated n anyway).
+			final RandomUnrank unrank = new RandomUnrank(rank,
+					rank == kss.length - 1 ? numOfSubsetsRequested - vec.size() : n, ppt, elems, maxLengthOfSubsets,
+							RandomEnumerableValues.get());
+
+			Value  subset;
+			while ((subset = unrank.randomSubset()) != null && vec.size() < numOfSubsetsRequested) {
+				vec.addElement(subset);
+			}
+		}
+		assert vec.size() == numOfSubsetsRequested;
+		return new SetEnumValue(vec, false, cm);
+	}
+
+	public class Unrank {
+
+		private final TreeMap<Long, Integer> sums = new TreeMap<>();
+		private final long[] partialPascalTable;
+		private final int maxK;
+
+		private final ValueVec elems;
+
+		private final int k; // rank of k-Subset
+
+		public Unrank(final int k, final long n, final long[] ppt, final ValueVec elems, final int maxK) {
+			this.k = k;
+			this.elems = elems;
+			this.partialPascalTable = ppt;
+			this.maxK = maxK - 1;
+
+			// Cache the sum of all binomials lt n for 0..elems.size choose k.
+			int choice = Math.max(k - 1, 0);
+			sums.put(-1L, choice); // As base for idx = 0 (see lowerEntry below);
+			long bin = 0L;
+			while ((bin = memoizedBinomial(choice, k)) < n) {
+				sums.put(bin, ++choice);
+			}
+		}
+
+		public Value subsetAt(long idx) {
+			// More subsets in this kSubset available.
+			final ValueVec vec = new ValueVec(k);
+
+			int y = k, choice = sums.lowerEntry(idx).getValue();
+			for (; choice >= 0 && k > 0; choice--) {
+				final long c = memoizedBinomial(choice, y);
+				if (c <= idx) {
+					idx -= c;
+					y--;
+					vec.addElement(this.elems.elementAt(choice));
+				}
+			}
+			return new SetEnumValue(vec, false, cm);
+		}
+
+		protected long memoizedBinomial(final int n, final int k) {
+			if (k == 0 || k == n) {
+				return (long) 1;
+			} else if (k == 1 || k == n - 1) {
+				return (long) n;
+			} else if (n == 0 || k > n) {
+				// Cannot choose from zero elements or more elements than present.
+				return 0;
+			}
+			final int pti = Combinatorics.choosePairToInt(n, k);
+			if (pti < Combinatorics.CHOOSETABLE.length) {
+				return Combinatorics.choose(n, k);
+			}
+			return partialPascalTable[(n - Combinatorics.MAXCHOOSENUM - 1) * maxK + k - 2];
+		}
+	}
+
+	private class RandomUnrank extends Unrank {
+
+		// Primes taken from: https://primes.utm.edu/lists/2small/0bit.html
+		// TODO: 9223372036854775783L; // 2^63 - 25
+//		private static final long x = 549755813881L; // 2^39 - 7 
+		private static final long x = 34359738337L; // 2^35 - 31
+
+		private final long n;
+		private final long a;
+		private long i;
+
+		public RandomUnrank(final int k, final long n, final long[] ppt, final ValueVec elems, final int maxK,
+				final Random random) {
+			super(k, n, ppt, elems, maxK);
+			this.n = n;
+			this.a = Math.abs(random.nextLong()) % n;
+		}
+
+		public Value  randomSubset() {
+			if (i < n) {
+				return subsetAt(((x * i++) + a) % n);
+			}
+			return null;
+		}
+	}
+
+	public EnumerableValue getRandomSetOfSubsets(final int numOfPicks, final double probability) {
+		final CoinTossingSubsetEnumerator enumerator = new CoinTossingSubsetEnumerator(numOfPicks, probability);
+		
+		// Using a set here instead of ValueVec preserves the set invariant (no
+		// duplicates). The alternative - a ValueVec which gets sorted to remove
+		// duplicates after the while loops is slower.
+		final int estimated = (int) (numOfPicks * probability);
+		final Collection<Value > sets = new HashSet<>(estimated);
+		Value  val;
+		while ((val = enumerator.nextElement()) != null) {
+			sets.add(val);
+		}
+		
+		return new SetEnumValue(new ValueVec(sets), false, cm);
+	}
+	
+	private final ValueEnumeration emptyEnumeration = new ValueEnumeration() {
+		private boolean done = false;
+
+		@Override
+		public void reset() {
+			done = false;
+		}
+		
+		@Override
+		public Value nextElement() {
+			if (done) { return null; }
+			done = true;
+			return new SetEnumValue(cm);
+		}
+	};
+
+	/**
+	 * @see file SubsetValue.tla.
+	 * <p>
+	 * In addition, this generates all subsets of this SubsetValue instance which extends
+	 * the order definition given in SubsetValue.tla such that a subset s is considered 
+	 * lower than t (s < t) if Cardinality(s) < Cardinality(t) \/ Definition in SubsetValue.tla.
+	 * <p>
+	 * The most noteworthy difference between bElements and 
+	 */
+	final ValueEnumeration elementsNormalized() {
+		final int n = set.size();
+		if (n == 0) {
+			emptyEnumeration.reset();
+			return emptyEnumeration;
+		}
+		// Only normalized inputs will yield a normalized output. Note that SEV#convert
+		// (unfortunately) enumerates the input. Thus "SUBSET SUBSET 1..10" will result
+		// in the nested/right SUBSET to be fully enumerated (1..10 obviously too).
+		final ValueVec elems = ((SetEnumValue) set.toSetEnum().normalize()).elems;
+		return new ValueEnumeration() {
+
+			private int k = 0;
+			private final int[] indices = new int[n];
+
+			@Override
+			public void reset() {
+				reset(0);
+			}
+
+			private void reset(final int k) {
+				this.k = k;
+				if (k > n) {
+					return;
+				}
+				for (int i = 0; i < k; i++) {
+					indices[i] = i;
+				}
+			}
+
+			@Override
+			public Value nextElement() {
+				if (k > n) {
+					return null;
+				} else if (k == 0) {
+					reset(k + 1);
+					return new SetEnumValue(cm);
+				}
+
+				final ValueVec vals = new ValueVec(k);
+				int i = k - 1;
+				for (int j = i; j >= 0; j--) {
+					vals.addElementAt(elems.elementAt(indices[j]), j);
+					if (indices[j] + k - j == n) {
+						i = j - 1;
+					}
+				}
+				final SetEnumValue result = new SetEnumValue(vals, true, cm);
+				
+				if (indices[0] == n - k) {
+					// Increment k to generate the set of k-subset for this k.
+					reset(k + 1);
+					return result;
+				}
+
+				// Increment the right element r by one and remember its old value.
+				indices[i]++;
+
+				// Adjust all the elements right to the right element r. For all indices j > i,
+				// the element at j is set to p[i] + j.
+				for (i++; i < k; i++) {
+					indices[i] = indices[i - 1] + 1;
+				}
+				return result;
+			}
+		};
+	}
+	
+	/**
+	 * @see SubsetValue#kElements(int)
+	 */
+	public final long numberOfKElements(final int k) {
+		final int size = this.set.size();
+		if (k < 0 || size < k || size > 62) {
+			throw new IllegalArgumentException(String.format("k=%s and n=%s", k, size));
+		}
+		if (k == 0 || k == size) {
+			return 1;
+		}
+		return Combinatorics.choose(size, k);
+	}
+	
+	/**
+	 * [S]^k (sometimes denoted S^[k]) == { t \in SUBSET S : Cardinality(t) = k }
+	 * @param k
+	 * @return
+	 */
+	public final ValueEnumeration kElements(final int k) {
+		if (k < 0 || this.set.size() < k) {
+			throw new IllegalArgumentException();
+		}
+		if (k == 0) {
+			emptyEnumeration.reset();
+			return emptyEnumeration;
+		}
+
+		return new KElementEnumerator(k);
+	}
+	
+	public final class KElementEnumerator implements ValueEnumeration {
+		private final ValueVec elems;
+		private final int numKSubsetElems;
+		private final int k;
+		
+		private int index;
+		private int cnt;
+
+		public KElementEnumerator(final int k) {
+			this.k = k;
+			
+			this.numKSubsetElems = (int) numberOfKElements(k); 
+			if (numKSubsetElems < 0) {
+				throw new IllegalArgumentException("Subset too large.");
+			}
+			
+			final SetEnumValue convert = (SetEnumValue) set.toSetEnum();
+			convert.normalize();
+			elems = convert.elems;
+
+			reset();
+		}
+		
+		@Override
+		public void reset() {
+			index = (1 << k) - 1;
+			cnt = 0;
+		}
+
+		// see "Compute the lexicographically next bit permutation" at
+		// http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
+		private int nextIndex() {
+			final int oldIdx = this.index;
+
+			final int t = (index | (index - 1)) + 1;
+			this.index = t | ((((t & -t) / (index & -index)) >> 1) - 1);
+
+			return oldIdx;
+		}
+
+		@Override
+		public Value nextElement() {
+			if (cnt >= numKSubsetElems) {
+				return null;
+			}
+			cnt++;
+
+			int bits = nextIndex();
+			final ValueVec vals = new ValueVec(Integer.bitCount(bits));
+			for (int i = 0; bits > 0 && i < elems.size(); i++) {
+				// Treat bits as a bitset and add the element of elem at current
+				// position i if the LSB of bits happens to be set.
+				if ((bits & 0x1) > 0) {
+					vals.addElement(elems.elementAt(i));
+				}
+				// ...right-shift zero-fill bits by one afterwards.
+				bits = bits >>> 1;
+			}
+			return new SetEnumValue(vals, false, cm);
+		}
+		
+		public KElementEnumerator sort() {
+			this.elems.sort(true);
+			return this;
+		}
+	}
+	
+	@Override
+	public ValueEnumeration elements(final Ordering ordering) {
+		// Use elementsNormalized regardless of requested ordering. Even for ordering
+		// UNDEFINED, elementsNormalized is fastest.
+		return elements();
+	}
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.pset == null || this.pset == SetEnumValue.DummyEnum) {
+    	  // See note on SetEnumValue#convert for SubsetValue wrt
+    	  // the normalized SetEnumValue result.
+    	  return elementsNormalized();
+      }
+      return this.pset.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+  
+  final ValueEnumeration elementsLexicographic() {
+      return new Enumerator();
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    private ValueVec elems;
+    private BitSet descriptor;
+
+    public Enumerator() {
+    	//WARNING! Mutates the outer instance!?
+      set = set.toSetEnum();
+      set.normalize();
+      this.elems = ((SetEnumValue)set).elems;
+      this.descriptor = new BitSet(this.elems.size());
+    }
+
+    @Override
+    public final void reset() {
+      this.descriptor = new BitSet(this.elems.size());
+    }
+
+    @Override
+    public final Value nextElement() {
+			if (this.descriptor == null)
+				return null;
+			ValueVec vals;
+			int sz = this.elems.size();
+			if (sz == 0) {
+				vals = new ValueVec(0);
+				this.descriptor = null;
+			} else {
+				vals = new ValueVec(this.descriptor.cardinality());
+				for (int i = 0; i < sz; i++) {
+					if (this.descriptor.get(i)) {
+						vals.addElement(this.elems.elementAt(i));
+					}
+				}
+				for (int i = 0; i < sz; i++) {
+					if (this.descriptor.get(i)) {
+						this.descriptor.clear(i);
+						if (i >= sz - 1) {
+							this.descriptor = null;
+							break;
+						}
+					} else {
+						this.descriptor.set(i);
+						break;
+					}
+				}
+			}
+	    	  if (coverage) { cm.incSecondary(vals.size()); }
+			return new SetEnumValue(vals, true, cm);
+	    }
+
+  }
+
+	@Override
+	public ValueEnumeration elements(final int k) {
+		final int sz = this.set.size();
+
+		// Use probabilistic CTSE if size of input set or k are too large. CoinTossing
+		// can yield duplicates though, thus k means number of picks.
+		if (sz >= 31 || k > (1 << 16)) {
+			return new CoinTossingSubsetEnumerator(k);
+		}
+		return new SubsetEnumerator(k);
+	}
+	
+	class SubsetEnumerator extends EnumerableValue.SubsetEnumerator {
+
+		private final ValueVec elems;
+
+		SubsetEnumerator(final int k) {
+			super(k, 1 << set.size());
+			final SetEnumValue convert = (SetEnumValue) set.toSetEnum();
+      		convert.normalize();
+      		this.elems = convert.elems;
+		}
+
+		@Override
+		public Value nextElement() {
+			if (!hasNext()) {
+				return null;
+			}
+			int bits = nextIndex();
+			final ValueVec vals = new ValueVec(Integer.bitCount(bits));
+			for (int i = 0; bits > 0 && i < this.elems.size(); i++) {
+				// Treat bits as a bitset and add the element of this.elem at current
+				// position i if the LSB of bits happens to be set.
+				if ((bits & 0x1) > 0) {
+					vals.addElement(this.elems.elementAt(i));
+				}
+				// ...right-shift zero-fill bits by one afterwards.
+				bits = bits >>> 1;
+			}
+			return new SetEnumValue(vals, false, cm);
+		}
+	}
+
+	/*
+	 * LL: I realized that efficiently choosing a random set of k elements in "SUBSET S"
+	 * is simple. Just compute S and randomly choose k elements SS of SUBSET S by
+	 * including each element of S in SS with probability 1/2.  This looks to me as
+	 * if it's completely equivalent to enumerating all the elements of SUBSET S and
+	 * choosing a random subset of those elements--except that if we want to choose
+	 * exactly k elements, then we'll have to throw away duplicates.
+	 */
+	class CoinTossingSubsetEnumerator implements ValueEnumeration {
+
+		private final ValueVec elems;
+		private final double probability;
+		private final int numOfPicks;
+		private int i;
+
+		public CoinTossingSubsetEnumerator(final int numOfPicks) {
+			this(numOfPicks, .5d);
+		}
+
+		public CoinTossingSubsetEnumerator(final int numOfPicks, final double probability) {
+			this.i = 0;
+			this.numOfPicks = numOfPicks;
+			this.probability = probability;
+
+			final SetEnumValue convert = (SetEnumValue) set.toSetEnum();
+			convert.normalize();
+			this.elems = convert.elems;
+		}
+
+		// Repeated invocation can yield duplicate elements due to the probabilistic
+		// nature of CoinTossingSubsetEnumerator.
+		@Override
+        public Value nextElement() {
+			if (!hasNext()) {
+				return null;
+			}
+			final ValueVec vals = new ValueVec(elems.size());
+			for (int i = 0; i < elems.size(); i++) {
+				if (RandomEnumerableValues.get().nextDouble() < probability) {
+					vals.addElement(elems.elementAt(i));
+				}
+			}
+			this.i++;
+			return new SetEnumValue(vals, false, cm);
+		}
+
+		private boolean hasNext() {
+			return this.i < this.numOfPicks;
+		}
+
+		@Override
+		public void reset() {
+			this.i = 0;
+		}
+
+		int getNumOfPicks() {
+			return numOfPicks;
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/SubsetValue.tla b/tlatools/src/tlc2/value/impl/SubsetValue.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d87d33e83d9e7f80782c17354762326bd719635b
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/SubsetValue.tla
@@ -0,0 +1,124 @@
+-------------------------- MODULE SubsetValue --------------------------
+EXTENDS Sequences, FiniteSets, Integers
+
+(* 
+  ASSUME Len(u) = Len(v)
+  
+  There exists a prefix p of u with length 0..Len(u)-1 s.t.
+      /\ \A i \in 1..p: u[i] <= v[i]
+      /\ u[p+1] < v[p+1]
+  In other words, if u < v then there exists a prefix of u of length 0..(Len(u)-1)
+  s.t. for all elements in the prefix of u are lower than the corresponding elements
+  in the prefix of v. 
+*)
+Sorted(u, v) == \E p \in 0..(Len(u) - 1) : /\ \A q \in 1..p : u[q] <= v[q]
+                                           /\ (u[p+1] + 1) = v[p+1]
+                                         
+-----------------------------------------------------------------------------
+
+CONSTANT N,
+         K
+
+ASSUME /\ \A nn \in N: nn \in Nat
+       /\ \A kk \in K: kk \in (Nat \ {0})
+       \* n >= k \* Via a state constraint, to not explicitly handle this case in the 
+                 \* algorithm. It makes no sense to draw subsets of cardinality k
+                 \* out of n elements if k > n.
+
+(* See Kunth's Volume 4 Fascicle 3, Generating All Combinations and Partitions (2005), 
+   vi+150pp. ISBN 0-201-85394-9, http://www-cs-faculty.stanford.edu/~knuth/taocp.html
+   (also http://www.cs.utsa.edu/~wagner/knuth/).
+   "Efficiently Enumerating the Subsets of a Set" 2000 by Loughry, van Hemmert, and Schoofs
+   found at http://www.applied-math.org/subset.pdf describe the same idea from a different
+   perspective (providing a recursive algorithm).
+   
+   The purpuse of this algorithm is to generate the k-Subsets 
+   { t \in SUBSET S : Cardinality(t) = k } out of the input set 0..(n-1) in a predefined
+   order. Even though sets have no notion of order, the technical representation of a set
+   in TLC has an order. This is e.g. important to efficiently determine equality of two
+   or more sets. If all sets adhere to a predefined order, equality can be determined
+   in O(n) time by a single pass over both sets.
+   Let u and v be two k-Subsets for a given k. The (total) order defined by this algorithm
+   is such that there exists a prefix p of u with length 0..Len(u)-1 s.t.
+   \A i \in 1..p: u[i] <= v[i] /\ u[p+1] < v[p+1]  (see Sorted operator above).
+--algorithm EnumerateSubsets {
+  variables n \in N; k \in K;
+            (* Represent sets as sequences because we are interested in order. *)
+            s = <<>>; r = <<>>;
+            i = 0; j = 0;
+            (* A strictly monotonically increasing sequence from 0 to k-1 used to
+               maintain the state of the algorithm.
+               Let k = 4 and n = 5. Initially, idx is [0,1,2,3] which corresponds to the 
+               first k-Subset with the first, second, third, fourth element of the 
+               original input set (which does not appear in this spec because it is
+               irrelevant for the actual algorithm). Next idx will change to [0,1,2,4],
+               followed by [0,1,2,5], [0,1,3,4], [0,1,3,5], ... *)
+            idx = [ii \in 0..(k - 1) |-> ii ];
+    {
+     (* The while below first generates the current k-subset determined by the state
+        of idx and then mutates the state of idx to be ready to generate the next
+         k-subset in the subsequent iteration. *)
+     start:
+     while (TRUE) {
+       j := k - 1; i := k - 1; s := <<>>;
+       
+       (* The l1 loop serves two purposes:
+          1) Generate the next k-Subset (depending on the state of idx)
+             by doing a full pass over idx from end to front.
+          2) As a byproduct, determine the next index i of idx which 
+             has to be incremented.
+       *)
+       l1:
+       while (j >= 0) {
+           \* 2) Determine next i.
+           if (idx[j] + k - j = n) {
+                 i := j - 1;
+           };
+           \* 1) Prepend idx[j] to seq s.
+           s := <<idx[j]>> \o s; j := j - 1;
+       };
+       \* Add s to the set r of k-Subsets. 
+       r := Append(r, s);
+       
+       l2: 
+       if (idx[0] = n - k) {
+           goto Done;
+       } else {
+           (* Increment the i-th idx (by one) and for all higher indices
+              h of i set idx[h] to idx[i] + (h - i). *)
+           idx[i] := idx[i] + 1; i := i + 1;
+           
+           l3: 
+           while (i < k) {
+                idx[i] := idx[i - 1] + 1; i := i + 1;
+           };
+       };
+     };
+  }
+}
+*)
+\* BEGIN TRANSLATION
+\* omitted
+\* END TRANSLATION
+
+-----------------------------------------------------------------------------
+\* Invariant and helpers:
+
+SeqOfSeqsToSetOfSets(seq) == LET (* The image of function f with Op applied to each element f[x]. *)
+                                 ImageApply(f, Op(_)) == { Op(f[x]) : x \in DOMAIN f }
+                                 Image(f) == ImageApply(f, LAMBDA x : x) \* Lambda is just identy
+                             IN ImageApply(seq, Image)
+
+(* The set S of all k-subsets ks (Cardinality(ks) = k) for the range 0 to n - 1. 
+   In other words, this is the expected set of subsets to be generated by the
+   EnumerateSubsets above. *)
+Expected == {ss \in SUBSET (0..(n-1)) : Cardinality(ss) = k }
+
+Inv == (pc = "Done") => \* When terminated...
+            \* ... the correct subsets have been generated...
+            /\ Expected = SeqOfSeqsToSetOfSets(r)
+            \* ... in the predefined order.
+            /\ \A ii \in 1..Len(r) - 1: Sorted(r[ii], r[ii+1])
+
+=============================================================================
+
diff --git a/tlatools/src/tlc2/value/impl/TupleValue.java b/tlatools/src/tlc2/value/impl/TupleValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0020e6098ce1d91712967e84a13971c7d931242
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/TupleValue.java
@@ -0,0 +1,419 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport
+//      modified on Fri Aug 10 15:10:22 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.util.Map;
+
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.EvalControl;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.ITupleValue;
+import tlc2.value.IValue;
+import tlc2.value.IValueInputStream;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.ValueInputStream;
+import tlc2.value.Values;
+import util.Assert;
+import util.UniqueString;
+
+public class TupleValue extends Value implements Applicable, ITupleValue {
+  public final Value[] elems;          // the elements of this tuple.
+  public static final TupleValue EmptyTuple = new TupleValue(new Value[0]);
+
+  /* Constructor */
+  public TupleValue(Value[] elems) { this.elems = elems; }
+
+  public TupleValue(Value v) {
+	  this(new Value[1]);
+    this.elems[0] = v;
+  }
+
+  public TupleValue(Value v1, Value v2) {
+	  this(new Value[2]);
+    this.elems[0] = v1;
+    this.elems[1] = v2;
+  }
+
+  public TupleValue(Value[] elems, CostModel cm) {
+	  this(elems);
+	  this.cm = cm;
+  }
+
+  @Override
+  public IValue getElem(int idx) {
+	  return elems[idx];
+  }
+  
+  @Override
+  public IValue[] getElems() {
+	  return elems;
+  }
+  
+  @Override
+  public final byte getKind() { return TUPLEVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      TupleValue tv = obj instanceof Value ? (TupleValue) ((Value)obj).toTuple() : null;
+      if (tv == null) {
+        // Well, we have to convert this to function and compare.
+        return this.toFcnRcd().compareTo(obj);
+      }
+      int len = this.elems.length;
+      int cmp = len - tv.elems.length;
+      if (cmp == 0) {
+        for (int i = 0; i < len; i++) {
+          cmp = this.elems[i].compareTo(tv.elems[i]);
+          if (cmp != 0) break;
+        }
+      }
+      return cmp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      TupleValue tv = obj instanceof Value ? (TupleValue) ((Value)obj).toTuple() : null;
+      if (tv == null) {
+        // Well, we have to convert this to function and compare.
+        return this.toFcnRcd().equals(obj);
+      }
+      int len = this.elems.length;
+      if (len != tv.elems.length)
+        return false;
+      for (int i = 0; i < len; i++) {
+        if (!this.elems[i].equals(tv.elems[i]))
+          return false;
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check set membership in a tuple value.");
+      return false;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() { return true; }
+
+  @Override
+  public final Value apply(Value arg, int control) {
+    try {
+      if (!(arg instanceof IntValue)) {
+        Assert.fail("Attempted to apply tuple to a non-integer argument.");
+      }
+      int idx = ((IntValue)arg).val;
+      if (idx <= 0 || idx > this.elems.length) {
+        Assert.fail("Attempted to apply tuple\n" + Values.ppr(this.toString()) +
+        "\nto integer " + idx + " which is out of domain.");
+      }
+      return (Value) this.elems[idx-1];
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value apply(Value[] args, int control) {
+    try {
+      if (args.length != 1) {
+        Assert.fail("Attetmpted to apply tuple with wrong number of arguments.");
+      }
+      return this.apply(args[0], EvalControl.Clear);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value select(Value arg) {
+    try {
+      if (!(arg instanceof IntValue)) {
+        Assert.fail("Attempted to apply tuple to a non-integer argument " +
+        Values.ppr(arg.toString()) + ".");
+      }
+      int idx = ((IntValue)arg).val;
+      if (idx > 0 && idx <= this.elems.length) {
+        return (Value) this.elems[idx-1];
+      }
+      return null;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        int tlen = this.elems.length;
+        Value[] newElems = new Value[tlen];
+        Value arcVal = ex.path[ex.idx];
+        if (arcVal instanceof IntValue) {
+          int idx = ((IntValue)arcVal).val - 1;
+          if (0 <= idx && idx < tlen) {
+            for (int i = 0; i < tlen; i++) {
+              newElems[i] = this.elems[i];
+            }
+            ex.idx++;
+            newElems[idx] = this.elems[idx].takeExcept(ex);
+          }
+          return new TupleValue(newElems);
+        }
+        MP.printWarning(EC.TLC_WRONG_TUPLE_FIELD_NAME, new String[]{Values.ppr(arcVal.toString())});
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      Value val = this;
+      for (int i = 0; i < exs.length; i++) {
+        val = val.takeExcept(exs[i]);
+      }
+      return val;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value getDomain() {
+    try {
+      return new IntervalValue(1, this.size());
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() { return this.elems.length; }
+
+  @Override
+  public final void deepNormalize() {
+	  try {
+      for (int i = 0; i < elems.length; i++) {
+          elems[i].deepNormalize();
+        }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final Value toTuple() {
+	  return this;
+  }
+  
+  @Override
+  public final Value toRcd() {
+	  return size() == 0 ? RecordValue.EmptyRcd : super.toRcd();
+  }
+
+	@Override
+	public final Value toFcnRcd() {
+        final IntervalValue intv = new IntervalValue(1, this.elems.length);
+        if (coverage) {cm.incSecondary(this.elems.length);}
+        return new FcnRcdValue(intv, this.elems, cm);
+	}
+
+  /* The normalization of the value. */
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*nop*/return this; }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      boolean defined = true;
+      for (int i = 0; i < this.elems.length; i++) {
+        defined = defined && this.elems[i].isDefined();
+      }
+      return defined;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() {
+    try {
+    	Value[] vals = new Value[this.elems.length];
+      for (int i = 0; i < this.elems.length; i++) {
+        vals[i] = (Value) this.elems[i].deepCopy();
+      }
+      return new TupleValue(vals);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      boolean canAssign = ((val instanceof TupleValue) &&
+         (this.elems.length == ((TupleValue)val).elems.length));
+      if (!canAssign) return false;
+      for (int i = 0; i < this.elems.length; i++) {
+        canAssign = canAssign && this.elems[i].assignable(((TupleValue)val).elems[i]);
+      }
+      return canAssign;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	@Override
+	public final void write(IValueOutputStream vos) throws IOException {
+		final int index = vos.put(this);
+		if (index == -1) {
+			vos.writeByte(TUPLEVALUE);
+			final int len = elems.length;
+			vos.writeNat(len);
+			for (int i = 0; i < len; i++) {
+				elems[i].write(vos);
+			}
+		} else {
+			vos.writeByte(DUMMYVALUE);
+			vos.writeNat(index);
+		}
+	}
+
+  /* The fingerprint method: tuples are functions. */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      int len = this.elems.length;
+      fp = FP64.Extend(fp, FCNRCDVALUE);
+      fp = FP64.Extend(fp, len);
+      for (int i = 0; i < len; i++) {
+        fp = FP64.Extend(fp, INTVALUE);
+        fp = FP64.Extend(fp, i+1);
+        fp = this.elems[i].fingerPrint(fp);
+      }
+      return fp;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+    	Value[] vals = new Value[this.elems.length];
+      boolean changed = false;
+      for (int i = 0; i < vals.length; i++) {
+        vals[i] = (Value) this.elems[i].permute(perm);
+        changed = changed || (vals[i] != this.elems[i]);
+      }
+      if (changed) {
+        return new TupleValue(vals);
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation of this value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      sb.append("<<");
+      int len = this.elems.length;
+      if (len > 0) {
+        sb = this.elems[0].toString(sb, offset, swallow);
+      }
+      for (int i = 1; i < len; i++) {
+        sb = sb.append(", ");
+        sb = this.elems[i].toString(sb, offset, swallow);
+      }
+      sb.append(">>");
+      return sb;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+	public static IValue createFrom(final IValueInputStream vos) throws IOException {
+		final int index = vos.getIndex();
+		final int len = vos.readNat();
+		final Value[] elems = new Value[len];
+		for (int i = 0; i < len; i++) {
+			elems[i] = (Value) vos.read();
+		}
+		final Value res = new TupleValue(elems);
+		vos.assign(res, index);
+		return res;
+	}
+
+	public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException {
+		final int index = vos.getIndex();
+		final int len = vos.readNat();
+		final Value[] elems = new Value[len];
+		for (int i = 0; i < len; i++) {
+			elems[i] = (Value) vos.read(tbl);
+		}
+		final Value res = new TupleValue(elems);
+		vos.assign(res, index);
+		return res;
+	}
+}
diff --git a/tlatools/src/tlc2/value/impl/UndefValue.java b/tlatools/src/tlc2/value/impl/UndefValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..81d0062cf44eba330600da1f1d88f3d68b477de5
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/UndefValue.java
@@ -0,0 +1,145 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:06 PST by lamport
+//      modified on Tue Aug 15 23:08:23 PDT 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import tlc2.tool.FingerprintException;
+import tlc2.value.IValue;
+import tlc2.value.Values;
+import util.Assert;
+
+public class UndefValue extends Value {
+
+  public static final UndefValue ValUndef = new UndefValue();
+
+  public UndefValue() { /*SKIP*/ }
+
+  @Override
+  public byte getKind() { return UNDEFVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      return (obj instanceof UndefValue) ? 0 : 1;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      return (obj instanceof UndefValue);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) +
+      "\nis an element " + Values.ppr(this.toString()));
+      return false;    // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      Assert.fail("Attempted to check if the value " + Values.ppr(this.toString()) +
+      " is a finite set.");
+      return false;    // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT construct to the value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT construct to the value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the value " +
+      Values.ppr(this.toString()) + ".");
+      return 0;     // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public boolean mutates() {
+	  return false;
+  }
+
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*nop*/return this; }
+
+  @Override
+  public final boolean isDefined() { return false; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) { return true; }
+
+  /* The string representation. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      return sb.append("UNDEF");
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/UnionValue.java b/tlatools/src/tlc2/value/impl/UnionValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3d78f9152251f14e582ee05dc026c89b41a9532
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/UnionValue.java
@@ -0,0 +1,399 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:46:50 PST by lamport
+//      modified on Fri Aug 10 15:10:39 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.Values;
+import util.Assert;
+
+public class UnionValue extends EnumerableValue implements Enumerable {
+  public final Value set;
+  protected SetEnumValue realSet;
+
+  /* Constructor */
+  public UnionValue(Value set) {
+    this.set = set;
+    this.realSet = null;
+  }
+
+  public UnionValue(Value val, CostModel cm) {
+	  this(val);
+	  this.cm = cm;
+  }
+
+  @Override
+  public byte getKind() { return UNIONVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.realSet.compareTo(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      this.convertAndCache();
+      return this.realSet.equals(obj);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value elem) {
+    try {
+      if (!(this.set instanceof Enumerable)) {
+        Assert.fail("Attempted to check if:\n " + Values.ppr(elem.toString()) +
+        "\nis an element of the non-enumerable set:\n " +
+        Values.ppr(this.toString()));
+      }
+      ValueEnumeration Enum = ((Enumerable)this.set).elements();
+      Value val;
+      while ((val = Enum.nextElement()) != null) {
+        if (val.member(elem)) return true;
+      }
+      return false;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      if (!(this.set instanceof Enumerable)) {
+        Assert.fail("Attempted to check if the nonenumerable set:\n" + Values.ppr(this.toString()) +
+        "\nis a finite set.");
+      }
+      ValueEnumeration Enum = ((Enumerable)this.set).elements();
+      Value val;
+      while ((val = Enum.nextElement()) != null) {
+        if (!val.isFinite()) return false;
+      }
+      return true;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the set:\n" + Values.ppr(this.toString()));
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the set:\n " + Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      this.convertAndCache();
+      return this.realSet.size();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isNormalized() {
+    try {
+      return (this.realSet != null &&
+        this.realSet != SetEnumValue.DummyEnum &&
+        this.realSet.isNormalized());
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value normalize() {
+    try {
+      if (this.realSet != null && this.realSet != SetEnumValue.DummyEnum) {
+        this.realSet.normalize();
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final void deepNormalize() {
+	    try {
+			// MAK 09/17/2019: Added call to this.set.deepNormalize() to align with pattern
+			// generally found in overwrites of Value#deepNormalize.
+	    	// This omission surfaced through a race condition that led to a spurious
+	    	// safety violation (https://github.com/tlaplus/tlaplus/issues/361):
+	    	// 1) A TLA+ spec defines a (zero-arity) operator s.a. "Foo == UNION { ... }"
+	    	//    that appears in an invariant.
+	    	// 2) SpecProcessor#processConstantDefns eagerly evaluates the operator Foo at startup
+	    	//    and inserts its Value result UV into the corresponding node of the semantic graph.
+	    	// 3) Two workers check if two states violate the invariant which triggers UnionValue#member,
+	    	//    which internally causes this.set to be normalized.  Since Value instances are not thread-safe
+	    	//    because they are expected to be fully normalized during state space exploration, the
+	    	//    two workers race to normalize this.set.
+	    	// 4) Worker A gets ahead and loops over the elements in UV#member while worker B still normalizes UV.
+	    	//    Worker A reads inconsistent data and thus reports the invariant to be violated.
+	    	// Thanks to Calvin Loncaric for suggesting this fix.
+	    	this.set.deepNormalize();
+	    	
+      if (realSet == null) {
+        realSet = SetEnumValue.DummyEnum;
+      }
+      else if (realSet != SetEnumValue.DummyEnum) {
+        realSet.deepNormalize();
+      }
+	    }
+	    catch (RuntimeException | OutOfMemoryError e) {
+	      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+	      else { throw e; }
+	    }
+  }
+
+  @Override
+  public final boolean isDefined() {
+    try {
+      return this.set.isDefined();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public static Value union(Value val) {
+    boolean canCombine = (val instanceof SetEnumValue);
+    if (canCombine) {
+      ValueVec elems = ((SetEnumValue)val).elems;
+      for (int i = 0; i < elems.size(); i++) {
+        canCombine = (canCombine &&
+                (elems.elementAt(i) instanceof SetEnumValue));
+      }
+      if (canCombine) {
+        ValueVec resElems = new ValueVec();
+        Value result = new SetEnumValue(resElems, false, val.getCostModel());
+        for (int i = 0; i < elems.size(); i++) {
+          ValueVec elems1 = ((SetEnumValue)elems.elementAt(i)).elems;
+          for (int j = 0; j < elems1.size(); j++) {
+        	  Value elem = elems1.elementAt(j);
+            if (!result.member(elem)) {
+            	resElems.addElement(elem);
+            }
+          }
+        }
+        return result;
+      }
+    }
+    return new UnionValue(val, val.getCostModel());
+  }
+
+	@Override
+	public void write(final IValueOutputStream vos) throws IOException {
+		realSet.write(vos);
+	}
+
+  /* The fingerprint  */
+  @Override
+  public final long fingerPrint(long fp) {
+    try {
+      this.convertAndCache();
+      return this.realSet.fingerPrint(fp);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final IValue permute(IMVPerm perm) {
+    try {
+      this.convertAndCache();
+      return this.realSet.permute(perm);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  private final void convertAndCache() {
+    if (this.realSet == null) {
+      this.realSet = (SetEnumValue) this.toSetEnum();
+    }
+    else if (this.realSet == SetEnumValue.DummyEnum) {
+      SetEnumValue val = null;
+      synchronized(this) {
+        if (this.realSet == SetEnumValue.DummyEnum) {
+          val = (SetEnumValue) this.toSetEnum();
+          val.deepNormalize();
+        }
+      }
+      synchronized(this) {
+        if (this.realSet == SetEnumValue.DummyEnum) { this.realSet = val; }
+      }
+    }
+  }
+
+  @Override
+  public final Value toSetEnum() {
+      if (this.realSet != null && this.realSet != SetEnumValue.DummyEnum) {
+        return this.realSet;
+      }
+      ValueVec vals = new ValueVec();
+      ValueEnumeration Enum = this.elements();
+      Value elem;
+      while ((elem = Enum.nextElement()) != null) {
+        vals.addElement(elem);
+      }
+      if (coverage) {cm.incSecondary(vals.size());}
+      return new SetEnumValue(vals, false, cm);
+  }
+
+  /* String representation of this value. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      if (TLCGlobals.expand) {
+        Value val = this.toSetEnum();
+        return val.toString(sb, offset, swallow);
+      }
+      else {
+        sb = sb.append("UNION(");
+        sb = this.set.toString(sb, offset, swallow);
+        sb.append(")");
+        return sb;
+      }
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final ValueEnumeration elements() {
+    try {
+      if (this.realSet == null || this.realSet == SetEnumValue.DummyEnum) {
+        return new Enumerator();
+      }
+      return this.realSet.elements();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  final class Enumerator implements ValueEnumeration {
+    ValueEnumeration Enum;
+    Value elemSet;
+    ValueEnumeration elemSetEnum;
+
+    public Enumerator() {
+      if (!(set instanceof Enumerable)) {
+        Assert.fail("Attempted to enumerate the nonenumerable set:\n"+
+              Values.ppr(this.toString()));
+      }
+      this.Enum = ((Enumerable)set).elements();
+      this.elemSet = this.Enum.nextElement();
+      if (this.elemSet != null) {
+        if (!(this.elemSet instanceof Enumerable)) {
+          Assert.fail("Attempted to enumerate UNION(s), but some element of s is nonenumerable.");
+        }
+        this.elemSetEnum = ((Enumerable)this.elemSet).elements();
+      }
+    }
+
+    @Override
+    public final void reset() {
+      this.Enum.reset();
+      this.elemSet = this.Enum.nextElement();
+      this.elemSetEnum = ((Enumerable)this.elemSet).elements();
+    }
+
+    @Override
+    public final Value nextElement() {
+      if (this.elemSet == null) return null;
+      Value val = this.elemSetEnum.nextElement();
+      if (val == null) {
+        this.elemSet = this.Enum.nextElement();
+        if (this.elemSet == null) return null;
+        if (!(this.elemSet instanceof Enumerable)) {
+          Assert.fail("Attempted to enumerate the nonenumerable set:\n" +
+                Values.ppr(this.elemSet.toString()) +
+                "\nwhen enumerating:\n" + Values.ppr(this.toString()));
+        }
+        this.elemSetEnum = ((Enumerable)this.elemSet).elements();
+        val = this.nextElement();
+      }
+	  if (coverage) { cm.incSecondary(); }
+      return val;
+    }
+
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/UserObj.java b/tlatools/src/tlc2/value/impl/UserObj.java
similarity index 90%
rename from tlatools/src/tlc2/value/UserObj.java
rename to tlatools/src/tlc2/value/impl/UserObj.java
index 63f5c81d63e8a6e86f3a902f0f59eec51e850b10..a12baa077ea76693db032a66c21484fd82ad9e4c 100644
--- a/tlatools/src/tlc2/value/UserObj.java
+++ b/tlatools/src/tlc2/value/impl/UserObj.java
@@ -3,8 +3,7 @@
 // Last modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport
 //      modified on Mon Aug 20 10:53:55 PDT 2001 by yuanyu
 
-package tlc2.value;
-
+package tlc2.value.impl;
 
 public abstract class UserObj {
 
@@ -17,11 +16,11 @@ public abstract class UserObj {
   public abstract boolean isFinite();
   
   /* The String representation.    */
-  public abstract StringBuffer toString(StringBuffer sb, int offset);
+  public abstract StringBuffer toString(StringBuffer sb, int offset, boolean swallow);
 
   public final String toString() {
     StringBuffer sb = new StringBuffer();
-    sb = this.toString(sb, 0);
+    sb = this.toString(sb, 0, true);
     return sb.toString();
   }
   
diff --git a/tlatools/src/tlc2/value/impl/UserValue.java b/tlatools/src/tlc2/value/impl/UserValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..9518af3f3c6e598bffbf6d22ccd6813d73b5b7ba
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/UserValue.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport
+//      modified on Fri May 11 15:14:04 PDT 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import tlc2.tool.FingerprintException;
+import tlc2.value.IValue;
+import tlc2.value.Values;
+import util.Assert;
+
+public class UserValue extends Value {
+  public UserObj userObj;
+
+  public UserValue(UserObj obj) { this.userObj = obj; }
+
+  @Override
+  public final byte getKind() { return USERVALUE; }
+
+  @Override
+  public final int compareTo(Object obj) {
+    try {
+      if (obj instanceof UserValue) {
+        return this.userObj.compareTo((Value)obj);
+      }
+      if (!(obj instanceof ModelValue))
+        Assert.fail("Attempted to compare overridden value " + Values.ppr(this.toString()) +
+        " with non-overridden value:\n" + Values.ppr(obj.toString()));
+      return 1;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  public final boolean equals(Object obj) {
+    try {
+      return (this.compareTo(obj) == 0);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean member(Value val) {
+    try {
+      return this.userObj.member(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final boolean isFinite() {
+    try {
+      return this.userObj.isFinite();
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept ex) {
+    try {
+      if (ex.idx < ex.path.length) {
+        Assert.fail("Attempted to apply EXCEPT to the overridden value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return ex.value;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final Value takeExcept(ValueExcept[] exs) {
+    try {
+      if (exs.length != 0) {
+        Assert.fail("Attempted to apply EXCEPT to the overridden value " +
+        Values.ppr(this.toString()) + ".");
+      }
+      return this;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  @Override
+  public final int size() {
+    try {
+      Assert.fail("Attempted to compute the number of elements in the overridden value " +
+      Values.ppr(this.toString()) + ".");
+      return 0;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Nothing to normalize. */
+  @Override
+  public final boolean isNormalized() { return true; }
+
+  @Override
+  public final Value normalize() { /*SKIP*/return this; }
+
+  @Override
+  public final boolean isDefined() { return true; }
+
+  @Override
+  public final IValue deepCopy() { return this; }
+
+  @Override
+  public final boolean assignable(Value val) {
+    try {
+      return this.equals(val);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* The string representation. */
+  @Override
+  public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+    try {
+      return this.userObj.toString(sb, offset, swallow);
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+}
diff --git a/tlatools/src/tlc2/value/impl/Value.java b/tlatools/src/tlc2/value/impl/Value.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9b07185d7ff6591f77fa7f4152c5265f8a766bb
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/Value.java
@@ -0,0 +1,358 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves
+//      modified on Mon 30 Apr 2007 at 15:30:13 PST by lamport
+//      modified on Wed Dec  5 23:18:07 PST 2001 by yuanyu
+
+package tlc2.value.impl;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import tla2sany.semantic.SemanticNode;
+import tlc2.TLCGlobals;
+import tlc2.tool.FingerprintException;
+import tlc2.tool.coverage.CostModel;
+import tlc2.util.FP64;
+import tlc2.value.IMVPerm;
+import tlc2.value.IValue;
+import tlc2.value.IValueOutputStream;
+import tlc2.value.ValueConstants;
+import tlc2.value.Values;
+import util.Assert;
+import util.WrongInvocationException;
+
+public abstract class Value implements ValueConstants, Serializable, IValue {
+
+	  private static final String[] ValueImage = {
+	    "a Boolean value",                     // "BoolValue",
+	    "an integer",                          // "IntValue",
+	    "a real",                              // "RealValue",
+	    "a string",                            // "StringValue",
+	    "a record",                            // "RecordValue",
+	    "a set of the form {e1, ... ,eN}",     // "SetEnumValue",
+	    "a set of the form {x \\in S : expr}", // "SetPredValue",
+	    "a tuple",                             // "TupleValue",
+	    "a function of the form  [x \\in S |-> expr]",           // "FcnLambdaValue",
+	    "a function  of the form (d1 :> e1 @@ ... @@ dN :> eN)", // "FcnRcdValue",
+	    "an operator",                                // "OpLambdaValue",
+	    "a constant operator",                        // "OpRcdValue",
+	    "a java method",                              // "MethodValue",    
+	    "a set of the form [S -> T]",                 // "SetOfFcnsValue",
+	    "a set of the form [d1 : S1, ... , dN : SN]", // "SetOfRcdsValue",
+	    "a cartesian product",                        // "SetOfTuplesValue",
+	    "a set of the form SUBSET S",                 // "SubsetValue",
+	    "a set of the form S \\ T",                   // "SetDiffValue",
+	    "a set of the form S \\cap T",                // "SetCapValue",
+	    "a set of the form S \\cup T",                // "SetCupValue",
+	    "a set of the form UNION  S",                 // "UnionValue",
+	    "a model value",                              // "ModelValue",
+	    "a special set constant",                     // "UserValue",
+	    "a set of the form i..j",                     // "IntervalValue",
+	    "an undefined value",                         // "UndefValue",
+	    "a value represented in lazy form",           // "LazyValue",
+	    "a dummy for not-a-value",                    // "DummyValue",    
+	  };
+	  
+	/**
+	 * @see See note on performance in CostModelCreator.
+	 */
+	protected static final boolean coverage = TLCGlobals.isCoverageEnabled();
+  /**
+   * For each kind of value, we introduce a subclass of Value.
+   * All the subclasses are given in this value package.
+	   * This method returns the value kind: an integer that represents
+	   * the kind of this value. See the interface ValueConstants.java.
+	   */
+	public abstract byte getKind();
+
+  public String getKindString() {
+    try {
+      return ValueImage[this.getKind()];
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This method returns true iff elem is a member of this. */
+  public abstract boolean member(Value elem);
+  
+  /* This method returns a new value after taking the except. */
+  public abstract Value takeExcept(ValueExcept ex);
+
+  /* This method returns a new value after taking the excepts. */
+  public abstract Value takeExcept(ValueExcept[] exs);
+
+  /* This method returns true iff val can be assigned to this. */
+  abstract boolean assignable(Value val);
+  
+  public abstract Value normalize();
+
+  @Override
+  public void write(IValueOutputStream vos) throws IOException {
+		throw new WrongInvocationException("ValueOutputStream: Can not pickle the value\n" +
+			    Values.ppr(toString()));
+  }
+
+  public transient CostModel cm = CostModel.DO_NOT_RECORD;
+  
+  @Override
+  public IValue setCostModel(CostModel cm) {
+	  this.cm = cm;
+	  return this;
+  }
+  
+  @Override
+  public CostModel getCostModel() {
+	  return this.cm;
+  }
+  
+  /**
+   * These methods allow storage and retrieval of the SemanticNode used to create the Value,
+   * which is helpful for FingerprintException.
+   */
+  private transient SemanticNode source = null;
+
+  @Override
+  public void setSource(final SemanticNode semanticNode) {
+    source = semanticNode;
+  }
+
+  @Override
+  public SemanticNode getSource() {
+    return source;
+  }
+  
+  @Override
+  public boolean hasSource() {
+	  return source != null;
+  }
+  
+  public boolean hasData() {
+	  return false;
+  }
+  
+  public Object getData() {
+	  return null;
+  }
+  
+  public Object setData(final Object obj) {
+		throw new WrongInvocationException("Value: Can not set data\n" +
+			    Values.ppr(toString()));
+  }
+
+  public final boolean isEmpty() {
+    try {
+
+      switch (this.getKind()) {
+        case SETENUMVALUE:
+          {
+            SetEnumValue set = (SetEnumValue)this;
+            return set.elems.size() == 0;
+          }
+        case INTERVALVALUE:
+          {
+            IntervalValue intv = (IntervalValue)this;
+            return intv.size() == 0;
+          }
+        case SETCAPVALUE:
+          {
+            SetCapValue cap = (SetCapValue)this;
+            return cap.elements().nextElement() == null;
+          }
+        case SETCUPVALUE:
+          {
+            SetCupValue cup = (SetCupValue)this;
+            return cup.elements().nextElement() == null;
+          }
+        case SETDIFFVALUE:
+          {
+            SetDiffValue diff = (SetDiffValue)this;
+            return diff.elements().nextElement() == null;
+          }
+        case SETOFFCNSVALUE:
+          {
+            SetOfFcnsValue fcns = (SetOfFcnsValue)this;
+            return fcns.elements().nextElement() == null;
+          }
+        case SETOFRCDSVALUE:
+          {
+            SetOfRcdsValue srv = (SetOfRcdsValue)this;
+            return srv.elements().nextElement() == null;
+          }
+        case SETOFTUPLESVALUE:
+          {
+            SetOfTuplesValue stv = (SetOfTuplesValue)this;
+            return stv.elements().nextElement() == null;
+          }
+        case SUBSETVALUE:
+          {
+            // SUBSET S is never empty.  (It always contains {}.)
+            return false;
+          }
+        case UNIONVALUE:
+          {
+            UnionValue uv = (UnionValue)this;
+            return uv.elements().nextElement() == null;
+          }
+        case SETPREDVALUE:
+          {
+            SetPredValue spv = (SetPredValue)this;
+            return spv.elements().nextElement() == null;
+          }
+        default:
+          Assert.fail("Shouldn't call isEmpty() on value " + Values.ppr(this.toString()));
+          return false;
+      }
+
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Fully normalize this (composite) value. */
+  @Override
+  public void deepNormalize() {
+  }
+
+  /* This method returns the fingerprint of this value. */
+  @Override
+  public long fingerPrint(long fp) {
+    try {
+      Assert.fail("TLC has found a state in which the value of a variable contains " +
+      Values.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access
+      return 0;      // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /**
+   * This method returns the value permuted by the permutation. It
+   * returns this if nothing is permuted.
+   */
+  @Override
+  public IValue permute(IMVPerm perm) {
+    try {
+      Assert.fail("TLC has found a state in which the value of a variable contains " +
+      Values.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access
+      return null;   // make compiler happy
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* This method returns the hash code of this value. */
+  @Override
+  public final int hashCode() {
+    try {
+      long fp = this.fingerPrint(FP64.New());
+      int high = (int)(fp >> 32);
+      int low = (int)fp;
+      return high ^ low;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /**
+   * This method selects the component of this value. The component is
+   * specified by path.
+   */
+  public final Value select(Value[] path) {
+    try {
+      Value result = this;
+      for (int i = 0; i < path.length; i++) {
+        if (!(result instanceof Applicable)) {
+          Assert.fail("Attempted to apply EXCEPT construct to the value " +
+                Values.ppr(result.toString()) + ".");
+        }
+        Value elem = path[i];
+        result = ((Applicable)result).select(elem);
+        if (result == null) return null;
+      }
+      return result;
+    }
+    catch (RuntimeException | OutOfMemoryError e) {
+      if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+      else { throw e; }
+    }
+  }
+
+  /* Convert val into a SetEnumValue.  Returns null if not possible. */
+  public Value toSetEnum() {
+	  return null;
+  }
+
+  /*
+   * This method converts a value to a function value. It returns
+   * null if the conversion fails.
+   */
+  public Value toFcnRcd() {
+	  return null;
+  }
+
+  /*
+   * This method converts a value to a function value. It returns
+   * null if the conversion fails.
+   */
+  public Value toRcd() {
+	  return null;
+  }
+
+  /*
+   * This method converts a value to a tuple value. It returns
+   * null if the conversion fails.
+   */
+  public Value toTuple() {
+	  return null;
+  }
+  
+  /* The string representation of this value */
+  @Override
+  public final String toString() {
+	  return toStringImpl("", true);
+  }
+  
+  /* Same as toString except that nested exceptions won't be silently discarded */
+  public final String toStringUnchecked() {
+	  return toStringImpl("", false);
+  }
+
+  /* Same as toString. */
+  @Override
+  public String toUnquotedString() {
+	  return toString();
+  }
+
+  @Override
+  public final String toString(final String delim) {
+	  return toStringImpl(delim, true);
+  }
+
+  public final String toStringUnchecked(final String delim) {
+	  return toStringImpl(delim, false);
+  }
+  
+  private final String toStringImpl(final String delim, final boolean checked) {
+    try {
+        final StringBuffer sb = this.toString(new StringBuffer(), 0, checked);
+        sb.append(delim);
+        return sb.toString();
+      }
+      catch (RuntimeException | OutOfMemoryError e) {
+        if (hasSource()) { throw FingerprintException.getNewHead(this, e); }
+        else { throw e; }
+      }
+  }
+}
diff --git a/tlatools/src/tlc2/value/impl/ValueEnumeration.java b/tlatools/src/tlc2/value/impl/ValueEnumeration.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc4d77947c3c249bb37c3fa89aebe4d09e5d9c96
--- /dev/null
+++ b/tlatools/src/tlc2/value/impl/ValueEnumeration.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2003 Compaq Corporation.  All rights reserved.
+// Portions Copyright (c) 2003 Microsoft Corporation.  All rights reserved.
+// Last modified on Mon 30 Apr 2007 at 13:21:10 PST by lamport
+//      modified on Tue Aug 22 11:56:52 PDT 2000 by yuanyu
+
+package tlc2.value.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public interface ValueEnumeration {
+  /* Reset allows repeated use of this enumerator. */
+  void reset();
+
+  /* Return the next element if there is one. Otherwise return null. */
+  Value nextElement();
+  
+	default List<Value> all() {
+		final List<Value> values = new ArrayList<Value>();
+		Value elem;
+		while ((elem = nextElement()) != null) {
+			values.add(elem);
+		}
+		return values;
+	}
+
+	default void forEach(final Consumer<? super Value> action) {
+		Value elem;
+		while ((elem = nextElement()) != null) {
+			action.accept(elem);
+		}
+	}
+}
diff --git a/tlatools/src/tlc2/value/ValueExcept.java b/tlatools/src/tlc2/value/impl/ValueExcept.java
similarity index 72%
rename from tlatools/src/tlc2/value/ValueExcept.java
rename to tlatools/src/tlc2/value/impl/ValueExcept.java
index 8f4e67aad7a54fd8953c07912ecc1f5e62f77665..6dcdf261e15e16b3b5f884c1da6b76e534a18355 100644
--- a/tlatools/src/tlc2/value/ValueExcept.java
+++ b/tlatools/src/tlc2/value/impl/ValueExcept.java
@@ -3,7 +3,7 @@
 // Last modified on Wed  4 Jul 2007 at 17:31:51 PST by lamport
 //      modified on Wed Jul 25 11:04:30 PDT 2001 by yuanyu
 
-package tlc2.value;
+package tlc2.value.impl;
 
 import tla2sany.semantic.FormalParamNode;
 
@@ -12,7 +12,7 @@ public class ValueExcept {
   public Value value;
   public int idx;
 
-  public ValueExcept(Value[] lhs, Value rhs) {
+  public ValueExcept(Value [] lhs, Value  rhs) {
     this.path = lhs;
     this.value = rhs;
     this.idx = 0;
@@ -25,17 +25,17 @@ public class ValueExcept {
   }
 
   public final ValueExcept checkArg(FcnLambdaValue fcn) {
-    Value argv = this.path[idx];
-    if (fcn.params.length() == 1) {
-      if (!fcn.params.domains[0].member(argv)) return null;
+    Value  argv = this.path[idx];
+    if (fcn.getParams().length() == 1) {
+      if (!fcn.getParams().domains[0].member(argv)) return null;
     }
     else {
       TupleValue tval = (TupleValue)argv;
-      Value[] argList = tval.elems;
-      FormalParamNode[][] formals = fcn.params.formals;
-      Value[] domains = fcn.params.domains;
+      Value [] argList = tval.elems;
+      FormalParamNode[][] formals = fcn.getParams().formals;
+      Value [] domains = fcn.getParams().domains;
       int argn = 0;
-      for (int i = 0; i < fcn.params.formals.length; i++) {
+      for (int i = 0; i < fcn.getParams().formals.length; i++) {
         FormalParamNode[] formal = formals[i];
         for (int j = 0; j < formal.length; j++) {
           if (!domains[i].member(argList[argn++])) return null;
@@ -45,7 +45,7 @@ public class ValueExcept {
     return this;
   }
 
-  public final Value current() { return this.path[this.idx]; }
+  public final Value  current() { return this.path[this.idx]; }
 
   public final boolean isLast() {
     return this.idx == (this.path.length - 1);
diff --git a/tlatools/src/tlc2/value/ValueVec.java b/tlatools/src/tlc2/value/impl/ValueVec.java
similarity index 75%
rename from tlatools/src/tlc2/value/ValueVec.java
rename to tlatools/src/tlc2/value/impl/ValueVec.java
index e2a0dab2889d3929e31a4811453a9720b5ba195d..e2c89fd9758eb24333e85809956c303013f8365b 100644
--- a/tlatools/src/tlc2/value/ValueVec.java
+++ b/tlatools/src/tlc2/value/impl/ValueVec.java
@@ -3,18 +3,19 @@
 // Last modified on Tue  1 May 2007 at 13:40:06 PST by lamport
 //      modified on Tue Aug 21 17:02:26 PDT 2001 by yuanyu
 
-package tlc2.value;
+package tlc2.value.impl;
 
 import java.io.Serializable;
+import java.util.Collection;
 
 import tlc2.TLCGlobals;
 import util.WrongInvocationException;
 
 public class ValueVec implements Cloneable, Serializable {
-  private Value[] elementData;
+  private Value [] elementData;
   private int elementCount;
          
-  static private final Value[] empty = new Value[0];
+  private static final Value [] empty = new Value [0];
 
   public ValueVec() { this(10); }
 
@@ -24,15 +25,26 @@ public class ValueVec implements Cloneable, Serializable {
       this.elementData = empty;
     }
     else {
-      this.elementData = new Value[initialCapacity];
+      this.elementData = new Value [initialCapacity];
     }
   }
 
-  public ValueVec(Value[] elems) {
+  public ValueVec(Value [] elems) {
     this.elementData = elems;
     this.elementCount = elems.length;
   }
-  
+
+    public ValueVec(Collection<Value > elems) {
+    	this(elems.size());
+    	for (Value  value : elems) {
+			addElement(value);
+		}
+    }
+    public final void addElementAt(Value  val, int index) {
+        this.elementData[index] = val;
+        this.elementCount++;
+    }
+
   public final void addElement(Value val) {
     if (this.elementCount == this.elementData.length) {
       ensureCapacity(this.elementCount+1);
@@ -40,7 +52,7 @@ public class ValueVec implements Cloneable, Serializable {
     this.elementData[this.elementCount++] = val;
   }
 
-  public final void addElement1(Value val) {
+  public final void addElement1(Value  val) {
     if (this.elementCount == this.elementData.length) {
       ensureCapacity(this.elementCount+1);
     }
@@ -61,6 +73,7 @@ public class ValueVec implements Cloneable, Serializable {
 
   public final int capacity() { return elementData.length; }
 
+  @Override
   public final Object clone() {
     ValueVec v = new ValueVec(this.elementData.length);
 	
@@ -69,15 +82,15 @@ public class ValueVec implements Cloneable, Serializable {
     return v;
   }
 
-  public final boolean contains(Value elem) {
+  public final boolean contains(Value  elem) {
     return (indexOf(elem) != -1);
   }
 
-  public final void copyInto(Value anArray[]) {
+  public final void copyInto(Value  anArray[]) {
     System.arraycopy(elementData, 0, anArray, 0, elementCount);
   }
 
-  public final Value elementAt(int index) {
+  public final Value  elementAt(int index) {
     // Assert.check(index < this.elementCount);
     return this.elementData[index];
   }
@@ -95,25 +108,25 @@ public class ValueVec implements Cloneable, Serializable {
       if (newCapacity > TLCGlobals.setBound) {
 	newCapacity = TLCGlobals.setBound;
       }
-      Value oldData[] = this.elementData;
-      this.elementData = new Value[newCapacity];
+      Value  oldData[] = this.elementData;
+      this.elementData = new Value [newCapacity];
 
       System.arraycopy(oldData, 0, elementData, 0, elementCount);
     }
   }
 
-  public final Value firstElement() { return this.elementData[0]; }
+  public final Value  firstElement() { return this.elementData[0]; }
 
-  public final int indexOf(Value elem) { return indexOf(elem, 0); }
+  public final int indexOf(Value  elem) { return indexOf(elem, 0); }
 
-  public final int indexOf(Value elem, int index) {
+  public final int indexOf(Value  elem, int index) {
     for (int pos = index; pos < elementCount; pos++) {
       if (elem.equals(elementData[pos])) return pos;
     }
     return -1;
   }
 
-  public final void insertElementAt(Value obj, int index) {
+  public final void insertElementAt(Value  obj, int index) {
     if (elementCount == elementData.length) {
       this.ensureCapacity(elementCount+1);
     }
@@ -124,18 +137,18 @@ public class ValueVec implements Cloneable, Serializable {
 
   public final boolean isEmpty() { return (elementCount == 0); }
 
-  public final Value lastElement() {
+  public final Value  lastElement() {
     return this.elementData[this.elementCount-1];
   }
 
-  public final void setElementAt(Value obj, int index)	{
+  public final void setElementAt(Value  obj, int index)	{
     this.elementData[index] = obj;
   }
 
   public final int size() { return this.elementCount; }
 
   /* Assume that the elements are sorted. */
-  public final boolean search(Value elem, boolean sorted) {
+  public final boolean search(Value  elem, boolean sorted) {
     if (sorted) {
       int cmp = 0, mid = 0, low = 0, high = this.elementCount;
       while (low < high) {
@@ -160,10 +173,10 @@ public class ValueVec implements Cloneable, Serializable {
     return false;
   }
 
-  public final void sort(boolean noDup) {
+  public final ValueVec sort(boolean noDup) {
     int newCount = (this.elementCount == 0) ? 0 : 1;
     for (int i = 1; i < this.elementCount; i++) {
-      Value elem = this.elementData[i];
+      Value  elem = this.elementData[i];
       int cmp = 0, idx = 0, low = 0, high = newCount;
       while (low < high) {
 	idx = (low + high) >> 1;
@@ -186,6 +199,7 @@ public class ValueVec implements Cloneable, Serializable {
       }
     }
     this.elementCount = newCount;
+    return this;
   }
   
   public final String toString() {
@@ -203,4 +217,10 @@ public class ValueVec implements Cloneable, Serializable {
     return sb.toString();
   }
 
+	public Value [] toArray() {
+		final Value [] copy = new Value [elementCount];
+		System.arraycopy(elementData, 0, copy, 0, elementCount);
+		return copy;
+	}
+
 }
diff --git a/tlatools/src/util/Assert.java b/tlatools/src/util/Assert.java
index 3984967242870b9b71a1e248a283d3b7f488bf69..99a054407afad41c6373b9b33f364e7352fb51f4 100644
--- a/tlatools/src/util/Assert.java
+++ b/tlatools/src/util/Assert.java
@@ -11,18 +11,16 @@ import tlc2.output.MP;
  * A toolkit for checking conditions and throwing unchecked exceptions if they are not met.
  * 
  * @author Yuan Yu, Simon Zambrovski 
- * @version $Id$
  */
 public class Assert
 {
     /**
      * Unconditioned way to throw an exception
      * @param reason the explaining message to be enclosed into the exception
-     * @deprecated Use {@link EC} constants instead
      */
     public static void fail(String reason) throws RuntimeException
     {
-        throw new RuntimeException(reason);
+        throw new TLCRuntimeException(reason);
     }
 
     /**
@@ -32,7 +30,7 @@ public class Assert
      */
     public static void fail(int errorCode, String[] parameters)
     {
-        throw new RuntimeException(MP.getMessage(errorCode, parameters));
+        throw new TLCRuntimeException(errorCode, parameters, MP.getMessage(errorCode, parameters));
     }
     
     /**
@@ -42,7 +40,7 @@ public class Assert
      */
     public static void fail(int errorCode, String parameter)
     {
-        throw new RuntimeException(MP.getMessage(errorCode, parameter));
+        throw new TLCRuntimeException(errorCode, new String[] {parameter}, MP.getMessage(errorCode, parameter));
     }
 
     /**
@@ -52,7 +50,7 @@ public class Assert
      */
     public static void fail(int errorCode, Throwable cause)
     {
-        throw new RuntimeException(MP.getMessage(errorCode, cause.getMessage()), cause);
+        throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, cause.getMessage()), cause);
     }
 
     /**
@@ -61,7 +59,7 @@ public class Assert
      */
     public static void fail(int errorCode)
     {
-        throw new RuntimeException(MP.getMessage(errorCode));
+        throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode));
     }
 
     /**
@@ -75,7 +73,7 @@ public class Assert
     {
         if (!condition) 
         {
-            throw new RuntimeException(MP.getMessage(errorCode, parameters));
+            throw new TLCRuntimeException(errorCode, parameters, MP.getMessage(errorCode, parameters));
         }
     }
 
@@ -90,7 +88,7 @@ public class Assert
     {
         if (!condition) 
         {
-            throw new RuntimeException(MP.getMessage(errorCode, parameter));
+            throw new TLCRuntimeException(errorCode, new String[] {parameter}, MP.getMessage(errorCode, parameter));
         }
     }
 
@@ -104,7 +102,7 @@ public class Assert
     {
         if (!condition) 
         {
-            throw new RuntimeException(MP.getMessage(errorCode));
+            throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode));
         }
     }
 
@@ -126,8 +124,34 @@ public class Assert
     {
         if (!condition) 
         {
-            throw new RuntimeException(errorMsg);
+            throw new TLCRuntimeException(errorMsg);
         }
     }
 
+    @SuppressWarnings("serial")
+	public static class TLCRuntimeException extends RuntimeException {
+
+		public final int errorCode;
+		public String[] parameters = null;
+
+		public TLCRuntimeException(String errorMsg) {
+			super(errorMsg);
+			this.errorCode = EC.GENERAL; // Unknown error code.
+		}
+		
+		public TLCRuntimeException(int errorCode, String message) {
+			super(message);
+			this.errorCode = errorCode;
+		}
+
+		public TLCRuntimeException(int errorCode, String message, Throwable cause) {
+			super(message, cause);
+			this.errorCode = errorCode;
+		}
+
+		public TLCRuntimeException(int errorCode, String[] parameters, String message) {
+			this(errorCode, message);
+			this.parameters = parameters;
+		}
+    }
 }
diff --git a/tlatools/src/util/BufferedDataInputStream.java b/tlatools/src/util/BufferedDataInputStream.java
index 713deed933787a00b84edabd11c1bf181d33584f..c9c53cac9da192f514f45c85d91552679961e8a9 100644
--- a/tlatools/src/util/BufferedDataInputStream.java
+++ b/tlatools/src/util/BufferedDataInputStream.java
@@ -22,7 +22,7 @@ import tlc2.output.EC;
     the comments of this class by the specification 
     <TT>REQUIRES LL = SELF</TT>. */
 
-public class BufferedDataInputStream extends FilterInputStream {
+public final class BufferedDataInputStream extends FilterInputStream implements IDataInputStream {
     /* protected by SELF */
     private byte[] buff;    /* buffer of bytes to read */
     private int len;        /* number of valid bytes in "buff" */
@@ -99,7 +99,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     
     /** REQUIRES LL = SELF */
     /** Returns <code>true</code> iff the stream is exhausted. */
-    public boolean atEOF() {
+    public final boolean atEOF() {
         return (this.len < 0);
     }
     
@@ -107,7 +107,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     /** Reads up to <code>b.length</code> bytes into <code>b</code>, 
         and returns the number of bytes read, or -1 if the stream is 
         exhausted on entry. */
-    public int read(byte[] b) throws IOException {
+    public final int read(byte[] b) throws IOException {
         return this.read(b, 0, b.length);
     }
     
@@ -115,7 +115,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     /** Reads up to <code>n</code> bytes into <code>b</code> starting
         at position <code>off</code>, and returns the number of bytes
         read, or -1 if the stream is exhausted on entry. */
-    public int read(byte[] b, int off, int n) throws IOException {
+    public final int read(byte[] b, int off, int n) throws IOException {
         if (this.len < 0) return -1;
         int offInit = off;
         while (n > 0 && this.len > 0) {
@@ -136,7 +136,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     /** Reads <code>b.length</code> bytes into <code>b</code>, or
         throws <code>EOFException</code> if the stream contains fewer
         than <code>b.length</code> bytes. */
-    public void readFully(byte[] b) throws IOException, EOFException {
+    public final void readFully(byte[] b) throws IOException, EOFException {
         this.readFully(b, 0, b.length);
     }
     
@@ -144,7 +144,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     /** Reads <code>n</code> bytes into <code>b</code> starting at
         position <code>off</code>, or throws <code>EOFException</code>
         if the stream contains fewer than <code>n</code> bytes. */
-    public void readFully(byte[] b, int off, int n)
+    public final void readFully(byte[] b, int off, int n)
       throws IOException, EOFException {
         while (n > 0) {
             int numRead = this.read(b, off, n);
@@ -156,7 +156,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     /** REQUIRES LL = SELF */
     /** Reads and returns the next byte of this stream, or throws
         <code>EOFException</code> if the stream is exhausted. */
-    public byte readByte() throws IOException, EOFException {
+    public final byte readByte() throws IOException, EOFException {
         if (this.len < 0) throw new EOFException();
         byte res = this.buff[this.curr++];
         if (this.curr == this.len) {
@@ -173,7 +173,7 @@ public class BufferedDataInputStream extends FilterInputStream {
         encoded in the next byte of this stream, or
         throws <code>EOFException</code> if the stream is
         exhausted. */
-    public boolean readBoolean() throws IOException, EOFException {
+    public final boolean readBoolean() throws IOException, EOFException {
         return (this.readByte() != 0);
     }
     
@@ -182,7 +182,7 @@ public class BufferedDataInputStream extends FilterInputStream {
         encoded in the next two bytes of this stream, or
         throw <code>EOFException</code> if the stream contains
         fewer than two bytes. */
-    public short readShort() throws IOException, EOFException {
+    public final short readShort() throws IOException, EOFException {
         this.readFully(this.temp, 0, 2);
         return (short) ((temp[0] << 8) | (temp[1] & 0xff));
     }
@@ -192,7 +192,7 @@ public class BufferedDataInputStream extends FilterInputStream {
         encoded in the next four bytes of this stream, or
         throws <code>EOFException</code> if the stream contains
         fewer than four bytes. */
-    public int readInt() throws IOException, EOFException {
+    public final int readInt() throws IOException, EOFException {
         this.readFully(this.temp, 0, 4);
         int res = temp[0];
         res <<= 8; res |= (temp[1] & 0xff);
@@ -206,7 +206,7 @@ public class BufferedDataInputStream extends FilterInputStream {
         encoded in the next eight bytes of this stream, or
         throws <code>EOFException</code> if the stream contains
         fewer than eight bytes. */
-    public long readLong() throws IOException, EOFException {
+    public final long readLong() throws IOException, EOFException {
         this.readFully(this.temp, 0, 8);
         long res = temp[0];
         res <<= 8; res |= (temp[1] & 0xff);
@@ -226,7 +226,7 @@ public class BufferedDataInputStream extends FilterInputStream {
         by a carriage return character (<code>'\r'</code>), a newline 
         character (<code>'\n'</code>), a carriage return immediately 
         followed by a newline, or by the end of the stream. */
-    public String readLine() throws IOException {
+    public final String readLine() throws IOException {
         String res = null;
         while (this.len > 0) {
             for (int i = this.curr; i < this.len; i++) {
@@ -262,7 +262,7 @@ public class BufferedDataInputStream extends FilterInputStream {
         return res;
     }
 
-    public String readString(int n) throws IOException {
+    public final String readString(int n) throws IOException {
       char[] b = new char[n];
       int off = 0;
       while (n > 0) {
@@ -291,7 +291,7 @@ public class BufferedDataInputStream extends FilterInputStream {
     /** Skips over the next <code>n</code> bytes in this stream,
         or throws <code>EOFException</code> if it contains fewer
         than <code>n</code> bytes. */
-    public void skip(int n) throws IOException, EOFException {
+    public final void skip(int n) throws IOException, EOFException {
         while (this.len > 0 && this.curr + n >= this.len) {
             n -= (this.len - this.curr);
             // refill buffer from underlying input stream
diff --git a/tlatools/src/util/BufferedDataOutputStream.java b/tlatools/src/util/BufferedDataOutputStream.java
index fc136e422abd4343727f2e8ea039b68cc3c92901..fa2dd63602ce578c68c3987facf9b89fb67926d3 100644
--- a/tlatools/src/util/BufferedDataOutputStream.java
+++ b/tlatools/src/util/BufferedDataOutputStream.java
@@ -16,7 +16,7 @@ import java.io.OutputStream;
     unmonitored. Hence, it is the client's responsibility to lock 
     the stream before using it. */
 
-public class BufferedDataOutputStream extends FilterOutputStream {
+public final class BufferedDataOutputStream extends FilterOutputStream implements IDataOutputStream {
     private byte[] buff; /* buffer of bytes to write */
     private int len;     /* number of valid bytes in "buff" */
     private byte[] temp; /* temporary array used by various methods */
@@ -83,7 +83,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
 
     /** Flush all bytes written to this stream to the underlying
         output stream. */
-    public void flush() throws IOException {
+    public final void flush() throws IOException {
         this.out.write(this.buff, 0, this.len);
         this.out.flush();
         this.len = 0;
@@ -91,26 +91,26 @@ public class BufferedDataOutputStream extends FilterOutputStream {
 
     /** Closes this stream and its underlying stream, after first
         flushing any buffered data. */
-    public void close() throws IOException {
+    public final void close() throws IOException {
         this.flush();
         this.out.close();
         this.out = null;
     }
     
     /** Write <code>b</code> to this stream. */
-    public void write(byte b) throws IOException {
+    public final void write(byte b) throws IOException {
         this.writeByte(b);
     }
     
     /** Write the <code>b.length</code> bytes of <code>b</code> to 
         this stream. */
-    public void write(byte[] b) throws IOException {
+    public final void write(byte[] b) throws IOException {
         this.write(b, 0, b.length);
     }
     
     /** Write <code>n</code> bytes of <code>b</code> starting
-        at possition <code>off</code> to this stream. */
-    public void write(byte[] b, int off, int n) throws IOException {
+        at position <code>off</code> to this stream. */
+    public final void write(byte[] b, int off, int n) throws IOException {
         while (n > 0) {
             int toCopy = Math.min(n, this.buff.length - this.len);
             System.arraycopy(b, off, this.buff, this.len, toCopy);
@@ -124,7 +124,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     }
     
     /** Write the byte <code>b</code> to this stream. */
-    public void writeByte(byte b) throws IOException {
+    public final void writeByte(byte b) throws IOException {
         this.buff[this.len++] = b;
         if (this.buff.length == this.len) {
             // write buffer to underlying stream
@@ -135,14 +135,14 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     
     /** Write the boolean value <code>b</code> to this stream as
         a single byte. */
-    public void writeBoolean(boolean bool) throws IOException {
+    public final void writeBoolean(boolean bool) throws IOException {
         byte b = (bool ? (byte)1 : (byte)0);
         this.writeByte(b);
     }
     
     /** Write the short value <code>s</code> to this stream as
         two bytes. */
-    public void writeShort(short s) throws IOException {
+    public final void writeShort(short s) throws IOException {
         this.temp[0] = (byte) ((s >>> 8) & 0xff);
         this.temp[1] = (byte) (s & 0xff);
         this.write(this.temp, 0, 2);
@@ -150,7 +150,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     
     /** Write the integer value <code>i</code> to this stream as
         four bytes. */
-    public void writeInt(int i) throws IOException {
+    public final void writeInt(int i) throws IOException {
         this.temp[0] = (byte) ((i >>> 24) & 0xff);
         this.temp[1] = (byte) ((i >>> 16) & 0xff);
         this.temp[2] = (byte) ((i >>> 8) & 0xff);
@@ -160,7 +160,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     
     /** Write the long value <code>l</code> to this stream as
         eight bytes. */
-    public void writeLong(long l) throws IOException {
+    public final void writeLong(long l) throws IOException {
         this.temp[0] = (byte) ((l >>> 56) & 0xff);
         this.temp[1] = (byte) ((l >>> 48) & 0xff);
         this.temp[2] = (byte) ((l >>> 40) & 0xff);
@@ -174,19 +174,19 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     
     /** Write the float value <code>f</code> to this stream as
         four bytes. */
-    public void writeFloat(float f) throws IOException {
+    public final void writeFloat(float f) throws IOException {
 	    this.writeInt(Float.floatToIntBits(f));
     }
     
     /** Write the double value <code>d</code> to this stream as
         eight bytes. */
-    public void writeDouble(double d) throws IOException {
+    public final void writeDouble(double d) throws IOException {
 	    this.writeLong(Double.doubleToLongBits(d));
     }
 
     /** Write the characters of the string <code>s</code> to this
         stream as a sequence of bytes. */
-    public void writeString(String s) throws IOException {
+    public final void writeString(String s) throws IOException {
         int n = s.length();
         int off = 0;
         while (n > 0) {
@@ -203,7 +203,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     
     /** Write <code>n</code> characters of <code>chars</code> starting 
         at offset <code>off</code> to this stream as a sequence of bytes. */
-    public void writeChars(char[] chars, int off, int n) throws IOException {
+    public final void writeChars(char[] chars, int off, int n) throws IOException {
         int finOff = off + n;
         while (off < finOff) {
             // Copy (part of) chars to this.buff
@@ -223,7 +223,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
         can be read back by <code>BufferedDataInputStream.readAnyString</code>,
         even if <code>s</code> is <code>null</code> or if it contains newline 
         characters. */
-    public void writeAnyString(String s) throws IOException {
+    public final void writeAnyString(String s) throws IOException {
       if (s == null) {
 	this.writeInt(-1);
       }
@@ -235,7 +235,7 @@ public class BufferedDataOutputStream extends FilterOutputStream {
     
     /** Write the characters of the string <code>s</code> to this
         stream as a sequence of bytes, followed by a newline. */
-    public void writeLine(String s) throws IOException {
+    public final void writeLine(String s) throws IOException {
         this.writeString(s);
         this.writeByte((byte) '\n');
     }
diff --git a/tlatools/src/util/ByteArrayInputStream.java b/tlatools/src/util/ByteArrayInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/src/util/ExecutionStatisticsCollector.java b/tlatools/src/util/ExecutionStatisticsCollector.java
new file mode 100644
index 0000000000000000000000000000000000000000..376f843fdc177790582f8d536b491aeb3880d76c
--- /dev/null
+++ b/tlatools/src/util/ExecutionStatisticsCollector.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class ExecutionStatisticsCollector {
+
+	static final String RND_ID_STR = "RANDOM_IDENTIFIER";
+	static final String NO_ESC_STR = "NO_STATISTICS";
+	
+	public enum Selection {
+		ON, RANDOM_IDENTIFIER, NO_ESC;
+
+		@Override
+		public String toString() {
+			if (this == ON) {
+				return getRandomIdentifier();
+			} else if (this == RANDOM_IDENTIFIER) {
+				return RND_ID_STR;
+			} else {
+				return NO_ESC_STR;
+			}
+		}
+	}
+	
+	private static final String PATH = System.getProperty("user.home", "") + File.separator + ".tlaplus" + File.separator + "esc.txt";
+
+	public static final String PROP = ExecutionStatisticsCollector.class.getName() + ".id";
+	
+	private final boolean isOptOut;
+	private final String pathname;
+
+	public ExecutionStatisticsCollector() {
+		this(isOptOut(), PATH);
+	}
+	
+	ExecutionStatisticsCollector(String path) {
+		this(false, path);
+	}
+	
+	ExecutionStatisticsCollector(final boolean optOut, final String path) {
+		this.isOptOut = optOut;
+		this.pathname = path;
+	}
+	
+	public void collect(final Map<String, String> parameters) {
+		collect(parameters, true);
+	}
+
+	private void collect(final Map<String, String> parameters, final boolean dontWaitForCompletion) {
+		// Do not block TLC startup but send this to the background immediately. If
+		// dontWaitForCompletion is true, the VM will terminate this thread regardless
+		// of its state if the VM decides to shutdown (e.g. because TLC is done).
+		final Thread thread = new Thread(new Runnable() {
+			@Override
+			public void run() {
+				if (isEnabled()) {
+					// Include identifier to track individual installations (not users!).
+					parameters.put("id", getIdentifier());
+					submit(parameters, dontWaitForCompletion);
+				}
+			}
+		}, "TLC Execution Statistics Collector");
+		thread.setDaemon(dontWaitForCompletion);
+		thread.start();
+	}
+	
+	/*
+	 * file == ~/.tlaplus/esc.txt
+	 * fl == first line of file interpreted as a string without terminal chars
+	 * in/out == opt-in & opt-out
+	 * y/r/n == data collected with constant id/data collected with random id/data not collected
+	 * 
+	 *       | No file | fl unreadable | fl empty | fl = "NO_UDC" | fl = "RANDOM_IDENTIFIER" | fl any other string
+	 * ==========================================================================================================
+	 * | out |   y     |       n       |    n     |       n       |            r             |         y         |
+	 * ----------------------------------------------------------------------------------------------------------
+	 * | in  |   n     |       n       |    n     |       n       |            r             |         y         |
+	 * ----------------------------------------------------------------------------------------------------------
+	 */
+	public String getIdentifier() {
+		if (System.getProperty(PROP) != null) {
+			return System.getProperty(PROP);
+		}
+		String identifier;
+
+		final File udcFile = new File(pathname);
+		if (!udcFile.exists() && isOptOut) {
+			try (BufferedWriter br = new BufferedWriter(new FileWriter(udcFile))) {
+				br.write(getRandomIdentifier());
+			} catch (Exception e) {
+				// Something went wrong writing to file ~/.tlaplus/esc.txt. Consider ESC failed.
+				return null;
+			}
+		}
+		if (!udcFile.exists()) {
+			// No file ~/.tlaplus/esc.txt.
+			return null;
+		}
+		try (BufferedReader br = new BufferedReader(new FileReader(udcFile))) {
+			identifier = br.readLine();
+		} catch (Exception e) {
+			// Something went wrong reading file ~/.tlaplus/esc.txt
+			return null;
+		}
+		if (identifier == null || NO_ESC_STR.equals(identifier.trim())) {
+			// File is empty or its first line is "NO_STATISTICS".
+			return null;
+		} else if (identifier == null || RND_ID_STR.equals(identifier.trim())) {
+			identifier = getRandomIdentifier();
+		}
+		
+		// truncate the identifier no matter what, but first remove leading and trailing whitespaces.
+		final String trimmed = identifier.trim();
+		return trimmed.substring(0, Math.min(trimmed.length(), 32));
+	}
+
+	private boolean escFileExists() {
+		return new File(pathname).exists();
+	}
+
+	public boolean isEnabled() {
+		return getIdentifier() != null;
+	}
+
+	public void set(final Selection c) throws IOException {
+		final File udcFile = new File(PATH);
+		udcFile.createNewFile();
+		
+		try (BufferedWriter br = new BufferedWriter(new FileWriter(udcFile))) {
+			br.write(c.toString() + "\n");
+		} catch (IOException e) {
+			throw e;
+		}
+	}
+	
+	public Selection get() {
+		if (isEnabled()) {
+			try (BufferedReader br = new BufferedReader(new FileReader(new File(pathname)))) {
+				String line = br.readLine();
+				if (RND_ID_STR.equals(line)) {
+					return Selection.RANDOM_IDENTIFIER;
+				} else {
+					return Selection.ON;
+				}
+			} catch (Exception e) {
+			}
+		}
+		return Selection.NO_ESC;
+	}
+	
+	public static boolean promptUser() {
+		return !(new ExecutionStatisticsCollector().escFileExists());
+	}
+
+	private static String getRandomIdentifier() {
+		return UUID.randomUUID().toString().replaceAll("-", "");
+	}
+
+	private static boolean isOptOut() {
+		return false; // Nobody is opt-out right now
+		
+		// Below is a way how we could detect Microsoft corpnet machines: This check is
+		// conservative because we don't identify Microsoft employees but corporate
+		// Microsoft computers. We don't set ESC to opt-out for Microsoft machines yet
+		// but might in the future.
+//		final String userDNSDomain = System.getenv("USERDNSDOMAIN");
+//		return userDNSDomain != null && userDNSDomain.toUpperCase().endsWith(".CORP.MICROSOFT.COM");
+	}
+
+	// Send the request.
+	private static void submit(final Map<String, String> parameters, final boolean dontWaitForCompletion) {
+		// Include a timestamp to cause HEAD to be un-cachable.
+		parameters.put("ts", Long.toString(System.currentTimeMillis()));
+		parameters.put("optout", Boolean.toString(isOptOut()));
+		
+		try {
+			// Opt-out data doesn't report to the opt-in endpoint. The opt-in endpoint is
+			// public data, the opt-out endpoint doesn't get shared with the public.
+			final URL url = new URL(
+					"https://" + (isOptOut() ? "esc02" : "esc01") + ".tlapl.us/?" + encode(parameters));
+
+			final HttpURLConnection con = (HttpURLConnection) url.openConnection();
+			con.setRequestMethod("HEAD");
+			con.getResponseMessage();
+			con.disconnect();
+		} catch (Exception ignoreCompletely) {
+			// A TLC user doesn't care if execution statistics collection doesn't work.
+		}
+	}	
+	
+	private static String encode(final Map<String, String> parameters) throws UnsupportedEncodingException {
+		final StringBuffer buf = new StringBuffer();
+
+		for (String key : parameters.keySet()) {
+			String value = parameters.get(key);
+			buf.append(URLEncoder.encode(key, "UTF-8"));
+			buf.append("=");
+			buf.append(URLEncoder.encode(value, "UTF-8"));
+			buf.append(",");
+		}
+		
+		return buf.toString().replaceFirst(",$", "");
+	}
+
+	// for manual testing //
+	
+	public static void main(String[] args) {
+		new ExecutionStatisticsCollector().collect(new HashMap<>(), false);
+	}
+}
diff --git a/tlatools/src/util/ExecutionStatisticsCollector.md b/tlatools/src/util/ExecutionStatisticsCollector.md
new file mode 100644
index 0000000000000000000000000000000000000000..e4b51b4429217adba6d603c1dd923aeac9e6f43f
--- /dev/null
+++ b/tlatools/src/util/ExecutionStatisticsCollector.md
@@ -0,0 +1,14 @@
+You can help make the TLA+ tools better by allowing TLC to collect execution statistics. Execution statistics are made [publicly available](https://exec-stats.tlapl.us) on the web and contain the following information:
+
+* Total number of cores and cores assigned to TLC
+* Heap and off-heap memory allocated to TLC
+* TLC version  (git commit SHA)
+* If breadth-first search, depth-first search or simulation mode is active
+* TLC implemenation for the sets of seen and unseen states
+* If TLC has been launched from an IDE
+* Name, version, and architecture of the OS
+* Vendor, version, and architecture of JVM
+* The current date and time
+* An installation ID which allows to group execution statistic (optionally)
+
+Execution statistics do not contain personal information. You can opt-out any time.
diff --git a/tlatools/src/util/FileUtil.java b/tlatools/src/util/FileUtil.java
index d5c18f4a6c6891078faa8abf8c354265c594a280..401984fb906e7e686996c08a9cd7082def0adb6a 100644
--- a/tlatools/src/util/FileUtil.java
+++ b/tlatools/src/util/FileUtil.java
@@ -16,6 +16,8 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.OutputStream;
 import java.math.BigInteger;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.zip.GZIPInputStream;
@@ -37,6 +39,19 @@ public class FileUtil
     public static final String separator = File.separator;
     public static final String pathSeparator = File.pathSeparator;
 
+    /**
+     * Parses the directory path from a filename. If the filename
+     * is already a basename, returns the empty string.
+     */
+    public static String parseDirname(String filename) {
+        int lastSep = filename.lastIndexOf(separatorChar);
+        if (lastSep == -1) {
+            // No parent directory.
+            return "";
+        }
+        return filename.substring(0, lastSep + 1);
+    }
+
     /**
      * Deletes the file or directory. Returns true iff the deletion
      * succeeds. The argument recurse forces the deletion of non-empty
@@ -180,21 +195,24 @@ public class FileUtil
         }
     }
 
-    public static void copyFile(String fromName, String toName) throws IOException
-    {
-        // REFACTOR
-        FileInputStream fis = new FileInputStream(fromName);
-        FileOutputStream fos = new FileOutputStream(toName);
-        byte[] buf = new byte[8192];
-        int n;
-        while ((n = fis.read(buf)) != -1)
-        {
-            fos.write(buf, 0, n);
-        }
-        fis.close();
-        fos.close();
+    public static void copyFile(final String fromName, final String toName) throws IOException {
+    	copyFile(new File(fromName), new File(toName));
+    }
+    
+    
+    public static void copyFile(final File source, final File destination) throws IOException {
+    	Files.copy(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING);
     }
 
+	/**
+	 * Atomically replaces the file targetName with the file sourceName.
+	 * @param sourceName
+	 * @param targetName
+	 * @throws IOException
+	 */
+	public static void replaceFile(String sourceName, String targetName) throws IOException {
+		Files.move(new File(sourceName).toPath(), new File(targetName).toPath(), StandardCopyOption.REPLACE_EXISTING);
+	}
 
     /**
      * The MetaDir is fromChkpt if it is not null. Otherwise, create a
@@ -204,6 +222,11 @@ public class FileUtil
      *
      */
     public static String makeMetaDir(String specDir, String fromChkpt)
+    {
+    	return makeMetaDir(new Date(), specDir, fromChkpt);
+    }
+    
+    public static String makeMetaDir(Date date, String specDir, String fromChkpt)
     {
         if (fromChkpt != null)
         {
@@ -216,15 +239,20 @@ public class FileUtil
             metadir = specDir + TLCGlobals.metaRoot + FileUtil.separator;
         }
 
-        SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd-HH-mm-ss");
-        metadir += sdf.format(new Date());
+        SimpleDateFormat sdf;
+        if (Boolean.getBoolean(FileUtil.class.getName() + ".milliseconds")) {
+        	sdf = new SimpleDateFormat("yy-MM-dd-HH-mm-ss.SSS");
+        } else {
+        	sdf = new SimpleDateFormat("yy-MM-dd-HH-mm-ss");
+        }
+        metadir += sdf.format(date);
         File filedir = new File(metadir);
 
         // ensure the non-existence
-        Assert.check(!filedir.exists(), EC.SYSTEM_METADIR_EXISTS, metadir);
+        Assert.check(!filedir.exists(), EC.SYSTEM_METADIR_EXISTS, filedir.getAbsolutePath());
 
         // ensure the dirs are created
-        Assert.check(filedir.mkdirs(), EC.SYSTEM_METADIR_CREATION_ERROR, metadir);
+        Assert.check(filedir.mkdirs(), EC.SYSTEM_METADIR_CREATION_ERROR, filedir.getAbsolutePath());
 
         return metadir;
     }
@@ -251,16 +279,16 @@ public class FileUtil
         // or name=/frob/bar/somemod
 
         // Make sure the file name ends with ".tla".
-        if (name.toLowerCase().endsWith(".tla"))
+        if (name.toLowerCase().endsWith(TLAConstants.Files.TLA_EXTENSION))
         {
-            name = name.substring(0, name.length() - 4);
+            name = name.substring(0, (name.length() - TLAConstants.Files.TLA_EXTENSION.length()));
         }
 
         // now name=/frob/bar/somemod
 
         // filename is a path ending with .tla
         // sourceFilename=/frob/bar/somemod
-        sourceFileName = name + ".tla";
+        sourceFileName = name + TLAConstants.Files.TLA_EXTENSION;
 
         // module name is =somemod
         sourceModuleName = name.substring(name.lastIndexOf(FileUtil.separator) + 1);
@@ -333,27 +361,18 @@ public class FileUtil
      * retrieves a new buffered file output stream
      * @param name
      * @return
+     * @throws FileNotFoundException 
      */
-    public static OutputStream newBFOS(String name)
+    public static OutputStream newBFOS(String name) throws FileNotFoundException
     {
-        File file = new File(name);
-
-        // LL removed file.exists() test on 10 Nov 2012 because
-        // it causes an error when TLC called with -dump option
-        // for a file that doesn't already exist.  Also changed
-        // the error message to something more helpful.
-        if (file != null /* && file.exists() */)
+        try
         {
-            try
-            {
-                FileOutputStream fos = new FileOutputStream(file);
-                return fos;
-            } catch (FileNotFoundException e)
-            {
-                ToolIO.out.println("Error: Unable to write to file " + name);
-            }
+            return new FileOutputStream(new File(name));
+        } catch (FileNotFoundException e)
+        {
+            ToolIO.out.println("Error: Unable to write to file " + name);
+            throw e;
         }
-        return null;
     }
 
     public static BufferedDataInputStream newBdFIS(boolean useGZIP, File file) throws IOException
diff --git a/tlatools/src/util/FilenameToStream.java b/tlatools/src/util/FilenameToStream.java
index 11dc64cb397173c415de83de4632c3f7c67e1086..87dec15b19795182ce142de425b73aaea3966467 100644
--- a/tlatools/src/util/FilenameToStream.java
+++ b/tlatools/src/util/FilenameToStream.java
@@ -3,6 +3,7 @@
 package util;
 
 import java.io.File;
+import java.util.regex.Pattern;
 
 
 /**
@@ -14,11 +15,65 @@ import java.io.File;
  *
  * @author Leslie Lamport
  * @author Simon Zambrovski
- * @version $Id$
  */
 public interface FilenameToStream
 {
 
+	/*
+	 * Higher layers of TLC (and the Toolbox) have to determine if a module was
+	 * loaded from a library location s.a. those defined by TLA_LIBRARY (see
+	 * SimpleFilenameToStream). Thus, capture this information at module load
+	 * time when it is known where a module was loaded from.
+	 */
+	@SuppressWarnings("serial")
+	public static class TLAFile extends File {
+		// The following regex is concerned with determining whether the provided 'parent' string to our
+		//	parent/child constructor looks like the start of a legal absolute file path potentially including
+		//	a drive letter.
+		private static final String ROOT_PATH_REGEX
+								= "^([a-zA-Z]+:)?" + ((File.separatorChar == '\\') ? "\\\\" : File.separator);
+		private static final Pattern ROOT_PATH_PATTERN = Pattern.compile(ROOT_PATH_REGEX);
+		
+		
+		private final boolean isLibraryModule;
+		private transient final FilenameToStream resolver;
+
+		public TLAFile(String pathname, FilenameToStream fts) {
+			this(pathname, false, fts);
+		}
+
+		public TLAFile(String pathname, boolean isLibraryModule, FilenameToStream fts) {
+			super(pathname);
+			this.isLibraryModule = isLibraryModule;
+			this.resolver = fts;
+		}
+
+		public TLAFile(String parent, String child, FilenameToStream fts) {
+			super(parent,
+				  ((ROOT_PATH_PATTERN.matcher(parent).find() && child.startsWith(parent))
+						  ? child.substring(parent.length())
+					      : child));
+			this.isLibraryModule = false;
+			this.resolver = fts;
+		}
+
+		public boolean isLibraryModule() {
+			return isLibraryModule;
+		}
+
+		/**
+		 * @return null if no TLC module override for this module exists.
+		 */
+		public File getModuleOverride() {
+			final File moduleOverride = resolver.resolve(getName().replaceAll(".tla$", ".class"), false);
+			if (moduleOverride.exists()) {
+				// resolve(...) return a File instance even if the file doesn't exist.
+				return moduleOverride;
+			}
+			return null;
+		}
+	}
+	
     /**
      * This method resolves the logical name to the OS-resource
      * @param filename
@@ -39,6 +94,22 @@ public interface FilenameToStream
      *
      * @param moduleName
      * @return
+	 * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule()
+	 * @see StandardModules.isDefinedInStandardModule()
      */
     public boolean isStandardModule(String moduleName) ;
+
+	default boolean isLibraryModule(String moduleName) {
+		return isStandardModule(moduleName);
+	}
+   
+	static final String TMPDIR = System.getProperty("java.io.tmpdir");
+
+	static boolean isInJar(final String aString) {
+		return aString.startsWith("jar:") || aString.endsWith(".jar");
+	}
+
+	static boolean isArchive(String aString) {
+		return isInJar(aString) || aString.endsWith(".zip");
+	}
 }
diff --git a/tlatools/src/util/IDataInputStream.java b/tlatools/src/util/IDataInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..90d944f5f94f6dc055bddb176246bd83b9540bd7
--- /dev/null
+++ b/tlatools/src/util/IDataInputStream.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+public interface IDataInputStream {
+
+	int readInt() throws IOException, EOFException;
+
+	String readString(int slen) throws IOException;
+
+}
diff --git a/tlatools/src/util/IDataOutputStream.java b/tlatools/src/util/IDataOutputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4ac8bb7c36e2c9f4bcb36766bd8893e8385ab8c
--- /dev/null
+++ b/tlatools/src/util/IDataOutputStream.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import java.io.IOException;
+
+public interface IDataOutputStream {
+
+	void writeInt(int varLoc) throws IOException;
+
+	void writeString(String s) throws IOException;
+
+}
diff --git a/tlatools/src/util/InternTable.java b/tlatools/src/util/InternTable.java
index aec111a2f4ac973b67ab766bd22d43e244f48945..92dba8b5348ee9d5c72ca5a59ebfff6131b478f3 100644
--- a/tlatools/src/util/InternTable.java
+++ b/tlatools/src/util/InternTable.java
@@ -6,6 +6,8 @@ import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
 
 import tlc2.output.EC;
 import tlc2.tool.distributed.InternRMI;
@@ -196,4 +198,26 @@ public final class InternTable implements Serializable
 		this.internSource = source;
 	}
 
+	public UniqueString find(final String str) {
+        for (int i = 0; i < this.table.length; i++)
+        {
+            UniqueString var = this.table[i];
+            if (var != null && str.equals(var.toString()))
+            {
+                return var;
+            }
+        }
+        return null;
+	}
+
+	public final Map<String, UniqueString> toMap() {
+		final Map<String, UniqueString> map = new HashMap<String, UniqueString>();
+		for (int i = 0; i < this.table.length; i++) {
+			UniqueString var = this.table[i];
+			if (var != null) {
+				map.put(var.toString(), var);
+			}
+		}
+		return map;
+	}
 }
diff --git a/tlatools/src/util/MailSender.java b/tlatools/src/util/MailSender.java
index a9e8a0d09063d8fd7c1fa92afbd1c7e41d6b6033..c4e36a820d19525ac9649ef9fb9845c86bfe460d 100644
--- a/tlatools/src/util/MailSender.java
+++ b/tlatools/src/util/MailSender.java
@@ -3,19 +3,21 @@ package util;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.PrintStream;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Properties;
+import java.util.Scanner;
 
-import javax.activation.DataHandler;
-import javax.activation.FileDataSource;
 import javax.mail.Message;
 import javax.mail.MessagingException;
 import javax.mail.Multipart;
+import javax.mail.SendFailedException;
 import javax.mail.Session;
 import javax.mail.Transport;
 import javax.mail.internet.AddressException;
@@ -28,48 +30,78 @@ import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
 import javax.naming.directory.InitialDirContext;
 
-// Requires Java >=6 due to javax.activation only part starting with 6
+import model.ModelInJar;
+import tlc2.output.MP;
+
 public class MailSender {
 
+	public static final String MODEL_NAME = "modelName";
+	public static final String SPEC_NAME = "specName";
+	public static final String MAIL_ADDRESS = "result.mail.address";
+
 	/**
 	 * @param from "Foo bar <foo@bar.com>"
 	 * @param to An email address _with_ domain part (foo@bar.com)
-	 * @param domain domain part (bar.com). Used to lookup mx records
 	 * @param subject
 	 * @param messages
 	 */
-	private static boolean send(final String from, final String to,
-			final String domain, final String subject, final File[] files) {
+	private static boolean send(final InternetAddress from, final InternetAddress to, final String subject, final String body, final File[] files) {
 		
+		// https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html
 		final Properties properties = System.getProperties();
 		//properties.put("mail.debug", "true");
 		
+		if (!to.getAddress().contains("@")) {
+			// no domain, no MX record to lookup
+			return false;
+		} else if (!to.getAddress().endsWith("localhost")) {
+			// Prefer email to be delivered encrypted (assumes to lower likelihood of SMTP
+			// rejection or classification as spam too). Falls back to plain text if SMTP
+			// server does not support starttls. Only activate unless sending to localhost
+			// which is the (postfix) SMTP running locally which only has a self-signed
+			// certificate which gets rejected by java mail.
+			properties.put("mail.smtp.starttls.enable", "true");
+		}
+		
+		List<MXRecord> mailhosts;
 		try {
-			final List<MXRecord> mailhosts = getMXForDomain(domain);
-				
-			// retry all mx host
-			for (MXRecord mxRecord : mailhosts) {
-				properties.put("mail.smtp.host", mxRecord.hostname);
+			mailhosts = getMXForDomain(to.getAddress().split("@")[1]);
+		} catch (NamingException e) {
+			e.printStackTrace();
+			return false;
+		}
 				
+		// retry all mx host
+		for (int i = 0; i < mailhosts.size(); i++) {
+			final MXRecord mxRecord = mailhosts.get(i);
+			properties.put("mail.smtp.host", mxRecord.hostname);
+			try {
 				final Session session = Session.getDefaultInstance(properties);
 				final Message msg = new MimeMessage(session);
-				msg.setFrom(new InternetAddress(from));
-				msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
+				msg.setFrom(from);
+				msg.addRecipient(Message.RecipientType.TO, to);
 				msg.setSubject(subject);
 				
 				// not sure why the extra body part is needed here
 				MimeBodyPart messageBodyPart = new MimeBodyPart();
-
+	
 				final Multipart multipart = new MimeMultipart();
-
+				
+				// The main body part. Having a main body appears to have a very
+				// positive effect on the spam score compared to emails with
+				// just attachments. It is also visually more appealing to e.g.
+				// Outlook users who otherwise see an empty mail.
+				messageBodyPart = new MimeBodyPart();
+				messageBodyPart.setContent(body, "text/plain");
+				multipart.addBodyPart(messageBodyPart);
+	
 				// attach file(s)
 				for (File file : files) {
 					if (file == null) {
 						continue;
 					}
 					messageBodyPart = new MimeBodyPart();
-					messageBodyPart.setDataHandler(new DataHandler(
-							new FileDataSource(file)));
+					messageBodyPart.attachFile(file);
 					messageBodyPart.setFileName(file.getName());
 					messageBodyPart.setHeader("Content-Type", "text/plain");
 					multipart.addBodyPart(messageBodyPart);
@@ -78,16 +110,41 @@ public class MailSender {
 				
 		        Transport.send(msg);
 				return true;
+			} catch (SendFailedException e) {
+				final Exception next = e.getNextException();
+				if (next != null && next.getMessage() != null && next.getMessage().toLowerCase().contains("greylist")
+						&& !properties.containsKey((String) properties.get("mail.smtp.host") + ".greylisted")) {
+					// mark receiver as greylisted to not retry over and over again.
+					properties.put((String) properties.get("mail.smtp.host") + ".greylisted", "true");
+					throttleRetry(String.format(
+							"%s EMail Report: Detected greylisting when sending to %s at %s, will retry in %s minutes...",
+							new Date(), to.getAddress(), mxRecord.hostname, 10L), 10L);
+					i = i - 1;
+				} else {
+					throttleRetry(String.format(
+							"%s EMail Report: Slowing down due to errors when sending to %s at %s, will continue in %d minute...",
+							new Date(), to.getAddress(), mxRecord.hostname, 1L), 1L);
+				}
+			} catch (AddressException e) {
+				e.printStackTrace();
+			} catch (MessagingException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
 			}
-		} catch (NamingException e) {
-			e.printStackTrace();
-		} catch (AddressException e) {
-			e.printStackTrace();
-		} catch (MessagingException e) {
-			e.printStackTrace();
 		}
 		return false;
 	}
+	
+	private static void throttleRetry(final String msg, long minutes) {
+		try {
+			System.err.println(msg);
+			System.out.println(msg);
+			Thread.sleep(minutes * 60L * 1000L);
+		} catch (InterruptedException e1) {
+			e1.printStackTrace();
+		}
+	}
 
 	private static List<MXRecord> getMXForDomain(String aDomain) throws NamingException {
 		final InitialDirContext ctx = new InitialDirContext();
@@ -100,16 +157,16 @@ public class MailSender {
 		// RFC 974
 		if (attr == null) {
 			list.add(new MXRecord(0, aDomain));
-		}
-
-		// split pref from hostname
-		for (int i = 0; i < attr.size(); i++) {
-			Object object = attr.get(i);
-			if (object != null && object instanceof String) {
-				String[] split = ((String) object).split("\\s+");
-				if (split != null && split.length == 2) {
-					Integer weight = Integer.parseInt(split[0]);
-					list.add(new MXRecord(weight, split[1]));
+		} else {
+			// split pref from hostname
+			for (int i = 0; i < attr.size(); i++) {
+				Object object = attr.get(i);
+				if (object != null && object instanceof String) {
+					String[] split = ((String) object).split("\\s+");
+					if (split != null && split.length == 2) {
+						Integer weight = Integer.parseInt(split[0]);
+						list.add(new MXRecord(weight, split[1]));
+					}
 				}
 			}
 		}
@@ -133,57 +190,110 @@ public class MailSender {
 			return weight.compareTo(o.weight);
 		}
 	}
+	
+	// For testing only.
+	public static void main(String[] args) throws AddressException, FileNotFoundException, UnknownHostException {
+		MailSender mailSender = new MailSender();
+		mailSender.send();
+	}
 
-	// if null, no Mail is going to be send
-	private final String mailto;
 	
-	private String mainFile;
+	private String modelName = "unknown model";
+	private String specName = "unknown spec";
 	private File err;
 	private File out;
-	private String from;
-	private String domain;
+	// if null, no Mail is going to be send
+	private InternetAddress[] toAddresses;
+	private InternetAddress from;
+	private InternetAddress fromAlt;
 
-	public MailSender(String aMainFile) throws FileNotFoundException, UnknownHostException
-			 {
-		mailto = System.getProperty("result.mail.address");
+	public MailSender() throws FileNotFoundException, UnknownHostException, AddressException {
+		ModelInJar.loadProperties(); // Reads result.mail.address and so on.
+		final String mailto = System.getProperty(MAIL_ADDRESS);
 		if (mailto != null) {
-			domain = mailto.split("@")[1];
+			this.toAddresses = InternetAddress.parse(mailto);
 			
-			from = "TLC - The friendly model checker <"
+			this.from = new InternetAddress("TLC - The friendly model checker <"
+					+ toAddresses[0].getAddress() + ">");
+			this.fromAlt = new InternetAddress("TLC - The friendly model checker <"
 					+ System.getProperty("user.name") + "@"
-					+ InetAddress.getLocalHost().getHostName() + ">";
-			
-			mainFile = aMainFile;
+					+ InetAddress.getLocalHost().getHostName() + ">");
 			
 			// Record/Log output to later send it by email
 			final String tmpdir = System.getProperty("java.io.tmpdir");
-			out = new File(tmpdir + File.separator + "MC.out");
+			this.out = new File(tmpdir + File.separator + TLAConstants.Files.MODEL_CHECK_OUTPUT_FILE);
 			ToolIO.out = new LogPrintStream(out);
-			err = new File(tmpdir + File.separator + "MC.err");
-			ToolIO.err = new LogPrintStream(err);
+			this.err = new File(tmpdir + File.separator + TLAConstants.Files.MODEL_CHECK_ERROR_FILE);
+			ToolIO.err = new ErrLogPrintStream(err);
 		}
 	}
 	
+	public MailSender(String mainFile) throws FileNotFoundException, UnknownHostException, AddressException {
+		this();
+		setModelName(mainFile);
+	}
+	
+	public void setModelName(String modelName) {
+		this.modelName = modelName;
+	}
+
+	public void setSpecName(String specName) {
+		this.specName = specName;
+	}
+
 	public boolean send() {
 		return send(new ArrayList<File>());
 	}
 
 	public boolean send(List<File> files) {
-		if (mailto != null) {
+		if (toAddresses != null) {
+			files.add(0, out);
 			// Only add the err file if there is actually content 
-			files.add(out);
 			if (err.length() != 0L) {
-				files.add(err);
+				files.add(0, err);
 			}
-			// Try sending the mail with the model checking result to the receiver 
-			return send(from, mailto, domain, "Model Checking result for " + mainFile,
-					files.toArray(new File[files.size()]));
+			// Try sending the mail with the model checking result to the receivers. Returns
+			// true if a least one email was delivered successfully.
+			boolean success = false;
+			for (final InternetAddress toAddress : toAddresses) {
+				if (send(from, toAddress, "Model Checking result for " + modelName + " with spec " + specName,
+						extractBody(out), files.toArray(new File[files.size()]))) {
+					success = true;
+				} else if (send(fromAlt, toAddress, "Model Checking result for " + modelName + " with spec " + specName,
+						extractBody(out), files.toArray(new File[files.size()]))) {
+					// Try with alternative from address which some receivers might actually accept.
+					success = true;
+				}
+			}
+			return success;
 		} else {
 			// ignore, just signal everything is fine
 			return true;
 		}
 	}	
     
+	/**
+	 * @return The human readable lines in the log file. 
+	 */
+	private String extractBody(File out) {
+		StringBuffer result = new StringBuffer();
+		try {
+			final Scanner scanner = new Scanner(out);
+			while (scanner.hasNext()) {
+				final String line = scanner.nextLine();
+				if (line != null && !line.startsWith(MP.DELIM)) {
+					result.append(line);
+					result.append("\n");
+				}
+			}
+			scanner.close();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			result.append("Failed to find file " + out.getAbsolutePath()); 
+		}
+		return result.toString();
+	}
+
 	/**
 	 * A LogPrintStream writes the logging statements to a file _and_ to
 	 * System.out.
@@ -193,20 +303,26 @@ public class MailSender {
     	public LogPrintStream(File file) throws FileNotFoundException  {
     		super(new FileOutputStream(file));
 		}
-    	
+
     	/* (non-Javadoc)
-    	 * @see java.io.PrintStream#print(java.lang.String)
+    	 * @see java.io.PrintStream#println(java.lang.String)
     	 */
-    	public void print(String str) {
-    		System.out.print(str);
-    		super.print(str);
+    	public void println(String str) {
+    		System.out.println(str);
+    		super.println(str);
     	}
+    }
+    
+    private static class ErrLogPrintStream extends PrintStream {
+    	public ErrLogPrintStream(File file) throws FileNotFoundException  {
+    		super(new FileOutputStream(file));
+		}
 
     	/* (non-Javadoc)
     	 * @see java.io.PrintStream#println(java.lang.String)
     	 */
     	public void println(String str) {
-    		System.out.println(str);
+    		System.err.println(str);
     		super.println(str);
     	}
     }
diff --git a/tlatools/src/util/NamedInputStream.java b/tlatools/src/util/NamedInputStream.java
index e635475b00a543ad329bacfe8a621bcdccaae0e7..273dcd4dd0f7c6c248bce436c5a11d427d92dde0 100644
--- a/tlatools/src/util/NamedInputStream.java
+++ b/tlatools/src/util/NamedInputStream.java
@@ -6,6 +6,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Path;
 
 // SZ Feb 20, 2009: moved to util and reformatted
 
@@ -65,6 +66,15 @@ public class NamedInputStream extends FileInputStream
     {
         return inputFile;
     }
+    
+    /**
+	 * @return The absolute, resolved path. In case a file is symlinked, resolve the
+	 *         final target (doesn't work for cygwin symlinks but for mklink)
+     * @throws IOException 
+	 */
+    public final Path getAbsoluteResolvedPath() throws IOException {
+		return inputFile.toPath().toRealPath();
+    }
 
     public final String toString()
     {
diff --git a/tlatools/src/util/SimpleFilenameToStream.java b/tlatools/src/util/SimpleFilenameToStream.java
index 62a5ce87528532291efbb9ead56883e07566664b..e1a088cb9c7de6f49417e9eaf3c407ec3d28f931 100644
--- a/tlatools/src/util/SimpleFilenameToStream.java
+++ b/tlatools/src/util/SimpleFilenameToStream.java
@@ -36,7 +36,6 @@ public class SimpleFilenameToStream implements FilenameToStream {
 
 	private static final ClassLoader cl = SimpleFilenameToStream.class.getClassLoader();
 
-	private static final String TMPDIR = System.getProperty("java.io.tmpdir");
 	private static final String STANDARD_MODULES = "tla2sany"
 			+ '/' + STANDARD_MODULES_FOLDER + '/';
 
@@ -51,6 +50,10 @@ public class SimpleFilenameToStream implements FilenameToStream {
 	  libraryPaths = getLibraryPaths(getInstallationBasePath(), null);
   }
 
+  public SimpleFilenameToStream(final String libraryPath) {
+	  this(new String[] {libraryPath});
+  }
+  
 /**
  * August 2014 - TL
  * This constructor was on the interface but was not implemented.
@@ -75,7 +78,7 @@ public class SimpleFilenameToStream implements FilenameToStream {
     final String path = url.toString();
 	try {
     	// convert to URI which handles paths correctly (even OS dependently)
-    	if(!isInJar(path)) {
+    	if(!FilenameToStream.isInJar(path)) {
     	final URI uri = new URI(path);
     		return new File(uri).getAbsolutePath();
     	}
@@ -91,10 +94,6 @@ public class SimpleFilenameToStream implements FilenameToStream {
     return path;
    }
 
-  private static boolean isInJar(String aString) {
-	return aString.startsWith("jar:");
-  }
-
   /**
    * August 2014 - TL
    * added an informative method for returning the actual path used by this resolver
@@ -189,10 +188,11 @@ public class SimpleFilenameToStream implements FilenameToStream {
     * field in util/ToolIO.                                                *
     ***********************************************************************/
     int idx = 0;
+    InputStream is;
     while (true)
     {
         if ((idx == 0) && (ToolIO.getUserDir() != null)) {
-            sourceFile = new File(ToolIO.getUserDir(), name );
+            sourceFile = new TLAFile(ToolIO.getUserDir(), name, this );
         }
         else
         {
@@ -202,38 +202,31 @@ public class SimpleFilenameToStream implements FilenameToStream {
         	//
         	// This would be a lot simpler if TLC would not depend on
         	// File but on InputStream instead
-        	if(isInJar(prefix)) {
-					InputStream is = cl
-							.getResourceAsStream(STANDARD_MODULES
-									+ name);
-
+        	if(FilenameToStream.isInJar(prefix)) {
+				is = cl.getResourceAsStream(STANDARD_MODULES + name);
 				if(is != null) {
-					try {
-						sourceFile = new File(TMPDIR + File.separator + name);
-						sourceFile.deleteOnExit();
-
-						FileOutputStream fos = new FileOutputStream(sourceFile);
-
-						byte buf[] = new byte[1024];
-						int len;
-						while ((len = is.read(buf)) > 0) {
-							fos.write(buf, 0, len);
-						}
-						fos.close();
-						is.close();
-					} catch (IOException e) {
-						// must not happen
-						e.printStackTrace();
-					}
+					sourceFile = read(name, is);
 				}
         	} else {
-        		sourceFile = new File( prefix + name );
+        		sourceFile = new TLAFile( prefix + name, true, this );
         	}
         }
         // Debug
         // System.out.println("Looking for file " + sourceFile);
         if ( sourceFile.exists() )  break;
-        if (idx >= libraryPaths.length) break;
+        if (idx >= libraryPaths.length) {
+			// As a last resort, try to load resource from the Java classpath. Give up, if it
+			// fails.
+			// The use case for this strategy is to load additional TLA+ module collections
+			// - e.g. community-driven ones - which ship a single jar containing the .tla
+			// operator definitions as well as Java module overwrites as .class files.
+        	is = cl.getResourceAsStream(name);
+        	if(is != null) {
+				return read(name, is);
+			} else {
+				break;
+			}
+        }
         prefix = libraryPaths[idx++];
     } // end while
 
@@ -241,6 +234,27 @@ public class SimpleFilenameToStream implements FilenameToStream {
 
   } // end locate()
 
+  private File read(String name, InputStream is) {
+    final File sourceFile = new TLAFile(TMPDIR + File.separator + name, true, this);
+	sourceFile.deleteOnExit();
+	try {
+
+		final FileOutputStream fos = new FileOutputStream(sourceFile);
+
+		byte buf[] = new byte[1024];
+		int len;
+		while ((len = is.read(buf)) > 0) {
+			fos.write(buf, 0, len);
+		}
+		fos.close();
+		is.close();
+	} catch (IOException e) {
+		// must not happen
+		e.printStackTrace();
+	}
+	return sourceFile;
+  }
+
   /**
    * Returns a file
    * obtained by some standard method from the string name.  For example,
@@ -266,11 +280,11 @@ public class SimpleFilenameToStream implements FilenameToStream {
       {
           // SZ Feb 20, 2009:
           // Make sure the file name ends with ".tla".
-          if (name.toLowerCase().endsWith(".tla"))
+          if (name.toLowerCase().endsWith(TLAConstants.Files.TLA_EXTENSION))
           {
-              name = name.substring(0, name.length() - 4);
+              name = name.substring(0, (name.length() - TLAConstants.Files.TLA_EXTENSION.length()));
           }
-          sourceFileName = name + ".tla";
+          sourceFileName = name + TLAConstants.Files.TLA_EXTENSION;
       } else
       {
           // SZ Feb 20, 2009: for other files
@@ -295,6 +309,8 @@ public class SimpleFilenameToStream implements FilenameToStream {
 	 * This method is used to set the isStandard field of the module's ModuleNode.
 	 * I don't know if this is every called with a module that
 	 * Added by LL on 24 July 2013.
+	 * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule()
+	 * @see StandardModules.isDefinedInStandardModule()
 	 */
 	public boolean isStandardModule(String moduleName) {
 		 File file = this.resolve(moduleName, true) ;
diff --git a/tlatools/src/util/StringHelper.java b/tlatools/src/util/StringHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c13a928c5400c3d163bbe5a7fd4fb4f18b75edc
--- /dev/null
+++ b/tlatools/src/util/StringHelper.java
@@ -0,0 +1,149 @@
+package util;
+
+/**
+ * Contains some useful methods for manipulating strings.
+ * 
+ * @author lamport
+ */
+public class StringHelper {
+    /*
+     * The following defines newline to be a new-line character in whatever
+     * system this is.
+     */
+    public static String PLATFORM_NEWLINE = System.getProperty("line.separator");
+
+    /**
+     * Returns the result of concatenating 'copies' copies of string str, 
+     * or the empty string if copies < 0. 
+     */
+    /*
+     * It uses the  following algorithm, which produces a sequence of 1s
+     * of length 'copies'.
+     * 
+     * --algorithm copy {
+     * variables copies \in -5..100,        \* input: The number of repetitions
+     *           result = << >>,            \* set to the output
+     *           powerOf2Copies = << 1 >>,  \* 2^k copies of the input, for some k
+     *           remaining = copies ;       \* see the invariant in the assert statement
+     *   { while (remaining > 0) 
+     *       {  assert copies = remaining * Len(powerOf2Copies) + Len(result) ;
+     *          if (remaining % 2 # 0) { result := result \o powerOf2Copies };
+     *          remaining := remaining \div 2;
+     *          if (remaining # 0) {powerOf2Copies := powerOf2Copies \o powerOf2Copies ;} ;
+     *       };
+     *     assert (copies > 0) => (copies = Len(result)) ;
+     *   }
+     * }
+     */
+    public static final String copyString(final String str, final int copies) {
+        String result = "";
+        String powerOf2Copies = str;
+        int    remaining = copies;
+        while (remaining > 0) {
+            if (remaining %2 != 0) {
+                result = result + powerOf2Copies;
+            }
+            remaining = remaining / 2;
+            if (remaining != 0) {
+                powerOf2Copies = powerOf2Copies + powerOf2Copies;
+            }
+        }
+        return result;
+    }
+    
+    /**
+     *  Returns true if the string str contains only whitespace 
+     */
+    public static final boolean onlySpaces(final String str) {
+        return str.trim().equals("");
+    }
+    
+    /**
+     * Returns str with any leading whitespace removed. 
+     */
+    public static final String trimFront(final String str) {
+        int position = 0;
+        while ((position < str.length()) && 
+                Character.isWhitespace(str.charAt(position))) {
+          position++;
+        }
+        return str.substring(position, str.length());
+        
+        // Alternatively
+        //return s.replaceAll("^\\s+", "");
+    }
+    
+    /**
+     * Returns str with any terminating whitespace removed. 
+     */
+    public static final String trimEnd(final String str) {
+        int position = str.length();
+        while ((position > 0) && 
+                Character.isWhitespace(str.charAt(position - 1))) {
+          position--;
+        }
+        return str.substring(0, position);
+        
+        // Alternatively
+        //return s.replaceAll("\\s+$", "");
+    }
+    
+    /**
+     * Returns the number of leading spaces in the string str.
+     * @param str
+     * @return
+     */
+    public static final int leadingSpaces(final String str) {
+        return str.length() - trimFront(str).length() ;
+    }
+    
+    /**
+     * Prints the elements of the array, one per line, enclosed between
+     * *- and -*, except with the first line enclosed with 0- and -0.
+     */
+    public static final void printArray(final Object[] array) {
+        if (array == null) {
+            System.out.println("null array");
+            return;
+        }
+        if (array.length == 0) {
+            System.out.println("zero-length array");
+            return;
+        }
+        System.out.println("0-" + array[0].toString() + "-0");
+        for (int i = 1; i < array.length; i++) {
+            System.out.println("*-" + array[i].toString() + "-*");
+        }
+    }
+        
+    /**
+     * Returns the sequence of words contained in str, where
+     * a word is any sequence of non-space characters.
+     * @param str
+     * @return
+     */
+    public static final String[] getWords(final String str) {
+    	final String[] result = trimFront(str).split("\\s+") ;
+        return result;
+    }
+    
+    /**
+     * Returns true iff str is a sequence of letters, "_" characters, and digits
+     * that is not all digits.  
+     * 
+     * @param str
+     * @return
+     */
+    public static final boolean isIdentifier(final String str) {
+        boolean result = true ;
+        boolean allChars = true ;
+        int i = 0;
+        while (result && (i < str.length())) {
+            char ch = str.charAt(i) ;
+            result = Character.isLetterOrDigit(ch) || (ch == '_') ;
+            allChars = allChars && Character.isDigit(ch) ;
+            i++;
+        }
+        return result && (! allChars) ;
+    }
+}
diff --git a/tlatools/src/util/TLAConstants.java b/tlatools/src/util/TLAConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8a4aaa1cae79d4bdd00da8a50ab447be472bf3a
--- /dev/null
+++ b/tlatools/src/util/TLAConstants.java
@@ -0,0 +1,143 @@
+package util;
+
+/**
+ * A class of bits of keywords, operators, and bears.
+ */
+public final class TLAConstants {
+	public final class ANSI {
+		public static final String BOLD_CODE = "\033[1m";
+		public static final String ITALIC_CODE = "\033[3m";
+		public static final String RESET_CODE = "\033[0m";
+	}
+	
+	public final class BuiltInModules {
+		public static final String TLC = "TLC";
+		public static final String TRACE_EXPRESSIONS = "Toolbox";
+	}
+	
+	public final class BuiltInOperators {
+		public static final String PERMUTATIONS = "Permutations";
+	}
+	
+	public final class Files {
+	    public static final String CONFIG_EXTENSION = ".cfg";
+	    public static final String ERROR_EXTENSION = ".err";
+	    public static final String OUTPUT_EXTENSION = ".out";
+	    public static final String TLA_EXTENSION = ".tla";
+		
+		public static final String MODEL_CHECK_FILE_BASENAME = "MC";
+		public static final String MODEL_CHECK_CONFIG_FILE = MODEL_CHECK_FILE_BASENAME + CONFIG_EXTENSION;
+		public static final String MODEL_CHECK_ERROR_FILE = MODEL_CHECK_FILE_BASENAME + ERROR_EXTENSION;
+		public static final String MODEL_CHECK_OUTPUT_FILE = MODEL_CHECK_FILE_BASENAME + OUTPUT_EXTENSION;
+		public static final String MODEL_CHECK_TLA_FILE = MODEL_CHECK_FILE_BASENAME + TLA_EXTENSION;
+	}
+	
+	public final class KeyWords {
+		public static final String ACTION_CONSTRAINT = "ACTION_CONSTRAINT";
+	    public static final String CONSTANT = "CONSTANT";
+	    public static final String CONSTANTS = CONSTANT + 'S';
+	    public static final String EXTENDS = "EXTENDS";
+	    public static final String INIT = "INIT";
+	    public static final String INVARIANT = "INVARIANT";
+	    public static final String MODULE = "MODULE";
+	    public static final String NEXT = "NEXT";
+	    public static final String PROPERTY = "PROPERTY";
+	    public static final String SPECIFICATION = "SPECIFICATION";
+	    public static final String SYMMETRY = "SYMMETRY";
+	    public static final String TRUE = "TRUE";
+	    public static final String UNION = "\\union";
+	    public static final String VARIABLE = "VARIABLE";
+	}
+	
+	public final class LoggingAtoms {
+		public static final String PARSING_FILE = "Parsing file";
+	}
+    
+	public final class Schemes {
+		public static final String SPEC_SCHEME = "spec";
+		public static final String INIT_SCHEME = "init";
+		public static final String NEXT_SCHEME = "next";
+		public static final String CONSTANT_SCHEME = "const";
+		public static final String SYMMETRY_SCHEME = "symm";
+		public static final String DEFOV_SCHEME = "def_ov";
+		public static final String CONSTRAINT_SCHEME = "constr";
+		public static final String ACTIONCONSTRAINT_SCHEME = "action_constr";
+		public static final String INVARIANT_SCHEME = "inv";
+		public static final String PROP_SCHEME = "prop";
+		public static final String VIEW_SCHEME = "view";
+		public static final String CONSTANTEXPR_SCHEME = "const_expr";
+		public static final String TRACE_EXPR_VAR_SCHEME = "__trace_var";
+		public static final String TRACE_EXPR_DEF_SCHEME = "trace_def";
+	}
+	
+	public final class TraceExplore {
+	    public static final String ERROR_STATES_MODULE_NAME = "SpecTE";
+	    public static final String EXPLORATION_MODULE_NAME = "TE";
+		public static final String ACTION = "_TEAction";
+		public static final String POSITION = "_TEPosition";
+		public static final String TRACE = "_TETrace";
+	    /**
+	     * expressions to be evaluated at each state of the trace
+	     * when the trace explorer is run
+	     */
+	    public static final String TRACE_EXPLORE_EXPRESSIONS = "traceExploreExpressions";
+	    /**
+	     * Conjunction of variable values in the initial state of a trace
+	     * Should only include spec variables, not trace expression variables.
+	     */
+	    public static final String TRACE_EXPLORE_INIT = "traceExploreInit";
+	    /**
+	     * Disjunction of actions used for trace exploration without the trace
+	     * expression variables.
+	     */
+	    public static final String TRACE_EXPLORE_NEXT = "traceExploreNext";
+	    /**
+	     * The tuple of ordered sub-action names representing the trace states. 
+	     */
+	    public static final String TRACE_EXPLORE_ACTION_CONSTRAINT = "traceExploreActionConstraint";
+	}
+
+	
+	public static final String BACK_TO_STATE = "Back to state";
+	public static final String GENERATION_TIMESTAMP_PREFIX = "\\* Generated on ";
+	public static final String LINE = "line ";
+	public static final String STUTTERING = " Stuttering";
+	
+	public static final String MODULE_OPENING_PREFIX_REGEX = "^([-]+ " + TLAConstants.KeyWords.MODULE + ") ";
+	public static final String MODULE_CLOSING_REGEX = "^[=]+$";
+
+    public static final String SPACE = " ";
+    public static final String INDENT = "    ";
+    public static final String CR = "\n";
+    public static final String SEP = "----";
+    public static final String EQ = " = ";
+    public static final String ARROW = " <- ";
+    public static final String RECORD_ARROW = " |-> ";
+    public static final String DEFINES = " == ";
+    public static final String DEFINES_CR = " ==\n";
+    public static final String COMMENT = "\\* ";
+    public static final String ATTRIBUTE = "@";
+    public static final String COLON = ":";
+    public static final String EMPTY_STRING = "";
+    public static final String CONSTANT_EXPRESSION_EVAL_IDENTIFIER = "\"$!@$!@$!@$!@$!\"";
+    public static final String COMMA = ",";
+    public static final String BEGIN_TUPLE = "<<";
+    public static final String END_TUPLE = ">>";
+    public static final String PRIME = "'";
+    public static final String QUOTE = "\"";
+    public static final String TLA_AND = "/\\";
+    public static final String TLA_OR = "\\/";
+    public static final String TLA_NOT = "~";
+    public static final String TLA_EVENTUALLY_ALWAYS = "<>[]";
+    public static final String TLA_INF_OFTEN = "[]<>";
+    public static final String TRACE_NA = "\"--\"";
+    public static final String L_PAREN = "(";
+    public static final String R_PAREN = ")";
+    public static final String L_SQUARE_BRACKET = "[";
+    public static final String R_SQUARE_BRACKET = "]";
+
+    public static final String INDENTED_CONJUNCTIVE = TLAConstants.INDENT + TLAConstants.TLA_AND + TLAConstants.SPACE;
+    public static final String INDENTED_DISJUNCTIVE = TLAConstants.INDENT + TLAConstants.TLA_OR + TLAConstants.SPACE;
+    
+	private TLAConstants() { }
+}
diff --git a/tlatools/src/util/TLAFlightRecorder.java b/tlatools/src/util/TLAFlightRecorder.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2ca81232961c5b29e8cc5f14b244cdf9a82fbd1
--- /dev/null
+++ b/tlatools/src/util/TLAFlightRecorder.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+// Label is only used by Mission control but not relevant for the recording itself.
+public class TLAFlightRecorder {
+	
+    @jdk.jfr.Label("Progress")
+    @jdk.jfr.Category({"TLC", "Progress"})
+    @jdk.jfr.StackTrace(false)// No need to capture the stack of the reporting (main) thread when it emits the event.
+	private static class ProgressEvent extends jdk.jfr.Event {
+		@jdk.jfr.Label("States generated per minute")
+		@jdk.jfr.Unsigned
+		private long spm;
+		@jdk.jfr.Label("Distinct states generated per minute")
+		@jdk.jfr.Unsigned
+		private long dspm;
+		@jdk.jfr.Label("Unseen States")
+		@jdk.jfr.Unsigned
+		private long unseen;
+		@jdk.jfr.Label("Diameter")
+		@jdk.jfr.Unsigned
+		private int diameter;
+		@jdk.jfr.Label("States Generated")
+		@jdk.jfr.Unsigned
+		private long states;
+		@jdk.jfr.Label("Distinct States Generated")
+		@jdk.jfr.Unsigned
+		private long distStates;
+		@jdk.jfr.Label("Model Checking done")
+		private boolean isFinal;
+	}
+
+	public static void progress(boolean isFinal, final int diameter, final long states, final long distinctStates, final long unseen,
+			final long statesPerMinute, final long distinctStatesPerMinute) {
+		try {
+			final ProgressEvent e = new ProgressEvent();
+			e.isFinal = isFinal;
+			e.spm = statesPerMinute;
+			e.dspm = distinctStatesPerMinute;
+			e.diameter = diameter;
+			e.unseen = unseen;
+			e.states = states;
+			e.distStates = distinctStates;
+			e.commit();
+		} catch (NoClassDefFoundError e) {
+			// Java 1.8 doesn't have jdk.jfr.Event and thus this flight recording is broken.
+		}
+	}
+	
+    @jdk.jfr.Label("Message")
+    @jdk.jfr.Category({"TLC"})
+    @jdk.jfr.StackTrace(false)// No need to capture the stack of the reporting (main) thread when it emits the event.
+	private static class MessageEvent extends jdk.jfr.Event {
+		@jdk.jfr.Label("Message")
+		public String message;
+    }
+
+	public static String message(final String message) {
+		try {
+			final MessageEvent e = new MessageEvent();
+			e.message = message;
+			e.commit();
+		} catch (NoClassDefFoundError e) {
+			// Java 1.8 doesn't have jdk.jfr.Event and thus this flight recording is broken.
+		}
+		return message;
+	}
+}
\ No newline at end of file
diff --git a/tlatools/src/util/TLCRuntime.java b/tlatools/src/util/TLCRuntime.java
index 64ceb5765f6feb8bfed7c0ea4233da98ce00b55d..93e9354e1ff0263e8315f98078f0b4d2bc8b6867 100644
--- a/tlatools/src/util/TLCRuntime.java
+++ b/tlatools/src/util/TLCRuntime.java
@@ -2,12 +2,19 @@
 
 package util;
 
+import java.lang.management.GarbageCollectorMXBean;
 import java.lang.management.ManagementFactory;
-import java.lang.management.OperatingSystemMXBean;
 import java.lang.management.RuntimeMXBean;
-import java.lang.reflect.Method;
 import java.util.List;
 
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
 import tlc2.tool.fp.FPSet;
 
 /**
@@ -30,33 +37,23 @@ public class TLCRuntime {
 		}
 		return runtime;
 	}
+	
 
 	private long physicalSystemMemory = -1;
-
+	
 	/**
 	 * @return the total amount of memory, measured in bytes.
 	 */
 	private long getPhysicalSystemMemory() {
-
-		// try to read the total physical memory via a MXBean. Unfortunately,
-		// these methods are not meant as public API, which requires us to pull
-		// a visibility reflection hack.
-		// This hack is expected to work on Linux, Windows (up to 7) and Max OSX
-		final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory
-				.getOperatingSystemMXBean();
-		for (Method method : operatingSystemMXBean.getClass()
-				.getDeclaredMethods()) {
-			if (method.getName().equals("getTotalPhysicalMemorySize")) {
-				method.setAccessible(true);
-				try {
-					return (Long) method.invoke(operatingSystemMXBean);
-				} catch (Exception e) {
-					break;
-				}
-			}
+		final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+		try {
+			return (Long) mBeanServer.getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"),
+					"TotalPhysicalMemorySize");
+		} catch (InstanceNotFoundException | AttributeNotFoundException | MalformedObjectNameException
+				| ReflectionException | MBeanException | ClassCastException e) {
+			// as a safeguard default to the total memory available to this JVM
+			return Runtime.getRuntime().totalMemory();
 		}
-		// as a safeguard default to the total memory available to this JVM
-		return Runtime.getRuntime().totalMemory();
 	}
 
 	/**
@@ -148,4 +145,54 @@ public class TLCRuntime {
 		}
 		return (long) fpMemSize;
 	}
+
+	public enum ARCH {
+		x86,
+		x86_64;
+	}
+	
+	public ARCH getArchitecture() {
+		if (System.getProperty("sun.arch.data.model") != null
+				&& System.getProperty("sun.arch.data.model").equals("64")) {
+			return ARCH.x86_64;
+		}
+		if (System.getProperty("com.ibm.vm.bitmode") != null 
+				&& System.getProperty("com.ibm.vm.bitmode").equals("64")) {
+			return ARCH.x86_64;
+		}
+		if (System.getProperty("java.vm.version") != null 
+				&& System.getProperty("java.vm.version").contains("_64")) {
+			return ARCH.x86_64;
+		}
+		return ARCH.x86;
+	}
+
+	// See java.lang.ProcessHandle.current().pid() or -1 when Java version -lt 9.
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public long pid() {
+		// Once Java9 is minimum BREE, change to:
+        // return java.lang.ProcessHandle.current().pid();
+		try {
+			// Get class.
+			final ClassLoader classLoader = getClass().getClassLoader();
+	        final Class aClass = classLoader.loadClass("java.lang.ProcessHandle");
+	        // Execute static current()
+	        final Object o = aClass.getMethod("current").invoke(null, (Object[]) null);
+	        // Execute instance method pid()
+	        return (long) aClass.getMethod("pid").invoke(o, (Object[]) null);
+	    } catch (Exception e) {
+			return -1;
+		}
+	}
+
+	public boolean isThroughputOptimizedGC() {
+		final List<GarbageCollectorMXBean> gcs = ManagementFactory.getGarbageCollectorMXBeans();
+		for (GarbageCollectorMXBean gc : gcs) {
+			// This might not be a reliable way to identify the currently active GC.
+			if ("PS Scavenge".equals(gc.getName())) {
+				return true;
+			}
+		}
+		return false;
+	}
 }
diff --git a/tlatools/src/util/ToolIO.java b/tlatools/src/util/ToolIO.java
index 02378d628d149a42e43f2ee472edb89268b4e7f1..6be583f9f3b7a859635899d1ddf0970b5a886aed 100644
--- a/tlatools/src/util/ToolIO.java
+++ b/tlatools/src/util/ToolIO.java
@@ -2,11 +2,7 @@ package util;
 
 import java.io.PipedOutputStream;
 import java.io.PrintStream;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import tla2sany.semantic.SemanticNode;
+import java.util.Locale;
 
 /***************************************************************************
 * SANY and TLC were written to communicate only by calling the             *
@@ -76,19 +72,13 @@ public class ToolIO
 	 * Care must be taken with out and err not being synchronized. Concurrent
 	 * writes will cause interleaved output.
 	 * 
-	 * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=221
+	 * @see Bug #221 in general/bugzilla/index.html
 	 */
     public static PrintStream err = System.err;
 
     // = 1 for testing. Should be set to reasonable value like 1000.
     private static final int InitialMaxLength = 1;
 
-    /**
-     * List of semantic nodes which are used by tools
-     * @see ToolIO#registerSemanticNode() 
-     */
-    private static List semanticNodes = new LinkedList();
-
     /**
      * The current sequence of messages is messages[0] ...                  
      * messages[length-1] A single message may contain \n characters.       
@@ -229,46 +219,6 @@ public class ToolIO
         ToolIO.defaultResolver = resolver;
     }
 
-    /**
-     * Registers the semantic node
-     * @param node the node containing tool specific information
-     * @param toolId the id of the tool (currently not used)
-     * 
-     * <br><b>Note:</b><br>
-     * This method is called from {@link SemanticNode#setToolObject(int, Object)} 
-     * and identifies the semantic node that carries tool specific information. 
-     * This information can be deleted using {@link ToolIO#cleanToolObjects(int)} 
-     */
-    public static void registerSemanticNode(SemanticNode node, int toolId)
-    {
-        if (!semanticNodes.contains(node)) 
-        {
-            semanticNodes.add(node);
-        }
-    }
-
-    /**
-     * Sets the tool-specific object for all listed semantic nodes to <code>null</code>
-     * @param toolId the Id of the tool to reset the tool specific information
-     */
-    public static void cleanToolObjects(int toolId)
-    {
-        Iterator iter = semanticNodes.iterator();
-        while(iter.hasNext())
-        {
-            SemanticNode node = (SemanticNode) iter.next();
-            node.setToolObject(toolId, null);
-        }
-    }
-
-    /**
-     * Deletes the information about semantic nodes used with tool-specific information
-     */
-    public static void unregisterSemanticNodes()
-    {
-        semanticNodes = new LinkedList();
-    }
-
 } // class ToolIO
 
 class ToolPrintStream extends PrintStream
@@ -282,8 +232,28 @@ class ToolPrintStream extends PrintStream
         ToolIO.out = this;
         ToolIO.err = this;
     }
+    
+    /* (non-Javadoc)
+     * @see java.io.PrintStream#printf(java.lang.String, java.lang.Object[])
+     */
+    @Override
+	public PrintStream printf(String format, Object... args) {
+		// See special logic in println. If super.printf(...) gets used, Toolbox
+		// functionality breaks.
+    	throw new UnsupportedOperationException("use println instead");
+	}
+
+	/* (non-Javadoc)
+	 * @see java.io.PrintStream#printf(java.util.Locale, java.lang.String, java.lang.Object[])
+	 */
+	@Override
+	public PrintStream printf(Locale l, String format, Object... args) {
+		// See special logic in println. If super.printf(...) gets used, Toolbox
+		// functionality breaks.
+    	throw new UnsupportedOperationException("use println instead");
+	}
 
-    /**
+	/**
      * Prints a string in to the ToolIO buffer in a separate line
      * @param str String to be printed
      */
diff --git a/tlatools/src/util/UniqueString.java b/tlatools/src/util/UniqueString.java
index d550b446a5d46a79a4d3608548746d92e373e7a8..f9846b218089c832b03ffa8fe5c72c27ad1ee6b6 100644
--- a/tlatools/src/util/UniqueString.java
+++ b/tlatools/src/util/UniqueString.java
@@ -4,6 +4,7 @@ package util;
 
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.Map;
 
 import tlc2.tool.Defns;
 import tlc2.tool.TLCState;
@@ -260,6 +261,10 @@ public final class UniqueString implements Serializable
         return internTbl.put(str);
     }
 
+	public static UniqueString of(String str) {
+		return uniqueStringOf(str);
+	}
+
     /**
      * If there exists a UniqueString object obj such that obj.getTok()
      * equals tok, then uidToUniqueString(i) returns obj; otherwise,    
@@ -279,7 +284,7 @@ public final class UniqueString implements Serializable
      * @return
      * @throws IOException
      */
-    public final void write(BufferedDataOutputStream dos) throws IOException
+    public final void write(IDataOutputStream dos) throws IOException
     {
         dos.writeInt(this.tok);
         dos.writeInt(this. getVarLoc()); 
@@ -296,7 +301,7 @@ public final class UniqueString implements Serializable
      * 
      * The method does not change member/class variables
      */
-    public static UniqueString read(BufferedDataInputStream dis) throws IOException
+    public static UniqueString read(IDataInputStream dis) throws IOException
     {
         int tok1 = dis.readInt();
         int loc1 = dis.readInt();
@@ -304,6 +309,15 @@ public final class UniqueString implements Serializable
         String str = dis.readString(slen);
         return new UniqueString(str, tok1, loc1);
     }
+    
+    public static UniqueString read(IDataInputStream dis, final Map<String, UniqueString> tbl) throws IOException
+    {
+        dis.readInt(); // skip, because invalid for the given internTbl
+        dis.readInt(); // skip, because invalid for the given internTbl
+        final int slen = dis.readInt();
+        final String str = dis.readString(slen);
+        return tbl.get(str);
+    }
 
 
     /**
@@ -314,5 +328,4 @@ public final class UniqueString implements Serializable
     {
         internTbl.setSource(source);
     }
-
 }
diff --git a/tlatools/src/util/UsageGenerator.java b/tlatools/src/util/UsageGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f00329bbc3999dd5c0742ab5520da602b4b5943
--- /dev/null
+++ b/tlatools/src/util/UsageGenerator.java
@@ -0,0 +1,373 @@
+package util;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A helper class to print usage text for a command-line-application in a motif reminiscent of manpages.
+ *  
+ * In a world where we were not concerned with jar size, i would import Apache Commons CLI and take advantage
+ * 	of those classes.
+ */
+public class UsageGenerator {
+	private static final String NAME = "NAME";
+	private static final String SYNOPSIS = "SYNOPSIS";
+	private static final String DESCRIPTION = "DESCRIPTION";
+	private static final String OPTIONS = "OPTIONS";
+	private static final String TIPS = "TIPS";
+	
+	private static Comparator<Argument> NAME_COMPARATOR = new Comparator<Argument>() {
+		@Override
+		public int compare(final Argument a, final Argument b) {
+			return a.getArgumentName().compareTo(b.getArgumentName());
+		}
+	};
+	
+	private static Comparator<Argument> NAME_DASH_COMPARATOR = new Comparator<Argument>() {
+		@Override
+		public int compare(final Argument a, final Argument b) {
+			final boolean aDash = a.isDashArgument();
+			final boolean bDash = b.isDashArgument();
+			
+			if (aDash != bDash) {
+				return aDash ? -1 : 1;
+			}
+			
+			return a.getArgumentName().compareTo(b.getArgumentName());
+		}
+	};
+	
+	
+	public static void displayUsage(final PrintStream ps, final String commandName, final String version,
+									final String commandShortSummary, final String commandDescription,
+									final List<List<Argument>> commandVariants, final List<String> tips,
+									final char valuedArgumentsSeparator) {
+		ps.println();
+		ps.println(generateSectionHeader(NAME));
+		ps.println('\t' + commandName + " - " + commandShortSummary
+						+ ((version != null) ? (" - " + version) : "") + "\n\n");
+
+		
+		final String boldName = markupWord(commandName, true);
+		
+		
+		final HashSet<Argument> arguments = new HashSet<>();
+		ps.println(generateSectionHeader(SYNOPSIS));
+		for (final List<Argument> variant : commandVariants) {
+			ps.println("\t" + generateCommandForVariant(boldName, variant, arguments, valuedArgumentsSeparator));
+		}
+		ps.println();
+		
+		
+		final String commandNameRegex = commandName + "(\\)|\\s|$)";
+		final Pattern p = Pattern.compile(commandNameRegex);
+		final Matcher m = p.matcher(commandDescription);
+		final String markedUpDescription;
+		if (m.find()) {
+			final StringBuilder sb = new StringBuilder();
+			
+			if (m.start() > 0) {
+				sb.append(commandDescription.substring(0, m.start()));
+			}
+			sb.append(boldName).append(m.group(1));
+			
+			int lastEnd = m.end();
+			while (m.find()) {
+				sb.append(commandDescription.substring(lastEnd, m.start())).append(boldName).append(m.group(1));
+				lastEnd = m.end();
+			}
+			sb.append(commandDescription.substring(lastEnd, commandDescription.length()));
+			
+			markedUpDescription = sb.toString();
+		} else {
+			markedUpDescription = commandDescription;
+		}
+		
+		ps.println(generateSectionHeader(DESCRIPTION));
+		ps.println("\t" + markedUpDescription.replaceAll("(\\r\\n|\\r|\\n)", "\n\t"));
+		ps.println();
+		
+		
+		final List<Argument> orderedArguments = new ArrayList<>(arguments);
+		Collections.sort(orderedArguments, NAME_COMPARATOR);
+		ps.println(generateSectionHeader(OPTIONS));
+		for (final Argument arg : orderedArguments) {
+			if (arg.expectsValue() || arg.isOptional() || (!arg.expectsValue() && arg.isDashArgument())) {
+				ps.println(generateOptionText(arg, valuedArgumentsSeparator));
+			}
+		}
+		ps.println();
+		
+		
+		if ((tips != null) && (tips.size() > 0)) {
+			ps.println(generateSectionHeader(TIPS));
+			for (final String tip : tips) {
+				ps.println("\t" + tip.replaceAll("(\\r\\n|\\r|\\n)", "\n\t") + "\n");
+			}
+		}
+	}
+	
+	private static String generateCommandForVariant(final String boldedCommandName, final List<Argument> variant,
+													final HashSet<Argument> arguments,
+													final char valuedArgumentsSeparator) {
+		final List<Argument> optionalSingleDashValueless = new ArrayList<>();
+		final List<Argument> optionalDoubleDashValueless = new ArrayList<>();
+		final List<Argument> optionalValued = new ArrayList<>();
+		final List<Argument> requiredValued = new ArrayList<>();
+		final List<Argument> requiredValueless = new ArrayList<>();
+		
+		for (final Argument arg : variant) {
+			if (arg.expectsValue()) {
+				if (arg.isOptional()) {
+					optionalValued.add(arg);
+				} else {
+					requiredValued.add(arg);
+				}
+			} else {
+				if (arg.isOptional()) {
+					if (arg.isDashArgument()) {
+						optionalSingleDashValueless.add(arg);
+					} else if (arg.isDashDashArgument()) {
+						optionalDoubleDashValueless.add(arg);
+					}
+				} else {
+					requiredValueless.add(arg);
+				}
+			}
+		}
+
+		Collections.sort(optionalSingleDashValueless, NAME_COMPARATOR);
+		Collections.sort(optionalDoubleDashValueless, NAME_COMPARATOR);
+		Collections.sort(optionalValued, NAME_COMPARATOR);
+		Collections.sort(requiredValued, NAME_COMPARATOR);
+		Collections.sort(requiredValueless, NAME_DASH_COMPARATOR);
+
+		final StringBuilder sb = new StringBuilder(boldedCommandName);
+		
+		if (optionalSingleDashValueless.size() > 0) {
+			final StringBuilder concatenation = new StringBuilder("-");
+			final List<Argument> nonShortArguments = new ArrayList<>();
+			for (final Argument arg : optionalSingleDashValueless) {
+				if (arg.isShortArgument()) {
+					concatenation.append(arg.getDashlessArgumentName());
+				} else {
+					nonShortArguments.add(arg);
+				}
+			}
+			if (concatenation.length() > 1) {
+				sb.append(" [").append(markupWord(concatenation.toString(), true)).append(']');
+			}
+			
+			for (final Argument arg : nonShortArguments) {
+				sb.append(" [").append(markupWord(("-" + arg.getDashlessArgumentName()), true));
+				if (arg.hasSubOptions()) {
+					sb.append(" [").append(arg.getSubOptions()).append("]");
+				}
+				sb.append(']');
+			}
+		}
+		
+		if (optionalDoubleDashValueless.size() > 0) {
+			for (final Argument arg : optionalDoubleDashValueless) {
+				sb.append(" [").append(markupWord(arg.getArgumentName(), true));
+				if (arg.hasSubOptions()) {
+					sb.append(" [").append(arg.getSubOptions()).append("]");
+				}
+				sb.append(']');
+			}
+		}
+		
+		if (optionalValued.size() > 0) {
+			for (final Argument arg : optionalValued) {
+				sb.append(" [").append(markupWord(arg.getArgumentName(), true)).append(valuedArgumentsSeparator);
+				if (arg.hasSubOptions()) {
+					sb.append("[").append(arg.getSubOptions()).append("] ");
+				}
+				sb.append(markupWord(arg.getSampleValue(), false)).append(']');
+			}
+		}
+		
+		if (requiredValued.size() > 0) {
+			for (final Argument arg : requiredValued) {
+				sb.append(" ").append(markupWord(arg.getArgumentName(), true)).append(valuedArgumentsSeparator);
+				sb.append(markupWord(arg.getSampleValue(), false));
+			}
+		}
+		
+		if (requiredValueless.size() > 0) {
+			for (final Argument arg : requiredValueless) {
+				sb.append(" ").append(arg.getArgumentName());
+				if (arg.hasSubOptions()) {
+					sb.append(" [").append(arg.getSubOptions()).append("]");
+				}
+			}
+		}
+		
+		arguments.addAll(variant);
+		
+		return sb.toString();
+	}
+	
+	private static String generateOptionText(final Argument argument, final char valuedArgumentsSeparator) {
+		final StringBuilder sb = new StringBuilder("\t");
+
+		sb.append(markupWord(argument.getArgumentName(), true));
+		if (argument.expectsValue()) {
+			sb.append(valuedArgumentsSeparator).append(markupWord(argument.getSampleValue(), false));
+		}
+		sb.append("\n\t\t").append(argument.getDescription().replaceAll("(\\r\\n|\\r|\\n)", "\n\t\t"));
+
+		return sb.toString();
+	}
+		
+	private static String generateSectionHeader(final String title) {
+		final StringBuilder sb = new StringBuilder(markupWord(title, true));
+		
+		sb.append('\n');
+		
+		return sb.toString();
+	}
+	
+	/**
+	 * @param bold if true, the word will be bolded; false, the word will be italicized
+	 */
+	private static String markupWord(final String word, final boolean bold) {
+		final StringBuilder sb = new StringBuilder(bold ? TLAConstants.ANSI.BOLD_CODE : TLAConstants.ANSI.ITALIC_CODE);
+		
+		sb.append(word).append(TLAConstants.ANSI.RESET_CODE);
+		
+		return sb.toString();
+	}
+	
+	
+	public static class Argument {
+		private final String argumentName;
+		private final String sampleValue;
+		private final String description;
+		private final boolean optional;
+		private final String subOptions;
+		
+		/**
+		 * This calls {@code this(key, optionDescription, false);}
+		 * 
+		 * @param key
+		 * @param optionDescription
+		 */
+		public Argument(final String key, final String optionDescription) {
+			this(key, optionDescription, false);
+		}
+		
+		public Argument(final String key, final String optionDescription, final boolean isOptional) {
+			this(key, null, optionDescription, isOptional);
+		}
+		
+		/**
+		 * This calls {@code this(key, exampleValue, optionDescription, false);}
+		 */
+		public Argument(final String key, final String exampleValue, final String optionDescription) {
+			this(key, exampleValue, optionDescription, false);
+		}
+		
+		public Argument(final String key, final String exampleValue, final String optionDescription,
+						final boolean isOptional) {
+			this(key, exampleValue, optionDescription, isOptional, null);
+		}
+		
+		public Argument(final String key, final String exampleValue, final String optionDescription,
+						final boolean isOptional, final String concatenatedSuboptions) {
+			argumentName = key;
+			sampleValue = exampleValue;
+			description = optionDescription;
+			optional = isOptional;
+			subOptions = concatenatedSuboptions;
+		}
+		
+		public boolean isOptional() {
+			return optional;
+		}
+		
+		public boolean expectsValue() {
+			return (sampleValue != null);
+		}
+		
+		/**
+		 * @return if the argument name starts with "-", but not "--", this returns true
+		 */
+		public boolean isDashArgument() {
+			return argumentName.startsWith("-") && !isDashDashArgument();
+		}
+		
+		public boolean isDashDashArgument() {
+			return argumentName.startsWith("--");
+		}
+		
+		/**
+		 * @return true if the argument name is of length 1 (two if this is a dash argument)
+		 */
+		public boolean isShortArgument() {
+			return ((isDashArgument() && (argumentName.length() == 2)) || (argumentName.length() == 1));
+		}
+		
+		public boolean hasSubOptions() {
+			return (subOptions != null);
+		}
+
+		public String getArgumentName() {
+			return argumentName;
+		}
+		
+		/**
+		 * @return if {@link #isDashArgument()} returns true, this retuns the argument name without the prefacing dash,
+		 * 				otherwise this returns the entire argument name
+		 */
+		public String getDashlessArgumentName() {
+			if (isDashArgument()) {
+				return argumentName.substring(1);
+			}
+			
+			return argumentName;
+		}
+
+		public String getSampleValue() {
+			return sampleValue;
+		}
+
+		public String getDescription() {
+			return description;
+		}
+		
+		public String getSubOptions() {
+			return subOptions;
+		}
+
+		@Override
+		public int hashCode() {
+			return Objects.hash(argumentName);
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj) {
+				return true;
+			}
+			
+			if (obj == null) {
+				return false;
+			}
+			
+			if (getClass() != obj.getClass()) {
+				return false;
+			}
+			
+			final Argument other = (Argument) obj;
+			return Objects.equals(argumentName, other.argumentName);
+		}
+	}
+}
diff --git a/tlatools/test-benchmark/ModuleOverwrites.java b/tlatools/test-benchmark/ModuleOverwrites.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1f922db66bbacfebdc3a407fe169e85ace5e495
--- /dev/null
+++ b/tlatools/test-benchmark/ModuleOverwrites.java
@@ -0,0 +1,36 @@
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import tlc2.value.IValue;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.IntValue;
+
+public class ModuleOverwrites {
+
+	public static IValue noDupesOverwrite(final FcnRcdValue frv, final IntValue exclude) {
+		// LET sub == SelectSeq(t, LAMBDA e: e # emp)
+		// IN ...
+		final List<IValue> filtered = Arrays.asList(frv.values).stream().filter(e -> e != exclude).collect(Collectors.toList());
+		
+		// IF Len(sub) < 2 THEN TRUE ...
+		if (filtered.size() < 2) {
+			return BoolValue.ValTrue;
+		}
+		
+		// ~n^2:
+		// \A i \in 1..(Len(sub) - 1):
+        //    \A j \in (i+1)..Len(sub):
+        //       abs(sub[i]) # abs(sub[j])
+		for (int i = 0; i < filtered.size() - 1; i++) {
+			for (int j = i + 1; j < filtered.size(); j++) {
+				if (filtered.get(i) == filtered.get(j)) {
+					return BoolValue.ValFalse;
+				}
+			}			
+		}
+		return BoolValue.ValTrue;
+	}
+}
diff --git a/tlatools/test-benchmark/README.txt b/tlatools/test-benchmark/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a8a9061c62999338033248ad375fb31143b6c6d8
--- /dev/null
+++ b/tlatools/test-benchmark/README.txt
@@ -0,0 +1,18 @@
+Run JMH benchmarks from inside Eclipse:
+---------------------------------------
+
+a) Activate Annotation Processing in the project preferences of the tlatools project under "Java Compiler" > "Annotation Processing"
+b) Add the two jars from lib/jmh/jmh-*.jar as annotation processors to "Java Compiler" > "Annotation Processing" > "Factory Path"
+c) Add lib/jmh/commons-math3-*.jar to the launch configs classpath
+d) Add a main to the benchmark as shown in the various JMH examples (https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/)
+
+Run JMH benchmarks from ant (customBuilds.xml):
+-----------------------------------------------
+
+ant -f customBuild.xml compile compile-test benchmark &&
+java -jar target/benchmarks.jar -wi 1 -i1 -f1 \
+-rf json \
+-rff DiskQueueBenachmark-$(date +%s)-$(git rev-parse --short HEAD).json \
+-jvmArgsPrepend "-ea -Xms8192m -Xmx8192m" \
+-jvmArgsAppend "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" \
+tlc2.tool.queue.DiskQueueBenachmark
diff --git a/tlatools/test-benchmark/heapstats.jfc b/tlatools/test-benchmark/heapstats.jfc
new file mode 100644
index 0000000000000000000000000000000000000000..5700f60b04b99ac85c9bc2f340746bc6f5bb91de
--- /dev/null
+++ b/tlatools/test-benchmark/heapstats.jfc
@@ -0,0 +1,550 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration version="1.0" name="heapstats" description="" provider="Oracle">
+
+  <producer uri="http://www.oracle.com/hotspot/jvm/" label="Oracle JDK">
+
+    <control>
+
+      <selection name="gc-level" default="all" label="Garbage Collector">
+        <option label="Off" name="off">off</option>
+        <option label="Normal" name="detailed">normal</option>
+        <option label="All" name="all">all</option>
+      </selection>
+
+      <condition name="gc-enabled-normal" true="true" false="false">
+        <or>
+          <test name="gc-level" operator="equal" value="normal"/>
+          <test name="gc-level" operator="equal" value="all"/>
+        </or>
+      </condition>
+
+      <condition name="gc-enabled-all" true="true" false="false">
+        <test name="gc-level" operator="equal" value="all"/>
+      </condition>
+
+      <selection name="compiler-level" default="normal" label="Compiler">
+        <option label="Off" name="off">off</option>
+        <option label="Normal" name="normal">normal</option>
+        <option label="Detailed" name="detailed">detailed</option>
+        <option label="All" name="all">all</option>
+      </selection>
+
+      <condition name="compiler-enabled" true="false" false="true">
+        <test name="compiler-level" operator="equal" value="off"/>
+      </condition>
+
+      <condition name="compiler-enabled-failure" true="true" false="false">
+        <or>
+          <test name="compiler-level" operator="equal" value="detailed"/>
+          <test name="compiler-level" operator="equal" value="all"/>
+        </or>
+      </condition>
+
+      <condition name="compiler-sweeper-threshold" true="0 ms" false="100 ms">
+        <test name="compiler-level" operator="equal" value="all"/>
+      </condition>
+
+      <condition name="compiler-compilation-threshold" true="1000 ms">
+        <test name="compiler-level" operator="equal" value="normal"/>
+      </condition>
+
+      <condition name="compiler-compilation-threshold" true="100 ms">
+        <test name="compiler-level" operator="equal" value="detailed"/>
+      </condition>
+
+      <condition name="compiler-compilation-threshold" true="0 ms">
+        <test name="compiler-level" operator="equal" value="all"/>
+      </condition>
+
+      <condition name="compiler-phase-threshold" true="60 s">
+        <test name="compiler-level" operator="equal" value="normal"/>
+      </condition>
+
+      <condition name="compiler-phase-threshold" true="10 s">
+        <test name="compiler-level" operator="equal" value="detailed"/>
+      </condition>
+
+      <condition name="compiler-phase-threshold" true="0 s">
+        <test name="compiler-level" operator="equal" value="all"/>
+      </condition>
+
+      <selection name="method-sampling-interval" default="maximum" label="Method Sampling">
+        <option label="Off" name="off">999 d</option>
+        <option label="Normal" name="normal">20 ms</option>
+        <option label="Maximum" name="maximum">10 ms</option>
+      </selection>
+
+      <condition name="method-sampling-enabled" true="false" false="true">
+        <test name="method-sampling-interval" operator="equal" value="999 d"/>
+      </condition>
+
+      <selection name="thread-dump-interval" default="everySecond" label="Thread Dump">
+        <option label="Off" name="off">999 d</option>
+        <option label="At least Once" name="normal">everyChunk</option>
+        <option label="Every 60 s" name="everyMinute">60 s</option>
+        <option label="Every 10 s" name="everyTenSecond">10 s</option>
+        <option label="Every 1 s" name="everySecond">1 s</option>
+      </selection>
+
+      <condition name="thread-dump-enabled" true="false" false="true">
+        <test name="thread-dump-interval" operator="equal" value="999 d"/>
+      </condition>
+
+      <selection name="exception-level" default="errors" label="Exceptions">
+        <option label="Off" name="off">off</option>
+        <option label="Errors Only" name="errors">errors</option>
+        <option label="All Exceptions, including Errors" name="all">all</option>
+      </selection>
+
+      <condition name="enable-errors" true="true" false="false">
+        <or>
+          <test name="exception-level" operator="equal" value="errors"/>
+          <test name="exception-level" operator="equal" value="all"/>
+        </or>
+      </condition>
+
+      <condition name="enable-exceptions" true="true" false="false">
+        <test name="exception-level" operator="equal" value="all"/>
+      </condition>
+
+      <text name="synchronization-threshold" label="Synchronization Threshold" contentType="timespan" minimum="0 s">10 ms</text>
+
+      <text name="file-io-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
+
+      <text name="socket-io-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
+
+      <flag name="heap-statistics-enabled" label="Heap Statistics">true</flag>
+
+      <flag name="class-loading-enabled" label="Class Loading">false</flag>
+
+      <flag name="allocation-profiling-enabled" label="Allocation Profiling">true</flag>
+
+    </control>
+
+    <event path="java/statistics/thread_allocation">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="java/statistics/class_loading">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="java/statistics/threads">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="java/thread_start">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="java/thread_end">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="java/thread_sleep">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="synchronization-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/thread_park">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="synchronization-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/monitor_enter">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="synchronization-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/monitor_wait">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="synchronization-threshold">10 ms</setting>
+    </event>
+
+    <event path="vm/class/load">
+      <setting name="enabled" control="class-loading-enabled">false</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/class/unload">
+      <setting name="enabled" control="class-loading-enabled">false</setting>
+    </event>
+
+    <event path="vm/info">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/initial_system_property">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/prof/execution_sample">
+      <setting name="enabled" control="method-sampling-enabled">true</setting>
+      <setting name="period" control="method-sampling-interval">10 ms</setting>
+    </event>
+
+    <event path="vm/prof/execution_sampling_info">
+      <setting name="enabled">false</setting>
+      <setting name="threshold">1 ms</setting>
+    </event>
+
+    <event path="vm/runtime/execute_vm_operation">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">10 ms</setting>
+    </event>
+
+    <event path="vm/runtime/thread_dump">
+      <setting name="enabled" control="thread-dump-enabled">true</setting>
+      <setting name="period" control="thread-dump-interval">1 s</setting>
+    </event>
+
+    <event path="vm/flag/long">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/ulong">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/double">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/boolean">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/string">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/flag/long_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/ulong_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/double_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/boolean_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/flag/string_changed">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/object_count">
+      <setting name="enabled" control="heap-statistics-enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/gc">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/heap">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/young_generation">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/tlab">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/configuration/survivor">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/gc/detailed/object_count_after_gc">
+      <setting name="enabled">false</setting>
+    </event>
+
+    <event path="vm/gc/heap/summary">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/heap/ps_summary">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/heap/metaspace_summary">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/gc_threshold">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/allocation_failure">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/out_of_memory">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="vm/gc/metaspace/chunk_free_list_summary">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/collector/garbage_collection">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/parold_garbage_collection">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/young_garbage_collection">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/old_garbage_collection">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/collector/g1_garbage_collection">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause_level_1">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause_level_2">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/phases/pause_level_3">
+      <setting name="enabled" control="gc-enabled-all">true</setting>
+      <setting name="threshold">0 ms</setting>
+    </event>
+
+    <event path="vm/gc/reference/statistics">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/promotion_failed">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/evacuation_failed">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/evacuation_info">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/concurrent_mode_failure">
+      <setting name="enabled" control="gc-enabled-normal">true</setting>
+    </event>
+
+    <event path="vm/gc/detailed/allocation_requiring_gc">
+      <setting name="enabled" control="gc-enabled-all">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="vm/compiler/config">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/compiler/stats">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="vm/compiler/compilation">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="threshold" control="compiler-compilation-threshold">1000 ms</setting>
+    </event>
+
+    <event path="vm/compiler/phase">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="threshold" control="compiler-phase-threshold">60 s</setting>
+    </event>
+
+    <event path="vm/compiler/failure">
+      <setting name="enabled" control="compiler-enabled-failure">false</setting>
+    </event>
+
+    <event path="vm/code_sweeper/config">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_sweeper/stats">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_sweeper/sweep">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="threshold" control="compiler-sweeper-threshold">100 ms</setting>
+    </event>
+
+    <event path="vm/code_cache/config">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_cache/stats">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="vm/code_cache/full">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+    </event>
+
+    <event path="os/information">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/processor/cpu_information">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/processor/context_switch_rate">
+      <setting name="enabled" control="compiler-enabled">true</setting>
+      <setting name="period">10 s</setting>
+    </event>
+
+    <event path="os/processor/cpu_load">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+    <event path="os/processor/cpu_tsc">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/system_process">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/initial_environment_variable">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="os/memory/physical_memory">
+      <setting name="enabled">true</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event path="java/object_alloc_in_new_TLAB">
+      <setting name="enabled" control="allocation-profiling-enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="java/object_alloc_outside_TLAB">
+      <setting name="enabled" control="allocation-profiling-enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+  </producer>
+
+  <producer uri="http://www.oracle.com/hotspot/jdk/" label="Oracle JDK">
+
+    <event path="java/file_read">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/file-io-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/file_write">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/file-io-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/socket_read">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/socket-io-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/socket_write">
+      <setting name="enabled">true</setting>
+      <setting name="stackTrace">true</setting>
+      <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/socket-io-threshold">10 ms</setting>
+    </event>
+
+    <event path="java/exception_throw">
+      <setting name="enabled" control="http://www.oracle.com/hotspot/jvm/enable-exceptions">false</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="java/error_throw">
+      <setting name="enabled" control="http://www.oracle.com/hotspot/jvm/enable-errors">true</setting>
+      <setting name="stackTrace">true</setting>
+    </event>
+
+    <event path="java/statistics/throwables">
+      <setting name="enabled">true</setting>
+      <setting name="period">1000 ms</setting>
+    </event>
+
+  </producer>
+
+  <producer uri="http://www.oracle.com/hotspot/jfr-info/" label="Oracle JDK">
+
+    <event path="recordings/recording">
+      <setting name="enabled">true</setting>
+    </event>
+
+    <event path="recordings/recording_setting">
+      <setting name="enabled">true</setting>
+    </event>
+
+  </producer>
+
+</configuration>
diff --git a/tlatools/test-benchmark/tlc2/tool/ModuleOverwrites-1531220029-80dc6de2b.json b/tlatools/test-benchmark/tlc2/tool/ModuleOverwrites-1531220029-80dc6de2b.json
new file mode 100644
index 0000000000000000000000000000000000000000..9460d8682ea6f71625e3d2f6b61ea8256c72091a
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/ModuleOverwrites-1531220029-80dc6de2b.json
@@ -0,0 +1,169 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.ModuleOverwritesBenchmark.aNoModuleOverwrite",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-9-oracle/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "9.0.4",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "9.0.4+11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 22425.663005813956,
+            "scoreError" : 5954.899764560438,
+            "scoreConfidence" : [
+                16470.763241253517,
+                28380.562770374396
+            ],
+            "scorePercentiles" : {
+                "0.0" : 21271.141489690363,
+                "50.0" : 22502.033943221788,
+                "90.0" : 23427.442647121898,
+                "95.0" : 23427.442647121898,
+                "99.0" : 23427.442647121898,
+                "99.9" : 23427.442647121898,
+                "99.99" : 23427.442647121898,
+                "99.999" : 23427.442647121898,
+                "99.9999" : 23427.442647121898,
+                "100.0" : 23427.442647121898
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    21271.141489690363,
+                    22186.209276647664
+                ],
+                [
+                    22817.85860979591,
+                    23427.442647121898
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.ModuleOverwritesBenchmark.bModuleOverwrite",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-9-oracle/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "9.0.4",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "9.0.4+11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1142037.5355511934,
+            "scoreError" : 201257.82920798412,
+            "scoreConfidence" : [
+                940779.7063432093,
+                1343295.3647591774
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1115758.2755510376,
+                "50.0" : 1135402.653804632,
+                "90.0" : 1181586.5590444724,
+                "95.0" : 1181586.5590444724,
+                "99.0" : 1181586.5590444724,
+                "99.9" : 1181586.5590444724,
+                "99.99" : 1181586.5590444724,
+                "99.999" : 1181586.5590444724,
+                "99.9999" : 1181586.5590444724,
+                "100.0" : 1181586.5590444724
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1115758.2755510376,
+                    1181586.5590444724
+                ],
+                [
+                    1118561.6168647762,
+                    1152243.6907444876
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.ModuleOverwritesBenchmark.cModuleOverwriteLinear",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-9-oracle/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "9.0.4",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "9.0.4+11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 3708550.6057697954,
+            "scoreError" : 824356.4201994061,
+            "scoreConfidence" : [
+                2884194.185570389,
+                4532907.025969202
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3595787.9785506492,
+                "50.0" : 3700259.3284566575,
+                "90.0" : 3837895.787615218,
+                "95.0" : 3837895.787615218,
+                "99.0" : 3837895.787615218,
+                "99.9" : 3837895.787615218,
+                "99.99" : 3837895.787615218,
+                "99.999" : 3837895.787615218,
+                "99.9999" : 3837895.787615218,
+                "100.0" : 3837895.787615218
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3798339.118508976,
+                    3595787.9785506492
+                ],
+                [
+                    3602179.538404339,
+                    3837895.787615218
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/tool/ModuleOverwritesBenchmark.java b/tlatools/test-benchmark/tlc2/tool/ModuleOverwritesBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbfe6e8b04fd90ab7052e18f6abc286760f00e97
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/ModuleOverwritesBenchmark.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.tool.impl.FastTool;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.Value;
+import util.SimpleFilenameToStream;
+import util.ToolIO;
+import util.UniqueString;
+
+@State(Scope.Benchmark)
+public class ModuleOverwritesBenchmark {
+
+	/*
+	 * Run with: java -jar target/benchmarks.jar -wi 2 -i 2 -f2 -rf json -rff
+	 * ModuleOverwritesBenchmark-$(de +%s)-$(git rev-parse --short HEAD).json
+	 * -jvmArgsPrepend "-Xms8192m -Xmx8192m" -jvmArgsAppend
+	 * "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model tlc2.tool.ModuleOverwritesBenchmark "
+	 */
+	
+	private static final String BASE_PATH = System
+			.getProperty(ModuleOverwritesBenchmark.class.getName() + ".base");
+
+	private static final ITool tool;
+	private static final TLCStateMut state;
+
+	static {
+		String dir = BASE_PATH + File.separator + "ModuleOverwrites";
+		System.err.println(dir);
+		ToolIO.setUserDir(dir);
+
+		tool = new FastTool("", "ModuleOverwrites", "ModuleOverwrites", new SimpleFilenameToStream());
+
+		state = (TLCStateMut) tool.getInitStates().elementAt(0);
+	}
+
+	@Benchmark
+	public boolean aNoModuleOverwrite() {
+		shuffleValues();
+		return tool.isValid(tool.getInvariants()[0], state);
+	}
+
+	@Benchmark
+	public boolean bModuleOverwrite() {
+		shuffleValues();
+		return tool.isValid(tool.getInvariants()[1], state);
+	}
+
+	@Benchmark
+	public boolean cModuleOverwriteLinear() {
+		shuffleValues();
+		return tool.isValid(tool.getInvariants()[2], state);
+	}
+
+	private static final void shuffleValues() {
+		final FcnRcdValue frv = (FcnRcdValue) state.getVals().get(UniqueString.uniqueStringOf("t"));
+
+		final List<Value> values = Arrays.asList(frv.values);
+		Collections.shuffle(values);
+
+		for (int i = 0; i < values.size(); i++) {
+			frv.values[i] = values.get(i);
+		}
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark-1535138934-548ce71f5.json b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark-1535138934-548ce71f5.json
new file mode 100644
index 0000000000000000000000000000000000000000..e3705ddc5063cac5811bbe0b9c1740ad76958e22
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark-1535138934-548ce71f5.json
@@ -0,0 +1,128 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayBenchmark.AswapWithCopy",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-11-openjdk-amd64/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "10.0.2",
+        "vmName" : "OpenJDK 64-Bit Server VM",
+        "vmVersion" : "10.0.2+13-Ubuntu-1ubuntu0.18.04.1",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.9284288211942446E8,
+            "scoreError" : 1.2894808885787597E7,
+            "scoreConfidence" : [
+                1.7994807323363686E8,
+                2.0573769100521207E8
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.8376569492012057E8,
+                "50.0" : 1.9164345746308792E8,
+                "90.0" : 2.0265063750222045E8,
+                "95.0" : 2.0265063750222045E8,
+                "99.0" : 2.0265063750222045E8,
+                "99.9" : 2.0265063750222045E8,
+                "99.99" : 2.0265063750222045E8,
+                "99.999" : 2.0265063750222045E8,
+                "99.9999" : 2.0265063750222045E8,
+                "100.0" : 2.0265063750222045E8
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2.0265063750222045E8,
+                    2.0163514267585754E8,
+                    2.0146764720797616E8
+                ],
+                [
+                    1.8787724608521232E8,
+                    1.8376569492012057E8,
+                    1.8502766664432153E8
+                ],
+                [
+                    1.9164345746308792E8,
+                    1.860197486552854E8,
+                    1.9549869792073822E8
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayBenchmark.BswapGetSet",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-11-openjdk-amd64/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "10.0.2",
+        "vmName" : "OpenJDK 64-Bit Server VM",
+        "vmVersion" : "10.0.2+13-Ubuntu-1ubuntu0.18.04.1",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 6.087182668510927E8,
+            "scoreError" : 1.2923203076996244E7,
+            "scoreConfidence" : [
+                5.957950637740965E8,
+                6.216414699280889E8
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5.959914922634013E8,
+                "50.0" : 6.098126314774866E8,
+                "90.0" : 6.174645254898823E8,
+                "95.0" : 6.174645254898823E8,
+                "99.0" : 6.174645254898823E8,
+                "99.9" : 6.174645254898823E8,
+                "99.99" : 6.174645254898823E8,
+                "99.999" : 6.174645254898823E8,
+                "99.9999" : 6.174645254898823E8,
+                "100.0" : 6.174645254898823E8
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.970025135703455E8,
+                    5.959914922634013E8,
+                    6.090513375990788E8
+                ],
+                [
+                    6.098126314774866E8,
+                    6.123331210694003E8,
+                    6.067840687850246E8
+                ],
+                [
+                    6.148359838927962E8,
+                    6.151887275124179E8,
+                    6.174645254898823E8
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark.java b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc345c51099aa67ff7e6e8e5085bc25a5db5a806
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import java.io.IOException;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+@State(Scope.Benchmark)
+public class LongArrayBenchmark {
+	private LongArray array;
+
+    @Setup
+    public void up() throws IOException {
+        final long elements = 1L << 10;
+		array = new LongArray(elements);
+		array.zeroMemory(1);
+		for (long i = 0L; i < elements; i++) {
+			array.set(i, Long.MAX_VALUE - i);
+		}
+    }
+    
+    @Benchmark
+    public void AswapWithCopy() {
+    	array.swapCopy(0, array.size() - 1);
+    }
+    
+	@Benchmark
+	public void BswapGetSet() {
+    	array.swap(0, array.size() - 1);
+    }
+}
diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark-1540420474-5f451e67c.json b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark-1540420474-5f451e67c.json
new file mode 100644
index 0000000000000000000000000000000000000000..527d1818cc98683b02f88d0cd6afa765698c5adc
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark-1540420474-5f451e67c.json
@@ -0,0 +1,829 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 15691.037304952166,
+            "scoreError" : 3227.6738320727072,
+            "scoreConfidence" : [
+                12463.363472879459,
+                18918.711137024875
+            ],
+            "scorePercentiles" : {
+                "0.0" : 15149.456337242189,
+                "50.0" : 15709.295867528572,
+                "90.0" : 16196.101147509331,
+                "95.0" : 16196.101147509331,
+                "99.0" : 16196.101147509331,
+                "99.9" : 16196.101147509331,
+                "99.99" : 16196.101147509331,
+                "99.999" : 16196.101147509331,
+                "99.9999" : 16196.101147509331,
+                "100.0" : 16196.101147509331
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    16025.043319946451,
+                    15393.548415110692
+                ],
+                [
+                    16196.101147509331,
+                    15149.456337242189
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 10257.952132803022,
+            "scoreError" : 1609.8651787441936,
+            "scoreConfidence" : [
+                8648.086954058828,
+                11867.817311547216
+            ],
+            "scorePercentiles" : {
+                "0.0" : 10002.640123830502,
+                "50.0" : 10219.707935833161,
+                "90.0" : 10589.752535715264,
+                "95.0" : 10589.752535715264,
+                "99.0" : 10589.752535715264,
+                "99.9" : 10589.752535715264,
+                "99.99" : 10589.752535715264,
+                "99.999" : 10589.752535715264,
+                "99.9999" : 10589.752535715264,
+                "100.0" : 10589.752535715264
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    10589.752535715264,
+                    10156.48634104379
+                ],
+                [
+                    10282.929530622534,
+                    10002.640123830502
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "24"
+        },
+        "primaryMetric" : {
+            "score" : 76.898812891649,
+            "scoreError" : 2.7474408988974544,
+            "scoreConfidence" : [
+                74.15137199275155,
+                79.64625379054645
+            ],
+            "scorePercentiles" : {
+                "0.0" : 76.48035969067071,
+                "50.0" : 76.8971373633593,
+                "90.0" : 77.32061714920668,
+                "95.0" : 77.32061714920668,
+                "99.0" : 77.32061714920668,
+                "99.9" : 77.32061714920668,
+                "99.99" : 77.32061714920668,
+                "99.999" : 77.32061714920668,
+                "99.9999" : 77.32061714920668,
+                "100.0" : 77.32061714920668
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    77.32061714920668,
+                    76.48035969067071
+                ],
+                [
+                    77.20477337813603,
+                    76.58950134858259
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "28"
+        },
+        "primaryMetric" : {
+            "score" : 4.8919062820566035,
+            "scoreError" : 0.13982428649979478,
+            "scoreConfidence" : [
+                4.752081995556809,
+                5.031730568556398
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.869023032502967,
+                "50.0" : 4.892757182572337,
+                "90.0" : 4.913087730578771,
+                "95.0" : 4.913087730578771,
+                "99.0" : 4.913087730578771,
+                "99.9" : 4.913087730578771,
+                "99.99" : 4.913087730578771,
+                "99.999" : 4.913087730578771,
+                "99.9999" : 4.913087730578771,
+                "100.0" : 4.913087730578771
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4.913087730578771,
+                    4.869023032502967
+                ],
+                [
+                    4.907434706339786,
+                    4.8780796588048885
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "31"
+        },
+        "primaryMetric" : {
+            "score" : 0.6054237172952491,
+            "scoreError" : 0.057545101881470405,
+            "scoreConfidence" : [
+                0.5478786154137787,
+                0.6629688191767196
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.5920856333767331,
+                "50.0" : 0.6095564484306197,
+                "90.0" : 0.6104963389430239,
+                "95.0" : 0.6104963389430239,
+                "99.0" : 0.6104963389430239,
+                "99.9" : 0.6104963389430239,
+                "99.99" : 0.6104963389430239,
+                "99.999" : 0.6104963389430239,
+                "99.9999" : 0.6104963389430239,
+                "100.0" : 0.6104963389430239
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.6093206980085581,
+                    0.5920856333767331
+                ],
+                [
+                    0.6097921988526813,
+                    0.6104963389430239
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 5823.508981235277,
+            "scoreError" : 331.24359175202756,
+            "scoreConfidence" : [
+                5492.265389483249,
+                6154.752572987305
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5753.76878817967,
+                "50.0" : 5838.788951447104,
+                "90.0" : 5862.689233867229,
+                "95.0" : 5862.689233867229,
+                "99.0" : 5862.689233867229,
+                "99.9" : 5862.689233867229,
+                "99.99" : 5862.689233867229,
+                "99.999" : 5862.689233867229,
+                "99.9999" : 5862.689233867229,
+                "100.0" : 5862.689233867229
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5861.34019538168,
+                    5816.237707512529
+                ],
+                [
+                    5862.689233867229,
+                    5753.76878817967
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 5123.978800612953,
+            "scoreError" : 106.38946063324629,
+            "scoreConfidence" : [
+                5017.589339979707,
+                5230.368261246199
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5100.830231354179,
+                "50.0" : 5128.5271012328085,
+                "90.0" : 5138.030768632014,
+                "95.0" : 5138.030768632014,
+                "99.0" : 5138.030768632014,
+                "99.9" : 5138.030768632014,
+                "99.99" : 5138.030768632014,
+                "99.999" : 5138.030768632014,
+                "99.9999" : 5138.030768632014,
+                "100.0" : 5138.030768632014
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5138.030768632014,
+                    5132.914045761264
+                ],
+                [
+                    5100.830231354179,
+                    5124.140156704352
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "24"
+        },
+        "primaryMetric" : {
+            "score" : 68.97129891399425,
+            "scoreError" : 2.413345681248791,
+            "scoreConfidence" : [
+                66.55795323274546,
+                71.38464459524303
+            ],
+            "scorePercentiles" : {
+                "0.0" : 68.58880458551265,
+                "50.0" : 68.91555687043092,
+                "90.0" : 69.4652773296025,
+                "95.0" : 69.4652773296025,
+                "99.0" : 69.4652773296025,
+                "99.9" : 69.4652773296025,
+                "99.99" : 69.4652773296025,
+                "99.999" : 69.4652773296025,
+                "99.9999" : 69.4652773296025,
+                "100.0" : 69.4652773296025
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    68.81090527067398,
+                    69.02020847018787
+                ],
+                [
+                    68.58880458551265,
+                    69.4652773296025
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "28"
+        },
+        "primaryMetric" : {
+            "score" : 4.295134928414715,
+            "scoreError" : 0.107034811914763,
+            "scoreConfidence" : [
+                4.188100116499952,
+                4.4021697403294775
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.278376300240857,
+                "50.0" : 4.295239726791536,
+                "90.0" : 4.311683959834933,
+                "95.0" : 4.311683959834933,
+                "99.0" : 4.311683959834933,
+                "99.9" : 4.311683959834933,
+                "99.99" : 4.311683959834933,
+                "99.999" : 4.311683959834933,
+                "99.9999" : 4.311683959834933,
+                "100.0" : 4.311683959834933
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4.278376300240857,
+                    4.311683959834933
+                ],
+                [
+                    4.2836567656737925,
+                    4.30682268790928
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "31"
+        },
+        "primaryMetric" : {
+            "score" : 0.5397866544024822,
+            "scoreError" : 0.0015897046802224838,
+            "scoreConfidence" : [
+                0.5381969497222597,
+                0.5413763590827047
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.5394561862215702,
+                "50.0" : 0.539844153558601,
+                "90.0" : 0.5400021242711565,
+                "95.0" : 0.5400021242711565,
+                "99.0" : 0.5400021242711565,
+                "99.9" : 0.5400021242711565,
+                "99.99" : 0.5400021242711565,
+                "99.999" : 0.5400021242711565,
+                "99.9999" : 0.5400021242711565,
+                "100.0" : 0.5400021242711565
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.5400021242711565,
+                    0.5399424181107918
+                ],
+                [
+                    0.5397458890064103,
+                    0.5394561862215702
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 1.7117812650403537E7,
+            "scoreError" : 1858981.951784521,
+            "scoreConfidence" : [
+                1.5258830698619016E7,
+                1.897679460218806E7
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.6809496813037805E7,
+                "50.0" : 1.7132330412883125E7,
+                "90.0" : 1.7397092962810084E7,
+                "95.0" : 1.7397092962810084E7,
+                "99.0" : 1.7397092962810084E7,
+                "99.9" : 1.7397092962810084E7,
+                "99.99" : 1.7397092962810084E7,
+                "99.999" : 1.7397092962810084E7,
+                "99.9999" : 1.7397092962810084E7,
+                "100.0" : 1.7397092962810084E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.6809496813037805E7,
+                    1.6938938543361507E7
+                ],
+                [
+                    1.7325722282404747E7,
+                    1.7397092962810084E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 79289.53265336789,
+            "scoreError" : 4239.252439267255,
+            "scoreConfidence" : [
+                75050.28021410064,
+                83528.78509263514
+            ],
+            "scorePercentiles" : {
+                "0.0" : 78885.94805588268,
+                "50.0" : 79001.56006617282,
+                "90.0" : 80269.06242524325,
+                "95.0" : 80269.06242524325,
+                "99.0" : 80269.06242524325,
+                "99.9" : 80269.06242524325,
+                "99.99" : 80269.06242524325,
+                "99.999" : 80269.06242524325,
+                "99.9999" : 80269.06242524325,
+                "100.0" : 80269.06242524325
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    79039.68258539782,
+                    80269.06242524325
+                ],
+                [
+                    78885.94805588268,
+                    78963.43754694781
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "24"
+        },
+        "primaryMetric" : {
+            "score" : 81.08833107753068,
+            "scoreError" : 2.958405915843286,
+            "scoreConfidence" : [
+                78.1299251616874,
+                84.04673699337397
+            ],
+            "scorePercentiles" : {
+                "0.0" : 80.60014890852541,
+                "50.0" : 81.07172218289426,
+                "90.0" : 81.60973103580875,
+                "95.0" : 81.60973103580875,
+                "99.0" : 81.60973103580875,
+                "99.9" : 81.60973103580875,
+                "99.99" : 81.60973103580875,
+                "99.999" : 81.60973103580875,
+                "99.9999" : 81.60973103580875,
+                "100.0" : 81.60973103580875
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    80.82876474476265,
+                    81.60973103580875
+                ],
+                [
+                    80.60014890852541,
+                    81.31467962102589
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "28"
+        },
+        "primaryMetric" : {
+            "score" : 5.073907234951268,
+            "scoreError" : 0.14121152721716443,
+            "scoreConfidence" : [
+                4.932695707734104,
+                5.215118762168433
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5.054526541541467,
+                "50.0" : 5.074118390666934,
+                "90.0" : 5.092865616929739,
+                "95.0" : 5.092865616929739,
+                "99.0" : 5.092865616929739,
+                "99.9" : 5.092865616929739,
+                "99.99" : 5.092865616929739,
+                "99.999" : 5.092865616929739,
+                "99.9999" : 5.092865616929739,
+                "100.0" : 5.092865616929739
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.055443635693725,
+                    5.092793145640142
+                ],
+                [
+                    5.054526541541467,
+                    5.092865616929739
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 2,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_191",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.191-b12",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "size" : "31"
+        },
+        "primaryMetric" : {
+            "score" : 0.6308208674513708,
+            "scoreError" : 0.0021647577060876096,
+            "scoreConfidence" : [
+                0.6286561097452832,
+                0.6329856251574584
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.6303389225544128,
+                "50.0" : 0.6309177899582723,
+                "90.0" : 0.6311089673345258,
+                "95.0" : 0.6311089673345258,
+                "99.0" : 0.6311089673345258,
+                "99.9" : 0.6311089673345258,
+                "99.99" : 0.6311089673345258,
+                "99.999" : 0.6311089673345258,
+                "99.9999" : 0.6311089673345258,
+                "100.0" : 0.6311089673345258
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.6308816519037619,
+                    0.6311089673345258
+                ],
+                [
+                    0.6303389225544128,
+                    0.6309539280127826
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark.java b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..de8b2876f46024a0edae589b2a49ce066d82259c
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import java.io.IOException;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+@State(Scope.Benchmark)
+public class LongArrayInitializeBenchmark {
+	
+	private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
+	private LongArray array;
+
+	@Param({"8", "16", "24", "28", "31"})
+	public int size;
+	
+    @Setup
+    public void up() throws IOException {
+        final long elements = 1L << size;
+		array = new LongArray(elements);
+    }
+    
+    @Benchmark
+    public void AputAddressSingle() throws IOException {
+    	array.zeroMemory(1);
+    }
+    
+    @Benchmark
+    public void BputAddressConcurrent() throws IOException {
+    	array.zeroMemory(AVAILABLE_PROCESSORS);
+    }
+    
+	@Benchmark
+	public void CsetMemory() throws IOException {
+    	array.zeroMemory();
+    }
+}
diff --git a/tlatools/test-benchmark/tlc2/tool/queue/DiskQueueBenachmark.java b/tlatools/test-benchmark/tlc2/tool/queue/DiskQueueBenachmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f07157a42fd8a003cebbe221c4cc7568f95e133
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/queue/DiskQueueBenachmark.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.queue;
+
+import java.io.IOException;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.GroupThreads;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import tlc2.tool.TLCState;
+import tlc2.tool.TLCStates;
+import tlc2.util.FlightRecorderProfiler;
+
+@State(Scope.Group)
+@BenchmarkMode(Mode.Throughput)
+public class DiskQueueBenachmark {
+	
+	@Param({"1", "2", "4", "8", "16", "32", "64"})
+	public int vars;
+
+	@Param({"DiskByteArrayQueue", "DiskStateQueue"})
+	public String impl;
+	
+	private IStateQueue dsq;
+
+	private TLCState state;
+	
+    @Setup
+    public void up() throws IOException {
+		if (impl.equals("DiskByteArrayQueue")) {
+			this.dsq = new DiskByteArrayQueue();
+		} else {
+			this.dsq = new DiskStateQueue();
+		}
+
+		this.state = TLCStates.createDummyState(vars);
+    }
+    
+    @TearDown
+    public void down() throws IOException {
+    	this.dsq.delete();
+    }
+    
+    @Benchmark
+    @Group("g02")
+    @GroupThreads(1)
+    public TLCState consumer1() {
+        return this.dsq.sDequeue();
+    }
+
+    @Benchmark
+    @Group("g02")
+    @GroupThreads(1)
+    public void producer1() {
+    	this.dsq.sEnqueue(this.state);
+    }
+    
+    
+    @Benchmark
+    @Group("g04")
+    @GroupThreads(2)
+    public TLCState consumer2() {
+        return this.dsq.sDequeue();
+    }
+
+    @Benchmark
+    @Group("g04")
+    @GroupThreads(2)
+    public void producer2() {
+    	this.dsq.sEnqueue(this.state);
+    }
+
+    
+    @Benchmark
+    @Group("g08")
+    @GroupThreads(4)
+    public TLCState consumer4() {
+        return this.dsq.sDequeue();
+    }
+
+    @Benchmark
+    @Group("g08")
+    @GroupThreads(4)
+    public void producer4() {
+    	this.dsq.sEnqueue(this.state);
+    }
+
+    
+    @Benchmark
+    @Group("g16")
+    @GroupThreads(8)
+    public TLCState consumer8() {
+        return this.dsq.sDequeue();
+    }
+
+    @Benchmark
+    @Group("g16")
+    @GroupThreads(8)
+    public void producer8() {
+    	this.dsq.sEnqueue(this.state);
+    }
+    
+    public static void main(String[] args) throws RunnerException {
+        Options opt = new OptionsBuilder()
+                .include(DiskQueueBenachmark.class.getSimpleName())
+                .addProfiler(FlightRecorderProfiler.class)
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/tlatools/test-benchmark/tlc2/tool/queue/StateQueueBenachmark.java b/tlatools/test-benchmark/tlc2/tool/queue/StateQueueBenachmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8221bab3a15a03748d31e4cc4e6cb0ab71f97ca
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/queue/StateQueueBenachmark.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.queue;
+
+import java.io.IOException;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.GroupThreads;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+
+import tlc2.tool.TLCState;
+import tlc2.tool.TLCStates;
+
+@State(Scope.Group)
+public class StateQueueBenachmark {
+
+	@Param({"1", "2", "4", "8", "16", "32", "64"})
+	public int size;
+	
+	private IStateQueue s;
+
+	private TLCState[] batch;
+
+    @Setup
+    public void up() throws IOException {
+        s = new DiskStateQueue();
+        
+    	// balance off the costs for creating the TLCState[].
+    	this.batch = new TLCState[size];
+    	for (int i = 0; i < batch.length; i++) {
+			batch[i] = TLCStates.createDummyState();
+		}
+    }
+    
+    @TearDown
+    public void down() throws IOException {
+    	this.s.delete();
+    }
+  
+    @Benchmark
+    @Group("single")
+    @GroupThreads(2)
+    public TLCState[] consumerSingle() {
+    	final TLCState[] res = new TLCState[batch.length];
+    	for (int i = 0; i < batch.length; i++) {
+    		res[i] = this.s.sDequeue();
+    	}
+        return res;
+    }
+
+    @Benchmark
+    @Group("single")
+    @GroupThreads(2)
+    public void producerSingle() {
+    	for (int i = 0; i < batch.length; i++) {
+    		this.s.sEnqueue(batch[i]);
+		}
+    }
+
+    
+    /* Batches of enqueue only */
+    
+    @Benchmark
+    @Group("batchasym")
+    @GroupThreads(2)
+    public TLCState[] consumerBatch() {
+    	final TLCState[] res = new TLCState[batch.length];
+    	for (int i = 0; i < batch.length; i++) {
+    		res[i] = this.s.sDequeue();
+    	}
+        return res;
+    }
+
+    @Benchmark
+    @Group("batchasym")
+    @GroupThreads(2)
+    public void producerBatch() {
+    	this.s.sEnqueue(batch);
+    }
+
+    
+    /* Batches of dequeue & enqueue */
+    
+    @Benchmark
+    @Group("batchsym")
+    @GroupThreads(2)
+    public TLCState[] consumerBatchSym() {
+        return this.s.sDequeue(size);
+    }
+
+    @Benchmark
+    @Group("batchsym")
+    @GroupThreads(2)
+    public void producerBatchSym() {
+    	this.s.sEnqueue(batch);
+    }
+}
diff --git a/tlatools/test-benchmark/tlc2/tool/simulation/SimulatorBenchmark.java b/tlatools/test-benchmark/tlc2/tool/simulation/SimulatorBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c2c2176d850fae3d40fa19f1e404eeed6ab3079
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/tool/simulation/SimulatorBenchmark.java
@@ -0,0 +1,74 @@
+package tlc2.tool.simulation;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import tla2sany.modanalyzer.SpecObj;
+import tlc2.tool.Simulator;
+import tlc2.util.RandomGenerator;
+import util.SimpleFilenameToStream;
+import util.ToolIO;
+
+/**
+ * A benchmark to measure multi-threaded simulation performance.
+ */
+@State(Scope.Benchmark)
+public class SimulatorBenchmark {
+	
+    @Param({ "1", "2", "3", "4", "5", "6"})
+    public int nWorkers;
+    
+	Simulator simulator;
+	SpecObj specObj;
+	RandomGenerator rng = new RandomGenerator(0);
+	long seed = 0;
+
+    @Setup
+    public void up() throws IOException {
+		ToolIO.setUserDir("test-model" + File.separator + "simulation" + File.separator + "BenchmarkSpec");
+    }  
+    
+    @Setup(Level.Iteration)
+    public void reseedIter() {
+    	// For each iteration of a benchmark, we set the seed to a known value, so that each 
+    	// benchmark fork starts a particular iteration with the same seed. 
+		seed += 1;
+		rng.setSeed(seed);
+    }
+    
+    @Setup(Level.Trial)
+    public void reseed() {
+    	// We reset the random number generator to a fixed seed before benchmarking a specific worker count.
+    	// This should help to make these benchmarks more deterministic for a fixed set of benchmark parameters. The initial
+    	// seed of this random number generator should effectively determine what traces are explored in what order by the simulation
+    	// workers, so a particular seed should correspond to a fixed exploration order of the behavior space.
+		rng.setSeed(0);
+		seed = 0;
+    }
+    
+    public void simulatorBenchmark(int nWorkers) {
+		try {
+			int maxTraceDepth = 20;
+			Simulator simulator = new Simulator("BenchmarkSpec", "MCInv", null, false, maxTraceDepth, Long.MAX_VALUE, rng, 0,
+					new SimpleFilenameToStream(), nWorkers);
+			simulator.simulate();
+		} catch (Exception e) {
+			System.out.println(e.getMessage());
+		}    
+    }
+	
+	@Benchmark
+	@BenchmarkMode(Mode.AverageTime)
+	public void SimulatorWorkers() {
+		simulatorBenchmark(nWorkers);  
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/util/CombinatoricsBenchmark.java b/tlatools/test-benchmark/tlc2/util/CombinatoricsBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..db27ffc507566401a0c1404eac6b725a75850cdb
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/util/CombinatoricsBenchmark.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+@State(Scope.Benchmark)
+public class CombinatoricsBenchmark {
+	
+	private static List<BigInteger> bincoef;
+	private static List<BigInteger> slowBincoef;
+
+	static {
+		bincoef = new ArrayList<BigInteger>(187489);
+		slowBincoef = new ArrayList<BigInteger>(187489);
+	}
+	
+	@Benchmark
+	@Warmup(iterations = 3, time = 1)
+	@Measurement(iterations = 3, time = 1)
+	@BenchmarkMode(Mode.Throughput)
+	public List<BigInteger> bigChoose() {
+		for (int n = Combinatorics.MAXCHOOSENUM + 1; n < Combinatorics.MAXCHOOSENUM << 3; n++) {
+			for (int k = Combinatorics.MAXCHOOSENUM + 1; k < Combinatorics.MAXCHOOSENUM << 3; k++) {
+				bincoef.add(Combinatorics.bigChoose(n, k));
+			}
+		}
+		return bincoef;
+	}
+
+	@Benchmark
+	@Warmup(iterations = 3, time = 1)
+	@Measurement(iterations = 3, time = 1)
+	@BenchmarkMode(Mode.Throughput)
+	public List<BigInteger> slowBigChoose() {
+		for (int n = Combinatorics.MAXCHOOSENUM + 1; n < Combinatorics.MAXCHOOSENUM << 3; n++) {
+			for (int k = Combinatorics.MAXCHOOSENUM + 1; k < Combinatorics.MAXCHOOSENUM << 3; k++) {
+				slowBincoef.add(Combinatorics.slowBigChoose(n, k));
+			}
+		}
+		return slowBincoef;
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/util/FP64Benchmark.java b/tlatools/test-benchmark/tlc2/util/FP64Benchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1c2010f55ce0eed05d59c78a00817e7a5fb625e
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/util/FP64Benchmark.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import java.io.IOException;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+@State(Scope.Benchmark)
+public class FP64Benchmark {
+
+	/*
+	 * Run with: java -jar target/benchmarks.jar -wi 2 -i 2 -f2 -rf json -rff
+	 * FP64Benchmark-$(date +%s)-$(git rev-parse --short HEAD).json
+	 * -jvmArgsPrepend "-Xms8192m -Xmx8192m" -jvmArgsAppend tlc2.util.FP64Benchmark "
+	 */
+
+    @Setup
+    public void up() throws IOException {
+    	FP64.Init();
+    }
+    
+    @Benchmark
+    public long extendIntLoop() {
+    	return FP64.ExtendLoop(72316478964978L, 3876421);
+    }
+
+    @Benchmark
+    public long extendIntUnrolled() {
+    	return FP64.Extend(72316478964978L, 3876421);
+    }
+
+}
diff --git a/tlatools/test-benchmark/tlc2/util/FlightRecorderProfiler.java b/tlatools/test-benchmark/tlc2/util/FlightRecorderProfiler.java
new file mode 100644
index 0000000000000000000000000000000000000000..b001afb8464d2ebdcd1b85a3ce236691f4d53e84
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/util/FlightRecorderProfiler.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.openjdk.jmh.infra.BenchmarkParams;
+import org.openjdk.jmh.profile.ExternalProfiler;
+import org.openjdk.jmh.results.BenchmarkResult;
+import org.openjdk.jmh.results.Result;
+
+public class FlightRecorderProfiler implements ExternalProfiler {
+
+	// Inspired by https://github.com/nicoulaj/jmh-utils
+	
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.Profiler#getDescription()
+	 */
+	@Override
+	public String getDescription() {
+		return FlightRecorderProfiler.class.getSimpleName();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.ExternalProfiler#addJVMInvokeOptions(org.openjdk.jmh.infra.BenchmarkParams)
+	 */
+	@Override
+	public Collection<String> addJVMInvokeOptions(BenchmarkParams arg0) {
+		return new ArrayList<String>();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.ExternalProfiler#allowPrintErr()
+	 */
+	@Override
+	public boolean allowPrintErr() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.ExternalProfiler#allowPrintOut()
+	 */
+	@Override
+	public boolean allowPrintOut() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.ExternalProfiler#beforeTrial(org.openjdk.jmh.infra.BenchmarkParams)
+	 */
+	@Override
+	public void beforeTrial(BenchmarkParams arg0) {
+		//noop
+	}
+
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.ExternalProfiler#afterTrial(org.openjdk.jmh.results.BenchmarkResult, long, java.io.File, java.io.File)
+	 */
+	@Override
+	public Collection<? extends Result> afterTrial(BenchmarkResult arg0, long arg1, File arg2, File arg3) {
+        return new ArrayList<>();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.openjdk.jmh.profile.ExternalProfiler#addJVMOptions(org.openjdk.jmh.infra.BenchmarkParams)
+	 */
+	@Override
+	public Collection<String> addJVMOptions(BenchmarkParams bp) {
+		// Create the jfr file in the current directory named after the benchmark.
+		return Arrays.asList("-XX:StartFlightRecording=settings=default,disk=true,dumponexit=true,filename=./" + bp.id() + ".jfr");
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/EnumerateSubsetBenchmark-1537381744-b7b00550b.json b/tlatools/test-benchmark/tlc2/value/EnumerateSubsetBenchmark-1537381744-b7b00550b.json
new file mode 100644
index 0000000000000000000000000000000000000000..3fda8d484f95b10a241a2c2e08fdc0899402f417
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/EnumerateSubsetBenchmark-1537381744-b7b00550b.json
@@ -0,0 +1,3184 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "0"
+        },
+        "primaryMetric" : {
+            "score" : 3.611267082827124E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.611267082827124E7,
+                "50.0" : 3.611267082827124E7,
+                "90.0" : 3.611267082827124E7,
+                "95.0" : 3.611267082827124E7,
+                "99.0" : 3.611267082827124E7,
+                "99.9" : 3.611267082827124E7,
+                "99.99" : 3.611267082827124E7,
+                "99.999" : 3.611267082827124E7,
+                "99.9999" : 3.611267082827124E7,
+                "100.0" : 3.611267082827124E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.611267082827124E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "1"
+        },
+        "primaryMetric" : {
+            "score" : 1.0354789744882155E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.0354789744882155E7,
+                "50.0" : 1.0354789744882155E7,
+                "90.0" : 1.0354789744882155E7,
+                "95.0" : 1.0354789744882155E7,
+                "99.0" : 1.0354789744882155E7,
+                "99.9" : 1.0354789744882155E7,
+                "99.99" : 1.0354789744882155E7,
+                "99.999" : 1.0354789744882155E7,
+                "99.9999" : 1.0354789744882155E7,
+                "100.0" : 1.0354789744882155E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.0354789744882155E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "2"
+        },
+        "primaryMetric" : {
+            "score" : 7424758.104352328,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7424758.104352328,
+                "50.0" : 7424758.104352328,
+                "90.0" : 7424758.104352328,
+                "95.0" : 7424758.104352328,
+                "99.0" : 7424758.104352328,
+                "99.9" : 7424758.104352328,
+                "99.99" : 7424758.104352328,
+                "99.999" : 7424758.104352328,
+                "99.9999" : 7424758.104352328,
+                "100.0" : 7424758.104352328
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    7424758.104352328
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "3"
+        },
+        "primaryMetric" : {
+            "score" : 4520093.423989947,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4520093.423989947,
+                "50.0" : 4520093.423989947,
+                "90.0" : 4520093.423989947,
+                "95.0" : 4520093.423989947,
+                "99.0" : 4520093.423989947,
+                "99.9" : 4520093.423989947,
+                "99.99" : 4520093.423989947,
+                "99.999" : 4520093.423989947,
+                "99.9999" : 4520093.423989947,
+                "100.0" : 4520093.423989947
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4520093.423989947
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "4"
+        },
+        "primaryMetric" : {
+            "score" : 1925178.7174611264,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1925178.7174611264,
+                "50.0" : 1925178.7174611264,
+                "90.0" : 1925178.7174611264,
+                "95.0" : 1925178.7174611264,
+                "99.0" : 1925178.7174611264,
+                "99.9" : 1925178.7174611264,
+                "99.99" : 1925178.7174611264,
+                "99.999" : 1925178.7174611264,
+                "99.9999" : 1925178.7174611264,
+                "100.0" : 1925178.7174611264
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1925178.7174611264
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 133009.5244991998,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 133009.5244991998,
+                "50.0" : 133009.5244991998,
+                "90.0" : 133009.5244991998,
+                "95.0" : 133009.5244991998,
+                "99.0" : 133009.5244991998,
+                "99.9" : 133009.5244991998,
+                "99.99" : 133009.5244991998,
+                "99.999" : 133009.5244991998,
+                "99.9999" : 133009.5244991998,
+                "100.0" : 133009.5244991998
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    133009.5244991998
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10"
+        },
+        "primaryMetric" : {
+            "score" : 29226.867534242203,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 29226.867534242203,
+                "50.0" : 29226.867534242203,
+                "90.0" : 29226.867534242203,
+                "95.0" : 29226.867534242203,
+                "99.0" : 29226.867534242203,
+                "99.9" : 29226.867534242203,
+                "99.99" : 29226.867534242203,
+                "99.999" : 29226.867534242203,
+                "99.9999" : 29226.867534242203,
+                "100.0" : 29226.867534242203
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    29226.867534242203
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12"
+        },
+        "primaryMetric" : {
+            "score" : 6081.382406132828,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 6081.382406132828,
+                "50.0" : 6081.382406132828,
+                "90.0" : 6081.382406132828,
+                "95.0" : 6081.382406132828,
+                "99.0" : 6081.382406132828,
+                "99.9" : 6081.382406132828,
+                "99.99" : 6081.382406132828,
+                "99.999" : 6081.382406132828,
+                "99.9999" : 6081.382406132828,
+                "100.0" : 6081.382406132828
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6081.382406132828
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14"
+        },
+        "primaryMetric" : {
+            "score" : 1390.845676349573,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1390.845676349573,
+                "50.0" : 1390.845676349573,
+                "90.0" : 1390.845676349573,
+                "95.0" : 1390.845676349573,
+                "99.0" : 1390.845676349573,
+                "99.9" : 1390.845676349573,
+                "99.99" : 1390.845676349573,
+                "99.999" : 1390.845676349573,
+                "99.9999" : 1390.845676349573,
+                "100.0" : 1390.845676349573
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1390.845676349573
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 330.27592509790907,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 330.27592509790907,
+                "50.0" : 330.27592509790907,
+                "90.0" : 330.27592509790907,
+                "95.0" : 330.27592509790907,
+                "99.0" : 330.27592509790907,
+                "99.9" : 330.27592509790907,
+                "99.99" : 330.27592509790907,
+                "99.999" : 330.27592509790907,
+                "99.9999" : 330.27592509790907,
+                "100.0" : 330.27592509790907
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    330.27592509790907
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 73.27729464188555,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 73.27729464188555,
+                "50.0" : 73.27729464188555,
+                "90.0" : 73.27729464188555,
+                "95.0" : 73.27729464188555,
+                "99.0" : 73.27729464188555,
+                "99.9" : 73.27729464188555,
+                "99.99" : 73.27729464188555,
+                "99.999" : 73.27729464188555,
+                "99.9999" : 73.27729464188555,
+                "100.0" : 73.27729464188555
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    73.27729464188555
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "19"
+        },
+        "primaryMetric" : {
+            "score" : 27.9553224857132,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 27.9553224857132,
+                "50.0" : 27.9553224857132,
+                "90.0" : 27.9553224857132,
+                "95.0" : 27.9553224857132,
+                "99.0" : 27.9553224857132,
+                "99.9" : 27.9553224857132,
+                "99.99" : 27.9553224857132,
+                "99.999" : 27.9553224857132,
+                "99.9999" : 27.9553224857132,
+                "100.0" : 27.9553224857132
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    27.9553224857132
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "0"
+        },
+        "primaryMetric" : {
+            "score" : 1.3197374943346184E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.3197374943346184E7,
+                "50.0" : 1.3197374943346184E7,
+                "90.0" : 1.3197374943346184E7,
+                "95.0" : 1.3197374943346184E7,
+                "99.0" : 1.3197374943346184E7,
+                "99.9" : 1.3197374943346184E7,
+                "99.99" : 1.3197374943346184E7,
+                "99.999" : 1.3197374943346184E7,
+                "99.9999" : 1.3197374943346184E7,
+                "100.0" : 1.3197374943346184E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.3197374943346184E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "1"
+        },
+        "primaryMetric" : {
+            "score" : 6123381.900721031,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 6123381.900721031,
+                "50.0" : 6123381.900721031,
+                "90.0" : 6123381.900721031,
+                "95.0" : 6123381.900721031,
+                "99.0" : 6123381.900721031,
+                "99.9" : 6123381.900721031,
+                "99.99" : 6123381.900721031,
+                "99.999" : 6123381.900721031,
+                "99.9999" : 6123381.900721031,
+                "100.0" : 6123381.900721031
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6123381.900721031
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "2"
+        },
+        "primaryMetric" : {
+            "score" : 4565321.513276472,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4565321.513276472,
+                "50.0" : 4565321.513276472,
+                "90.0" : 4565321.513276472,
+                "95.0" : 4565321.513276472,
+                "99.0" : 4565321.513276472,
+                "99.9" : 4565321.513276472,
+                "99.99" : 4565321.513276472,
+                "99.999" : 4565321.513276472,
+                "99.9999" : 4565321.513276472,
+                "100.0" : 4565321.513276472
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4565321.513276472
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "3"
+        },
+        "primaryMetric" : {
+            "score" : 2608382.546140382,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2608382.546140382,
+                "50.0" : 2608382.546140382,
+                "90.0" : 2608382.546140382,
+                "95.0" : 2608382.546140382,
+                "99.0" : 2608382.546140382,
+                "99.9" : 2608382.546140382,
+                "99.99" : 2608382.546140382,
+                "99.999" : 2608382.546140382,
+                "99.9999" : 2608382.546140382,
+                "100.0" : 2608382.546140382
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2608382.546140382
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "4"
+        },
+        "primaryMetric" : {
+            "score" : 1122145.0197631393,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1122145.0197631393,
+                "50.0" : 1122145.0197631393,
+                "90.0" : 1122145.0197631393,
+                "95.0" : 1122145.0197631393,
+                "99.0" : 1122145.0197631393,
+                "99.9" : 1122145.0197631393,
+                "99.99" : 1122145.0197631393,
+                "99.999" : 1122145.0197631393,
+                "99.9999" : 1122145.0197631393,
+                "100.0" : 1122145.0197631393
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1122145.0197631393
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 25729.526497328603,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 25729.526497328603,
+                "50.0" : 25729.526497328603,
+                "90.0" : 25729.526497328603,
+                "95.0" : 25729.526497328603,
+                "99.0" : 25729.526497328603,
+                "99.9" : 25729.526497328603,
+                "99.99" : 25729.526497328603,
+                "99.999" : 25729.526497328603,
+                "99.9999" : 25729.526497328603,
+                "100.0" : 25729.526497328603
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    25729.526497328603
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10"
+        },
+        "primaryMetric" : {
+            "score" : 2847.859944849876,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2847.859944849876,
+                "50.0" : 2847.859944849876,
+                "90.0" : 2847.859944849876,
+                "95.0" : 2847.859944849876,
+                "99.0" : 2847.859944849876,
+                "99.9" : 2847.859944849876,
+                "99.99" : 2847.859944849876,
+                "99.999" : 2847.859944849876,
+                "99.9999" : 2847.859944849876,
+                "100.0" : 2847.859944849876
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2847.859944849876
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12"
+        },
+        "primaryMetric" : {
+            "score" : 324.61093701658825,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 324.61093701658825,
+                "50.0" : 324.61093701658825,
+                "90.0" : 324.61093701658825,
+                "95.0" : 324.61093701658825,
+                "99.0" : 324.61093701658825,
+                "99.9" : 324.61093701658825,
+                "99.99" : 324.61093701658825,
+                "99.999" : 324.61093701658825,
+                "99.9999" : 324.61093701658825,
+                "100.0" : 324.61093701658825
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    324.61093701658825
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14"
+        },
+        "primaryMetric" : {
+            "score" : 29.229042769052267,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 29.229042769052267,
+                "50.0" : 29.229042769052267,
+                "90.0" : 29.229042769052267,
+                "95.0" : 29.229042769052267,
+                "99.0" : 29.229042769052267,
+                "99.9" : 29.229042769052267,
+                "99.99" : 29.229042769052267,
+                "99.999" : 29.229042769052267,
+                "99.9999" : 29.229042769052267,
+                "100.0" : 29.229042769052267
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    29.229042769052267
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 1.9081657766508895,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.9081657766508895,
+                "50.0" : 1.9081657766508895,
+                "90.0" : 1.9081657766508895,
+                "95.0" : 1.9081657766508895,
+                "99.0" : 1.9081657766508895,
+                "99.9" : 1.9081657766508895,
+                "99.99" : 1.9081657766508895,
+                "99.999" : 1.9081657766508895,
+                "99.9999" : 1.9081657766508895,
+                "100.0" : 1.9081657766508895
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.9081657766508895
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 0.128569060485454,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.128569060485454,
+                "50.0" : 0.128569060485454,
+                "90.0" : 0.128569060485454,
+                "95.0" : 0.128569060485454,
+                "99.0" : 0.128569060485454,
+                "99.9" : 0.128569060485454,
+                "99.99" : 0.128569060485454,
+                "99.999" : 0.128569060485454,
+                "99.9999" : 0.128569060485454,
+                "100.0" : 0.128569060485454
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.128569060485454
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "19"
+        },
+        "primaryMetric" : {
+            "score" : 0.031828143851718595,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.031828143851718595,
+                "50.0" : 0.031828143851718595,
+                "90.0" : 0.031828143851718595,
+                "95.0" : 0.031828143851718595,
+                "99.0" : 0.031828143851718595,
+                "99.9" : 0.031828143851718595,
+                "99.99" : 0.031828143851718595,
+                "99.999" : 0.031828143851718595,
+                "99.9999" : 0.031828143851718595,
+                "100.0" : 0.031828143851718595
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.031828143851718595
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "0"
+        },
+        "primaryMetric" : {
+            "score" : 1.700831123275485E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.700831123275485E7,
+                "50.0" : 1.700831123275485E7,
+                "90.0" : 1.700831123275485E7,
+                "95.0" : 1.700831123275485E7,
+                "99.0" : 1.700831123275485E7,
+                "99.9" : 1.700831123275485E7,
+                "99.99" : 1.700831123275485E7,
+                "99.999" : 1.700831123275485E7,
+                "99.9999" : 1.700831123275485E7,
+                "100.0" : 1.700831123275485E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.700831123275485E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "1"
+        },
+        "primaryMetric" : {
+            "score" : 1.1138688178715378E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.1138688178715378E7,
+                "50.0" : 1.1138688178715378E7,
+                "90.0" : 1.1138688178715378E7,
+                "95.0" : 1.1138688178715378E7,
+                "99.0" : 1.1138688178715378E7,
+                "99.9" : 1.1138688178715378E7,
+                "99.99" : 1.1138688178715378E7,
+                "99.999" : 1.1138688178715378E7,
+                "99.9999" : 1.1138688178715378E7,
+                "100.0" : 1.1138688178715378E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.1138688178715378E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "2"
+        },
+        "primaryMetric" : {
+            "score" : 7759027.958066327,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7759027.958066327,
+                "50.0" : 7759027.958066327,
+                "90.0" : 7759027.958066327,
+                "95.0" : 7759027.958066327,
+                "99.0" : 7759027.958066327,
+                "99.9" : 7759027.958066327,
+                "99.99" : 7759027.958066327,
+                "99.999" : 7759027.958066327,
+                "99.9999" : 7759027.958066327,
+                "100.0" : 7759027.958066327
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    7759027.958066327
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "3"
+        },
+        "primaryMetric" : {
+            "score" : 4547334.765941614,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4547334.765941614,
+                "50.0" : 4547334.765941614,
+                "90.0" : 4547334.765941614,
+                "95.0" : 4547334.765941614,
+                "99.0" : 4547334.765941614,
+                "99.9" : 4547334.765941614,
+                "99.99" : 4547334.765941614,
+                "99.999" : 4547334.765941614,
+                "99.9999" : 4547334.765941614,
+                "100.0" : 4547334.765941614
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4547334.765941614
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "4"
+        },
+        "primaryMetric" : {
+            "score" : 2563987.258485594,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2563987.258485594,
+                "50.0" : 2563987.258485594,
+                "90.0" : 2563987.258485594,
+                "95.0" : 2563987.258485594,
+                "99.0" : 2563987.258485594,
+                "99.9" : 2563987.258485594,
+                "99.99" : 2563987.258485594,
+                "99.999" : 2563987.258485594,
+                "99.9999" : 2563987.258485594,
+                "100.0" : 2563987.258485594
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2563987.258485594
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 131149.70918675946,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 131149.70918675946,
+                "50.0" : 131149.70918675946,
+                "90.0" : 131149.70918675946,
+                "95.0" : 131149.70918675946,
+                "99.0" : 131149.70918675946,
+                "99.9" : 131149.70918675946,
+                "99.99" : 131149.70918675946,
+                "99.999" : 131149.70918675946,
+                "99.9999" : 131149.70918675946,
+                "100.0" : 131149.70918675946
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    131149.70918675946
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10"
+        },
+        "primaryMetric" : {
+            "score" : 12308.28613737422,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 12308.28613737422,
+                "50.0" : 12308.28613737422,
+                "90.0" : 12308.28613737422,
+                "95.0" : 12308.28613737422,
+                "99.0" : 12308.28613737422,
+                "99.9" : 12308.28613737422,
+                "99.99" : 12308.28613737422,
+                "99.999" : 12308.28613737422,
+                "99.9999" : 12308.28613737422,
+                "100.0" : 12308.28613737422
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    12308.28613737422
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12"
+        },
+        "primaryMetric" : {
+            "score" : 2598.9041516352163,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2598.9041516352163,
+                "50.0" : 2598.9041516352163,
+                "90.0" : 2598.9041516352163,
+                "95.0" : 2598.9041516352163,
+                "99.0" : 2598.9041516352163,
+                "99.9" : 2598.9041516352163,
+                "99.99" : 2598.9041516352163,
+                "99.999" : 2598.9041516352163,
+                "99.9999" : 2598.9041516352163,
+                "100.0" : 2598.9041516352163
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2598.9041516352163
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14"
+        },
+        "primaryMetric" : {
+            "score" : 621.0476255011224,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 621.0476255011224,
+                "50.0" : 621.0476255011224,
+                "90.0" : 621.0476255011224,
+                "95.0" : 621.0476255011224,
+                "99.0" : 621.0476255011224,
+                "99.9" : 621.0476255011224,
+                "99.99" : 621.0476255011224,
+                "99.999" : 621.0476255011224,
+                "99.9999" : 621.0476255011224,
+                "100.0" : 621.0476255011224
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    621.0476255011224
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 139.82630658657763,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 139.82630658657763,
+                "50.0" : 139.82630658657763,
+                "90.0" : 139.82630658657763,
+                "95.0" : 139.82630658657763,
+                "99.0" : 139.82630658657763,
+                "99.9" : 139.82630658657763,
+                "99.99" : 139.82630658657763,
+                "99.999" : 139.82630658657763,
+                "99.9999" : 139.82630658657763,
+                "100.0" : 139.82630658657763
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    139.82630658657763
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 32.359718767696705,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 32.359718767696705,
+                "50.0" : 32.359718767696705,
+                "90.0" : 32.359718767696705,
+                "95.0" : 32.359718767696705,
+                "99.0" : 32.359718767696705,
+                "99.9" : 32.359718767696705,
+                "99.99" : 32.359718767696705,
+                "99.999" : 32.359718767696705,
+                "99.9999" : 32.359718767696705,
+                "100.0" : 32.359718767696705
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    32.359718767696705
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "19"
+        },
+        "primaryMetric" : {
+            "score" : 15.903491038693316,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 15.903491038693316,
+                "50.0" : 15.903491038693316,
+                "90.0" : 15.903491038693316,
+                "95.0" : 15.903491038693316,
+                "99.0" : 15.903491038693316,
+                "99.9" : 15.903491038693316,
+                "99.99" : 15.903491038693316,
+                "99.999" : 15.903491038693316,
+                "99.9999" : 15.903491038693316,
+                "100.0" : 15.903491038693316
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    15.903491038693316
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "0"
+        },
+        "primaryMetric" : {
+            "score" : 3.0952316339848503E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.0952316339848503E7,
+                "50.0" : 3.0952316339848503E7,
+                "90.0" : 3.0952316339848503E7,
+                "95.0" : 3.0952316339848503E7,
+                "99.0" : 3.0952316339848503E7,
+                "99.9" : 3.0952316339848503E7,
+                "99.99" : 3.0952316339848503E7,
+                "99.999" : 3.0952316339848503E7,
+                "99.9999" : 3.0952316339848503E7,
+                "100.0" : 3.0952316339848503E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.0952316339848503E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "1"
+        },
+        "primaryMetric" : {
+            "score" : 7560521.642894856,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7560521.642894856,
+                "50.0" : 7560521.642894856,
+                "90.0" : 7560521.642894856,
+                "95.0" : 7560521.642894856,
+                "99.0" : 7560521.642894856,
+                "99.9" : 7560521.642894856,
+                "99.99" : 7560521.642894856,
+                "99.999" : 7560521.642894856,
+                "99.9999" : 7560521.642894856,
+                "100.0" : 7560521.642894856
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    7560521.642894856
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "2"
+        },
+        "primaryMetric" : {
+            "score" : 3557526.180877572,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3557526.180877572,
+                "50.0" : 3557526.180877572,
+                "90.0" : 3557526.180877572,
+                "95.0" : 3557526.180877572,
+                "99.0" : 3557526.180877572,
+                "99.9" : 3557526.180877572,
+                "99.99" : 3557526.180877572,
+                "99.999" : 3557526.180877572,
+                "99.9999" : 3557526.180877572,
+                "100.0" : 3557526.180877572
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3557526.180877572
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "3"
+        },
+        "primaryMetric" : {
+            "score" : 1721980.583759659,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1721980.583759659,
+                "50.0" : 1721980.583759659,
+                "90.0" : 1721980.583759659,
+                "95.0" : 1721980.583759659,
+                "99.0" : 1721980.583759659,
+                "99.9" : 1721980.583759659,
+                "99.99" : 1721980.583759659,
+                "99.999" : 1721980.583759659,
+                "99.9999" : 1721980.583759659,
+                "100.0" : 1721980.583759659
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1721980.583759659
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "4"
+        },
+        "primaryMetric" : {
+            "score" : 737795.2258201947,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 737795.2258201947,
+                "50.0" : 737795.2258201947,
+                "90.0" : 737795.2258201947,
+                "95.0" : 737795.2258201947,
+                "99.0" : 737795.2258201947,
+                "99.9" : 737795.2258201947,
+                "99.99" : 737795.2258201947,
+                "99.999" : 737795.2258201947,
+                "99.9999" : 737795.2258201947,
+                "100.0" : 737795.2258201947
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    737795.2258201947
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 14470.141135737491,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 14470.141135737491,
+                "50.0" : 14470.141135737491,
+                "90.0" : 14470.141135737491,
+                "95.0" : 14470.141135737491,
+                "99.0" : 14470.141135737491,
+                "99.9" : 14470.141135737491,
+                "99.99" : 14470.141135737491,
+                "99.999" : 14470.141135737491,
+                "99.9999" : 14470.141135737491,
+                "100.0" : 14470.141135737491
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    14470.141135737491
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10"
+        },
+        "primaryMetric" : {
+            "score" : 3409.1535618738417,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3409.1535618738417,
+                "50.0" : 3409.1535618738417,
+                "90.0" : 3409.1535618738417,
+                "95.0" : 3409.1535618738417,
+                "99.0" : 3409.1535618738417,
+                "99.9" : 3409.1535618738417,
+                "99.99" : 3409.1535618738417,
+                "99.999" : 3409.1535618738417,
+                "99.9999" : 3409.1535618738417,
+                "100.0" : 3409.1535618738417
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3409.1535618738417
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12"
+        },
+        "primaryMetric" : {
+            "score" : 662.9793727227765,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 662.9793727227765,
+                "50.0" : 662.9793727227765,
+                "90.0" : 662.9793727227765,
+                "95.0" : 662.9793727227765,
+                "99.0" : 662.9793727227765,
+                "99.9" : 662.9793727227765,
+                "99.99" : 662.9793727227765,
+                "99.999" : 662.9793727227765,
+                "99.9999" : 662.9793727227765,
+                "100.0" : 662.9793727227765
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    662.9793727227765
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14"
+        },
+        "primaryMetric" : {
+            "score" : 89.74606370164268,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 89.74606370164268,
+                "50.0" : 89.74606370164268,
+                "90.0" : 89.74606370164268,
+                "95.0" : 89.74606370164268,
+                "99.0" : 89.74606370164268,
+                "99.9" : 89.74606370164268,
+                "99.99" : 89.74606370164268,
+                "99.999" : 89.74606370164268,
+                "99.9999" : 89.74606370164268,
+                "100.0" : 89.74606370164268
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    89.74606370164268
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 9.497863578110545,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 9.497863578110545,
+                "50.0" : 9.497863578110545,
+                "90.0" : 9.497863578110545,
+                "95.0" : 9.497863578110545,
+                "99.0" : 9.497863578110545,
+                "99.9" : 9.497863578110545,
+                "99.99" : 9.497863578110545,
+                "99.999" : 9.497863578110545,
+                "99.9999" : 9.497863578110545,
+                "100.0" : 9.497863578110545
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    9.497863578110545
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 0.7630303029631332,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.7630303029631332,
+                "50.0" : 0.7630303029631332,
+                "90.0" : 0.7630303029631332,
+                "95.0" : 0.7630303029631332,
+                "99.0" : 0.7630303029631332,
+                "99.9" : 0.7630303029631332,
+                "99.99" : 0.7630303029631332,
+                "99.999" : 0.7630303029631332,
+                "99.9999" : 0.7630303029631332,
+                "100.0" : 0.7630303029631332
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.7630303029631332
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "19"
+        },
+        "primaryMetric" : {
+            "score" : 0.18791317607485522,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.18791317607485522,
+                "50.0" : 0.18791317607485522,
+                "90.0" : 0.18791317607485522,
+                "95.0" : 0.18791317607485522,
+                "99.0" : 0.18791317607485522,
+                "99.9" : 0.18791317607485522,
+                "99.99" : 0.18791317607485522,
+                "99.999" : 0.18791317607485522,
+                "99.9999" : 0.18791317607485522,
+                "100.0" : 0.18791317607485522
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.18791317607485522
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "0"
+        },
+        "primaryMetric" : {
+            "score" : 3.916296940504498E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.916296940504498E7,
+                "50.0" : 3.916296940504498E7,
+                "90.0" : 3.916296940504498E7,
+                "95.0" : 3.916296940504498E7,
+                "99.0" : 3.916296940504498E7,
+                "99.9" : 3.916296940504498E7,
+                "99.99" : 3.916296940504498E7,
+                "99.999" : 3.916296940504498E7,
+                "99.9999" : 3.916296940504498E7,
+                "100.0" : 3.916296940504498E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.916296940504498E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "1"
+        },
+        "primaryMetric" : {
+            "score" : 1.0229672606085628E7,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.0229672606085628E7,
+                "50.0" : 1.0229672606085628E7,
+                "90.0" : 1.0229672606085628E7,
+                "95.0" : 1.0229672606085628E7,
+                "99.0" : 1.0229672606085628E7,
+                "99.9" : 1.0229672606085628E7,
+                "99.99" : 1.0229672606085628E7,
+                "99.999" : 1.0229672606085628E7,
+                "99.9999" : 1.0229672606085628E7,
+                "100.0" : 1.0229672606085628E7
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.0229672606085628E7
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "2"
+        },
+        "primaryMetric" : {
+            "score" : 5187949.167063834,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5187949.167063834,
+                "50.0" : 5187949.167063834,
+                "90.0" : 5187949.167063834,
+                "95.0" : 5187949.167063834,
+                "99.0" : 5187949.167063834,
+                "99.9" : 5187949.167063834,
+                "99.99" : 5187949.167063834,
+                "99.999" : 5187949.167063834,
+                "99.9999" : 5187949.167063834,
+                "100.0" : 5187949.167063834
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5187949.167063834
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "3"
+        },
+        "primaryMetric" : {
+            "score" : 2615973.0534431874,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2615973.0534431874,
+                "50.0" : 2615973.0534431874,
+                "90.0" : 2615973.0534431874,
+                "95.0" : 2615973.0534431874,
+                "99.0" : 2615973.0534431874,
+                "99.9" : 2615973.0534431874,
+                "99.99" : 2615973.0534431874,
+                "99.999" : 2615973.0534431874,
+                "99.9999" : 2615973.0534431874,
+                "100.0" : 2615973.0534431874
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2615973.0534431874
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "4"
+        },
+        "primaryMetric" : {
+            "score" : 1488347.4408349597,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1488347.4408349597,
+                "50.0" : 1488347.4408349597,
+                "90.0" : 1488347.4408349597,
+                "95.0" : 1488347.4408349597,
+                "99.0" : 1488347.4408349597,
+                "99.9" : 1488347.4408349597,
+                "99.99" : 1488347.4408349597,
+                "99.999" : 1488347.4408349597,
+                "99.9999" : 1488347.4408349597,
+                "100.0" : 1488347.4408349597
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1488347.4408349597
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "8"
+        },
+        "primaryMetric" : {
+            "score" : 88358.67738981682,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 88358.67738981682,
+                "50.0" : 88358.67738981682,
+                "90.0" : 88358.67738981682,
+                "95.0" : 88358.67738981682,
+                "99.0" : 88358.67738981682,
+                "99.9" : 88358.67738981682,
+                "99.99" : 88358.67738981682,
+                "99.999" : 88358.67738981682,
+                "99.9999" : 88358.67738981682,
+                "100.0" : 88358.67738981682
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    88358.67738981682
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10"
+        },
+        "primaryMetric" : {
+            "score" : 16036.52761637113,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 16036.52761637113,
+                "50.0" : 16036.52761637113,
+                "90.0" : 16036.52761637113,
+                "95.0" : 16036.52761637113,
+                "99.0" : 16036.52761637113,
+                "99.9" : 16036.52761637113,
+                "99.99" : 16036.52761637113,
+                "99.999" : 16036.52761637113,
+                "99.9999" : 16036.52761637113,
+                "100.0" : 16036.52761637113
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    16036.52761637113
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12"
+        },
+        "primaryMetric" : {
+            "score" : 3750.424490260002,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3750.424490260002,
+                "50.0" : 3750.424490260002,
+                "90.0" : 3750.424490260002,
+                "95.0" : 3750.424490260002,
+                "99.0" : 3750.424490260002,
+                "99.9" : 3750.424490260002,
+                "99.99" : 3750.424490260002,
+                "99.999" : 3750.424490260002,
+                "99.9999" : 3750.424490260002,
+                "100.0" : 3750.424490260002
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3750.424490260002
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14"
+        },
+        "primaryMetric" : {
+            "score" : 825.1117433250652,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 825.1117433250652,
+                "50.0" : 825.1117433250652,
+                "90.0" : 825.1117433250652,
+                "95.0" : 825.1117433250652,
+                "99.0" : 825.1117433250652,
+                "99.9" : 825.1117433250652,
+                "99.99" : 825.1117433250652,
+                "99.999" : 825.1117433250652,
+                "99.9999" : 825.1117433250652,
+                "100.0" : 825.1117433250652
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    825.1117433250652
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 212.8694242448053,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 212.8694242448053,
+                "50.0" : 212.8694242448053,
+                "90.0" : 212.8694242448053,
+                "95.0" : 212.8694242448053,
+                "99.0" : 212.8694242448053,
+                "99.9" : 212.8694242448053,
+                "99.99" : 212.8694242448053,
+                "99.999" : 212.8694242448053,
+                "99.9999" : 212.8694242448053,
+                "100.0" : 212.8694242448053
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    212.8694242448053
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 51.088303317628935,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 51.088303317628935,
+                "50.0" : 51.088303317628935,
+                "90.0" : 51.088303317628935,
+                "95.0" : 51.088303317628935,
+                "99.0" : 51.088303317628935,
+                "99.9" : 51.088303317628935,
+                "99.99" : 51.088303317628935,
+                "99.999" : 51.088303317628935,
+                "99.9999" : 51.088303317628935,
+                "100.0" : 51.088303317628935
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    51.088303317628935
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 1,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms8192m",
+            "-Xmx8192m",
+            "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model"
+        ],
+        "jdkVersion" : "1.8.0_181",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.181-b13",
+        "warmupIterations" : 1,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 1,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "19"
+        },
+        "primaryMetric" : {
+            "score" : 24.809251093862,
+            "scoreError" : "NaN",
+            "scoreConfidence" : [
+                "NaN",
+                "NaN"
+            ],
+            "scorePercentiles" : {
+                "0.0" : 24.809251093862,
+                "50.0" : 24.809251093862,
+                "90.0" : 24.809251093862,
+                "95.0" : 24.809251093862,
+                "99.0" : 24.809251093862,
+                "99.9" : 24.809251093862,
+                "99.99" : 24.809251093862,
+                "99.999" : 24.809251093862,
+                "99.9999" : 24.809251093862,
+                "100.0" : 24.809251093862
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    24.809251093862
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530025487-ebb8802.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530025487-ebb8802.json
new file mode 100644
index 0000000000000000000000000000000000000000..d9aaedad0627acc2d8cef1befdcdb0508f6508a8
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530025487-ebb8802.json
@@ -0,0 +1,1540 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 9.814265639856911,
+            "scoreError" : 3.5519226084921507,
+            "scoreConfidence" : [
+                6.262343031364761,
+                13.366188248349061
+            ],
+            "scorePercentiles" : {
+                "0.0" : 6.753607668439613,
+                "50.0" : 10.470070397652428,
+                "90.0" : 12.827941145274211,
+                "95.0" : 12.858649761728246,
+                "99.0" : 12.858649761728246,
+                "99.9" : 12.858649761728246,
+                "99.99" : 12.858649761728246,
+                "99.999" : 12.858649761728246,
+                "99.9999" : 12.858649761728246,
+                "100.0" : 12.858649761728246
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    12.858649761728246,
+                    6.911478184305761
+                ],
+                [
+                    11.208217963617345,
+                    12.551563597187895
+                ],
+                [
+                    10.767867782062131,
+                    11.546268236993155
+                ],
+                [
+                    7.480349528344792,
+                    7.892380662647449
+                ],
+                [
+                    6.753607668439613,
+                    10.172273013242725
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 2.7932446205666954,
+            "scoreError" : 0.7212536165365129,
+            "scoreConfidence" : [
+                2.0719910040301825,
+                3.5144982371032083
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.9673773629805331,
+                "50.0" : 3.088890563191904,
+                "90.0" : 3.201938518939384,
+                "95.0" : 3.2048232297325607,
+                "99.0" : 3.2048232297325607,
+                "99.9" : 3.2048232297325607,
+                "99.99" : 3.2048232297325607,
+                "99.999" : 3.2048232297325607,
+                "99.9999" : 3.2048232297325607,
+                "100.0" : 3.2048232297325607
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.1759761218007947,
+                    3.1546063327804683
+                ],
+                [
+                    3.136413968282235,
+                    2.5629672227690783
+                ],
+                [
+                    1.9673773629805331,
+                    2.313233836993042
+                ],
+                [
+                    3.1541066773395916,
+                    3.2048232297325607
+                ],
+                [
+                    3.0413671581015724,
+                    2.221574294887079
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 28.38809904359423,
+            "scoreError" : 9.202418157455796,
+            "scoreConfidence" : [
+                19.185680886138435,
+                37.59051720105003
+            ],
+            "scorePercentiles" : {
+                "0.0" : 19.36331095829827,
+                "50.0" : 28.60973170258842,
+                "90.0" : 36.29196537233252,
+                "95.0" : 36.29504934912339,
+                "99.0" : 36.29504934912339,
+                "99.9" : 36.29504934912339,
+                "99.99" : 36.29504934912339,
+                "99.999" : 36.29504934912339,
+                "99.9999" : 36.29504934912339,
+                "100.0" : 36.29504934912339
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    29.685271545719385,
+                    24.61858183441453
+                ],
+                [
+                    33.0776564207316,
+                    20.534132532710004
+                ],
+                [
+                    24.572440645545356,
+                    27.53419185945745
+                ],
+                [
+                    31.93614570872759,
+                    19.36331095829827
+                ],
+                [
+                    36.26420958121468,
+                    36.29504934912339
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 10.52227709850522,
+            "scoreError" : 1.6101589911941476,
+            "scoreConfidence" : [
+                8.912118107311073,
+                12.132436089699368
+            ],
+            "scorePercentiles" : {
+                "0.0" : 8.491378801933548,
+                "50.0" : 11.247893814845149,
+                "90.0" : 11.420671041101858,
+                "95.0" : 11.432291287500947,
+                "99.0" : 11.432291287500947,
+                "99.9" : 11.432291287500947,
+                "99.99" : 11.432291287500947,
+                "99.999" : 11.432291287500947,
+                "99.9999" : 11.432291287500947,
+                "100.0" : 11.432291287500947
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    8.491378801933548,
+                    9.657996752431464
+                ],
+                [
+                    9.574077581775065,
+                    9.66664321520015
+                ],
+                [
+                    11.2822253177283,
+                    11.31608882351006
+                ],
+                [
+                    11.214644306366141,
+                    11.432291287500947
+                ],
+                [
+                    11.281143323324155,
+                    11.306281575282378
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 2.7945558732575426,
+            "scoreError" : 0.3736130106734233,
+            "scoreConfidence" : [
+                2.4209428625841194,
+                3.168168883930966
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2.221249810954392,
+                "50.0" : 2.8431194315606305,
+                "90.0" : 3.0276341790221912,
+                "95.0" : 3.0276871486165895,
+                "99.0" : 3.0276871486165895,
+                "99.9" : 3.0276871486165895,
+                "99.99" : 3.0276871486165895,
+                "99.999" : 3.0276871486165895,
+                "99.9999" : 3.0276871486165895,
+                "100.0" : 3.0276871486165895
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2.8392579355058656,
+                    3.027157452672608
+                ],
+                [
+                    2.571440923836749,
+                    3.0276871486165895
+                ],
+                [
+                    2.221249810954392,
+                    2.8485126236395106
+                ],
+                [
+                    2.846980927615396,
+                    3.0254151539107506
+                ],
+                [
+                    2.7837884068656784,
+                    2.7540683489578917
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 2.645667431385532,
+            "scoreError" : 0.16743465996444615,
+            "scoreConfidence" : [
+                2.478232771421086,
+                2.8131020913499785
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2.4741351571772863,
+                "50.0" : 2.625042773404063,
+                "90.0" : 2.8393349288272796,
+                "95.0" : 2.8481004996723,
+                "99.0" : 2.8481004996723,
+                "99.9" : 2.8481004996723,
+                "99.99" : 2.8481004996723,
+                "99.999" : 2.8481004996723,
+                "99.9999" : 2.8481004996723,
+                "100.0" : 2.8481004996723
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2.5924849093010445,
+                    2.5767759287385683
+                ],
+                [
+                    2.7243545950925228,
+                    2.760444791222093
+                ],
+                [
+                    2.657600637507082,
+                    2.4741351571772863
+                ],
+                [
+                    2.5567216005732463,
+                    2.5847076500537622
+                ],
+                [
+                    2.68134854451742,
+                    2.8481004996723
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 11.298601269411964,
+            "scoreError" : 0.720388691798619,
+            "scoreConfidence" : [
+                10.578212577613346,
+                12.018989961210583
+            ],
+            "scorePercentiles" : {
+                "0.0" : 10.287617624566224,
+                "50.0" : 11.423558384797065,
+                "90.0" : 11.721178226261209,
+                "95.0" : 11.729644309762948,
+                "99.0" : 11.729644309762948,
+                "99.9" : 11.729644309762948,
+                "99.99" : 11.729644309762948,
+                "99.999" : 11.729644309762948,
+                "99.9999" : 11.729644309762948,
+                "100.0" : 11.729644309762948
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11.45647631886964,
+                    11.644983474745558
+                ],
+                [
+                    11.403544772058899,
+                    11.415097940763632
+                ],
+                [
+                    11.729644309762948,
+                    11.642029824041085
+                ],
+                [
+                    10.577135428368546,
+                    10.287617624566224
+                ],
+                [
+                    11.432018828830499,
+                    11.397464172112608
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 8.69862560268995,
+            "scoreError" : 2.850730402828029,
+            "scoreConfidence" : [
+                5.84789519986192,
+                11.549356005517978
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5.770652554442691,
+                "50.0" : 9.55560067282565,
+                "90.0" : 10.610404276107895,
+                "95.0" : 10.648561829452847,
+                "99.0" : 10.648561829452847,
+                "99.9" : 10.648561829452847,
+                "99.99" : 10.648561829452847,
+                "99.999" : 10.648561829452847,
+                "99.9999" : 10.648561829452847,
+                "100.0" : 10.648561829452847
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    10.648561829452847,
+                    10.266986296003333
+                ],
+                [
+                    10.212611163875144,
+                    9.078390005156265
+                ],
+                [
+                    10.032811340495034,
+                    6.596330980326417
+                ],
+                [
+                    6.265218422598946,
+                    10.141051281441365
+                ],
+                [
+                    7.973642153107459,
+                    5.770652554442691
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.5248786293018326,
+            "scoreError" : 0.17380994878025358,
+            "scoreConfidence" : [
+                1.351068680521579,
+                1.6986885780820862
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.4303491635625107,
+                "50.0" : 1.4914262953464568,
+                "90.0" : 1.7974093389527392,
+                "95.0" : 1.8189693047549174,
+                "99.0" : 1.8189693047549174,
+                "99.9" : 1.8189693047549174,
+                "99.99" : 1.8189693047549174,
+                "99.999" : 1.8189693047549174,
+                "99.9999" : 1.8189693047549174,
+                "100.0" : 1.8189693047549174
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.4598484244159358,
+                    1.4624449978364078
+                ],
+                [
+                    1.4303491635625107,
+                    1.4416282783013532
+                ],
+                [
+                    1.4885810066800946,
+                    1.6033696467331364
+                ],
+                [
+                    1.4942715840128191,
+                    1.5194815860617874
+                ],
+                [
+                    1.5298423006593602,
+                    1.8189693047549174
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.5171087604788753,
+            "scoreError" : 0.137741029417015,
+            "scoreConfidence" : [
+                1.3793677310618604,
+                1.6548497898958903
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.374140686846801,
+                "50.0" : 1.5061976180530554,
+                "90.0" : 1.6719182167808382,
+                "95.0" : 1.6755564936470366,
+                "99.0" : 1.6755564936470366,
+                "99.9" : 1.6755564936470366,
+                "99.99" : 1.6755564936470366,
+                "99.999" : 1.6755564936470366,
+                "99.9999" : 1.6755564936470366,
+                "100.0" : 1.6755564936470366
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.4550262673134087,
+                    1.4716255018554139
+                ],
+                [
+                    1.54464295541126,
+                    1.4655570993307343
+                ],
+                [
+                    1.6391737249850518,
+                    1.540769734250697
+                ],
+                [
+                    1.374140686846801,
+                    1.4580145430017348
+                ],
+                [
+                    1.5465805981466152,
+                    1.6755564936470366
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 5.522708358140621,
+            "scoreError" : 0.8619263972210056,
+            "scoreConfidence" : [
+                4.660781960919615,
+                6.384634755361627
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.557760347176983,
+                "50.0" : 5.562722302155111,
+                "90.0" : 6.1258120806090774,
+                "95.0" : 6.133386755058857,
+                "99.0" : 6.133386755058857,
+                "99.9" : 6.133386755058857,
+                "99.99" : 6.133386755058857,
+                "99.999" : 6.133386755058857,
+                "99.9999" : 6.133386755058857,
+                "100.0" : 6.133386755058857
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4.557760347176983,
+                    5.943804937625557
+                ],
+                [
+                    4.61878359597353,
+                    6.054253757090362
+                ],
+                [
+                    5.426317144240321,
+                    5.573249938573027
+                ],
+                [
+                    6.057640010561063,
+                    5.309692429369306
+                ],
+                [
+                    6.133386755058857,
+                    5.552194665737194
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 5.2034486056520395,
+            "scoreError" : 0.8396356557754167,
+            "scoreConfidence" : [
+                4.363812949876623,
+                6.043084261427456
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.2453851147076325,
+                "50.0" : 5.279500987356893,
+                "90.0" : 5.983987887291637,
+                "95.0" : 6.010975853830928,
+                "99.0" : 6.010975853830928,
+                "99.9" : 6.010975853830928,
+                "99.99" : 6.010975853830928,
+                "99.999" : 6.010975853830928,
+                "99.9999" : 6.010975853830928,
+                "100.0" : 6.010975853830928
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.243868701914112,
+                    5.741096188438025
+                ],
+                [
+                    5.315133272799674,
+                    5.184673423835484
+                ],
+                [
+                    5.546667181331337,
+                    5.371833327105703
+                ],
+                [
+                    5.021845325531628,
+                    6.010975853830928
+                ],
+                [
+                    4.353007667025872,
+                    4.2453851147076325
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.339924631382442,
+            "scoreError" : 0.04479747613250227,
+            "scoreConfidence" : [
+                1.2951271552499397,
+                1.3847221075149443
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.2974725063142822,
+                "50.0" : 1.3317294576636773,
+                "90.0" : 1.405961752460973,
+                "95.0" : 1.4112371767231358,
+                "99.0" : 1.4112371767231358,
+                "99.9" : 1.4112371767231358,
+                "99.99" : 1.4112371767231358,
+                "99.999" : 1.4112371767231358,
+                "99.9999" : 1.4112371767231358,
+                "100.0" : 1.4112371767231358
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.2974725063142822,
+                    1.3317783160699779
+                ],
+                [
+                    1.330023220063289,
+                    1.3217067595557936
+                ],
+                [
+                    1.3312044039027107,
+                    1.331680599257377
+                ],
+                [
+                    1.345338260664422,
+                    1.3584829341015097
+                ],
+                [
+                    1.3403221371719218,
+                    1.4112371767231358
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.2928807628469392,
+            "scoreError" : 0.09300984990006785,
+            "scoreConfidence" : [
+                1.1998709129468714,
+                1.385890612747007
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.23105772545197,
+                "50.0" : 1.2758400061388142,
+                "90.0" : 1.4029274412912995,
+                "95.0" : 1.4031941394477145,
+                "99.0" : 1.4031941394477145,
+                "99.9" : 1.4031941394477145,
+                "99.99" : 1.4031941394477145,
+                "99.999" : 1.4031941394477145,
+                "99.9999" : 1.4031941394477145,
+                "100.0" : 1.4031941394477145
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.23105772545197,
+                    1.2546742096555419
+                ],
+                [
+                    1.4031941394477145,
+                    1.291465109010951
+                ],
+                [
+                    1.2563893499415755,
+                    1.2967143565966486
+                ],
+                [
+                    1.2602149032666774,
+                    1.2927650207517143
+                ],
+                [
+                    1.2418056564630344,
+                    1.4005271578835636
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 4.5465162346852726,
+            "scoreError" : 0.44026456747307174,
+            "scoreConfidence" : [
+                4.1062516672122005,
+                4.986780802158345
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.079849518094677,
+                "50.0" : 4.5407630971316895,
+                "90.0" : 5.044907925189187,
+                "95.0" : 5.065693011936493,
+                "99.0" : 5.065693011936493,
+                "99.9" : 5.065693011936493,
+                "99.99" : 5.065693011936493,
+                "99.999" : 5.065693011936493,
+                "99.9999" : 5.065693011936493,
+                "100.0" : 5.065693011936493
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4.079849518094677,
+                    4.638800409435509
+                ],
+                [
+                    4.204652426820363,
+                    4.857842144463429
+                ],
+                [
+                    4.691995094785729,
+                    4.415265131211205
+                ],
+                [
+                    5.065693011936493,
+                    4.567625992390603
+                ],
+                [
+                    4.429538415841941,
+                    4.513900201872776
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 4.3364906992767995,
+            "scoreError" : 0.6361600103822059,
+            "scoreConfidence" : [
+                3.7003306888945935,
+                4.972650709659005
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.4850481939473967,
+                "50.0" : 4.4695550132572155,
+                "90.0" : 4.878136508784269,
+                "95.0" : 4.8966769796925815,
+                "99.0" : 4.8966769796925815,
+                "99.9" : 4.8966769796925815,
+                "99.99" : 4.8966769796925815,
+                "99.999" : 4.8966769796925815,
+                "99.9999" : 4.8966769796925815,
+                "100.0" : 4.8966769796925815
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4.8966769796925815,
+                    4.711272270609456
+                ],
+                [
+                    4.1235368739389076,
+                    4.476218983648658
+                ],
+                [
+                    4.644397477491868,
+                    4.5275343861681225
+                ],
+                [
+                    4.462891042865774,
+                    4.0115585617605545
+                ],
+                [
+                    3.4850481939473967,
+                    4.025772222644671
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.6148478717291934,
+            "scoreError" : 0.7446877881103863,
+            "scoreConfidence" : [
+                0.8701600836188071,
+                2.3595356598395796
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.1656347467176837,
+                "50.0" : 1.315218831808714,
+                "90.0" : 2.2482497486098527,
+                "95.0" : 2.2518752253276304,
+                "99.0" : 2.2518752253276304,
+                "99.9" : 2.2518752253276304,
+                "99.99" : 2.2518752253276304,
+                "99.999" : 2.2518752253276304,
+                "99.9999" : 2.2518752253276304,
+                "100.0" : 2.2518752253276304
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.21743252963011,
+                    1.1656347467176837
+                ],
+                [
+                    1.175409735645137,
+                    1.2585759163329866
+                ],
+                [
+                    1.231843853772198,
+                    1.3718617472844412
+                ],
+                [
+                    2.11681396227486,
+                    2.14341054215703
+                ],
+                [
+                    2.215620458149855,
+                    2.2518752253276304
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.8602219960318038,
+            "scoreError" : 0.39115633236720815,
+            "scoreConfidence" : [
+                1.4690656636645958,
+                2.251378328399012
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.4062697603214702,
+                "50.0" : 1.964762284880499,
+                "90.0" : 2.0888910844309247,
+                "95.0" : 2.0920717403135463,
+                "99.0" : 2.0920717403135463,
+                "99.9" : 2.0920717403135463,
+                "99.99" : 2.0920717403135463,
+                "99.999" : 2.0920717403135463,
+                "99.9999" : 2.0920717403135463,
+                "100.0" : 2.0920717403135463
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.7989008575828505,
+                    1.8112093511172844
+                ],
+                [
+                    2.0361709586331793,
+                    2.056975849771783
+                ],
+                [
+                    2.060265181487331,
+                    2.0920717403135463
+                ],
+                [
+                    1.4108316913295962,
+                    1.4062697603214702
+                ],
+                [
+                    1.9262588615098382,
+                    2.00326570825116
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 6.55685280713317,
+            "scoreError" : 1.521062607592989,
+            "scoreConfidence" : [
+                5.035790199540181,
+                8.07791541472616
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.722375583789737,
+                "50.0" : 6.866447622580452,
+                "90.0" : 7.070923082701012,
+                "95.0" : 7.07416375512963,
+                "99.0" : 7.07416375512963,
+                "99.9" : 7.07416375512963,
+                "99.99" : 7.07416375512963,
+                "99.999" : 7.07416375512963,
+                "99.9999" : 7.07416375512963,
+                "100.0" : 7.07416375512963
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6.8806062455577495,
+                    6.883754498127009
+                ],
+                [
+                    7.07416375512963,
+                    6.641330464691329
+                ],
+                [
+                    6.8522889996031555,
+                    7.007776420526916
+                ],
+                [
+                    7.041757030843449,
+                    6.763664651435106
+                ],
+                [
+                    3.722375583789737,
+                    6.700810421627625
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 5.301216427827282,
+            "scoreError" : 1.1289294375629328,
+            "scoreConfidence" : [
+                4.172286990264349,
+                6.430145865390215
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.542477847530514,
+                "50.0" : 5.271405080419002,
+                "90.0" : 6.102467200471888,
+                "95.0" : 6.106821842803865,
+                "99.0" : 6.106821842803865,
+                "99.9" : 6.106821842803865,
+                "99.99" : 6.106821842803865,
+                "99.999" : 6.106821842803865,
+                "99.9999" : 6.106821842803865,
+                "100.0" : 6.106821842803865
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.486593720751928,
+                    6.0632754194840945
+                ],
+                [
+                    5.223263080702427,
+                    3.542477847530514
+                ],
+                [
+                    5.018855922335199,
+                    5.100012670505602
+                ],
+                [
+                    6.106821842803865,
+                    5.319547080135576
+                ],
+                [
+                    5.137619833973987,
+                    6.0136968600496274
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.5860227730491998,
+            "scoreError" : 0.30379023334378413,
+            "scoreConfidence" : [
+                1.2822325397054157,
+                1.8898130063929839
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.0920813832707281,
+                "50.0" : 1.5994173400063993,
+                "90.0" : 1.791811943594711,
+                "95.0" : 1.7958322769866282,
+                "99.0" : 1.7958322769866282,
+                "99.9" : 1.7958322769866282,
+                "99.99" : 1.7958322769866282,
+                "99.999" : 1.7958322769866282,
+                "99.9999" : 1.7958322769866282,
+                "100.0" : 1.7958322769866282
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.0920813832707281,
+                    1.7958322769866282
+                ],
+                [
+                    1.5932804092121582,
+                    1.7556289430674565
+                ],
+                [
+                    1.52308380137161,
+                    1.715720305433191
+                ],
+                [
+                    1.7223091241348114,
+                    1.5188029344158969
+                ],
+                [
+                    1.6055542708006407,
+                    1.537934281798876
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1.1599289382358806,
+            "scoreError" : 0.22612698801505537,
+            "scoreConfidence" : [
+                0.9338019502208252,
+                1.386055926250936
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.9825177739479852,
+                "50.0" : 1.077451286867186,
+                "90.0" : 1.4022996800130283,
+                "95.0" : 1.410049661563603,
+                "99.0" : 1.410049661563603,
+                "99.9" : 1.410049661563603,
+                "99.99" : 1.410049661563603,
+                "99.999" : 1.410049661563603,
+                "99.9999" : 1.410049661563603,
+                "100.0" : 1.410049661563603
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.0763690618814368,
+                    1.0651796680221743
+                ],
+                [
+                    1.070265094440761,
+                    1.0249632943993892
+                ],
+                [
+                    0.9825177739479852,
+                    1.0785335118529353
+                ],
+                [
+                    1.3325498460578566,
+                    1.410049661563603
+                ],
+                [
+                    1.286618213721185,
+                    1.2722432564714805
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 6.027975265926844,
+            "scoreError" : 0.35112510651249884,
+            "scoreConfidence" : [
+                5.676850159414345,
+                6.379100372439343
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5.644475565004447,
+                "50.0" : 6.0550531061834345,
+                "90.0" : 6.354428372291083,
+                "95.0" : 6.3564761807971415,
+                "99.0" : 6.3564761807971415,
+                "99.9" : 6.3564761807971415,
+                "99.99" : 6.3564761807971415,
+                "99.999" : 6.3564761807971415,
+                "99.9999" : 6.3564761807971415,
+                "100.0" : 6.3564761807971415
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.97900037940529,
+                    6.335998095736555
+                ],
+                [
+                    5.8384965728694365,
+                    6.025064868648608
+                ],
+                [
+                    5.644475565004447,
+                    6.0918640144917955
+                ],
+                [
+                    6.3564761807971415,
+                    5.7602399161218445
+                ],
+                [
+                    6.163095722475063,
+                    6.08504134371826
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 4.627633904221076,
+            "scoreError" : 0.7331295121689335,
+            "scoreConfidence" : [
+                3.8945043920521427,
+                5.36076341639001
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.821230691559164,
+                "50.0" : 4.559809491822633,
+                "90.0" : 5.374791544313783,
+                "95.0" : 5.413207484888207,
+                "99.0" : 5.413207484888207,
+                "99.9" : 5.413207484888207,
+                "99.99" : 5.413207484888207,
+                "99.999" : 5.413207484888207,
+                "99.9999" : 5.413207484888207,
+                "100.0" : 5.413207484888207
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.0290480791439665,
+                    5.016734041159645
+                ],
+                [
+                    4.268812624543834,
+                    4.620526293998292
+                ],
+                [
+                    4.499092689646973,
+                    4.426721770032627
+                ],
+                [
+                    4.173392584090863,
+                    5.007572783147188
+                ],
+                [
+                    3.821230691559164,
+                    5.413207484888207
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530034012-4a34ace.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530034012-4a34ace.json
new file mode 100644
index 0000000000000000000000000000000000000000..a156c7da7c271207631634dd5169d6fd2187962f
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530034012-4a34ace.json
@@ -0,0 +1,1540 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 104.7011087104809,
+            "scoreError" : 3.7423761914164815,
+            "scoreConfidence" : [
+                100.95873251906441,
+                108.44348490189738
+            ],
+            "scorePercentiles" : {
+                "0.0" : 100.88954035368401,
+                "50.0" : 105.882966608304,
+                "90.0" : 107.34104031428244,
+                "95.0" : 107.42153923641156,
+                "99.0" : 107.42153923641156,
+                "99.9" : 107.42153923641156,
+                "99.99" : 107.42153923641156,
+                "99.999" : 107.42153923641156,
+                "99.9999" : 107.42153923641156,
+                "100.0" : 107.42153923641156
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    101.44416419113956,
+                    100.88954035368401
+                ],
+                [
+                    106.13510124450892,
+                    106.0740992343222
+                ],
+                [
+                    106.61655001512035,
+                    107.42153923641156
+                ],
+                [
+                    106.02129553873436,
+                    105.38379400794531
+                ],
+                [
+                    101.28036560506887,
+                    105.74463767787363
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 83.24147104170652,
+            "scoreError" : 4.554176259789341,
+            "scoreConfidence" : [
+                78.68729478191717,
+                87.79564730149586
+            ],
+            "scorePercentiles" : {
+                "0.0" : 78.55079784872044,
+                "50.0" : 83.91370138812889,
+                "90.0" : 86.58980075188902,
+                "95.0" : 86.64764393677703,
+                "99.0" : 86.64764393677703,
+                "99.9" : 86.64764393677703,
+                "99.99" : 86.64764393677703,
+                "99.999" : 86.64764393677703,
+                "99.9999" : 86.64764393677703,
+                "100.0" : 86.64764393677703
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    78.55079784872044,
+                    79.51303312164389
+                ],
+                [
+                    79.74896736709258,
+                    82.565057137537
+                ],
+                [
+                    83.75757469808822,
+                    86.06921208789687
+                ],
+                [
+                    85.80992118205909,
+                    86.64764393677703
+                ],
+                [
+                    84.06982807816954,
+                    85.6826749590806
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 224.85663488295086,
+            "scoreError" : 6.028591856716745,
+            "scoreConfidence" : [
+                218.8280430262341,
+                230.88522673966762
+            ],
+            "scorePercentiles" : {
+                "0.0" : 218.36459658547616,
+                "50.0" : 226.62847510687035,
+                "90.0" : 229.33631048633563,
+                "95.0" : 229.38790988428408,
+                "99.0" : 229.38790988428408,
+                "99.9" : 229.38790988428408,
+                "99.99" : 229.38790988428408,
+                "99.999" : 229.38790988428408,
+                "99.9999" : 229.38790988428408,
+                "100.0" : 229.38790988428408
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    226.63656062437886,
+                    226.7640288893431
+                ],
+                [
+                    219.78519797409433,
+                    219.94064929540707
+                ],
+                [
+                    229.38790988428408,
+                    228.87191590479955
+                ],
+                [
+                    218.36459658547616,
+                    226.62038958936185
+                ],
+                [
+                    225.25506764156407,
+                    226.94003244079963
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 185.97813137612835,
+            "scoreError" : 19.18711039638145,
+            "scoreConfidence" : [
+                166.7910209797469,
+                205.1652417725098
+            ],
+            "scorePercentiles" : {
+                "0.0" : 150.78714247391278,
+                "50.0" : 190.58386393172543,
+                "90.0" : 192.086769900789,
+                "95.0" : 192.1063873271388,
+                "99.0" : 192.1063873271388,
+                "99.9" : 192.1063873271388,
+                "99.99" : 192.1063873271388,
+                "99.999" : 192.1063873271388,
+                "99.9999" : 192.1063873271388,
+                "100.0" : 192.1063873271388
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    192.1063873271388,
+                    191.2646953076457
+                ],
+                [
+                    191.9102130636406,
+                    191.31943951811584
+                ],
+                [
+                    191.15971111652445,
+                    189.79898769959755
+                ],
+                [
+                    190.0080167469264,
+                    189.18588301321768
+                ],
+                [
+                    150.78714247391278,
+                    182.2408374945636
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 42.32871200796817,
+            "scoreError" : 17.81179967979969,
+            "scoreConfidence" : [
+                24.51691232816848,
+                60.14051168776786
+            ],
+            "scorePercentiles" : {
+                "0.0" : 24.31185136900342,
+                "50.0" : 44.95980125392232,
+                "90.0" : 57.23931162819738,
+                "95.0" : 57.50164238101311,
+                "99.0" : 57.50164238101311,
+                "99.9" : 57.50164238101311,
+                "99.99" : 57.50164238101311,
+                "99.999" : 57.50164238101311,
+                "99.9999" : 57.50164238101311,
+                "100.0" : 57.50164238101311
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    50.88292893960396,
+                    49.21247544702237
+                ],
+                [
+                    54.87833485285576,
+                    39.086245900476555
+                ],
+                [
+                    24.31185136900342,
+                    29.59827317841755
+                ],
+                [
+                    48.24622995596074,
+                    27.895765503444338
+                ],
+                [
+                    41.6733725518839,
+                    57.50164238101311
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 49.43683507637419,
+            "scoreError" : 8.920714718785595,
+            "scoreConfidence" : [
+                40.51612035758859,
+                58.357549795159784
+            ],
+            "scorePercentiles" : {
+                "0.0" : 35.51213429538773,
+                "50.0" : 51.44721321118388,
+                "90.0" : 53.52767867950377,
+                "95.0" : 53.54939167959375,
+                "99.0" : 53.54939167959375,
+                "99.9" : 53.54939167959375,
+                "99.99" : 53.54939167959375,
+                "99.999" : 53.54939167959375,
+                "99.9999" : 53.54939167959375,
+                "100.0" : 53.54939167959375
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    50.90779977793198,
+                    53.54939167959375
+                ],
+                [
+                    42.09399366036272,
+                    50.619658203534115
+                ],
+                [
+                    53.332261678693996,
+                    52.79084790579731
+                ],
+                [
+                    51.677694661744894,
+                    35.51213429538773
+                ],
+                [
+                    51.216731760622864,
+                    52.66783714007247
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 93.05935428685841,
+            "scoreError" : 38.50167771555803,
+            "scoreConfidence" : [
+                54.55767657130038,
+                131.56103200241643
+            ],
+            "scorePercentiles" : {
+                "0.0" : 57.93386263113723,
+                "50.0" : 94.70200646800936,
+                "90.0" : 123.68899208986174,
+                "95.0" : 124.27879912212194,
+                "99.0" : 124.27879912212194,
+                "99.9" : 124.27879912212194,
+                "99.99" : 124.27879912212194,
+                "99.999" : 124.27879912212194,
+                "99.9999" : 124.27879912212194,
+                "100.0" : 124.27879912212194
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    78.1520920575015,
+                    124.27879912212194
+                ],
+                [
+                    116.16801584966383,
+                    85.16604351364255
+                ],
+                [
+                    57.93386263113723,
+                    73.94587810450388
+                ],
+                [
+                    118.38072879951989,
+                    104.23796942237615
+                ],
+                [
+                    58.02829412796092,
+                    114.30185924015635
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 106.43974868838147,
+            "scoreError" : 23.394593630033878,
+            "scoreConfidence" : [
+                83.04515505834759,
+                129.83434231841534
+            ],
+            "scorePercentiles" : {
+                "0.0" : 74.25661377870344,
+                "50.0" : 114.07122657351444,
+                "90.0" : 120.05072448321653,
+                "95.0" : 120.2156072166627,
+                "99.0" : 120.2156072166627,
+                "99.9" : 120.2156072166627,
+                "99.99" : 120.2156072166627,
+                "99.999" : 120.2156072166627,
+                "99.9999" : 120.2156072166627,
+                "100.0" : 120.2156072166627
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    118.5667798822009,
+                    120.2156072166627
+                ],
+                [
+                    117.79734487632652,
+                    117.82734911263601
+                ],
+                [
+                    114.96353532253951,
+                    90.04572984768308
+                ],
+                [
+                    95.661695758385,
+                    113.17891782448936
+                ],
+                [
+                    101.88391326418818,
+                    74.25661377870344
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 21.38813297153077,
+            "scoreError" : 7.568160681308476,
+            "scoreConfidence" : [
+                13.819972290222292,
+                28.956293652839246
+            ],
+            "scorePercentiles" : {
+                "0.0" : 13.58606924078156,
+                "50.0" : 23.914049859153785,
+                "90.0" : 26.255547300671388,
+                "95.0" : 26.302995416083153,
+                "99.0" : 26.302995416083153,
+                "99.9" : 26.302995416083153,
+                "99.99" : 26.302995416083153,
+                "99.999" : 26.302995416083153,
+                "99.9999" : 26.302995416083153,
+                "100.0" : 26.302995416083153
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    13.811099062641686,
+                    17.941382206144834
+                ],
+                [
+                    13.58606924078156,
+                    18.262562513874666
+                ],
+                [
+                    25.337148269403375,
+                    25.828514261965516
+                ],
+                [
+                    24.983459026105365,
+                    26.302995416083153
+                ],
+                [
+                    24.38372946115993,
+                    23.44437025714764
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 19.622156265483028,
+            "scoreError" : 4.987868775642429,
+            "scoreConfidence" : [
+                14.634287489840599,
+                24.610025041125457
+            ],
+            "scorePercentiles" : {
+                "0.0" : 14.93138023958173,
+                "50.0" : 20.246976801211183,
+                "90.0" : 24.27346949430521,
+                "95.0" : 24.427045698424894,
+                "99.0" : 24.427045698424894,
+                "99.9" : 24.427045698424894,
+                "99.99" : 24.427045698424894,
+                "99.999" : 24.427045698424894,
+                "99.9999" : 24.427045698424894,
+                "100.0" : 24.427045698424894
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    22.89128365722806,
+                    24.427045698424894
+                ],
+                [
+                    19.844680591528572,
+                    20.649273010893797
+                ],
+                [
+                    15.873171889829425,
+                    18.1962620552237
+                ],
+                [
+                    14.93138023958173,
+                    15.739929179229202
+                ],
+                [
+                    21.57751635813752,
+                    22.091019974753376
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 74.26454714978158,
+            "scoreError" : 15.183553853622525,
+            "scoreConfidence" : [
+                59.08099329615905,
+                89.4481010034041
+            ],
+            "scorePercentiles" : {
+                "0.0" : 51.674450673992695,
+                "50.0" : 78.40778804239744,
+                "90.0" : 81.63045236364282,
+                "95.0" : 81.6569849685138,
+                "99.0" : 81.6569849685138,
+                "99.9" : 81.6569849685138,
+                "99.99" : 81.6569849685138,
+                "99.999" : 81.6569849685138,
+                "99.9999" : 81.6569849685138,
+                "100.0" : 81.6569849685138
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    80.39172776653989,
+                    81.6569849685138
+                ],
+                [
+                    81.39165891980406,
+                    81.14393282427004
+                ],
+                [
+                    78.82191071568474,
+                    51.674450673992695
+                ],
+                [
+                    76.76071269165135,
+                    61.78549587027839
+                ],
+                [
+                    71.02493169797056,
+                    77.99366536911016
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 60.853839105773126,
+            "scoreError" : 22.12483509675573,
+            "scoreConfidence" : [
+                38.729004009017395,
+                82.97867420252885
+            ],
+            "scorePercentiles" : {
+                "0.0" : 38.26110300104155,
+                "50.0" : 68.39288235960099,
+                "90.0" : 74.81830147083643,
+                "95.0" : 74.92553079057613,
+                "99.0" : 74.92553079057613,
+                "99.9" : 74.92553079057613,
+                "99.99" : 74.92553079057613,
+                "99.999" : 74.92553079057613,
+                "99.9999" : 74.92553079057613,
+                "100.0" : 74.92553079057613
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    50.71748836211502,
+                    48.6232060078198
+                ],
+                [
+                    40.686946926075855,
+                    38.26110300104155
+                ],
+                [
+                    66.04801192655289,
+                    71.26506936711934
+                ],
+                [
+                    73.42004429060252,
+                    70.73775279264909
+                ],
+                [
+                    73.85323759317912,
+                    74.92553079057613
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 10.639470911792866,
+            "scoreError" : 3.088289738509205,
+            "scoreConfidence" : [
+                7.551181173283661,
+                13.727760650302072
+            ],
+            "scorePercentiles" : {
+                "0.0" : 6.993385262401218,
+                "50.0" : 10.729512830841635,
+                "90.0" : 13.677033176490005,
+                "95.0" : 13.81168335274697,
+                "99.0" : 13.81168335274697,
+                "99.9" : 13.81168335274697,
+                "99.99" : 13.81168335274697,
+                "99.999" : 13.81168335274697,
+                "99.9999" : 13.81168335274697,
+                "100.0" : 13.81168335274697
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11.409030310947868,
+                    10.525541923680748
+                ],
+                [
+                    8.482542765083899,
+                    10.631500751171094
+                ],
+                [
+                    10.827524910512178,
+                    8.955246572761785
+                ],
+                [
+                    6.993385262401218,
+                    12.293071678445585
+                ],
+                [
+                    12.465181590177318,
+                    13.81168335274697
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 10.456715599876055,
+            "scoreError" : 1.9028778570206695,
+            "scoreConfidence" : [
+                8.553837742855386,
+                12.359593456896725
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7.864941352835479,
+                "50.0" : 10.939755490790464,
+                "90.0" : 11.714816996362217,
+                "95.0" : 11.753691593959166,
+                "99.0" : 11.753691593959166,
+                "99.9" : 11.753691593959166,
+                "99.99" : 11.753691593959166,
+                "99.999" : 11.753691593959166,
+                "99.9999" : 11.753691593959166,
+                "100.0" : 11.753691593959166
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    10.952660748060072,
+                    11.364945617989674
+                ],
+                [
+                    10.769860190657486,
+                    7.864941352835479
+                ],
+                [
+                    8.47649876887637,
+                    10.500627774438772
+                ],
+                [
+                    11.753691593959166,
+                    10.96587977575521
+                ],
+                [
+                    10.991199942667485,
+                    10.926850233520856
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 31.969197873061233,
+            "scoreError" : 1.6677155997623578,
+            "scoreConfidence" : [
+                30.301482273298877,
+                33.63691347282359
+            ],
+            "scorePercentiles" : {
+                "0.0" : 30.056920293183016,
+                "50.0" : 32.21477295504585,
+                "90.0" : 33.33626563901803,
+                "95.0" : 33.34763314031518,
+                "99.0" : 33.34763314031518,
+                "99.9" : 33.34763314031518,
+                "99.99" : 33.34763314031518,
+                "99.999" : 33.34763314031518,
+                "99.9999" : 33.34763314031518,
+                "100.0" : 33.34763314031518
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    32.419857477883355,
+                    30.91815408954798
+                ],
+                [
+                    32.64844059293107,
+                    30.056920293183016
+                ],
+                [
+                    32.760711407669184,
+                    32.00968843220835
+                ],
+                [
+                    33.34763314031518,
+                    30.990574339821343
+                ],
+                [
+                    31.30604082970921,
+                    33.23395812734369
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 28.20474698855128,
+            "scoreError" : 3.205925122939175,
+            "scoreConfidence" : [
+                24.998821865612104,
+                31.410672111490452
+            ],
+            "scorePercentiles" : {
+                "0.0" : 22.814486172933808,
+                "50.0" : 28.542282298254698,
+                "90.0" : 30.383993715660303,
+                "95.0" : 30.416778077286274,
+                "99.0" : 30.416778077286274,
+                "99.9" : 30.416778077286274,
+                "99.99" : 30.416778077286274,
+                "99.999" : 30.416778077286274,
+                "99.9999" : 30.416778077286274,
+                "100.0" : 30.416778077286274
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    27.921304255505774,
+                    28.829394237761125
+                ],
+                [
+                    28.255170358748266,
+                    27.439633953256468
+                ],
+                [
+                    22.814486172933808,
+                    30.416778077286274
+                ],
+                [
+                    29.197701087165743,
+                    30.088934461026557
+                ],
+                [
+                    29.133379644045366,
+                    27.950687637783403
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 8.203667020100887,
+            "scoreError" : 0.5884166824514152,
+            "scoreConfidence" : [
+                7.6152503376494725,
+                8.792083702552302
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7.360515269435855,
+                "50.0" : 8.338306550242326,
+                "90.0" : 8.592865318321673,
+                "95.0" : 8.594258152249708,
+                "99.0" : 8.594258152249708,
+                "99.9" : 8.594258152249708,
+                "99.99" : 8.594258152249708,
+                "99.999" : 8.594258152249708,
+                "99.9999" : 8.594258152249708,
+                "100.0" : 8.594258152249708
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    7.898286271236425,
+                    8.076259860587378
+                ],
+                [
+                    8.594258152249708,
+                    7.360515269435855
+                ],
+                [
+                    7.930906266852778,
+                    8.459445698375168
+                ],
+                [
+                    8.395991372547563,
+                    8.46005576881755
+                ],
+                [
+                    8.28062172793709,
+                    8.580329812969351
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 5.763911744882545,
+            "scoreError" : 1.5043550652048963,
+            "scoreConfidence" : [
+                4.259556679677649,
+                7.268266810087441
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.429803153294701,
+                "50.0" : 5.873212023012337,
+                "90.0" : 6.966232203393936,
+                "95.0" : 6.966942875753675,
+                "99.0" : 6.966942875753675,
+                "99.9" : 6.966942875753675,
+                "99.99" : 6.966942875753675,
+                "99.999" : 6.966942875753675,
+                "99.9999" : 6.966942875753675,
+                "100.0" : 6.966942875753675
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6.9598361521562815,
+                    5.064587996073755
+                ],
+                [
+                    4.946417436382128,
+                    6.4726554457373355
+                ],
+                [
+                    4.575986438005888,
+                    6.966942875753675
+                ],
+                [
+                    5.302652006486136,
+                    6.476463905397021
+                ],
+                [
+                    4.429803153294701,
+                    6.443772039538537
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 18.801957044765565,
+            "scoreError" : 1.1768612520141342,
+            "scoreConfidence" : [
+                17.62509579275143,
+                19.9788182967797
+            ],
+            "scorePercentiles" : {
+                "0.0" : 17.140027541125153,
+                "50.0" : 18.882789178775543,
+                "90.0" : 19.570854300260155,
+                "95.0" : 19.571867386585367,
+                "99.0" : 19.571867386585367,
+                "99.9" : 19.571867386585367,
+                "99.99" : 19.571867386585367,
+                "99.999" : 19.571867386585367,
+                "99.9999" : 19.571867386585367,
+                "100.0" : 19.571867386585367
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    19.42278989517915,
+                    18.714642574717033
+                ],
+                [
+                    17.140027541125153,
+                    18.975405204474427
+                ],
+                [
+                    19.571867386585367,
+                    18.601652618233114
+                ],
+                [
+                    19.325002434758275,
+                    17.916273116173247
+                ],
+                [
+                    18.79017315307666,
+                    19.561736523333238
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 14.422596923700885,
+            "scoreError" : 3.137380248280707,
+            "scoreConfidence" : [
+                11.285216675420179,
+                17.559977171981593
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11.383432415016602,
+                "50.0" : 14.75604222776579,
+                "90.0" : 17.026921267980402,
+                "95.0" : 17.04323687245957,
+                "99.0" : 17.04323687245957,
+                "99.9" : 17.04323687245957,
+                "99.99" : 17.04323687245957,
+                "99.999" : 17.04323687245957,
+                "99.9999" : 17.04323687245957,
+                "100.0" : 17.04323687245957
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    15.99247577392052,
+                    15.73714255929863
+                ],
+                [
+                    14.611301908761718,
+                    11.383432415016602
+                ],
+                [
+                    17.04323687245957,
+                    11.547055656321096
+                ],
+                [
+                    16.880080827667893,
+                    12.746244623874427
+                ],
+                [
+                    14.90078254676986,
+                    13.384216052918532
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 5.211916463832229,
+            "scoreError" : 0.2878694029810478,
+            "scoreConfidence" : [
+                4.924047060851182,
+                5.499785866813276
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.853473806617481,
+                "50.0" : 5.1995205258016615,
+                "90.0" : 5.471484334583664,
+                "95.0" : 5.474007785023183,
+                "99.0" : 5.474007785023183,
+                "99.9" : 5.474007785023183,
+                "99.99" : 5.474007785023183,
+                "99.999" : 5.474007785023183,
+                "99.9999" : 5.474007785023183,
+                "100.0" : 5.474007785023183
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.354418757268614,
+                    5.194312458985413
+                ],
+                [
+                    5.131591504423528,
+                    5.474007785023183
+                ],
+                [
+                    5.448773280627998,
+                    5.305461276121833
+                ],
+                [
+                    4.853473806617481,
+                    5.20472859261791
+                ],
+                [
+                    5.088690394104383,
+                    5.0637067825319475
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 3.629304527595204,
+            "scoreError" : 1.132757347058442,
+            "scoreConfidence" : [
+                2.496547180536762,
+                4.762061874653646
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2.915559309920613,
+                "50.0" : 3.364772640443146,
+                "90.0" : 4.990571914894521,
+                "95.0" : 5.0300565133233714,
+                "99.0" : 5.0300565133233714,
+                "99.9" : 5.0300565133233714,
+                "99.99" : 5.0300565133233714,
+                "99.999" : 5.0300565133233714,
+                "99.9999" : 5.0300565133233714,
+                "100.0" : 5.0300565133233714
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.3260879136804467,
+                    3.403457367205845
+                ],
+                [
+                    3.2699226571259983,
+                    4.635210529034868
+                ],
+                [
+                    3.6596373110352705,
+                    2.915559309920613
+                ],
+                [
+                    5.0300565133233714,
+                    4.193119686256376
+                ],
+                [
+                    2.92811952830071,
+                    2.9318744600685447
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 11.919790589132187,
+            "scoreError" : 2.3040130587387164,
+            "scoreConfidence" : [
+                9.615777530393471,
+                14.223803647870904
+            ],
+            "scorePercentiles" : {
+                "0.0" : 9.277005723195174,
+                "50.0" : 12.138283342462927,
+                "90.0" : 13.607971145445818,
+                "95.0" : 13.623840845084036,
+                "99.0" : 13.623840845084036,
+                "99.9" : 13.623840845084036,
+                "99.99" : 13.623840845084036,
+                "99.999" : 13.623840845084036,
+                "99.9999" : 13.623840845084036,
+                "100.0" : 13.623840845084036
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    12.70028229987442,
+                    13.465143848701851
+                ],
+                [
+                    9.277005723195174,
+                    11.576284385051434
+                ],
+                [
+                    10.71207001794642,
+                    13.3336465184469
+                ],
+                [
+                    12.93772688522609,
+                    13.623840845084036
+                ],
+                [
+                    10.140269780101741,
+                    11.431635587693814
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 2,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 10.814951479430821,
+            "scoreError" : 1.6082721225126038,
+            "scoreConfidence" : [
+                9.206679356918217,
+                12.423223601943425
+            ],
+            "scorePercentiles" : {
+                "0.0" : 8.09518224087975,
+                "50.0" : 11.009162887282663,
+                "90.0" : 11.986930428051336,
+                "95.0" : 12.040319596025027,
+                "99.0" : 12.040319596025027,
+                "99.9" : 12.040319596025027,
+                "99.99" : 12.040319596025027,
+                "99.999" : 12.040319596025027,
+                "99.9999" : 12.040319596025027,
+                "100.0" : 12.040319596025027
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11.409715740306465,
+                    10.62887792068522
+                ],
+                [
+                    11.220771101438357,
+                    12.040319596025027
+                ],
+                [
+                    10.549688373453217,
+                    8.09518224087975
+                ],
+                [
+                    10.797554673126971,
+                    11.506427916288116
+                ],
+                [
+                    11.221808921245392,
+                    10.679168310859689
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530090015-39e5e3b02.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530090015-39e5e3b02.json
new file mode 100644
index 0000000000000000000000000000000000000000..51e87eb6534dffee27aa39b6e1715157afc2df0c
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530090015-39e5e3b02.json
@@ -0,0 +1,2764 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K08",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1097.4100006265485,
+            "scoreError" : 18.746172610480603,
+            "scoreConfidence" : [
+                1078.6638280160678,
+                1116.1561732370292
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1070.9852942694877,
+                "50.0" : 1097.1172671563727,
+                "90.0" : 1117.9515997728565,
+                "95.0" : 1118.5004689143204,
+                "99.0" : 1118.5004689143204,
+                "99.9" : 1118.5004689143204,
+                "99.99" : 1118.5004689143204,
+                "99.999" : 1118.5004689143204,
+                "99.9999" : 1118.5004689143204,
+                "100.0" : 1118.5004689143204
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1113.7826718183462,
+                    1113.2646305679596,
+                    1114.9188538486044
+                ],
+                [
+                    1084.917793753047,
+                    1084.299986018495,
+                    1079.464182047453
+                ],
+                [
+                    1070.9852942694877,
+                    1075.1616840185263,
+                    1078.1363993743855
+                ],
+                [
+                    1096.1524577709054,
+                    1097.1172671563727,
+                    1099.277463166022
+                ],
+                [
+                    1117.585169662422,
+                    1118.5004689143204,
+                    1117.5856870118807
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K13",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1034.2552716486327,
+            "scoreError" : 27.35169469851862,
+            "scoreConfidence" : [
+                1006.9035769501141,
+                1061.6069663471515
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1005.3538384051334,
+                "50.0" : 1022.6534180179567,
+                "90.0" : 1067.0820977406106,
+                "95.0" : 1068.458879600175,
+                "99.0" : 1068.458879600175,
+                "99.9" : 1068.458879600175,
+                "99.99" : 1068.458879600175,
+                "99.999" : 1068.458879600175,
+                "99.9999" : 1068.458879600175,
+                "100.0" : 1068.458879600175
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1066.1642431675677,
+                    1063.3273758617393,
+                    1068.458879600175
+                ],
+                [
+                    1062.2765573564798,
+                    1061.573347389188,
+                    1061.5603793273897
+                ],
+                [
+                    1010.1720467924506,
+                    1015.693698660016,
+                    1005.3538384051334
+                ],
+                [
+                    1015.2201321834501,
+                    1025.303876739694,
+                    1022.6534180179567
+                ],
+                [
+                    1015.886233836108,
+                    1007.6578509270264,
+                    1012.5271964651157
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K208",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 502.66124243720077,
+            "scoreError" : 10.614974423466029,
+            "scoreConfidence" : [
+                492.04626801373473,
+                513.2762168606668
+            ],
+            "scorePercentiles" : {
+                "0.0" : 489.5148473340847,
+                "50.0" : 500.4178505896733,
+                "90.0" : 518.1309761542769,
+                "95.0" : 520.3806131370512,
+                "99.0" : 520.3806131370512,
+                "99.9" : 520.3806131370512,
+                "99.99" : 520.3806131370512,
+                "99.999" : 520.3806131370512,
+                "99.9999" : 520.3806131370512,
+                "100.0" : 520.3806131370512
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    510.77485406637885,
+                    505.81162738513854,
+                    509.07757893513616
+                ],
+                [
+                    493.85353690685673,
+                    504.07730401905195,
+                    500.4178505896733
+                ],
+                [
+                    493.85940376784185,
+                    489.5148473340847,
+                    493.74139475743834
+                ],
+                [
+                    516.6312181657607,
+                    515.8472703403316,
+                    520.3806131370512
+                ],
+                [
+                    495.2043172055357,
+                    493.9980281156843,
+                    496.7287918320494
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K213",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 462.0258729311534,
+            "scoreError" : 7.877473468365682,
+            "scoreConfidence" : [
+                454.1483994627877,
+                469.9033463995191
+            ],
+            "scorePercentiles" : {
+                "0.0" : 449.2511741938684,
+                "50.0" : 460.48813319259403,
+                "90.0" : 474.1827113566221,
+                "95.0" : 476.19767792691187,
+                "99.0" : 476.19767792691187,
+                "99.9" : 476.19767792691187,
+                "99.99" : 476.19767792691187,
+                "99.999" : 476.19767792691187,
+                "99.9999" : 476.19767792691187,
+                "100.0" : 476.19767792691187
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    464.9046948760132,
+                    458.8498608215445,
+                    460.6953931107954
+                ],
+                [
+                    460.48813319259403,
+                    449.2511741938684,
+                    459.6366661583392
+                ],
+                [
+                    472.8394003097623,
+                    470.8250313809336,
+                    476.19767792691187
+                ],
+                [
+                    456.7541738807125,
+                    453.99886375051915,
+                    456.1469952817119
+                ],
+                [
+                    466.8236790826741,
+                    458.0855305153289,
+                    464.89081948559203
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN060K08",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1076.5566631329848,
+            "scoreError" : 22.991060050267603,
+            "scoreConfidence" : [
+                1053.5656030827172,
+                1099.5477231832524
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1045.0955162635062,
+                "50.0" : 1075.3019088738981,
+                "90.0" : 1113.2633951944288,
+                "95.0" : 1113.6691955910264,
+                "99.0" : 1113.6691955910264,
+                "99.9" : 1113.6691955910264,
+                "99.99" : 1113.6691955910264,
+                "99.999" : 1113.6691955910264,
+                "99.9999" : 1113.6691955910264,
+                "100.0" : 1113.6691955910264
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1112.992861596697,
+                    1113.6691955910264,
+                    1111.3041072898486
+                ],
+                [
+                    1045.0955162635062,
+                    1051.3772405221043,
+                    1048.4291300952746
+                ],
+                [
+                    1076.000070587678,
+                    1076.0625748128045,
+                    1071.5587553529267
+                ],
+                [
+                    1071.6859312258673,
+                    1068.3009730999265,
+                    1071.4440678427177
+                ],
+                [
+                    1075.3019088738981,
+                    1077.0246306759236,
+                    1078.1029831645724
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN060K208",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 496.1030631646705,
+            "scoreError" : 3.512699094351389,
+            "scoreConfidence" : [
+                492.59036407031914,
+                499.6157622590219
+            ],
+            "scorePercentiles" : {
+                "0.0" : 491.43330033311906,
+                "50.0" : 494.94021411353697,
+                "90.0" : 501.88168312327156,
+                "95.0" : 502.1685014930431,
+                "99.0" : 502.1685014930431,
+                "99.9" : 502.1685014930431,
+                "99.99" : 502.1685014930431,
+                "99.999" : 502.1685014930431,
+                "99.9999" : 502.1685014930431,
+                "100.0" : 502.1685014930431
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    494.622303651207,
+                    495.90058307689736,
+                    492.580756602375
+                ],
+                [
+                    494.1116037743665,
+                    495.01071450868614,
+                    494.94021411353697
+                ],
+                [
+                    501.69047087675716,
+                    494.5425609152021,
+                    494.7138223319291
+                ],
+                [
+                    497.9790836860548,
+                    493.42693781498247,
+                    502.1685014930431
+                ],
+                [
+                    497.5099091980131,
+                    500.915185093889,
+                    491.43330033311906
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K08",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 1006.1274789066429,
+            "scoreError" : 8.632240073484734,
+            "scoreConfidence" : [
+                997.4952388331582,
+                1014.7597189801276
+            ],
+            "scorePercentiles" : {
+                "0.0" : 994.5489507073092,
+                "50.0" : 1008.2208529988354,
+                "90.0" : 1016.7314568936902,
+                "95.0" : 1017.9323197373135,
+                "99.0" : 1017.9323197373135,
+                "99.9" : 1017.9323197373135,
+                "99.99" : 1017.9323197373135,
+                "99.999" : 1017.9323197373135,
+                "99.9999" : 1017.9323197373135,
+                "100.0" : 1017.9323197373135
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1011.4325473447026,
+                    1012.3943441117173,
+                    1012.0558392628138
+                ],
+                [
+                    999.2841855514707,
+                    994.5489507073092,
+                    996.9796561999148
+                ],
+                [
+                    997.8109211825629,
+                    999.1303362912394,
+                    995.8581509199988
+                ],
+                [
+                    1008.7552140583837,
+                    1006.7978351857138,
+                    1008.2208529988354
+                ],
+                [
+                    1015.930881664608,
+                    1017.9323197373135,
+                    1014.7801483830583
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K10",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 970.6587710396896,
+            "scoreError" : 16.366794132058363,
+            "scoreConfidence" : [
+                954.2919769076312,
+                987.0255651717479
+            ],
+            "scorePercentiles" : {
+                "0.0" : 952.0599711325602,
+                "50.0" : 975.4598238934245,
+                "90.0" : 993.2203947074937,
+                "95.0" : 993.9927256858603,
+                "99.0" : 993.9927256858603,
+                "99.9" : 993.9927256858603,
+                "99.99" : 993.9927256858603,
+                "99.999" : 993.9927256858603,
+                "99.9999" : 993.9927256858603,
+                "100.0" : 993.9927256858603
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    978.8544718524862,
+                    975.9496048189004,
+                    976.2882003954288
+                ],
+                [
+                    955.7626937786133,
+                    953.4614389039157,
+                    952.0599711325602
+                ],
+                [
+                    989.752017782688,
+                    993.9927256858603,
+                    992.7055073885828
+                ],
+                [
+                    975.4598238934245,
+                    974.8941100428519,
+                    977.6110691282292
+                ],
+                [
+                    952.6560927866482,
+                    953.5554874785408,
+                    956.8783505266105
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K208",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 471.55749301741713,
+            "scoreError" : 18.06646722720142,
+            "scoreConfidence" : [
+                453.4910257902157,
+                489.62396024461856
+            ],
+            "scorePercentiles" : {
+                "0.0" : 444.2461877774847,
+                "50.0" : 478.64697777245993,
+                "90.0" : 488.6964600347632,
+                "95.0" : 489.07127104849866,
+                "99.0" : 489.07127104849866,
+                "99.9" : 489.07127104849866,
+                "99.99" : 489.07127104849866,
+                "99.999" : 489.07127104849866,
+                "99.9999" : 489.07127104849866,
+                "100.0" : 489.07127104849866
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    462.6847861327774,
+                    461.33662119031106,
+                    461.9008743593246
+                ],
+                [
+                    485.7371925332991,
+                    487.93614870638663,
+                    487.1728144213904
+                ],
+                [
+                    478.7600319803987,
+                    478.64697777245993,
+                    475.73798730951023
+                ],
+                [
+                    444.4951556999079,
+                    444.99416499519276,
+                    444.2461877774847
+                ],
+                [
+                    488.4465860256062,
+                    489.07127104849866,
+                    482.19559530870873
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K210",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 463.27979318017736,
+            "scoreError" : 2.738923148411572,
+            "scoreConfidence" : [
+                460.5408700317658,
+                466.01871632858894
+            ],
+            "scorePercentiles" : {
+                "0.0" : 455.0503335926937,
+                "50.0" : 463.59813051170687,
+                "90.0" : 465.5939719156776,
+                "95.0" : 465.6217761333669,
+                "99.0" : 465.6217761333669,
+                "99.9" : 465.6217761333669,
+                "99.99" : 465.6217761333669,
+                "99.999" : 465.6217761333669,
+                "99.9999" : 465.6217761333669,
+                "100.0" : 465.6217761333669
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    462.9965879231482,
+                    464.76841742491064,
+                    464.03749327877694
+                ],
+                [
+                    464.54349294814807,
+                    464.7117517675539,
+                    463.3785963327552
+                ],
+                [
+                    463.59813051170687,
+                    461.82233064366426,
+                    463.4625924577529
+                ],
+                [
+                    461.94882832573154,
+                    462.7852470345038,
+                    455.0503335926937
+                ],
+                [
+                    464.8958835573962,
+                    465.6217761333669,
+                    465.57543577055145
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN200K10",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 989.5388634227542,
+            "scoreError" : 14.134596639039657,
+            "scoreConfidence" : [
+                975.4042667837145,
+                1003.6734600617938
+            ],
+            "scorePercentiles" : {
+                "0.0" : 961.1299197289487,
+                "50.0" : 994.9023644719024,
+                "90.0" : 1001.4687219332199,
+                "95.0" : 1002.1899328794412,
+                "99.0" : 1002.1899328794412,
+                "99.9" : 1002.1899328794412,
+                "99.99" : 1002.1899328794412,
+                "99.999" : 1002.1899328794412,
+                "99.9999" : 1002.1899328794412,
+                "100.0" : 1002.1899328794412
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1002.1899328794412,
+                    1000.987914635739,
+                    999.3257493472246
+                ],
+                [
+                    994.0756147113772,
+                    992.0096401293727,
+                    983.5593316535548
+                ],
+                [
+                    969.9189180491841,
+                    961.1299197289487,
+                    965.7159429222877
+                ],
+                [
+                    997.4301681073473,
+                    997.2109925387504,
+                    993.5771423426628
+                ],
+                [
+                    996.0496049913994,
+                    994.9023644719024,
+                    994.9997148321199
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN200K210",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 446.7645512891926,
+            "scoreError" : 5.442536243476984,
+            "scoreConfidence" : [
+                441.3220150457156,
+                452.20708753266956
+            ],
+            "scorePercentiles" : {
+                "0.0" : 439.7788610749823,
+                "50.0" : 444.6941886476842,
+                "90.0" : 454.3681767298469,
+                "95.0" : 455.1577951726488,
+                "99.0" : 455.1577951726488,
+                "99.9" : 455.1577951726488,
+                "99.99" : 455.1577951726488,
+                "99.999" : 455.1577951726488,
+                "99.9999" : 455.1577951726488,
+                "100.0" : 455.1577951726488
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    442.536439675751,
+                    442.1295516755243,
+                    443.61230124071636
+                ],
+                [
+                    441.93263323460167,
+                    443.15368820445184,
+                    439.7788610749823
+                ],
+                [
+                    451.37913040998745,
+                    453.119744620769,
+                    455.1577951726488
+                ],
+                [
+                    445.3849374919964,
+                    443.67885909223224,
+                    444.6941886476842
+                ],
+                [
+                    449.5295117305883,
+                    453.8417644346456,
+                    451.5388626313101
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN300K09",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 954.5732192493725,
+            "scoreError" : 5.551621199499133,
+            "scoreConfidence" : [
+                949.0215980498734,
+                960.1248404488716
+            ],
+            "scorePercentiles" : {
+                "0.0" : 945.3720405703026,
+                "50.0" : 953.6524108444851,
+                "90.0" : 962.1200566980764,
+                "95.0" : 962.5669852462835,
+                "99.0" : 962.5669852462835,
+                "99.9" : 962.5669852462835,
+                "99.99" : 962.5669852462835,
+                "99.999" : 962.5669852462835,
+                "99.9999" : 962.5669852462835,
+                "100.0" : 962.5669852462835
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    954.1688926211596,
+                    945.3720405703026,
+                    950.2428738759257
+                ],
+                [
+                    962.5669852462835,
+                    957.7397409724297,
+                    961.2017714264199
+                ],
+                [
+                    953.6524108444851,
+                    947.3565151947821,
+                    951.9325767720046
+                ],
+                [
+                    952.9053707672446,
+                    952.9197918501438,
+                    951.4168492777303
+                ],
+                [
+                    955.5011769209501,
+                    961.822104332605,
+                    959.7991880681216
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN300K209",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 450.89503297404093,
+            "scoreError" : 12.138825735933237,
+            "scoreConfidence" : [
+                438.7562072381077,
+                463.03385870997414
+            ],
+            "scorePercentiles" : {
+                "0.0" : 435.72432879783145,
+                "50.0" : 454.37173618852,
+                "90.0" : 462.3558539284659,
+                "95.0" : 462.8919887974014,
+                "99.0" : 462.8919887974014,
+                "99.9" : 462.8919887974014,
+                "99.99" : 462.8919887974014,
+                "99.999" : 462.8919887974014,
+                "99.9999" : 462.8919887974014,
+                "100.0" : 462.8919887974014
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    462.8919887974014,
+                    461.99843068250885,
+                    460.4487856098874
+                ],
+                [
+                    436.09229699373617,
+                    435.73679679316433,
+                    435.72432879783145
+                ],
+                [
+                    457.9076168075443,
+                    454.37173618852,
+                    453.84531942745616
+                ],
+                [
+                    461.18430422993384,
+                    461.55135982783224,
+                    461.21794818436797
+                ],
+                [
+                    439.49132061780773,
+                    436.9753218138549,
+                    443.98793983876783
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN400K09",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 983.4090697829313,
+            "scoreError" : 20.353875245485536,
+            "scoreConfidence" : [
+                963.0551945374458,
+                1003.7629450284168
+            ],
+            "scorePercentiles" : {
+                "0.0" : 961.3544835692653,
+                "50.0" : 986.8900359941699,
+                "90.0" : 1014.0635900707392,
+                "95.0" : 1015.3218017992034,
+                "99.0" : 1015.3218017992034,
+                "99.9" : 1015.3218017992034,
+                "99.99" : 1015.3218017992034,
+                "99.999" : 1015.3218017992034,
+                "99.9999" : 1015.3218017992034,
+                "100.0" : 1015.3218017992034
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1013.2247822517631,
+                    1008.5224693030892,
+                    1015.3218017992034
+                ],
+                [
+                    963.6600003262133,
+                    961.3544835692653,
+                    964.2604725503714
+                ],
+                [
+                    964.255848325267,
+                    963.6935738628946,
+                    965.7504960556753
+                ],
+                [
+                    982.4583162785699,
+                    986.8900359941699,
+                    990.323392540617
+                ],
+                [
+                    987.3693459858683,
+                    991.2407062635492,
+                    992.8103216374556
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN400K209",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 451.1764672222263,
+            "scoreError" : 2.545263432265774,
+            "scoreConfidence" : [
+                448.6312037899605,
+                453.72173065449203
+            ],
+            "scorePercentiles" : {
+                "0.0" : 446.6593536735987,
+                "50.0" : 452.20506456629676,
+                "90.0" : 453.91965046617594,
+                "95.0" : 454.35371900812015,
+                "99.0" : 454.35371900812015,
+                "99.9" : 454.35371900812015,
+                "99.99" : 454.35371900812015,
+                "99.999" : 454.35371900812015,
+                "99.9999" : 454.35371900812015,
+                "100.0" : 454.35371900812015
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    450.57287995912594,
+                    446.6593536735987,
+                    447.7813070872143
+                ],
+                [
+                    453.2936817739289,
+                    454.35371900812015,
+                    452.64494170279477
+                ],
+                [
+                    453.63027143821313,
+                    453.2989723622714,
+                    452.2870188343068
+                ],
+                [
+                    450.5136392458904,
+                    452.76369198433946,
+                    452.20506456629676
+                ],
+                [
+                    450.47627812762016,
+                    449.13553895483466,
+                    448.03064961483847
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 111.79662970943086,
+            "scoreError" : 0.7890127048293907,
+            "scoreConfidence" : [
+                111.00761700460147,
+                112.58564241426025
+            ],
+            "scorePercentiles" : {
+                "0.0" : 110.81792123675102,
+                "50.0" : 111.63634201501539,
+                "90.0" : 113.06924508737265,
+                "95.0" : 113.08224436332766,
+                "99.0" : 113.08224436332766,
+                "99.9" : 113.08224436332766,
+                "99.99" : 113.08224436332766,
+                "99.999" : 113.08224436332766,
+                "99.9999" : 113.08224436332766,
+                "100.0" : 113.08224436332766
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    111.29903017089258,
+                    111.42436747681585,
+                    111.63634201501539
+                ],
+                [
+                    111.00989580513003,
+                    110.93670869534073,
+                    110.81792123675102
+                ],
+                [
+                    111.52833563609899,
+                    112.1591021102084,
+                    111.1764979636141
+                ],
+                [
+                    112.36085370900122,
+                    112.05007826200827,
+                    111.78819153003603
+                ],
+                [
+                    113.06057890340264,
+                    112.61929776381965,
+                    113.08224436332766
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 89.7182022702618,
+            "scoreError" : 2.172963251231951,
+            "scoreConfidence" : [
+                87.54523901902985,
+                91.89116552149375
+            ],
+            "scorePercentiles" : {
+                "0.0" : 85.84200393117202,
+                "50.0" : 90.27090741911687,
+                "90.0" : 92.3810979004838,
+                "95.0" : 93.11756140139963,
+                "99.0" : 93.11756140139963,
+                "99.9" : 93.11756140139963,
+                "99.99" : 93.11756140139963,
+                "99.999" : 93.11756140139963,
+                "99.9999" : 93.11756140139963,
+                "100.0" : 93.11756140139963
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    85.84200393117202,
+                    89.1807854408291,
+                    90.27090741911687
+                ],
+                [
+                    87.3556755615599,
+                    88.79771050809072,
+                    88.51046955362463
+                ],
+                [
+                    91.55413764147796,
+                    91.89012223320658,
+                    93.11756140139963
+                ],
+                [
+                    86.51916283413281,
+                    90.98701469226555,
+                    90.52543821910433
+                ],
+                [
+                    90.98478201115844,
+                    89.75155479071478,
+                    90.48570781607357
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 202.3949374176122,
+            "scoreError" : 76.820246452902,
+            "scoreConfidence" : [
+                125.5746909647102,
+                279.2151838705142
+            ],
+            "scorePercentiles" : {
+                "0.0" : 62.533078280440755,
+                "50.0" : 236.98298162260488,
+                "90.0" : 238.04756155357592,
+                "95.0" : 238.44544251126433,
+                "99.0" : 238.44544251126433,
+                "99.9" : 238.44544251126433,
+                "99.99" : 238.44544251126433,
+                "99.999" : 238.44544251126433,
+                "99.9999" : 238.44544251126433,
+                "100.0" : 238.44544251126433
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    65.28482459982169,
+                    62.533078280440755,
+                    62.86501075965584
+                ],
+                [
+                    236.34452072668992,
+                    237.04304386424084,
+                    237.24788684662477
+                ],
+                [
+                    237.14014300559694,
+                    236.98298162260488,
+                    236.96093410427136
+                ],
+                [
+                    237.29981291785649,
+                    237.78230758178364,
+                    238.44544251126433
+                ],
+                [
+                    236.06974469885435,
+                    237.18346967861726,
+                    236.74086006585983
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 168.757445724798,
+            "scoreError" : 59.96352310536157,
+            "scoreConfidence" : [
+                108.79392261943644,
+                228.72096883015956
+            ],
+            "scorePercentiles" : {
+                "0.0" : 59.64677121868498,
+                "50.0" : 198.8785907076378,
+                "90.0" : 200.62793021114447,
+                "95.0" : 201.44893232158273,
+                "99.0" : 201.44893232158273,
+                "99.9" : 201.44893232158273,
+                "99.99" : 201.44893232158273,
+                "99.999" : 201.44893232158273,
+                "99.9999" : 201.44893232158273,
+                "100.0" : 201.44893232158273
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    199.59465968359842,
+                    198.8785907076378,
+                    201.44893232158273
+                ],
+                [
+                    185.05094006181417,
+                    181.90098450811843,
+                    184.69705621029397
+                ],
+                [
+                    198.1725657833017,
+                    199.40604617925544,
+                    200.0805954708523
+                ],
+                [
+                    200.07847647167017,
+                    199.12662121145598,
+                    199.65956416851796
+                ],
+                [
+                    61.03111129545997,
+                    62.58877057972579,
+                    59.64677121868498
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 41.88173804156052,
+            "scoreError" : 21.641052708911293,
+            "scoreConfidence" : [
+                20.240685332649225,
+                63.52279075047181
+            ],
+            "scorePercentiles" : {
+                "0.0" : 17.314578239882202,
+                "50.0" : 53.43711652682356,
+                "90.0" : 60.884092184012786,
+                "95.0" : 61.260559842110936,
+                "99.0" : 61.260559842110936,
+                "99.9" : 61.260559842110936,
+                "99.99" : 61.260559842110936,
+                "99.999" : 61.260559842110936,
+                "99.9999" : 61.260559842110936,
+                "100.0" : 61.260559842110936
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    59.33104684307764,
+                    59.53181275631804,
+                    53.43711652682356
+                ],
+                [
+                    17.80064132900073,
+                    18.55071640995103,
+                    18.58120889565767
+                ],
+                [
+                    58.03999543510948,
+                    53.4100942386382,
+                    58.052406438944026
+                ],
+                [
+                    17.85164062383314,
+                    18.333252045960474,
+                    17.314578239882202
+                ],
+                [
+                    60.63311374528069,
+                    56.097887252819845,
+                    61.260559842110936
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 36.56665003083814,
+            "scoreError" : 18.66373381114973,
+            "scoreConfidence" : [
+                17.90291621968841,
+                55.23038384198787
+            ],
+            "scorePercentiles" : {
+                "0.0" : 15.056264334102831,
+                "50.0" : 48.329605821660785,
+                "90.0" : 53.08653654903394,
+                "95.0" : 54.25752497444918,
+                "99.0" : 54.25752497444918,
+                "99.9" : 54.25752497444918,
+                "99.99" : 54.25752497444918,
+                "99.999" : 54.25752497444918,
+                "99.9999" : 54.25752497444918,
+                "100.0" : 54.25752497444918
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    52.30587759875711,
+                    50.89740808812191,
+                    48.490769953699825
+                ],
+                [
+                    16.81083935546008,
+                    15.564007832258609,
+                    16.39521871779643
+                ],
+                [
+                    16.733797362232355,
+                    15.516230446715122,
+                    15.056264334102831
+                ],
+                [
+                    50.11015442617881,
+                    48.329605821660785,
+                    48.684902971532644
+                ],
+                [
+                    47.545755585789614,
+                    54.25752497444918,
+                    51.80139299381688
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 78.2768276326478,
+            "scoreError" : 54.24124305658098,
+            "scoreConfidence" : [
+                24.035584576066817,
+                132.51807068922878
+            ],
+            "scorePercentiles" : {
+                "0.0" : 37.250187654772,
+                "50.0" : 38.96014368934481,
+                "90.0" : 139.48222343003533,
+                "95.0" : 140.34307206731017,
+                "99.0" : 140.34307206731017,
+                "99.9" : 140.34307206731017,
+                "99.99" : 140.34307206731017,
+                "99.999" : 140.34307206731017,
+                "99.9999" : 140.34307206731017,
+                "100.0" : 140.34307206731017
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    138.22101883517513,
+                    138.90832433851875,
+                    140.34307206731017
+                ],
+                [
+                    38.96014368934481,
+                    38.924566981836946,
+                    39.00455037162455
+                ],
+                [
+                    37.59438997098785,
+                    38.523063640560814,
+                    38.392137576295916
+                ],
+                [
+                    37.76841497596615,
+                    37.250187654772,
+                    37.936969239249734
+                ],
+                [
+                    136.21572250509894,
+                    137.9734908501482,
+                    138.13636179282656
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 124.03155340277755,
+            "scoreError" : 1.7041628682686132,
+            "scoreConfidence" : [
+                122.32739053450894,
+                125.73571627104616
+            ],
+            "scorePercentiles" : {
+                "0.0" : 121.03892315668844,
+                "50.0" : 123.98575598368575,
+                "90.0" : 126.25927433354428,
+                "95.0" : 127.84501521488984,
+                "99.0" : 127.84501521488984,
+                "99.9" : 127.84501521488984,
+                "99.99" : 127.84501521488984,
+                "99.999" : 127.84501521488984,
+                "99.9999" : 127.84501521488984,
+                "100.0" : 127.84501521488984
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    121.03892315668844,
+                    123.34968751497817,
+                    124.61003817070844
+                ],
+                [
+                    121.64343181005583,
+                    122.66636174264978,
+                    123.61521861057459
+                ],
+                [
+                    124.82761951845447,
+                    127.84501521488984,
+                    125.14647894863876
+                ],
+                [
+                    123.82364098353128,
+                    123.98575598368575,
+                    125.20211374598057
+                ],
+                [
+                    123.9664162877595,
+                    124.33974867593705,
+                    124.4128506771307
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 17.21948547671312,
+            "scoreError" : 9.768385836056055,
+            "scoreConfidence" : [
+                7.451099640657066,
+                26.987871312769176
+            ],
+            "scorePercentiles" : {
+                "0.0" : 9.531255528831748,
+                "50.0" : 10.471563319670624,
+                "90.0" : 28.820521988121587,
+                "95.0" : 29.828117035381403,
+                "99.0" : 29.828117035381403,
+                "99.9" : 29.828117035381403,
+                "99.99" : 29.828117035381403,
+                "99.999" : 29.828117035381403,
+                "99.9999" : 29.828117035381403,
+                "100.0" : 29.828117035381403
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    10.483362539127844,
+                    9.531255528831748,
+                    10.094349529554583
+                ],
+                [
+                    27.621117396416544,
+                    29.828117035381403,
+                    27.994409180320012
+                ],
+                [
+                    10.471563319670624,
+                    10.1331220370504,
+                    10.318472291948353
+                ],
+                [
+                    9.595252849157111,
+                    9.769539562772682,
+                    9.869845681337562
+                ],
+                [
+                    27.205193467975715,
+                    27.227889774537143,
+                    28.148791956615042
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 18.464359867970856,
+            "scoreError" : 8.311327421577667,
+            "scoreConfidence" : [
+                10.153032446393189,
+                26.77568728954852
+            ],
+            "scorePercentiles" : {
+                "0.0" : 8.229146693738992,
+                "50.0" : 22.49167287956409,
+                "90.0" : 25.804821887916685,
+                "95.0" : 25.829650222260966,
+                "99.0" : 25.829650222260966,
+                "99.9" : 25.829650222260966,
+                "99.99" : 25.829650222260966,
+                "99.999" : 25.829650222260966,
+                "99.9999" : 25.829650222260966,
+                "100.0" : 25.829650222260966
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    9.527894173773607,
+                    9.818854530996784,
+                    9.154779599286208
+                ],
+                [
+                    25.654878365292156,
+                    24.834351265024267,
+                    24.59512253294589
+                ],
+                [
+                    22.156172311251122,
+                    25.788269665020497,
+                    25.360154295341367
+                ],
+                [
+                    9.595702277507995,
+                    8.229146693738992,
+                    9.828474154678585
+                ],
+                [
+                    22.49167287956409,
+                    24.100275052880303,
+                    25.829650222260966
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 72.49527604120003,
+            "scoreError" : 28.035411213750177,
+            "scoreConfidence" : [
+                44.45986482744985,
+                100.53068725495021
+            ],
+            "scorePercentiles" : {
+                "0.0" : 21.47265569784971,
+                "50.0" : 84.75581903533245,
+                "90.0" : 86.00899397831984,
+                "95.0" : 86.22436713282667,
+                "99.0" : 86.22436713282667,
+                "99.9" : 86.22436713282667,
+                "99.99" : 86.22436713282667,
+                "99.999" : 86.22436713282667,
+                "99.9999" : 86.22436713282667,
+                "100.0" : 86.22436713282667
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    86.22436713282667,
+                    84.75581903533245,
+                    85.7171555039946
+                ],
+                [
+                    85.26646896110239,
+                    84.6158681141584,
+                    84.5742491024965
+                ],
+                [
+                    21.47265569784971,
+                    22.16855145680661,
+                    21.906505925320847
+                ],
+                [
+                    85.72688693918556,
+                    85.86541187531529,
+                    85.72165739057459
+                ],
+                [
+                    82.97022991080364,
+                    85.83572145510848,
+                    84.60759211712481
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 75.76316076457995,
+            "scoreError" : 1.08734402016124,
+            "scoreConfidence" : [
+                74.67581674441871,
+                76.85050478474119
+            ],
+            "scorePercentiles" : {
+                "0.0" : 74.6818791597644,
+                "50.0" : 75.48095324431024,
+                "90.0" : 77.58887233782316,
+                "95.0" : 77.83464678924264,
+                "99.0" : 77.83464678924264,
+                "99.9" : 77.83464678924264,
+                "99.99" : 77.83464678924264,
+                "99.999" : 77.83464678924264,
+                "99.9999" : 77.83464678924264,
+                "100.0" : 77.83464678924264
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    75.48095324431024,
+                    77.09733152820617,
+                    75.17226414701386
+                ],
+                [
+                    75.31666545428482,
+                    77.4250227035435,
+                    77.83464678924264
+                ],
+                [
+                    74.6818791597644,
+                    75.11506337121622,
+                    74.77331452668341
+                ],
+                [
+                    75.4896030608472,
+                    75.8044221987372,
+                    76.70930150903817
+                ],
+                [
+                    74.81801152854665,
+                    74.9970337339526,
+                    75.7318985133122
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 13.476450163931311,
+            "scoreError" : 1.2896892083531966,
+            "scoreConfidence" : [
+                12.186760955578114,
+                14.766139372284508
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11.671607189080921,
+                "50.0" : 13.469265986649162,
+                "90.0" : 15.118259520342173,
+                "95.0" : 15.163268753626065,
+                "99.0" : 15.163268753626065,
+                "99.9" : 15.163268753626065,
+                "99.99" : 15.163268753626065,
+                "99.999" : 15.163268753626065,
+                "99.9999" : 15.163268753626065,
+                "100.0" : 15.163268753626065
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    12.058133062196317,
+                    15.163268753626065,
+                    13.960561231856634
+                ],
+                [
+                    13.469265986649162,
+                    11.671607189080921,
+                    14.865003799454195
+                ],
+                [
+                    14.064668471493432,
+                    13.87572764864681,
+                    15.088253364819579
+                ],
+                [
+                    13.346198109882147,
+                    12.974736022371665,
+                    11.874707898705099
+                ],
+                [
+                    12.066633708048501,
+                    14.81302882386872,
+                    12.85495838827039
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 8.792444933870911,
+            "scoreError" : 3.804202494942238,
+            "scoreConfidence" : [
+                4.988242438928673,
+                12.59664742881315
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.149665279641244,
+                "50.0" : 10.400189052702512,
+                "90.0" : 12.909168961085117,
+                "95.0" : 13.298437179178826,
+                "99.0" : 13.298437179178826,
+                "99.9" : 13.298437179178826,
+                "99.99" : 13.298437179178826,
+                "99.999" : 13.298437179178826,
+                "99.9999" : 13.298437179178826,
+                "100.0" : 13.298437179178826
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    10.997988780122506,
+                    12.649656815689308,
+                    10.9454950321249
+                ],
+                [
+                    11.912332925901623,
+                    10.922879908100002,
+                    10.400189052702512
+                ],
+                [
+                    5.078954968203786,
+                    4.995431022633059,
+                    4.759604131263156
+                ],
+                [
+                    12.264762086331977,
+                    10.292533184425917,
+                    13.298437179178826
+                ],
+                [
+                    4.149665279641244,
+                    4.158323207461708,
+                    5.060420434283126
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 33.37684670457286,
+            "scoreError" : 1.1565852491337936,
+            "scoreConfidence" : [
+                32.22026145543907,
+                34.53343195370665
+            ],
+            "scorePercentiles" : {
+                "0.0" : 31.472270538496055,
+                "50.0" : 33.10015327046203,
+                "90.0" : 35.2133249446947,
+                "95.0" : 35.863345731220264,
+                "99.0" : 35.863345731220264,
+                "99.9" : 35.863345731220264,
+                "99.99" : 35.863345731220264,
+                "99.999" : 35.863345731220264,
+                "99.9999" : 35.863345731220264,
+                "100.0" : 35.863345731220264
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    33.10015327046203,
+                    32.89648344373738,
+                    33.39307641332626
+                ],
+                [
+                    32.867732424665064,
+                    32.68752414839513,
+                    34.38295008974919
+                ],
+                [
+                    32.74220812187432,
+                    32.84528659957435,
+                    33.70332732200391
+                ],
+                [
+                    32.280178487772815,
+                    33.52977072059713,
+                    35.863345731220264
+                ],
+                [
+                    31.472270538496055,
+                    34.779977753677656,
+                    34.10841550304143
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 30.151335851401438,
+            "scoreError" : 1.378331339199239,
+            "scoreConfidence" : [
+                28.7730045122022,
+                31.529667190600676
+            ],
+            "scorePercentiles" : {
+                "0.0" : 27.54284438702816,
+                "50.0" : 30.65998212492608,
+                "90.0" : 31.717771399770445,
+                "95.0" : 31.923578454605742,
+                "99.0" : 31.923578454605742,
+                "99.9" : 31.923578454605742,
+                "99.99" : 31.923578454605742,
+                "99.999" : 31.923578454605742,
+                "99.9999" : 31.923578454605742,
+                "100.0" : 31.923578454605742
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    29.596739267682036,
+                    30.941198236266363,
+                    31.011414237193954
+                ],
+                [
+                    30.65998212492608,
+                    28.831923022843988,
+                    31.580566696546914
+                ],
+                [
+                    27.54284438702816,
+                    31.105304347233353,
+                    31.923578454605742
+                ],
+                [
+                    30.77385159749443,
+                    31.16622091259212,
+                    30.311370773497615
+                ],
+                [
+                    28.27503088694388,
+                    28.95587964045146,
+                    29.594133185715393
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 7.61875429536946,
+            "scoreError" : 2.4566164405898365,
+            "scoreConfidence" : [
+                5.162137854779623,
+                10.075370735959297
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.144677802002079,
+                "50.0" : 8.745904272573547,
+                "90.0" : 8.89636638544586,
+                "95.0" : 8.903701335626923,
+                "99.0" : 8.903701335626923,
+                "99.9" : 8.903701335626923,
+                "99.99" : 8.903701335626923,
+                "99.999" : 8.903701335626923,
+                "99.9999" : 8.903701335626923,
+                "100.0" : 8.903701335626923
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    8.503088573991551,
+                    8.773988239024698,
+                    8.76367514142568
+                ],
+                [
+                    8.600631378190974,
+                    8.745904272573547,
+                    8.76158497674196
+                ],
+                [
+                    3.1969390106546505,
+                    3.144677802002079,
+                    3.2170094933240767
+                ],
+                [
+                    8.891476418658485,
+                    8.720486419629784,
+                    8.418913150443379
+                ],
+                [
+                    8.863072431677601,
+                    8.776165786576518,
+                    8.903701335626923
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 6.365052802025193,
+            "scoreError" : 1.930732054854557,
+            "scoreConfidence" : [
+                4.434320747170636,
+                8.29578485687975
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2.7971553338224524,
+                "50.0" : 7.135663135428521,
+                "90.0" : 7.683754720951823,
+                "95.0" : 7.7332392258994584,
+                "99.0" : 7.7332392258994584,
+                "99.9" : 7.7332392258994584,
+                "99.99" : 7.7332392258994584,
+                "99.999" : 7.7332392258994584,
+                "99.9999" : 7.7332392258994584,
+                "100.0" : 7.7332392258994584
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2.9216825985770014,
+                    3.040284362761938,
+                    2.7971553338224524
+                ],
+                [
+                    7.341871035609219,
+                    6.498861914849152,
+                    6.985421065283115
+                ],
+                [
+                    7.135663135428521,
+                    7.27932326531456,
+                    7.650765050986733
+                ],
+                [
+                    7.145284940127866,
+                    7.3495945036029475,
+                    7.1173658579880925
+                ],
+                [
+                    7.7332392258994584,
+                    7.103607107321174,
+                    7.375672632805673
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 14.785894114222918,
+            "scoreError" : 7.075300736265591,
+            "scoreConfidence" : [
+                7.710593377957327,
+                21.86119485048851
+            ],
+            "scorePercentiles" : {
+                "0.0" : 6.6238101234689175,
+                "50.0" : 18.63405522824858,
+                "90.0" : 22.137903119219704,
+                "95.0" : 23.443478066734833,
+                "99.0" : 23.443478066734833,
+                "99.9" : 23.443478066734833,
+                "99.99" : 23.443478066734833,
+                "99.999" : 23.443478066734833,
+                "99.9999" : 23.443478066734833,
+                "100.0" : 23.443478066734833
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    20.81463482193428,
+                    21.26751982087628,
+                    18.63405522824858
+                ],
+                [
+                    6.6238101234689175,
+                    6.75658101722185,
+                    7.051271053910943
+                ],
+                [
+                    19.0767889149469,
+                    19.13286549864879,
+                    20.00862757999917
+                ],
+                [
+                    8.333342952466932,
+                    6.795686318602955,
+                    7.148169626132261
+                ],
+                [
+                    17.922511600530722,
+                    23.443478066734833,
+                    18.77906908962039
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 15.069645555892203,
+            "scoreError" : 4.995764307788415,
+            "scoreConfidence" : [
+                10.073881248103788,
+                20.065409863680618
+            ],
+            "scorePercentiles" : {
+                "0.0" : 6.525438308782814,
+                "50.0" : 15.836747387967138,
+                "90.0" : 19.861826612604133,
+                "95.0" : 21.353181774527926,
+                "99.0" : 21.353181774527926,
+                "99.9" : 21.353181774527926,
+                "99.99" : 21.353181774527926,
+                "99.999" : 21.353181774527926,
+                "99.9999" : 21.353181774527926,
+                "100.0" : 21.353181774527926
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    18.4972820712625,
+                    15.836747387967138,
+                    14.398722570118466
+                ],
+                [
+                    18.19260235832646,
+                    18.867589837988273,
+                    21.353181774527926
+                ],
+                [
+                    15.637367010115758,
+                    15.44904856502463,
+                    17.03452840674345
+                ],
+                [
+                    14.766921479727264,
+                    18.15860626837125,
+                    17.654420461003873
+                ],
+                [
+                    6.681110290356621,
+                    6.525438308782814,
+                    6.991116548066628
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 5.9353103682546475,
+            "scoreError" : 0.5290397619836832,
+            "scoreConfidence" : [
+                5.406270606270964,
+                6.464350130238331
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.878922326878594,
+                "50.0" : 6.054419632488941,
+                "90.0" : 6.527640940958293,
+                "95.0" : 6.597539744033696,
+                "99.0" : 6.597539744033696,
+                "99.9" : 6.597539744033696,
+                "99.99" : 6.597539744033696,
+                "99.999" : 6.597539744033696,
+                "99.9999" : 6.597539744033696,
+                "100.0" : 6.597539744033696
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6.054419632488941,
+                    6.4116396040170525,
+                    5.328226475687357
+                ],
+                [
+                    6.144454845608136,
+                    6.481041738908025,
+                    6.004152066541914
+                ],
+                [
+                    4.878922326878594,
+                    6.3474334083378565,
+                    6.2586295143444
+                ],
+                [
+                    5.515362412224871,
+                    5.55586134418077,
+                    6.14224253754926
+                ],
+                [
+                    5.903052458531737,
+                    5.406677414487111,
+                    6.597539744033696
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 4.673432502837802,
+            "scoreError" : 1.552878455432352,
+            "scoreConfidence" : [
+                3.1205540474054496,
+                6.226310958270154
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2.181545141685607,
+                "50.0" : 5.214889853823411,
+                "90.0" : 6.017274704916607,
+                "95.0" : 6.1166478430723,
+                "99.0" : 6.1166478430723,
+                "99.9" : 6.1166478430723,
+                "99.99" : 6.1166478430723,
+                "99.999" : 6.1166478430723,
+                "99.9999" : 6.1166478430723,
+                "100.0" : 6.1166478430723
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5.856509945420771,
+                    5.737826324730627,
+                    5.771332586433917
+                ],
+                [
+                    2.181545141685607,
+                    2.229206724090047,
+                    2.3084518606427173
+                ],
+                [
+                    5.951025946146145,
+                    5.214889853823411,
+                    5.886929566087091
+                ],
+                [
+                    4.427686276216721,
+                    3.8137970569954778,
+                    4.275272285428256
+                ],
+                [
+                    6.1166478430723,
+                    4.422922211435889,
+                    5.907443920358064
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d01",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 14.191716644777708,
+            "scoreError" : 1.1430000694377032,
+            "scoreConfidence" : [
+                13.048716575340006,
+                15.33471671421541
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11.917408845733258,
+                "50.0" : 13.958179722702921,
+                "90.0" : 15.837807319547421,
+                "95.0" : 17.105871806738772,
+                "99.0" : 17.105871806738772,
+                "99.9" : 17.105871806738772,
+                "99.99" : 17.105871806738772,
+                "99.999" : 17.105871806738772,
+                "99.9999" : 17.105871806738772,
+                "100.0" : 17.105871806738772
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    14.992430994753187,
+                    14.017956171834632,
+                    13.770044082590667
+                ],
+                [
+                    14.736605508795641,
+                    14.292851076013473,
+                    13.791605869593202
+                ],
+                [
+                    14.608433896935244,
+                    17.105871806738772,
+                    13.643092918848009
+                ],
+                [
+                    14.530654082597914,
+                    13.85593344020735,
+                    13.958179722702921
+                ],
+                [
+                    11.917408845733258,
+                    13.822748519920893,
+                    13.83193273440047
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d02",
+        "mode" : "thrpt",
+        "threads" : 32,
+        "forks" : 5,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 2,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "primaryMetric" : {
+            "score" : 11.445924020087164,
+            "scoreError" : 0.7562865417460121,
+            "scoreConfidence" : [
+                10.689637478341151,
+                12.202210561833176
+            ],
+            "scorePercentiles" : {
+                "0.0" : 10.115188136208515,
+                "50.0" : 11.625739262267807,
+                "90.0" : 12.254490517068911,
+                "95.0" : 12.274847040996331,
+                "99.0" : 12.274847040996331,
+                "99.9" : 12.274847040996331,
+                "99.99" : 12.274847040996331,
+                "99.999" : 12.274847040996331,
+                "99.9999" : 12.274847040996331,
+                "100.0" : 12.274847040996331
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11.041682048939798,
+                    10.72140891631031,
+                    11.713493275401389
+                ],
+                [
+                    12.274847040996331,
+                    11.99574212912567,
+                    11.424147706282449
+                ],
+                [
+                    12.227950081408531,
+                    12.15024855716867,
+                    10.115188136208515
+                ],
+                [
+                    11.625739262267807,
+                    12.240919501117297,
+                    11.246913457541167
+                ],
+                [
+                    10.355403568190786,
+                    10.814855595486414,
+                    11.740321024862308
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530276206-f9351808e.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530276206-f9351808e.json
new file mode 100644
index 0000000000000000000000000000000000000000..620024cfc1c2b143d7f3f404519e535020256760
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530276206-f9351808e.json
@@ -0,0 +1,5480 @@
+[
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 49640.96317575774,
+            "scoreError" : 5313.823551389228,
+            "scoreConfidence" : [
+                44327.13962436851,
+                54954.786727146966
+            ],
+            "scorePercentiles" : {
+                "0.0" : 45299.63022275939,
+                "50.0" : 51707.01294679341,
+                "90.0" : 51792.0525170781,
+                "95.0" : 51792.0525170781,
+                "99.0" : 51792.0525170781,
+                "99.9" : 51792.0525170781,
+                "99.99" : 51792.0525170781,
+                "99.999" : 51792.0525170781,
+                "99.9999" : 51792.0525170781,
+                "100.0" : 51792.0525170781
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    45530.88567814011,
+                    51764.47020165688,
+                    51757.65739578748
+                ],
+                [
+                    45446.34432145206,
+                    51792.0525170781,
+                    51766.0839096055
+                ],
+                [
+                    45299.63022275939,
+                    51707.01294679341,
+                    51704.53138854678
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 50092.47797235212,
+            "scoreError" : 5392.976153483466,
+            "scoreConfidence" : [
+                44699.50181886865,
+                55485.45412583559
+            ],
+            "scorePercentiles" : {
+                "0.0" : 45806.15583093199,
+                "50.0" : 52212.23315720261,
+                "90.0" : 52291.239598679516,
+                "95.0" : 52291.239598679516,
+                "99.0" : 52291.239598679516,
+                "99.9" : 52291.239598679516,
+                "99.99" : 52291.239598679516,
+                "99.999" : 52291.239598679516,
+                "99.9999" : 52291.239598679516,
+                "100.0" : 52291.239598679516
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    45818.19205297116,
+                    52231.680868925316,
+                    52221.134396231224
+                ],
+                [
+                    45806.15583093199,
+                    52291.239598679516,
+                    52237.194933707266
+                ],
+                [
+                    45816.40300074469,
+                    52212.23315720261,
+                    52198.06791177533
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "20"
+        },
+        "primaryMetric" : {
+            "score" : 49990.28972514267,
+            "scoreError" : 5502.797921246911,
+            "scoreConfidence" : [
+                44487.491803895755,
+                55493.087646389584
+            ],
+            "scorePercentiles" : {
+                "0.0" : 45511.00241759965,
+                "50.0" : 52126.89606655572,
+                "90.0" : 52233.40400921543,
+                "95.0" : 52233.40400921543,
+                "99.0" : 52233.40400921543,
+                "99.9" : 52233.40400921543,
+                "99.99" : 52233.40400921543,
+                "99.999" : 52233.40400921543,
+                "99.9999" : 52233.40400921543,
+                "100.0" : 52233.40400921543
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    45511.00241759965,
+                    52233.40400921543,
+                    52205.53649296292
+                ],
+                [
+                    45667.504109107984,
+                    52124.212991879795,
+                    52126.89606655572
+                ],
+                [
+                    45696.077246698456,
+                    52170.13722017491,
+                    52177.83697208921
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "22"
+        },
+        "primaryMetric" : {
+            "score" : 50193.049249178745,
+            "scoreError" : 5503.959207285861,
+            "scoreConfidence" : [
+                44689.09004189288,
+                55697.00845646461
+            ],
+            "scorePercentiles" : {
+                "0.0" : 45643.817888990605,
+                "50.0" : 52301.39674013717,
+                "90.0" : 52462.18236414704,
+                "95.0" : 52462.18236414704,
+                "99.0" : 52462.18236414704,
+                "99.9" : 52462.18236414704,
+                "99.99" : 52462.18236414704,
+                "99.999" : 52462.18236414704,
+                "99.9999" : 52462.18236414704,
+                "100.0" : 52462.18236414704
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    45892.976137266036,
+                    52462.18236414704,
+                    52453.20770855828
+                ],
+                [
+                    45643.817888990605,
+                    52291.433577618795,
+                    52301.39674013717
+                ],
+                [
+                    45947.118771708076,
+                    52390.05153155216,
+                    52355.258522630495
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 12388.472482181454,
+            "scoreError" : 1310.6534167783188,
+            "scoreConfidence" : [
+                11077.819065403135,
+                13699.125898959774
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11285.266335573237,
+                "50.0" : 12834.514397250188,
+                "90.0" : 13044.637481341451,
+                "95.0" : 13044.637481341451,
+                "99.0" : 13044.637481341451,
+                "99.9" : 13044.637481341451,
+                "99.99" : 13044.637481341451,
+                "99.999" : 13044.637481341451,
+                "99.9999" : 13044.637481341451,
+                "100.0" : 13044.637481341451
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11285.266335573237,
+                    12834.514397250188,
+                    12829.567077442836
+                ],
+                [
+                    11314.962513672499,
+                    12837.195838018031,
+                    12834.883987297198
+                ],
+                [
+                    11470.809031188168,
+                    13044.41567784948,
+                    13044.637481341451
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 12613.63279333138,
+            "scoreError" : 1320.2113558877807,
+            "scoreConfidence" : [
+                11293.4214374436,
+                13933.844149219161
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11558.576256589442,
+                "50.0" : 13136.284044044995,
+                "90.0" : 13145.815445587406,
+                "95.0" : 13145.815445587406,
+                "99.0" : 13145.815445587406,
+                "99.9" : 13145.815445587406,
+                "99.99" : 13145.815445587406,
+                "99.999" : 13145.815445587406,
+                "99.9999" : 13145.815445587406,
+                "100.0" : 13145.815445587406
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11562.562122602727,
+                    13121.216969485053,
+                    13142.682578357562
+                ],
+                [
+                    11577.397173511414,
+                    13136.564495516533,
+                    13145.815445587406
+                ],
+                [
+                    11558.576256589442,
+                    13136.284044044995,
+                    13141.596054287289
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "20"
+        },
+        "primaryMetric" : {
+            "score" : 12767.277008962783,
+            "scoreError" : 1350.7430977066049,
+            "scoreConfidence" : [
+                11416.533911256178,
+                14118.020106669388
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11673.125520204727,
+                "50.0" : 13293.226993436885,
+                "90.0" : 13322.824248316305,
+                "95.0" : 13322.824248316305,
+                "99.0" : 13322.824248316305,
+                "99.9" : 13322.824248316305,
+                "99.99" : 13322.824248316305,
+                "99.999" : 13322.824248316305,
+                "99.9999" : 13322.824248316305,
+                "100.0" : 13322.824248316305
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11682.42453794474,
+                    13322.824248316305,
+                    13305.14901056783
+                ],
+                [
+                    11731.931211941232,
+                    13310.804851354042,
+                    13298.118294577689
+                ],
+                [
+                    11673.125520204727,
+                    13293.226993436885,
+                    13287.888412321618
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "22"
+        },
+        "primaryMetric" : {
+            "score" : 12782.30988288717,
+            "scoreError" : 1349.2729930773378,
+            "scoreConfidence" : [
+                11433.036889809831,
+                14131.582875964508
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11696.55425677517,
+                "50.0" : 13306.127044938077,
+                "90.0" : 13329.692162687668,
+                "95.0" : 13329.692162687668,
+                "99.0" : 13329.692162687668,
+                "99.9" : 13329.692162687668,
+                "99.99" : 13329.692162687668,
+                "99.999" : 13329.692162687668,
+                "99.9999" : 13329.692162687668,
+                "100.0" : 13329.692162687668
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11708.654623917859,
+                    13323.82703106391,
+                    13314.724906111427
+                ],
+                [
+                    11730.355758753562,
+                    13329.692162687668,
+                    13326.110985122861
+                ],
+                [
+                    11696.55425677517,
+                    13304.742176614005,
+                    13306.127044938077
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 3093.1495929008024,
+            "scoreError" : 346.74835334135383,
+            "scoreConfidence" : [
+                2746.4012395594486,
+                3439.897946242156
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2811.640231155538,
+                "50.0" : 3196.7520849762427,
+                "90.0" : 3250.545388604313,
+                "95.0" : 3250.545388604313,
+                "99.0" : 3250.545388604313,
+                "99.9" : 3250.545388604313,
+                "99.99" : 3250.545388604313,
+                "99.999" : 3250.545388604313,
+                "99.9999" : 3250.545388604313,
+                "100.0" : 3250.545388604313
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2826.116998686077,
+                    3250.545388604313,
+                    3247.768020740325
+                ],
+                [
+                    2820.5627690430083,
+                    3196.28472878314,
+                    3196.7520849762427
+                ],
+                [
+                    2811.640231155538,
+                    3244.312320960192,
+                    3244.3637931583835
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 3122.0276611038216,
+            "scoreError" : 348.9807966037056,
+            "scoreConfidence" : [
+                2773.046864500116,
+                3471.008457707527
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2843.861197248272,
+                "50.0" : 3225.4945702961986,
+                "90.0" : 3280.0974416640975,
+                "95.0" : 3280.0974416640975,
+                "99.0" : 3280.0974416640975,
+                "99.9" : 3280.0974416640975,
+                "99.99" : 3280.0974416640975,
+                "99.999" : 3280.0974416640975,
+                "99.9999" : 3280.0974416640975,
+                "100.0" : 3280.0974416640975
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2843.861197248272,
+                    3225.4945702961986,
+                    3224.285841135945
+                ],
+                [
+                    2851.2312950983455,
+                    3280.0974416640975,
+                    3279.2880277145277
+                ],
+                [
+                    2844.767137452957,
+                    3275.2140152716156,
+                    3274.009424052435
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "20"
+        },
+        "primaryMetric" : {
+            "score" : 3128.182505713841,
+            "scoreError" : 352.70588064925437,
+            "scoreConfidence" : [
+                2775.476625064586,
+                3480.8883863630954
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2847.3938357080215,
+                "50.0" : 3266.4757342822227,
+                "90.0" : 3270.3027372924234,
+                "95.0" : 3270.3027372924234,
+                "99.0" : 3270.3027372924234,
+                "99.9" : 3270.3027372924234,
+                "99.99" : 3270.3027372924234,
+                "99.999" : 3270.3027372924234,
+                "99.9999" : 3270.3027372924234,
+                "100.0" : 3270.3027372924234
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2849.4282134610653,
+                    3268.9208437052844,
+                    3268.7159804614453
+                ],
+                [
+                    2848.1879022834896,
+                    3268.6771978438255,
+                    3270.3027372924234
+                ],
+                [
+                    2847.3938357080215,
+                    3266.4757342822227,
+                    3265.5401063867894
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "22"
+        },
+        "primaryMetric" : {
+            "score" : 3153.7636821728956,
+            "scoreError" : 348.28080992188865,
+            "scoreConfidence" : [
+                2805.482872251007,
+                3502.0444920947843
+            ],
+            "scorePercentiles" : {
+                "0.0" : 2872.890876914191,
+                "50.0" : 3291.3750004584786,
+                "90.0" : 3293.583354708205,
+                "95.0" : 3293.583354708205,
+                "99.0" : 3293.583354708205,
+                "99.9" : 3293.583354708205,
+                "99.99" : 3293.583354708205,
+                "99.999" : 3293.583354708205,
+                "99.9999" : 3293.583354708205,
+                "100.0" : 3293.583354708205
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    2881.1285187344015,
+                    3293.447094293651,
+                    3293.583354708205
+                ],
+                [
+                    2878.301292488309,
+                    3291.590790172244,
+                    3291.8708447988733
+                ],
+                [
+                    2872.890876914191,
+                    3289.685366987708,
+                    3291.3750004584786
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 754.1516892747499,
+            "scoreError" : 79.62590658677267,
+            "scoreConfidence" : [
+                674.5257826879772,
+                833.7775958615225
+            ],
+            "scorePercentiles" : {
+                "0.0" : 689.815108835357,
+                "50.0" : 785.5121091682423,
+                "90.0" : 786.1593997389693,
+                "95.0" : 786.1593997389693,
+                "99.0" : 786.1593997389693,
+                "99.9" : 786.1593997389693,
+                "99.99" : 786.1593997389693,
+                "99.999" : 786.1593997389693,
+                "99.9999" : 786.1593997389693,
+                "100.0" : 786.1593997389693
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    689.815108835357,
+                    785.5741258481343,
+                    785.7560041624466
+                ],
+                [
+                    691.9066761852858,
+                    785.3832547314797,
+                    785.5121091682423
+                ],
+                [
+                    691.2113743557514,
+                    786.0471504470823,
+                    786.1593997389693
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 755.2970915553086,
+            "scoreError" : 80.71289326564478,
+            "scoreConfidence" : [
+                674.5841982896638,
+                836.0099848209534
+            ],
+            "scorePercentiles" : {
+                "0.0" : 689.3261645820087,
+                "50.0" : 787.1481893418177,
+                "90.0" : 787.4757133907947,
+                "95.0" : 787.4757133907947,
+                "99.0" : 787.4757133907947,
+                "99.9" : 787.4757133907947,
+                "99.99" : 787.4757133907947,
+                "99.999" : 787.4757133907947,
+                "99.9999" : 787.4757133907947,
+                "100.0" : 787.4757133907947
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    692.9483094744046,
+                    787.3657829481007,
+                    787.4757133907947
+                ],
+                [
+                    689.3261645820087,
+                    787.3677945014489,
+                    787.4191934017078
+                ],
+                [
+                    691.5283032251681,
+                    787.094373132326,
+                    787.1481893418177
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "20"
+        },
+        "primaryMetric" : {
+            "score" : 762.6279992742304,
+            "scoreError" : 80.34724687095452,
+            "scoreConfidence" : [
+                682.2807524032759,
+                842.975246145185
+            ],
+            "scorePercentiles" : {
+                "0.0" : 697.3980478833844,
+                "50.0" : 794.3445633019713,
+                "90.0" : 795.0085959260741,
+                "95.0" : 795.0085959260741,
+                "99.0" : 795.0085959260741,
+                "99.9" : 795.0085959260741,
+                "99.99" : 795.0085959260741,
+                "99.999" : 795.0085959260741,
+                "99.9999" : 795.0085959260741,
+                "100.0" : 795.0085959260741
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    698.4229637307766,
+                    794.3445633019713,
+                    793.9489816526121
+                ],
+                [
+                    700.8456003388544,
+                    794.5945236436294,
+                    794.4315156786275
+                ],
+                [
+                    697.3980478833844,
+                    795.0085959260741,
+                    794.6572013121432
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "22"
+        },
+        "primaryMetric" : {
+            "score" : 764.3750602448353,
+            "scoreError" : 80.68894396184207,
+            "scoreConfidence" : [
+                683.6861162829932,
+                845.0640042066773
+            ],
+            "scorePercentiles" : {
+                "0.0" : 699.7014110614892,
+                "50.0" : 796.2602155288497,
+                "90.0" : 796.5952973580177,
+                "95.0" : 796.5952973580177,
+                "99.0" : 796.5952973580177,
+                "99.9" : 796.5952973580177,
+                "99.99" : 796.5952973580177,
+                "99.999" : 796.5952973580177,
+                "99.9999" : 796.5952973580177,
+                "100.0" : 796.5952973580177
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    701.0430727080461,
+                    796.5131224765947,
+                    796.2602155288497
+                ],
+                [
+                    699.7014110614892,
+                    796.5952973580177,
+                    796.4591521020396
+                ],
+                [
+                    700.3195823264297,
+                    796.4214650040959,
+                    796.0622236379558
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 49502.210903830506,
+            "scoreError" : 2180.6384847775116,
+            "scoreConfidence" : [
+                47321.572419052995,
+                51682.84938860802
+            ],
+            "scorePercentiles" : {
+                "0.0" : 47655.33428821115,
+                "50.0" : 50230.826123322026,
+                "90.0" : 50501.60093043969,
+                "95.0" : 50501.60093043969,
+                "99.0" : 50501.60093043969,
+                "99.9" : 50501.60093043969,
+                "99.99" : 50501.60093043969,
+                "99.999" : 50501.60093043969,
+                "99.9999" : 50501.60093043969,
+                "100.0" : 50501.60093043969
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    50391.905166447745,
+                    47844.68146107775,
+                    50442.44941352523
+                ],
+                [
+                    50478.94781912993,
+                    47841.85382307288,
+                    50501.60093043969
+                ],
+                [
+                    50132.29910924824,
+                    47655.33428821115,
+                    50230.826123322026
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 43388.646770714564,
+            "scoreError" : 1824.0070133783458,
+            "scoreConfidence" : [
+                41564.63975733622,
+                45212.65378409291
+            ],
+            "scorePercentiles" : {
+                "0.0" : 41908.68208838806,
+                "50.0" : 44109.81981657809,
+                "90.0" : 44426.17983992118,
+                "95.0" : 44426.17983992118,
+                "99.0" : 44426.17983992118,
+                "99.9" : 44426.17983992118,
+                "99.99" : 44426.17983992118,
+                "99.999" : 44426.17983992118,
+                "99.9999" : 44426.17983992118,
+                "100.0" : 44426.17983992118
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    42959.98334772449,
+                    41908.68208838806,
+                    44426.17983992118
+                ],
+                [
+                    44146.3158770916,
+                    41945.847090831056,
+                    44231.11069648557
+                ],
+                [
+                    44109.81981657809,
+                    42362.86628906258,
+                    44407.0158903484
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 13073.928567644358,
+            "scoreError" : 455.0289758555767,
+            "scoreConfidence" : [
+                12618.89959178878,
+                13528.957543499935
+            ],
+            "scorePercentiles" : {
+                "0.0" : 12698.65628972352,
+                "50.0" : 13248.105996963992,
+                "90.0" : 13266.864886797623,
+                "95.0" : 13266.864886797623,
+                "99.0" : 13266.864886797623,
+                "99.9" : 13266.864886797623,
+                "99.99" : 13266.864886797623,
+                "99.999" : 13266.864886797623,
+                "99.9999" : 13266.864886797623,
+                "100.0" : 13266.864886797623
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    13249.94147626679,
+                    12718.420804415366,
+                    13246.593618380437
+                ],
+                [
+                    13264.846389306727,
+                    12698.65628972352,
+                    13266.864886797623
+                ],
+                [
+                    13248.105996963992,
+                    12722.25928141193,
+                    13249.668365532834
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 12501.577464061656,
+            "scoreError" : 444.6915074792037,
+            "scoreConfidence" : [
+                12056.885956582453,
+                12946.26897154086
+            ],
+            "scorePercentiles" : {
+                "0.0" : 12037.42229550993,
+                "50.0" : 12638.508815275294,
+                "90.0" : 12718.100722495303,
+                "95.0" : 12718.100722495303,
+                "99.0" : 12718.100722495303,
+                "99.9" : 12718.100722495303,
+                "99.99" : 12718.100722495303,
+                "99.999" : 12718.100722495303,
+                "99.9999" : 12718.100722495303,
+                "100.0" : 12718.100722495303
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    12718.100722495303,
+                    12228.434844498648,
+                    12710.659628481108
+                ],
+                [
+                    12689.742493400452,
+                    12224.186682821746,
+                    12716.142434180676
+                ],
+                [
+                    12550.999259891745,
+                    12037.42229550993,
+                    12638.508815275294
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 3284.772083671136,
+            "scoreError" : 81.60408630341242,
+            "scoreConfidence" : [
+                3203.1679973677237,
+                3366.3761699745482
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3189.1372114368223,
+                "50.0" : 3277.284569032633,
+                "90.0" : 3329.641950984068,
+                "95.0" : 3329.641950984068,
+                "99.0" : 3329.641950984068,
+                "99.9" : 3329.641950984068,
+                "99.99" : 3329.641950984068,
+                "99.999" : 3329.641950984068,
+                "99.9999" : 3329.641950984068,
+                "100.0" : 3329.641950984068
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3327.848346895691,
+                    3275.004005396584,
+                    3255.6325331662183
+                ],
+                [
+                    3329.641950984068,
+                    3277.284569032633,
+                    3251.9750289858857
+                ],
+                [
+                    3327.825256708278,
+                    3328.599850434049,
+                    3189.1372114368223
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 3230.6479624325852,
+            "scoreError" : 94.90664496969116,
+            "scoreConfidence" : [
+                3135.741317462894,
+                3325.5546074022764
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3153.754348280746,
+                "50.0" : 3267.453613803697,
+                "90.0" : 3273.684632912715,
+                "95.0" : 3273.684632912715,
+                "99.0" : 3273.684632912715,
+                "99.9" : 3273.684632912715,
+                "99.99" : 3273.684632912715,
+                "99.999" : 3273.684632912715,
+                "99.9999" : 3273.684632912715,
+                "100.0" : 3273.684632912715
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3273.684632912715,
+                    3153.754348280746,
+                    3267.453613803697
+                ],
+                [
+                    3258.1025910932767,
+                    3156.428497056323,
+                    3271.285152033579
+                ],
+                [
+                    3267.8386497664164,
+                    3156.5328019876342,
+                    3270.7513749588757
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 823.4316998112048,
+            "scoreError" : 25.939794171726785,
+            "scoreConfidence" : [
+                797.491905639478,
+                849.3714939829316
+            ],
+            "scorePercentiles" : {
+                "0.0" : 802.397710235852,
+                "50.0" : 833.5045147381396,
+                "90.0" : 834.2711558096636,
+                "95.0" : 834.2711558096636,
+                "99.0" : 834.2711558096636,
+                "99.9" : 834.2711558096636,
+                "99.99" : 834.2711558096636,
+                "99.999" : 834.2711558096636,
+                "99.9999" : 834.2711558096636,
+                "100.0" : 834.2711558096636
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    833.3595645394191,
+                    833.7893620355246,
+                    803.4402352532005
+                ],
+                [
+                    833.5045147381396,
+                    833.6484430823392,
+                    802.7288730250546
+                ],
+                [
+                    833.7454395816497,
+                    834.2711558096636,
+                    802.397710235852
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "18"
+        },
+        "primaryMetric" : {
+            "score" : 809.6322316499339,
+            "scoreError" : 16.253938423035617,
+            "scoreConfidence" : [
+                793.3782932268983,
+                825.8861700729695
+            ],
+            "scorePercentiles" : {
+                "0.0" : 796.0190033616673,
+                "50.0" : 811.8755776600925,
+                "90.0" : 819.4075214305818,
+                "95.0" : 819.4075214305818,
+                "99.0" : 819.4075214305818,
+                "99.9" : 819.4075214305818,
+                "99.99" : 819.4075214305818,
+                "99.999" : 819.4075214305818,
+                "99.9999" : 819.4075214305818,
+                "100.0" : 819.4075214305818
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    818.5031196447524,
+                    798.4081991109515,
+                    810.7415320198564
+                ],
+                [
+                    819.4075214305818,
+                    797.8467244104413,
+                    811.8755776600925
+                ],
+                [
+                    819.2693405867176,
+                    796.0190033616673,
+                    814.619066624345
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "sizeS" : "8",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 647.1867941761544,
+            "scoreError" : 3.7818910980181824,
+            "scoreConfidence" : [
+                643.4049030781363,
+                650.9686852741726
+            ],
+            "scorePercentiles" : {
+                "0.0" : 644.213016835251,
+                "50.0" : 647.9407506823107,
+                "90.0" : 649.5076908495989,
+                "95.0" : 649.5076908495989,
+                "99.0" : 649.5076908495989,
+                "99.9" : 649.5076908495989,
+                "99.99" : 649.5076908495989,
+                "99.999" : 649.5076908495989,
+                "99.9999" : 649.5076908495989,
+                "100.0" : 649.5076908495989
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    647.9407506823107,
+                    647.9116977621215,
+                    648.0067141135914
+                ],
+                [
+                    644.3013790717916,
+                    644.213016835251,
+                    644.3666218206417
+                ],
+                [
+                    649.2607806796373,
+                    649.1724957704467,
+                    649.5076908495989
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "sizeS" : "16",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 221.44655051938983,
+            "scoreError" : 13.909079062854891,
+            "scoreConfidence" : [
+                207.53747145653494,
+                235.35562958224472
+            ],
+            "scorePercentiles" : {
+                "0.0" : 204.8240311023435,
+                "50.0" : 224.0518031742482,
+                "90.0" : 228.35683319745365,
+                "95.0" : 228.35683319745365,
+                "99.0" : 228.35683319745365,
+                "99.9" : 228.35683319745365,
+                "99.99" : 228.35683319745365,
+                "99.999" : 228.35683319745365,
+                "99.9999" : 228.35683319745365,
+                "100.0" : 228.35683319745365
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    204.8240311023435,
+                    224.0518031742482,
+                    224.01327724454094
+                ],
+                [
+                    213.56829048978287,
+                    227.33864065781717,
+                    227.26232787518387
+                ],
+                [
+                    215.4858636393008,
+                    228.11788729383744,
+                    228.35683319745365
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "sizeS" : "16",
+            "sizeT" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 222.37137540024008,
+            "scoreError" : 5.559984450616226,
+            "scoreConfidence" : [
+                216.81139094962387,
+                227.9313598508563
+            ],
+            "scorePercentiles" : {
+                "0.0" : 216.58675683600657,
+                "50.0" : 222.70165156756354,
+                "90.0" : 226.16307043469672,
+                "95.0" : 226.16307043469672,
+                "99.0" : 226.16307043469672,
+                "99.9" : 226.16307043469672,
+                "99.99" : 226.16307043469672,
+                "99.999" : 226.16307043469672,
+                "99.9999" : 226.16307043469672,
+                "100.0" : 226.16307043469672
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    216.58675683600657,
+                    224.1371309829792,
+                    224.10075995959227
+                ],
+                [
+                    220.35363962879737,
+                    226.1492340225272,
+                    226.16307043469672
+                ],
+                [
+                    218.4864923788299,
+                    222.66364279116806,
+                    222.70165156756354
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "sizeS" : "46",
+            "sizeT" : "46"
+        },
+        "primaryMetric" : {
+            "score" : 75.72740097592362,
+            "scoreError" : 3.4132048806359965,
+            "scoreConfidence" : [
+                72.31419609528761,
+                79.14060585655962
+            ],
+            "scorePercentiles" : {
+                "0.0" : 72.72645314188827,
+                "50.0" : 76.95022373203598,
+                "90.0" : 77.27206641844197,
+                "95.0" : 77.27206641844197,
+                "99.0" : 77.27206641844197,
+                "99.9" : 77.27206641844197,
+                "99.99" : 77.27206641844197,
+                "99.999" : 77.27206641844197,
+                "99.9999" : 77.27206641844197,
+                "100.0" : 77.27206641844197
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    72.94548866083792,
+                    76.9410767018892,
+                    76.98245623710076
+                ],
+                [
+                    73.43212452775425,
+                    76.95022373203598,
+                    77.03967211843509
+                ],
+                [
+                    72.72645314188827,
+                    77.27206641844197,
+                    77.25704724492914
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "sizeS" : "8",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 161.53980160918428,
+            "scoreError" : 1.870134697144832,
+            "scoreConfidence" : [
+                159.66966691203945,
+                163.40993630632912
+            ],
+            "scorePercentiles" : {
+                "0.0" : 159.03524580466686,
+                "50.0" : 161.70340128551297,
+                "90.0" : 162.49031781942435,
+                "95.0" : 162.49031781942435,
+                "99.0" : 162.49031781942435,
+                "99.9" : 162.49031781942435,
+                "99.99" : 162.49031781942435,
+                "99.999" : 162.49031781942435,
+                "99.9999" : 162.49031781942435,
+                "100.0" : 162.49031781942435
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    162.07445600022487,
+                    160.57583425222728,
+                    159.03524580466686
+                ],
+                [
+                    161.70340128551297,
+                    161.68507671480938,
+                    161.50123217365086
+                ],
+                [
+                    162.49031781942435,
+                    162.38764340457288,
+                    162.40500702756916
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "sizeS" : "16",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 53.46499347299082,
+            "scoreError" : 5.867414373810976,
+            "scoreConfidence" : [
+                47.597579099179846,
+                59.33240784680179
+            ],
+            "scorePercentiles" : {
+                "0.0" : 45.94204023414223,
+                "50.0" : 53.479703035485905,
+                "90.0" : 56.75676236356654,
+                "95.0" : 56.75676236356654,
+                "99.0" : 56.75676236356654,
+                "99.9" : 56.75676236356654,
+                "99.99" : 56.75676236356654,
+                "99.999" : 56.75676236356654,
+                "99.9999" : 56.75676236356654,
+                "100.0" : 56.75676236356654
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    45.94204023414223,
+                    53.36608236984878,
+                    53.34870923323005
+                ],
+                [
+                    53.479703035485905,
+                    56.75676236356654,
+                    56.74956202778669
+                ],
+                [
+                    50.30251209236314,
+                    55.63350741616807,
+                    55.60606248432599
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "sizeS" : "16",
+            "sizeT" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 51.8439407388088,
+            "scoreError" : 4.484671845858238,
+            "scoreConfidence" : [
+                47.35926889295056,
+                56.328612584667034
+            ],
+            "scorePercentiles" : {
+                "0.0" : 47.27989289225459,
+                "50.0" : 53.492635674195995,
+                "90.0" : 53.757214799388535,
+                "95.0" : 53.757214799388535,
+                "99.0" : 53.757214799388535,
+                "99.9" : 53.757214799388535,
+                "99.99" : 53.757214799388535,
+                "99.999" : 53.757214799388535,
+                "99.9999" : 53.757214799388535,
+                "100.0" : 53.757214799388535
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    47.27989289225459,
+                    53.74038598329412,
+                    53.757214799388535
+                ],
+                [
+                    49.09660954034463,
+                    53.492635674195995,
+                    53.4736155993692
+                ],
+                [
+                    48.6573067020978,
+                    53.55710707161856,
+                    53.54069838671571
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "sizeS" : "46",
+            "sizeT" : "46"
+        },
+        "primaryMetric" : {
+            "score" : 18.90725915615425,
+            "scoreError" : 0.7804465567066122,
+            "scoreConfidence" : [
+                18.12681259944764,
+                19.68770571286086
+            ],
+            "scorePercentiles" : {
+                "0.0" : 18.083064307930712,
+                "50.0" : 19.14917876700788,
+                "90.0" : 19.249811875079445,
+                "95.0" : 19.249811875079445,
+                "99.0" : 19.249811875079445,
+                "99.9" : 19.249811875079445,
+                "99.99" : 19.249811875079445,
+                "99.999" : 19.249811875079445,
+                "99.9999" : 19.249811875079445,
+                "100.0" : 19.249811875079445
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    18.411711432236366,
+                    19.248074461825105,
+                    19.249811875079445
+                ],
+                [
+                    18.083064307930712,
+                    19.144228606307806,
+                    19.14917876700788
+                ],
+                [
+                    18.415538642092212,
+                    19.23014612115009,
+                    19.233578191758667
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "sizeS" : "8",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 39.624521240283165,
+            "scoreError" : 1.0488684170365838,
+            "scoreConfidence" : [
+                38.57565282324658,
+                40.67338965731975
+            ],
+            "scorePercentiles" : {
+                "0.0" : 39.125842021507026,
+                "50.0" : 39.28708732835848,
+                "90.0" : 40.46096128601025,
+                "95.0" : 40.46096128601025,
+                "99.0" : 40.46096128601025,
+                "99.9" : 40.46096128601025,
+                "99.99" : 40.46096128601025,
+                "99.999" : 40.46096128601025,
+                "99.9999" : 40.46096128601025,
+                "100.0" : 40.46096128601025
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    39.28708732835848,
+                    39.257636656375865,
+                    39.33546447082681
+                ],
+                [
+                    40.46096128601025,
+                    40.45441613380897,
+                    40.43723948355265
+                ],
+                [
+                    39.125842021507026,
+                    39.13170218795689,
+                    39.13034159415156
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "sizeS" : "16",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 13.274726190126739,
+            "scoreError" : 1.3039809195863328,
+            "scoreConfidence" : [
+                11.970745270540405,
+                14.578707109713072
+            ],
+            "scorePercentiles" : {
+                "0.0" : 11.624137750149469,
+                "50.0" : 13.414896159016187,
+                "90.0" : 13.918947078802928,
+                "95.0" : 13.918947078802928,
+                "99.0" : 13.918947078802928,
+                "99.9" : 13.918947078802928,
+                "99.99" : 13.918947078802928,
+                "99.999" : 13.918947078802928,
+                "99.9999" : 13.918947078802928,
+                "100.0" : 13.918947078802928
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    11.624137750149469,
+                    13.405266857198068,
+                    13.414896159016187
+                ],
+                [
+                    12.854352745998838,
+                    13.914432007900592,
+                    13.918947078802928
+                ],
+                [
+                    12.6445060460198,
+                    13.830851172745358,
+                    13.86514589330942
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "sizeS" : "16",
+            "sizeT" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 13.461700151375673,
+            "scoreError" : 0.8943109617012777,
+            "scoreConfidence" : [
+                12.567389189674396,
+                14.356011113076951
+            ],
+            "scorePercentiles" : {
+                "0.0" : 12.216580937652516,
+                "50.0" : 13.474822827150453,
+                "90.0" : 13.885334874438747,
+                "95.0" : 13.885334874438747,
+                "99.0" : 13.885334874438747,
+                "99.9" : 13.885334874438747,
+                "99.99" : 13.885334874438747,
+                "99.999" : 13.885334874438747,
+                "99.9999" : 13.885334874438747,
+                "100.0" : 13.885334874438747
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    13.474822827150453,
+                    13.885334874438747,
+                    13.880361830891577
+                ],
+                [
+                    12.216580937652516,
+                    13.34324755612668,
+                    13.333659592726146
+                ],
+                [
+                    13.312995951652352,
+                    13.855167548603434,
+                    13.85313024313915
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "sizeS" : "46",
+            "sizeT" : "46"
+        },
+        "primaryMetric" : {
+            "score" : 4.785473823060737,
+            "scoreError" : 0.09157201524952041,
+            "scoreConfidence" : [
+                4.693901807811217,
+                4.8770458383102575
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4.680392565751563,
+                "50.0" : 4.814195849833611,
+                "90.0" : 4.826405956632901,
+                "95.0" : 4.826405956632901,
+                "99.0" : 4.826405956632901,
+                "99.9" : 4.826405956632901,
+                "99.99" : 4.826405956632901,
+                "99.999" : 4.826405956632901,
+                "99.9999" : 4.826405956632901,
+                "100.0" : 4.826405956632901
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    4.825325890186347,
+                    4.826405956632901,
+                    4.824177954164531
+                ],
+                [
+                    4.7068701547087,
+                    4.816073932095836,
+                    4.814195849833611
+                ],
+                [
+                    4.680392565751563,
+                    4.788402722758373,
+                    4.787419381414775
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "sizeS" : "8",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 9.89982000722811,
+            "scoreError" : 0.33280867548870424,
+            "scoreConfidence" : [
+                9.567011331739405,
+                10.232628682716815
+            ],
+            "scorePercentiles" : {
+                "0.0" : 9.734914127138898,
+                "50.0" : 9.791439108813481,
+                "90.0" : 10.16822709776698,
+                "95.0" : 10.16822709776698,
+                "99.0" : 10.16822709776698,
+                "99.9" : 10.16822709776698,
+                "99.99" : 10.16822709776698,
+                "99.999" : 10.16822709776698,
+                "99.9999" : 10.16822709776698,
+                "100.0" : 10.16822709776698
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    9.734914127138898,
+                    9.7470755601293,
+                    9.758007599891789
+                ],
+                [
+                    9.784627853561423,
+                    9.791439108813481,
+                    9.79476878246331
+                ],
+                [
+                    10.163168560261175,
+                    10.156151375026647,
+                    10.16822709776698
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "sizeS" : "16",
+            "sizeT" : "16"
+        },
+        "primaryMetric" : {
+            "score" : 3.46851120656168,
+            "scoreError" : 0.15187211446914156,
+            "scoreConfidence" : [
+                3.3166390920925384,
+                3.620383321030822
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.3073638224575204,
+                "50.0" : 3.493397740973071,
+                "90.0" : 3.565974969542478,
+                "95.0" : 3.565974969542478,
+                "99.0" : 3.565974969542478,
+                "99.9" : 3.565974969542478,
+                "99.99" : 3.565974969542478,
+                "99.999" : 3.565974969542478,
+                "99.9999" : 3.565974969542478,
+                "100.0" : 3.565974969542478
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.347335173684363,
+                    3.4981533045812223,
+                    3.493397740973071
+                ],
+                [
+                    3.538081938209828,
+                    3.565974969542478,
+                    3.559943009691145
+                ],
+                [
+                    3.3073638224575204,
+                    3.4566397768670205,
+                    3.4497111230484716
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "sizeS" : "16",
+            "sizeT" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 3.320243517610311,
+            "scoreError" : 0.2033555773696232,
+            "scoreConfidence" : [
+                3.1168879402406877,
+                3.5235990949799345
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.128674595620351,
+                "50.0" : 3.320454421215615,
+                "90.0" : 3.4454338235067277,
+                "95.0" : 3.4454338235067277,
+                "99.0" : 3.4454338235067277,
+                "99.9" : 3.4454338235067277,
+                "99.99" : 3.4454338235067277,
+                "99.999" : 3.4454338235067277,
+                "99.9999" : 3.4454338235067277,
+                "100.0" : 3.4454338235067277
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.136340117029289,
+                    3.320454421215615,
+                    3.3238979868107763
+                ],
+                [
+                    3.444734865360478,
+                    3.444531840232602,
+                    3.4454338235067277
+                ],
+                [
+                    3.128674595620351,
+                    3.3184243644982026,
+                    3.3196996442187574
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "sizeS" : "46",
+            "sizeT" : "46"
+        },
+        "primaryMetric" : {
+            "score" : 1.2002835531139824,
+            "scoreError" : 0.00981363481252792,
+            "scoreConfidence" : [
+                1.1904699183014544,
+                1.2100971879265103
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.1888399240420688,
+                "50.0" : 1.201983800331197,
+                "90.0" : 1.2066745006827952,
+                "95.0" : 1.2066745006827952,
+                "99.0" : 1.2066745006827952,
+                "99.9" : 1.2066745006827952,
+                "99.99" : 1.2066745006827952,
+                "99.999" : 1.2066745006827952,
+                "99.9999" : 1.2066745006827952,
+                "100.0" : 1.2066745006827952
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.2041308148128782,
+                    1.201983800331197,
+                    1.1958477616169232
+                ],
+                [
+                    1.2063945371173965,
+                    1.2008293589671861,
+                    1.1888399240420688
+                ],
+                [
+                    1.2066745006827952,
+                    1.202267767625606,
+                    1.1955835128297898
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 940.8855661086935,
+            "scoreError" : 3.4223536604405456,
+            "scoreConfidence" : [
+                937.463212448253,
+                944.3079197691341
+            ],
+            "scorePercentiles" : {
+                "0.0" : 937.7496835501817,
+                "50.0" : 941.8177828251877,
+                "90.0" : 942.7714879849624,
+                "95.0" : 942.7714879849624,
+                "99.0" : 942.7714879849624,
+                "99.9" : 942.7714879849624,
+                "99.99" : 942.7714879849624,
+                "99.999" : 942.7714879849624,
+                "99.9999" : 942.7714879849624,
+                "100.0" : 942.7714879849624
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    941.7459055359798,
+                    937.7496835501817,
+                    941.8177828251877
+                ],
+                [
+                    942.5585323480059,
+                    938.2784667462907,
+                    942.1225315535781
+                ],
+                [
+                    942.2929621202793,
+                    938.6327423137767,
+                    942.7714879849624
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 488.3817704649295,
+            "scoreError" : 0.9632956295974225,
+            "scoreConfidence" : [
+                487.41847483533206,
+                489.34506609452694
+            ],
+            "scorePercentiles" : {
+                "0.0" : 487.5965761315435,
+                "50.0" : 488.38102095368896,
+                "90.0" : 489.3325381106533,
+                "95.0" : 489.3325381106533,
+                "99.0" : 489.3325381106533,
+                "99.9" : 489.3325381106533,
+                "99.99" : 489.3325381106533,
+                "99.999" : 489.3325381106533,
+                "99.9999" : 489.3325381106533,
+                "100.0" : 489.3325381106533
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    489.3325381106533,
+                    488.25101439577145,
+                    488.452599571124
+                ],
+                [
+                    488.9109274511842,
+                    487.812757100369,
+                    487.5965761315435
+                ],
+                [
+                    488.83932517783984,
+                    488.38102095368896,
+                    487.85917529219154
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 242.61365637195175,
+            "scoreError" : 1.9134005293665914,
+            "scoreConfidence" : [
+                240.70025584258516,
+                244.52705690131833
+            ],
+            "scorePercentiles" : {
+                "0.0" : 241.26479708971638,
+                "50.0" : 242.34398252470044,
+                "90.0" : 244.24675229669347,
+                "95.0" : 244.24675229669347,
+                "99.0" : 244.24675229669347,
+                "99.9" : 244.24675229669347,
+                "99.99" : 244.24675229669347,
+                "99.999" : 244.24675229669347,
+                "99.9999" : 244.24675229669347,
+                "100.0" : 244.24675229669347
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    241.4397517030553,
+                    244.01334660280847,
+                    242.34398252470044
+                ],
+                [
+                    241.26479708971638,
+                    243.7866250915524,
+                    242.12992466789876
+                ],
+                [
+                    241.68321929043304,
+                    244.24675229669347,
+                    242.61450808070728
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 120.85955941053479,
+            "scoreError" : 3.7280365091521346,
+            "scoreConfidence" : [
+                117.13152290138265,
+                124.58759591968692
+            ],
+            "scorePercentiles" : {
+                "0.0" : 116.6453650373679,
+                "50.0" : 120.06786377622636,
+                "90.0" : 122.98037514837691,
+                "95.0" : 122.98037514837691,
+                "99.0" : 122.98037514837691,
+                "99.9" : 122.98037514837691,
+                "99.99" : 122.98037514837691,
+                "99.999" : 122.98037514837691,
+                "99.9999" : 122.98037514837691,
+                "100.0" : 122.98037514837691
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    119.52903855196647,
+                    122.9665550276074,
+                    122.93574806155075
+                ],
+                [
+                    119.69657382921002,
+                    122.98037514837691,
+                    122.8541072830955
+                ],
+                [
+                    116.6453650373679,
+                    120.06040797941165,
+                    120.06786377622636
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 236.15568531499392,
+            "scoreError" : 0.9343184697856747,
+            "scoreConfidence" : [
+                235.22136684520825,
+                237.09000378477958
+            ],
+            "scorePercentiles" : {
+                "0.0" : 235.18235796901774,
+                "50.0" : 236.27009630502064,
+                "90.0" : 236.71074512210538,
+                "95.0" : 236.71074512210538,
+                "99.0" : 236.71074512210538,
+                "99.9" : 236.71074512210538,
+                "99.99" : 236.71074512210538,
+                "99.999" : 236.71074512210538,
+                "99.9999" : 236.71074512210538,
+                "100.0" : 236.71074512210538
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    236.69176980161902,
+                    235.72484818055145,
+                    236.71074512210538
+                ],
+                [
+                    236.5139670958129,
+                    235.52594275864817,
+                    236.60991270130964
+                ],
+                [
+                    236.27009630502064,
+                    235.18235796901774,
+                    236.17152790086047
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 120.86574418461974,
+            "scoreError" : 0.5189869088755038,
+            "scoreConfidence" : [
+                120.34675727574424,
+                121.38473109349525
+            ],
+            "scorePercentiles" : {
+                "0.0" : 120.43954658603056,
+                "50.0" : 121.00004093431384,
+                "90.0" : 121.18853293499319,
+                "95.0" : 121.18853293499319,
+                "99.0" : 121.18853293499319,
+                "99.9" : 121.18853293499319,
+                "99.99" : 121.18853293499319,
+                "99.999" : 121.18853293499319,
+                "99.9999" : 121.18853293499319,
+                "100.0" : 121.18853293499319
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    120.4515814273751,
+                    120.43954658603056,
+                    120.51591497018322
+                ],
+                [
+                    121.11987061106079,
+                    121.03536572279732,
+                    120.91070696956106
+                ],
+                [
+                    121.18853293499319,
+                    121.13013750526268,
+                    121.00004093431384
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 59.50769061781115,
+            "scoreError" : 2.491321344548971,
+            "scoreConfidence" : [
+                57.01636927326218,
+                61.99901196236012
+            ],
+            "scorePercentiles" : {
+                "0.0" : 55.55904745390125,
+                "50.0" : 59.99867807312548,
+                "90.0" : 60.12785914176523,
+                "95.0" : 60.12785914176523,
+                "99.0" : 60.12785914176523,
+                "99.9" : 60.12785914176523,
+                "99.99" : 60.12785914176523,
+                "99.999" : 60.12785914176523,
+                "99.9999" : 60.12785914176523,
+                "100.0" : 60.12785914176523
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    59.95975139329583,
+                    59.99867807312548,
+                    55.55904745390125
+                ],
+                [
+                    59.9533037865786,
+                    59.86773438632764,
+                    60.034152404427
+                ],
+                [
+                    60.062748602814544,
+                    60.00594031806476,
+                    60.12785914176523
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 30.035431778783398,
+            "scoreError" : 0.7720115667275985,
+            "scoreConfidence" : [
+                29.2634202120558,
+                30.807443345510997
+            ],
+            "scorePercentiles" : {
+                "0.0" : 29.36866906331322,
+                "50.0" : 30.259863504658576,
+                "90.0" : 30.38089095732961,
+                "95.0" : 30.38089095732961,
+                "99.0" : 30.38089095732961,
+                "99.9" : 30.38089095732961,
+                "99.99" : 30.38089095732961,
+                "99.999" : 30.38089095732961,
+                "99.9999" : 30.38089095732961,
+                "100.0" : 30.38089095732961
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    29.36866906331322,
+                    30.257560790621415,
+                    30.259863504658576
+                ],
+                [
+                    29.524440119872914,
+                    30.377688781433207,
+                    30.378318426064762
+                ],
+                [
+                    29.393690138840228,
+                    30.377764226916675,
+                    30.38089095732961
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 57.78449169282468,
+            "scoreError" : 0.25174915581279583,
+            "scoreConfidence" : [
+                57.532742537011885,
+                58.036240848637476
+            ],
+            "scorePercentiles" : {
+                "0.0" : 57.514251611175986,
+                "50.0" : 57.76518675310032,
+                "90.0" : 57.942565521247296,
+                "95.0" : 57.942565521247296,
+                "99.0" : 57.942565521247296,
+                "99.9" : 57.942565521247296,
+                "99.99" : 57.942565521247296,
+                "99.999" : 57.942565521247296,
+                "99.9999" : 57.942565521247296,
+                "100.0" : 57.942565521247296
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    57.76518675310032,
+                    57.514251611175986,
+                    57.74584132456393
+                ],
+                [
+                    57.92848099338778,
+                    57.693013889485805,
+                    57.942565521247296
+                ],
+                [
+                    57.89925314595382,
+                    57.64918655421162,
+                    57.92264544229567
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 29.651491243464402,
+            "scoreError" : 0.12291674794846726,
+            "scoreConfidence" : [
+                29.528574495515937,
+                29.77440799141287
+            ],
+            "scorePercentiles" : {
+                "0.0" : 29.564685568469237,
+                "50.0" : 29.628667539981386,
+                "90.0" : 29.750422375906208,
+                "95.0" : 29.750422375906208,
+                "99.0" : 29.750422375906208,
+                "99.9" : 29.750422375906208,
+                "99.99" : 29.750422375906208,
+                "99.999" : 29.750422375906208,
+                "99.9999" : 29.750422375906208,
+                "100.0" : 29.750422375906208
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    29.564685568469237,
+                    29.571865226878806,
+                    29.583432053296068
+                ],
+                [
+                    29.712168387905113,
+                    29.750422375906208,
+                    29.628667539981386
+                ],
+                [
+                    29.70578937525705,
+                    29.72982764429422,
+                    29.61656301919153
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 14.966346077321589,
+            "scoreError" : 0.14083917460810721,
+            "scoreConfidence" : [
+                14.825506902713482,
+                15.107185251929696
+            ],
+            "scorePercentiles" : {
+                "0.0" : 14.870153350883559,
+                "50.0" : 14.965133247853384,
+                "90.0" : 15.071977759947723,
+                "95.0" : 15.071977759947723,
+                "99.0" : 15.071977759947723,
+                "99.9" : 15.071977759947723,
+                "99.99" : 15.071977759947723,
+                "99.999" : 15.071977759947723,
+                "99.9999" : 15.071977759947723,
+                "100.0" : 15.071977759947723
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    14.872909128391527,
+                    15.07088173326791,
+                    14.966466458528696
+                ],
+                [
+                    14.876996820055009,
+                    15.054670031738755,
+                    14.947926165227734
+                ],
+                [
+                    14.870153350883559,
+                    15.071977759947723,
+                    14.965133247853384
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 7.4929123396556365,
+            "scoreError" : 0.17711661607330295,
+            "scoreConfidence" : [
+                7.315795723582333,
+                7.67002895572894
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7.344177741488789,
+                "50.0" : 7.560633453264906,
+                "90.0" : 7.5680383531645585,
+                "95.0" : 7.5680383531645585,
+                "99.0" : 7.5680383531645585,
+                "99.9" : 7.5680383531645585,
+                "99.99" : 7.5680383531645585,
+                "99.999" : 7.5680383531645585,
+                "99.9999" : 7.5680383531645585,
+                "100.0" : 7.5680383531645585
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    7.352927179314497,
+                    7.5680383531645585,
+                    7.568031280437031
+                ],
+                [
+                    7.344177741488789,
+                    7.558938968199607,
+                    7.561108925864961
+                ],
+                [
+                    7.360537627959725,
+                    7.560633453264906,
+                    7.561817527206654
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 14.12707487278992,
+            "scoreError" : 0.0705701121612911,
+            "scoreConfidence" : [
+                14.056504760628629,
+                14.197644984951213
+            ],
+            "scorePercentiles" : {
+                "0.0" : 14.045219248776148,
+                "50.0" : 14.12659745374599,
+                "90.0" : 14.174116369702018,
+                "95.0" : 14.174116369702018,
+                "99.0" : 14.174116369702018,
+                "99.9" : 14.174116369702018,
+                "99.99" : 14.174116369702018,
+                "99.999" : 14.174116369702018,
+                "99.9999" : 14.174116369702018,
+                "100.0" : 14.174116369702018
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    14.12659745374599,
+                    14.045219248776148,
+                    14.118116581295373
+                ],
+                [
+                    14.160373672620183,
+                    14.094305865194697,
+                    14.159211580439047
+                ],
+                [
+                    14.174116369702018,
+                    14.102596604618341,
+                    14.163136478717492
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 7.266381680744571,
+            "scoreError" : 0.0314834507050696,
+            "scoreConfidence" : [
+                7.2348982300395015,
+                7.29786513144964
+            ],
+            "scorePercentiles" : {
+                "0.0" : 7.242444363446909,
+                "50.0" : 7.267648124694403,
+                "90.0" : 7.288481059319786,
+                "95.0" : 7.288481059319786,
+                "99.0" : 7.288481059319786,
+                "99.9" : 7.288481059319786,
+                "99.99" : 7.288481059319786,
+                "99.999" : 7.288481059319786,
+                "99.9999" : 7.288481059319786,
+                "100.0" : 7.288481059319786
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    7.267648124694403,
+                    7.285342164764824,
+                    7.242444363446909
+                ],
+                [
+                    7.255021652408882,
+                    7.285224559232886,
+                    7.24294226646453
+                ],
+                [
+                    7.278377968993735,
+                    7.288481059319786,
+                    7.2519529673751855
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 3.6497554428776358,
+            "scoreError" : 0.03841688365133788,
+            "scoreConfidence" : [
+                3.611338559226298,
+                3.6881723265289734
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.6183155091861385,
+                "50.0" : 3.648096864564528,
+                "90.0" : 3.6841018017113205,
+                "95.0" : 3.6841018017113205,
+                "99.0" : 3.6841018017113205,
+                "99.9" : 3.6841018017113205,
+                "99.99" : 3.6841018017113205,
+                "99.999" : 3.6841018017113205,
+                "99.9999" : 3.6841018017113205,
+                "100.0" : 3.6841018017113205
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.634059338089468,
+                    3.6788480653647095,
+                    3.648096864564528
+                ],
+                [
+                    3.6183155091861385,
+                    3.6691475217581515,
+                    3.632623610621194
+                ],
+                [
+                    3.6340565733165495,
+                    3.6841018017113205,
+                    3.6485497012866586
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 1.8523095561784801,
+            "scoreError" : 0.030323853785498622,
+            "scoreConfidence" : [
+                1.8219857023929815,
+                1.8826334099639788
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1.8239117503947395,
+                "50.0" : 1.8621334722675167,
+                "90.0" : 1.8673452589640651,
+                "95.0" : 1.8673452589640651,
+                "99.0" : 1.8673452589640651,
+                "99.9" : 1.8673452589640651,
+                "99.99" : 1.8673452589640651,
+                "99.999" : 1.8673452589640651,
+                "99.9999" : 1.8673452589640651,
+                "100.0" : 1.8673452589640651
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1.8375132948596045,
+                    1.8673452589640651,
+                    1.8606371045545045
+                ],
+                [
+                    1.8253859071644025,
+                    1.8650899516618138,
+                    1.8663739030912672
+                ],
+                [
+                    1.8239117503947395,
+                    1.8621334722675167,
+                    1.8623953626484073
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 0.815405606395385,
+            "scoreError" : 0.007376600213618425,
+            "scoreConfidence" : [
+                0.8080290061817665,
+                0.8227822066090034
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.8100211392269732,
+                "50.0" : 0.8157161091242544,
+                "90.0" : 0.8222992984714158,
+                "95.0" : 0.8222992984714158,
+                "99.0" : 0.8222992984714158,
+                "99.9" : 0.8222992984714158,
+                "99.99" : 0.8222992984714158,
+                "99.999" : 0.8222992984714158,
+                "99.9999" : 0.8222992984714158,
+                "100.0" : 0.8222992984714158
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.8117869388228873,
+                    0.8145968267194525,
+                    0.8102262997624171
+                ],
+                [
+                    0.8100211392269732,
+                    0.8157161091242544,
+                    0.8162979185742621
+                ],
+                [
+                    0.8163625083929198,
+                    0.8213434184638821,
+                    0.8222992984714158
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 0.40328804844292354,
+            "scoreError" : 0.0028630303370206483,
+            "scoreConfidence" : [
+                0.4004250181059029,
+                0.40615107877994416
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.40163443030059487,
+                "50.0" : 0.40217276377360556,
+                "90.0" : 0.4061534001859155,
+                "95.0" : 0.4061534001859155,
+                "99.0" : 0.4061534001859155,
+                "99.9" : 0.4061534001859155,
+                "99.99" : 0.4061534001859155,
+                "99.999" : 0.4061534001859155,
+                "99.9999" : 0.4061534001859155,
+                "100.0" : 0.4061534001859155
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.40476771290873986,
+                    0.4037297178244125,
+                    0.4061534001859155
+                ],
+                [
+                    0.40215063632598,
+                    0.40163443030059487,
+                    0.4051522304851012
+                ],
+                [
+                    0.4021542415673392,
+                    0.4016773026146231,
+                    0.40217276377360556
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 0.21530835237739487,
+            "scoreError" : 0.00316882904311516,
+            "scoreConfidence" : [
+                0.2121395233342797,
+                0.21847718142051004
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.213179245449477,
+                "50.0" : 0.21498614336373545,
+                "90.0" : 0.21804435382366047,
+                "95.0" : 0.21804435382366047,
+                "99.0" : 0.21804435382366047,
+                "99.9" : 0.21804435382366047,
+                "99.99" : 0.21804435382366047,
+                "99.999" : 0.21804435382366047,
+                "99.9999" : 0.21804435382366047,
+                "100.0" : 0.21804435382366047
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.21361651479341479,
+                    0.21804435382366047,
+                    0.2153415105318766
+                ],
+                [
+                    0.213179245449477,
+                    0.21728117821674925,
+                    0.21455915572280176
+                ],
+                [
+                    0.21326258869209858,
+                    0.21750448080274012,
+                    0.21498614336373545
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 0.11238928761614583,
+            "scoreError" : 0.001728818027193669,
+            "scoreConfidence" : [
+                0.11066046958895216,
+                0.1141181056433395
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.11096554243148343,
+                "50.0" : 0.11236778839587218,
+                "90.0" : 0.11371225952719105,
+                "95.0" : 0.11371225952719105,
+                "99.0" : 0.11371225952719105,
+                "99.9" : 0.11371225952719105,
+                "99.99" : 0.11371225952719105,
+                "99.999" : 0.11371225952719105,
+                "99.9999" : 0.11371225952719105,
+                "100.0" : 0.11371225952719105
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.11096554243148343,
+                    0.11331316219066813,
+                    0.11236778839587218
+                ],
+                [
+                    0.11132446045043788,
+                    0.11371225952719105,
+                    0.11260766904717413
+                ],
+                [
+                    0.11141079827926309,
+                    0.11366691638640296,
+                    0.11213499183681985
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "24",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 0.04910733411972115,
+            "scoreError" : 9.919993036906951E-4,
+            "scoreConfidence" : [
+                0.048115334816030454,
+                0.05009933342341185
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.04785069270165027,
+                "50.0" : 0.04920083794793364,
+                "90.0" : 0.04974845748136814,
+                "95.0" : 0.04974845748136814,
+                "99.0" : 0.04974845748136814,
+                "99.9" : 0.04974845748136814,
+                "99.99" : 0.04974845748136814,
+                "99.999" : 0.04974845748136814,
+                "99.9999" : 0.04974845748136814,
+                "100.0" : 0.04974845748136814
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.04920083794793364,
+                    0.049013302202522674,
+                    0.04863697352455684
+                ],
+                [
+                    0.049574854456079974,
+                    0.04942860585264381,
+                    0.04974845748136814
+                ],
+                [
+                    0.04956659261874403,
+                    0.04785069270165027,
+                    0.04894569029199095
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "24",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 0.021996942226223695,
+            "scoreError" : 0.001223011827842711,
+            "scoreConfidence" : [
+                0.020773930398380984,
+                0.023219954054066405
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.021206605100633595,
+                "50.0" : 0.022204038901540282,
+                "90.0" : 0.02285143457296508,
+                "95.0" : 0.02285143457296508,
+                "99.0" : 0.02285143457296508,
+                "99.9" : 0.02285143457296508,
+                "99.99" : 0.02285143457296508,
+                "99.999" : 0.02285143457296508,
+                "99.9999" : 0.02285143457296508,
+                "100.0" : 0.02285143457296508
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.02285143457296508,
+                    0.021271412124217133,
+                    0.0222529103969887
+                ],
+                [
+                    0.02277063641256935,
+                    0.021206605100633595,
+                    0.021370956424092393
+                ],
+                [
+                    0.02281475408024271,
+                    0.021229732022763986,
+                    0.022204038901540282
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 6386.565795976956,
+            "scoreError" : 792.1663122224614,
+            "scoreConfidence" : [
+                5594.399483754494,
+                7178.732108199418
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5713.694702288845,
+                "50.0" : 6655.358764129061,
+                "90.0" : 6745.727252434273,
+                "95.0" : 6745.727252434273,
+                "99.0" : 6745.727252434273,
+                "99.9" : 6745.727252434273,
+                "99.99" : 6745.727252434273,
+                "99.999" : 6745.727252434273,
+                "99.9999" : 6745.727252434273,
+                "100.0" : 6745.727252434273
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6699.219916941565,
+                    5768.823450309422,
+                    6703.286814783046
+                ],
+                [
+                    6741.156445050042,
+                    5797.684182713598,
+                    6745.727252434273
+                ],
+                [
+                    6655.358764129061,
+                    5713.694702288845,
+                    6654.140635142748
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 5751.799729776909,
+            "scoreError" : 703.7751119214772,
+            "scoreConfidence" : [
+                5048.024617855432,
+                6455.574841698386
+            ],
+            "scorePercentiles" : {
+                "0.0" : 5188.826682026075,
+                "50.0" : 6027.003718632205,
+                "90.0" : 6041.565670269849,
+                "95.0" : 6041.565670269849,
+                "99.0" : 6041.565670269849,
+                "99.9" : 6041.565670269849,
+                "99.99" : 6041.565670269849,
+                "99.999" : 6041.565670269849,
+                "99.9999" : 6041.565670269849,
+                "100.0" : 6041.565670269849
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    6035.290118817631,
+                    5197.995356856098,
+                    6041.565670269849
+                ],
+                [
+                    6027.6365465635,
+                    5188.826682026075,
+                    6026.93874641699
+                ],
+                [
+                    6027.448315558002,
+                    5193.4924128518305,
+                    6027.003718632205
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 5396.41858036665,
+            "scoreError" : 692.4767567843886,
+            "scoreConfidence" : [
+                4703.941823582261,
+                6088.895337151038
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4717.085732529054,
+                "50.0" : 5449.325363271581,
+                "90.0" : 5804.387234518538,
+                "95.0" : 5804.387234518538,
+                "99.0" : 5804.387234518538,
+                "99.9" : 5804.387234518538,
+                "99.99" : 5804.387234518538,
+                "99.999" : 5804.387234518538,
+                "99.9999" : 5804.387234518538,
+                "100.0" : 5804.387234518538
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5710.726353968836,
+                    4936.6454282045515,
+                    5710.563537849235
+                ],
+                [
+                    5443.704529230653,
+                    4717.085732529054,
+                    5449.325363271581
+                ],
+                [
+                    5795.56767079827,
+                    4999.761372929123,
+                    5804.387234518538
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "10",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 5338.528833690778,
+            "scoreError" : 642.6865323262485,
+            "scoreConfidence" : [
+                4695.84230136453,
+                5981.215366017026
+            ],
+            "scorePercentiles" : {
+                "0.0" : 4816.211036391922,
+                "50.0" : 5592.290362015759,
+                "90.0" : 5598.237388965811,
+                "95.0" : 5598.237388965811,
+                "99.0" : 5598.237388965811,
+                "99.9" : 5598.237388965811,
+                "99.99" : 5598.237388965811,
+                "99.999" : 5598.237388965811,
+                "99.9999" : 5598.237388965811,
+                "100.0" : 5598.237388965811
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    5589.214694677729,
+                    4836.893728784345,
+                    5593.741175525523
+                ],
+                [
+                    5592.290362015759,
+                    4832.858876227758,
+                    5594.915771067337
+                ],
+                [
+                    5592.396469560819,
+                    4816.211036391922,
+                    5598.237388965811
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 1445.2994799478993,
+            "scoreError" : 175.00246806454027,
+            "scoreConfidence" : [
+                1270.2970118833591,
+                1620.3019480124394
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1304.453222626917,
+                "50.0" : 1512.8888905018887,
+                "90.0" : 1517.6364934622381,
+                "95.0" : 1517.6364934622381,
+                "99.0" : 1517.6364934622381,
+                "99.9" : 1517.6364934622381,
+                "99.99" : 1517.6364934622381,
+                "99.999" : 1517.6364934622381,
+                "99.9999" : 1517.6364934622381,
+                "100.0" : 1517.6364934622381
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1512.8548350143578,
+                    1513.0402455594708,
+                    1304.453222626917
+                ],
+                [
+                    1512.8888905018887,
+                    1515.3646737654515,
+                    1305.1104620235824
+                ],
+                [
+                    1516.482476628759,
+                    1517.6364934622381,
+                    1309.8640199484262
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 1315.0872776573538,
+            "scoreError" : 159.24200916012424,
+            "scoreConfidence" : [
+                1155.8452684972294,
+                1474.329286817478
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1177.8433003668486,
+                "50.0" : 1366.6742876812843,
+                "90.0" : 1385.2593526169985,
+                "95.0" : 1385.2593526169985,
+                "99.0" : 1385.2593526169985,
+                "99.9" : 1385.2593526169985,
+                "99.99" : 1385.2593526169985,
+                "99.999" : 1385.2593526169985,
+                "99.9999" : 1385.2593526169985,
+                "100.0" : 1385.2593526169985
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1382.8361977329685,
+                    1382.6820468439241,
+                    1195.6796392562026
+                ],
+                [
+                    1365.612489963466,
+                    1366.6742876812843,
+                    1177.8433003668486
+                ],
+                [
+                    1384.8586134304985,
+                    1385.2593526169985,
+                    1194.3395710239927
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 1283.678005805807,
+            "scoreError" : 157.03079680154033,
+            "scoreConfidence" : [
+                1126.6472090042666,
+                1440.7088026073475
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1153.4418641546772,
+                "50.0" : 1343.8744918106247,
+                "90.0" : 1347.442856430225,
+                "95.0" : 1347.442856430225,
+                "99.0" : 1347.442856430225,
+                "99.9" : 1347.442856430225,
+                "99.99" : 1347.442856430225,
+                "99.999" : 1347.442856430225,
+                "99.9999" : 1347.442856430225,
+                "100.0" : 1347.442856430225
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1346.9373529042812,
+                    1347.442856430225,
+                    1164.7204691032007
+                ],
+                [
+                    1343.8744918106247,
+                    1342.9954214911315,
+                    1153.4418641546772
+                ],
+                [
+                    1347.0783197578448,
+                    1347.303553607001,
+                    1159.3077229932755
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "12",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 1269.9493588394673,
+            "scoreError" : 152.09842493843254,
+            "scoreConfidence" : [
+                1117.8509339010348,
+                1422.0477837779
+            ],
+            "scorePercentiles" : {
+                "0.0" : 1147.258945682416,
+                "50.0" : 1326.3094960282574,
+                "90.0" : 1333.9773182234367,
+                "95.0" : 1333.9773182234367,
+                "99.0" : 1333.9773182234367,
+                "99.9" : 1333.9773182234367,
+                "99.99" : 1333.9773182234367,
+                "99.999" : 1333.9773182234367,
+                "99.9999" : 1333.9773182234367,
+                "100.0" : 1333.9773182234367
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    1330.937017988604,
+                    1331.781013712139,
+                    1151.3025128066724
+                ],
+                [
+                    1325.092552687089,
+                    1326.3094960282574,
+                    1147.258945682416
+                ],
+                [
+                    1333.430930831772,
+                    1333.9773182234367,
+                    1149.4544415948185
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 331.4211691811984,
+            "scoreError" : 39.72591885175523,
+            "scoreConfidence" : [
+                291.6952503294432,
+                371.14708803295366
+            ],
+            "scorePercentiles" : {
+                "0.0" : 299.70654954457757,
+                "50.0" : 346.81718517560597,
+                "90.0" : 347.5654842338588,
+                "95.0" : 347.5654842338588,
+                "99.0" : 347.5654842338588,
+                "99.9" : 347.5654842338588,
+                "99.99" : 347.5654842338588,
+                "99.999" : 347.5654842338588,
+                "99.9999" : 347.5654842338588,
+                "100.0" : 347.5654842338588
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    347.5654842338588,
+                    347.3078636522035,
+                    299.70654954457757
+                ],
+                [
+                    346.81718517560597,
+                    347.5600492130343,
+                    300.24013083607304
+                ],
+                [
+                    347.02901204498386,
+                    346.8002041858344,
+                    299.7640437446145
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 311.3392998224587,
+            "scoreError" : 8.092880027858497,
+            "scoreConfidence" : [
+                303.24641979460023,
+                319.4321798503172
+            ],
+            "scorePercentiles" : {
+                "0.0" : 303.864281154577,
+                "50.0" : 313.53343870279156,
+                "90.0" : 315.28847407610476,
+                "95.0" : 315.28847407610476,
+                "99.0" : 315.28847407610476,
+                "99.9" : 315.28847407610476,
+                "99.99" : 315.28847407610476,
+                "99.999" : 315.28847407610476,
+                "99.9999" : 315.28847407610476,
+                "100.0" : 315.28847407610476
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    313.53343870279156,
+                    313.52494012757575,
+                    306.9042783090912
+                ],
+                [
+                    314.64847282876036,
+                    314.63972158920654,
+                    304.42205790234715
+                ],
+                [
+                    315.228033711674,
+                    315.28847407610476,
+                    303.864281154577
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 302.2836732171543,
+            "scoreError" : 4.149706655572463,
+            "scoreConfidence" : [
+                298.1339665615818,
+                306.43337987272673
+            ],
+            "scorePercentiles" : {
+                "0.0" : 299.0505978738112,
+                "50.0" : 303.12956903610376,
+                "90.0" : 304.64899373895605,
+                "95.0" : 304.64899373895605,
+                "99.0" : 304.64899373895605,
+                "99.9" : 304.64899373895605,
+                "99.99" : 304.64899373895605,
+                "99.999" : 304.64899373895605,
+                "99.9999" : 304.64899373895605,
+                "100.0" : 304.64899373895605
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    299.10203001349043,
+                    299.0505978738112,
+                    299.15578250184234
+                ],
+                [
+                    303.1047767742677,
+                    303.12956903610376,
+                    303.1836065480273
+                ],
+                [
+                    304.64899373895605,
+                    304.54986000188603,
+                    304.62784246600364
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "14",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 300.95692329813187,
+            "scoreError" : 3.927290388673294,
+            "scoreConfidence" : [
+                297.0296329094586,
+                304.88421368680514
+            ],
+            "scorePercentiles" : {
+                "0.0" : 297.82178632724117,
+                "50.0" : 302.460585005198,
+                "90.0" : 302.6934983893305,
+                "95.0" : 302.6934983893305,
+                "99.0" : 302.6934983893305,
+                "99.9" : 302.6934983893305,
+                "99.99" : 302.6934983893305,
+                "99.999" : 302.6934983893305,
+                "99.9999" : 302.6934983893305,
+                "100.0" : 302.6934983893305
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    302.667066530584,
+                    302.6934983893305,
+                    302.6823762496941
+                ],
+                [
+                    302.460585005198,
+                    302.08539343860014,
+                    302.47158139038163
+                ],
+                [
+                    297.86992487307157,
+                    297.82178632724117,
+                    297.8600974790862
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 74.50878265101892,
+            "scoreError" : 1.5659955259011837,
+            "scoreConfidence" : [
+                72.94278712511773,
+                76.0747781769201
+            ],
+            "scorePercentiles" : {
+                "0.0" : 73.18420589697276,
+                "50.0" : 75.0801265833059,
+                "90.0" : 75.19920151231858,
+                "95.0" : 75.19920151231858,
+                "99.0" : 75.19920151231858,
+                "99.9" : 75.19920151231858,
+                "99.99" : 75.19920151231858,
+                "99.999" : 75.19920151231858,
+                "99.9999" : 75.19920151231858,
+                "100.0" : 75.19920151231858
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    75.07250433493931,
+                    75.0801265833059,
+                    75.08953871319032
+                ],
+                [
+                    73.18420589697276,
+                    73.30250562624731,
+                    73.31900441517166
+                ],
+                [
+                    75.19920151231858,
+                    75.15335166079,
+                    75.17860511623435
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 75.52429503392334,
+            "scoreError" : 0.5517480024585578,
+            "scoreConfidence" : [
+                74.97254703146479,
+                76.07604303638189
+            ],
+            "scorePercentiles" : {
+                "0.0" : 75.19908655669559,
+                "50.0" : 75.44663150488897,
+                "90.0" : 75.9517135006675,
+                "95.0" : 75.9517135006675,
+                "99.0" : 75.9517135006675,
+                "99.9" : 75.9517135006675,
+                "99.99" : 75.9517135006675,
+                "99.999" : 75.9517135006675,
+                "99.9999" : 75.9517135006675,
+                "100.0" : 75.9517135006675
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    75.35367838336924,
+                    75.4596740558918,
+                    75.44663150488897
+                ],
+                [
+                    75.93801711229243,
+                    75.93950246123045,
+                    75.9517135006675
+                ],
+                [
+                    75.19908655669559,
+                    75.21442172604338,
+                    75.21593000423054
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 67.47125972839588,
+            "scoreError" : 0.9761457967212661,
+            "scoreConfidence" : [
+                66.49511393167461,
+                68.44740552511715
+            ],
+            "scorePercentiles" : {
+                "0.0" : 66.9589801118839,
+                "50.0" : 67.17805812799128,
+                "90.0" : 68.25316810917784,
+                "95.0" : 68.25316810917784,
+                "99.0" : 68.25316810917784,
+                "99.9" : 68.25316810917784,
+                "99.99" : 68.25316810917784,
+                "99.999" : 68.25316810917784,
+                "99.9999" : 68.25316810917784,
+                "100.0" : 68.25316810917784
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    68.23832315328102,
+                    68.22025948080903,
+                    68.25316810917784
+                ],
+                [
+                    67.20470000773832,
+                    67.17224019849884,
+                    67.17805812799128
+                ],
+                [
+                    66.99686263071861,
+                    66.9589801118839,
+                    67.01874573546412
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "16",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 67.32137339767219,
+            "scoreError" : 0.6819868243441913,
+            "scoreConfidence" : [
+                66.639386573328,
+                68.00336022201638
+            ],
+            "scorePercentiles" : {
+                "0.0" : 66.89781446847066,
+                "50.0" : 67.22587345785756,
+                "90.0" : 67.83559662001014,
+                "95.0" : 67.83559662001014,
+                "99.0" : 67.83559662001014,
+                "99.9" : 67.83559662001014,
+                "99.99" : 67.83559662001014,
+                "99.999" : 67.83559662001014,
+                "99.9999" : 67.83559662001014,
+                "100.0" : 67.83559662001014
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    67.83559662001014,
+                    67.8322057260434,
+                    67.82536987354793
+                ],
+                [
+                    67.23765444299026,
+                    67.20703272536338,
+                    67.22587345785756
+                ],
+                [
+                    66.91126212224623,
+                    66.89781446847066,
+                    66.9195511425202
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 3.9488767206617124,
+            "scoreError" : 0.01271426348096421,
+            "scoreConfidence" : [
+                3.9361624571807483,
+                3.9615909841426764
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.9395433532608974,
+                "50.0" : 3.9479157994483596,
+                "90.0" : 3.961011353696486,
+                "95.0" : 3.961011353696486,
+                "99.0" : 3.961011353696486,
+                "99.9" : 3.961011353696486,
+                "99.99" : 3.961011353696486,
+                "99.999" : 3.961011353696486,
+                "99.9999" : 3.961011353696486,
+                "100.0" : 3.961011353696486
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.9474670664367095,
+                    3.9407630752460774,
+                    3.941580998321083
+                ],
+                [
+                    3.951314216557007,
+                    3.9479157994483596,
+                    3.9395433532608974
+                ],
+                [
+                    3.961011353696486,
+                    3.9580669899343075,
+                    3.9522276330544845
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 3.712183075627399,
+            "scoreError" : 0.004411977283529826,
+            "scoreConfidence" : [
+                3.707771098343869,
+                3.716595052910929
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.7088991397404043,
+                "50.0" : 3.71197990044313,
+                "90.0" : 3.716693076529153,
+                "95.0" : 3.716693076529153,
+                "99.0" : 3.716693076529153,
+                "99.9" : 3.716693076529153,
+                "99.99" : 3.716693076529153,
+                "99.999" : 3.716693076529153,
+                "99.9999" : 3.716693076529153,
+                "100.0" : 3.716693076529153
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.712300697056719,
+                    3.7088991397404043,
+                    3.712866800949536
+                ],
+                [
+                    3.7156716434094634,
+                    3.709451487937914,
+                    3.7103748198468636
+                ],
+                [
+                    3.716693076529153,
+                    3.711410114733411,
+                    3.71197990044313
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "128"
+        },
+        "primaryMetric" : {
+            "score" : 3.6510588192982802,
+            "scoreError" : 0.010158835052119179,
+            "scoreConfidence" : [
+                3.640899984246161,
+                3.6612176543503994
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.6417932096613206,
+                "50.0" : 3.652773141901331,
+                "90.0" : 3.659304341428677,
+                "95.0" : 3.659304341428677,
+                "99.0" : 3.659304341428677,
+                "99.9" : 3.659304341428677,
+                "99.99" : 3.659304341428677,
+                "99.999" : 3.659304341428677,
+                "99.9999" : 3.659304341428677,
+                "100.0" : 3.659304341428677
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.6417932096613206,
+                    3.6484140187033773,
+                    3.6427744353372957
+                ],
+                [
+                    3.6490259698619028,
+                    3.656541227931445,
+                    3.6532776508863405
+                ],
+                [
+                    3.652773141901331,
+                    3.659304341428677,
+                    3.65562537797283
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "20",
+            "size" : "256"
+        },
+        "primaryMetric" : {
+            "score" : 3.585564475367141,
+            "scoreError" : 0.05905549153815459,
+            "scoreConfidence" : [
+                3.5265089838289865,
+                3.6446199669052954
+            ],
+            "scorePercentiles" : {
+                "0.0" : 3.533524149742564,
+                "50.0" : 3.6065639777483764,
+                "90.0" : 3.6158764022769976,
+                "95.0" : 3.6158764022769976,
+                "99.0" : 3.6158764022769976,
+                "99.9" : 3.6158764022769976,
+                "99.99" : 3.6158764022769976,
+                "99.999" : 3.6158764022769976,
+                "99.9999" : 3.6158764022769976,
+                "100.0" : 3.6158764022769976
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    3.6065639777483764,
+                    3.6045966516377965,
+                    3.6119839861983194
+                ],
+                [
+                    3.6071137983733683,
+                    3.6067334754515645,
+                    3.6158764022769976
+                ],
+                [
+                    3.5447209034309646,
+                    3.5389669334443195,
+                    3.533524149742564
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "24",
+            "size" : "32"
+        },
+        "primaryMetric" : {
+            "score" : 0.20908264283795241,
+            "scoreError" : 0.005521587867919933,
+            "scoreConfidence" : [
+                0.2035610549700325,
+                0.21460423070587234
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.20496353683409937,
+                "50.0" : 0.20922435885392085,
+                "90.0" : 0.21325690252957974,
+                "95.0" : 0.21325690252957974,
+                "99.0" : 0.21325690252957974,
+                "99.9" : 0.21325690252957974,
+                "99.99" : 0.21325690252957974,
+                "99.999" : 0.21325690252957974,
+                "99.9999" : 0.21325690252957974,
+                "100.0" : 0.21325690252957974
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.20542256226005204,
+                    0.21325690252957974,
+                    0.20935781859286504
+                ],
+                [
+                    0.20496353683409937,
+                    0.21256935316767595,
+                    0.2086984266652402
+                ],
+                [
+                    0.20550737583398893,
+                    0.21274345080414955,
+                    0.20922435885392085
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    },
+    {
+        "jmhVersion" : "1.21",
+        "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact",
+        "mode" : "thrpt",
+        "threads" : 1,
+        "forks" : 3,
+        "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java",
+        "jvmArgs" : [
+            "-Xms32768m",
+            "-Xmx32768m"
+        ],
+        "jdkVersion" : "1.8.0_171",
+        "vmName" : "Java HotSpot(TM) 64-Bit Server VM",
+        "vmVersion" : "25.171-b11",
+        "warmupIterations" : 3,
+        "warmupTime" : "10 s",
+        "warmupBatchSize" : 1,
+        "measurementIterations" : 3,
+        "measurementTime" : "10 s",
+        "measurementBatchSize" : 1,
+        "params" : {
+            "numOfElements" : "24",
+            "size" : "64"
+        },
+        "primaryMetric" : {
+            "score" : 0.1974683037696797,
+            "scoreError" : 0.010640093913706952,
+            "scoreConfidence" : [
+                0.18682820985597273,
+                0.20810839768338665
+            ],
+            "scorePercentiles" : {
+                "0.0" : 0.19000299496590883,
+                "50.0" : 0.19685709664617682,
+                "90.0" : 0.20514302078860988,
+                "95.0" : 0.20514302078860988,
+                "99.0" : 0.20514302078860988,
+                "99.9" : 0.20514302078860988,
+                "99.99" : 0.20514302078860988,
+                "99.999" : 0.20514302078860988,
+                "99.9999" : 0.20514302078860988,
+                "100.0" : 0.20514302078860988
+            },
+            "scoreUnit" : "ops/s",
+            "rawData" : [
+                [
+                    0.2050238255850441,
+                    0.19685709664617682,
+                    0.19055863675929088
+                ],
+                [
+                    0.20514302078860988,
+                    0.19650966439452058,
+                    0.19000299496590883
+                ],
+                [
+                    0.20504185082390858,
+                    0.19712856235839904,
+                    0.19094908160525834
+                ]
+            ]
+        },
+        "secondaryMetrics" : {
+        }
+    }
+]
+
+
diff --git a/tlatools/test-benchmark/tlc2/value/impl/EnumerateSubsetBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/EnumerateSubsetBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..78995d3d950b6a33e8337988fe9ed0826a662003
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/EnumerateSubsetBenchmark.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+
+@State(Scope.Benchmark)
+public class EnumerateSubsetBenchmark {
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+	}
+	
+	@Param({"0", "1", "2", "3", "4", "8", "10", "12", "14", "16", "18", "19"})
+	public int numOfElements;
+
+	@Benchmark
+	public Enumerable elementsAlwaysNormalized() {
+		final IntervalValue inner = new IntervalValue(1, numOfElements);
+		final SubsetValue subset = new SubsetValue(inner);
+
+		final ValueVec vals = new ValueVec(subset.size());
+		final ValueEnumeration Enum = subset.elementsNormalized();
+		Value  elem;
+		while ((elem = Enum.nextElement()) != null) {
+			vals.addElement(elem);
+		}
+        return (Enumerable) new SetEnumValue(vals, true).normalize();
+	}
+
+	@Benchmark
+	public Enumerable kElementsNotNormalized() {
+		final IntervalValue inner = new IntervalValue(1, numOfElements);
+		final SubsetValue subset = new SubsetValue(inner);
+
+		final ValueVec vec = new ValueVec(subset.size());
+		for (int i = 0; i <= inner.size(); i++) {
+			final ValueEnumeration Enum = subset.kElements(i);
+			Value  elem;
+			while ((elem = Enum.nextElement()) != null) {
+				vec.addElement(elem);
+			}
+		}
+        return (Enumerable) new SetEnumValue(vec, false);
+	}
+	
+	@Benchmark
+	public Enumerable kElementsNormalized() {
+		final IntervalValue inner = new IntervalValue(1, numOfElements);
+		final SubsetValue subset = new SubsetValue(inner);
+
+		final ValueVec vec = new ValueVec(subset.size());
+		for (int i = 0; i <= inner.size(); i++) {
+			final ValueEnumeration Enum = subset.kElements(i);
+			Value  elem;
+			while ((elem = Enum.nextElement()) != null) {
+				vec.addElement(elem);
+			}
+		}
+        return (Enumerable) new SetEnumValue(vec, false).normalize();
+	}
+
+	@Benchmark
+	public Enumerable elementsNotNormalized() {
+		final IntervalValue inner = new IntervalValue(1, numOfElements);
+		final SubsetValue subset = new SubsetValue(inner);
+		
+		final ValueVec vals = new ValueVec(subset.size());
+		final ValueEnumeration Enum = subset.elementsLexicographic();
+		Value  elem;
+		while ((elem = Enum.nextElement()) != null) {
+			vals.addElement(elem);
+		}
+		return (Enumerable) new SetEnumValue(vals, false);
+	}
+
+	@Benchmark
+	public Enumerable elementsNormalized() {
+		final IntervalValue inner = new IntervalValue(1, numOfElements);
+		final SubsetValue subset = new SubsetValue(inner);
+		
+		final ValueVec vals = new ValueVec(subset.size());
+		final ValueEnumeration Enum = subset.elementsLexicographic();
+		Value  elem;
+		while ((elem = Enum.nextElement()) != null) {
+			vals.addElement(elem);
+		}
+		return (Enumerable) new SetEnumValue(vals, false).normalize();
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/impl/IntervalValueBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/IntervalValueBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f7bd8f2de99ededd742fbef27ce3597228b80f0
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/IntervalValueBenchmark.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntervalValue;
+
+@State(Scope.Benchmark)
+public class IntervalValueBenchmark {
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+	}
+	
+	@Param({"16", "18", "20", "22"})
+	public int size;
+	
+	@Param({"10", "12", "14", "16"})
+	public int numOfElements;
+
+	public Enumerable intervalValue;
+	
+	@Setup(Level.Invocation)
+	public void setup() {
+		intervalValue = (Enumerable) new IntervalValue(1, 1 << size).normalize();
+	}
+	
+	@Benchmark
+	public Enumerable randomSubset() {
+		return intervalValue.getRandomSubset(1 << numOfElements);
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/impl/RandomizationBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/RandomizationBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e168f4d590469ef0494387aef4148f409225c07
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/RandomizationBenchmark.java
@@ -0,0 +1,522 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.TLCGlobals;
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.SetOfFcnsValue;
+import tlc2.value.impl.SubsetValue;
+
+@State(Scope.Benchmark)
+public class RandomizationBenchmark {
+
+	private static final Enumerable enum016;
+	private static final Enumerable enum032;
+	private static final Enumerable enum064;
+	private static final Enumerable enum128;
+	private static final Enumerable enum256;
+	private static final Enumerable enum512;
+	private static final Enumerable enum1024;
+	private static final Enumerable enum2048;
+	private static final Enumerable enum4096;
+	private static final Enumerable enum8192;
+	private static final Enumerable enum16384;
+	private static final Enumerable enum32768;
+	private static final Enumerable enumTLCBound;
+	
+	private static final Enumerable interval16;
+	private static final Enumerable interval20;
+	private static final Enumerable interval24;
+	private static final Enumerable interval28;
+	private static final Enumerable interval31;
+
+	private static final int twoPow12 = (int) (Math.pow(2, 12) - 1);
+	private static final int twoPow08 = (int) (Math.pow(2, 8) - 1);
+	private static final int twoPow16 = (int) (Math.pow(2, 16) - 1);
+	private static final Enumerable fcns008x008; // 2^24
+	private static final Enumerable fcns011x011; // ~2^31
+	private static final Enumerable fcns016x008;
+	private static final Enumerable fcns016x016; // ~2^65
+	private static final Enumerable fcns032x016;
+	private static final Enumerable fcns032x032; // ~2^150
+	private static final Enumerable fcns048x048; // ~2^268
+
+	private static final SubsetValue subset2pow24; // 2^24
+	private static final SubsetValue subset2pow31; // ~2^31
+	private static final SubsetValue subset2pow65; // ~2^65
+	private static final SubsetValue subset2pow150; // ~2^150
+	private static final SubsetValue subset2pow268; // ~2^268
+	
+	private static ValueVec getValues(int from, int to) {
+		final ValueVec vec = new ValueVec(to - from);
+		for (int i = from; i <= to; i++) {
+			vec.addElement(IntValue.gen(i));
+		}
+		return vec;
+	}
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+
+		enum016 = new SetEnumValue(getValues(1, 16), true);
+		enum032 = new SetEnumValue(getValues(1, 32), true);
+		enum064 = new SetEnumValue(getValues(1, 64), true);
+		enum128 = new SetEnumValue(getValues(1, 128), true);
+		enum256 = new SetEnumValue(getValues(1, 256), true);
+		enum512 = new SetEnumValue(getValues(1, 512), true);
+		enum1024 = new SetEnumValue(getValues(1, 1024), true);
+		enum2048 = new SetEnumValue(getValues(1, 2048), true);
+		enum4096 = new SetEnumValue(getValues(1, 4096), true);
+		enum8192 = new SetEnumValue(getValues(1, 8192), true);
+		enum16384 = new SetEnumValue(getValues(1, 16384), true);
+		enum32768 = new SetEnumValue(getValues(1, 32768), true);
+		enumTLCBound = new SetEnumValue(getValues(1, TLCGlobals.setBound), true);
+
+		interval16 = new IntervalValue(0, 2 << 16);
+		interval20 = new IntervalValue(0, 2 << 20);
+		interval24 = new IntervalValue(0, 2 << 24);
+		interval28 = new IntervalValue(0, 2 << 28);
+		interval31 = new IntervalValue(0, Integer.MAX_VALUE); // maximum possible value for internal ValueVec
+		
+		fcns008x008 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 8), true),
+				new SetEnumValue(getValues(1, 8), true));
+		fcns011x011 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 11), true),
+				new SetEnumValue(getValues(1, 11), true));
+		fcns016x008 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 16), true),
+				new SetEnumValue(getValues(1, 8), true));
+		fcns016x016 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 16), true),
+				new SetEnumValue(getValues(1, 16), true));
+		fcns032x016 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 32), true),
+				new SetEnumValue(getValues(1, 16), true));
+		fcns032x032 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 32), true),
+				new SetEnumValue(getValues(1, 32), true));
+		fcns048x048 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 48), true),
+				new SetEnumValue(getValues(1, 48), true));
+		
+		subset2pow24 = new SubsetValue(new IntervalValue(1, 24));
+		subset2pow31 = new SubsetValue(new IntervalValue(1, 31));
+		subset2pow65 = new SubsetValue(new IntervalValue(1, 65));
+		subset2pow150 = new SubsetValue(new IntervalValue(1, 150));
+		subset2pow268 = new SubsetValue(new IntervalValue(1, 268));
+	}
+	
+	/* exact */
+	
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact024k008() {
+		return subset2pow24.getRandomSetOfSubsets(twoPow08, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact024k012() {
+		return subset2pow24.getRandomSetOfSubsets(twoPow12, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact024k016() {
+		return subset2pow24.getRandomSetOfSubsets(twoPow16, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact031k008() {
+		return subset2pow31.getRandomSetOfSubsets(twoPow08, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact031k012() {
+		return subset2pow31.getRandomSetOfSubsets(twoPow12, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact031k016() {
+		return subset2pow31.getRandomSetOfSubsets(twoPow16, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact65k008() {
+		return subset2pow65.getRandomSetOfSubsets(twoPow08, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact065k012() {
+		return subset2pow65.getRandomSetOfSubsets(twoPow12, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact065k016() {
+		return subset2pow65.getRandomSetOfSubsets(twoPow16, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact150k008() {
+		return subset2pow150.getRandomSetOfSubsets(twoPow08, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact150k012() {
+		return subset2pow150.getRandomSetOfSubsets(twoPow12, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact150k016() {
+		return subset2pow150.getRandomSetOfSubsets(twoPow16, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact268k008() {
+		return subset2pow268.getRandomSetOfSubsets(twoPow08, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact268k012() {
+		return subset2pow268.getRandomSetOfSubsets(twoPow12, 10);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetExact268k016() {
+		return subset2pow268.getRandomSetOfSubsets(twoPow16, 10);
+	}
+	
+	/* probabilistic */
+	
+	@Benchmark
+	public Enumerable randomSetOfSubset024k008() {
+		return subset2pow24.getRandomSetOfSubsets(twoPow08, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset024k012() {
+		return subset2pow24.getRandomSetOfSubsets(twoPow12, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset024k016() {
+		return subset2pow24.getRandomSetOfSubsets(twoPow16, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset031k008() {
+		return subset2pow31.getRandomSetOfSubsets(twoPow08, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset031k012() {
+		return subset2pow31.getRandomSetOfSubsets(twoPow12, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset031k016() {
+		return subset2pow31.getRandomSetOfSubsets(twoPow16, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset065k008() {
+		return subset2pow65.getRandomSetOfSubsets(twoPow08, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset065k012() {
+		return subset2pow65.getRandomSetOfSubsets(twoPow12, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset065k016() {
+		return subset2pow65.getRandomSetOfSubsets(twoPow16, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset150k008() {
+		return subset2pow150.getRandomSetOfSubsets(twoPow08, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset150k012() {
+		return subset2pow150.getRandomSetOfSubsets(twoPow12, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset150k016() {
+		return subset2pow150.getRandomSetOfSubsets(twoPow16, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset268k008() {
+		return subset2pow268.getRandomSetOfSubsets(twoPow08, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset268k012() {
+		return subset2pow268.getRandomSetOfSubsets(twoPow12, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubset268k016() {
+		return subset2pow268.getRandomSetOfSubsets(twoPow16, .1d);
+	}
+	
+	/* IntervalValue */
+	
+	@Benchmark
+	public Enumerable randomInterval016setBound() {
+		return interval16.getRandomSubset(TLCGlobals.setBound);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval020setBound() {
+		return interval20.getRandomSubset(TLCGlobals.setBound);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval024setBound() {
+		return interval24.getRandomSubset(TLCGlobals.setBound);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval028setBound() {
+		return interval28.getRandomSubset(TLCGlobals.setBound);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval031setBound() {
+		return interval31.getRandomSubset(TLCGlobals.setBound);
+	}
+	
+	@Benchmark
+	public Enumerable randomIntervalt016all() {
+		return interval16.getRandomSubset(interval16.size() - 1);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval020all() {
+		return interval20.getRandomSubset(interval20.size() - 1);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval024all() {
+		return interval24.getRandomSubset(interval24.size() - 1);
+	}
+	
+	@Benchmark
+	public Enumerable randomIntervalt016half() {
+		return interval16.getRandomSubset(interval16.size() / 2);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval020half() {
+		return interval20.getRandomSubset(interval20.size() / 2);
+	}
+	
+	@Benchmark
+	public Enumerable randomInterval024half() {
+		return interval24.getRandomSubset(interval24.size() / 2);
+	}
+	
+	/* SetEnumValue */
+	
+	@Benchmark
+	public Enumerable randomSubset016() {
+		return enum016.getRandomSubset(enum016.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset032() {
+		return enum032.getRandomSubset(enum032.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset064() {
+		return enum064.getRandomSubset(enum064.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset128() {
+		return enum128.getRandomSubset(enum128.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset256() {
+		return enum256.getRandomSubset(enum256.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset512() {
+		return enum512.getRandomSubset(enum512.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset1024() {
+		return enum1024.getRandomSubset(enum1024.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset2048() {
+		return enum2048.getRandomSubset(enum2048.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset4096() {
+		return enum4096.getRandomSubset(enum4096.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset8192() {
+		return enum8192.getRandomSubset(enum8192.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset16384() {
+		return enum16384.getRandomSubset(enum16384.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubset32768() {
+		return enum32768.getRandomSubset(enum32768.size() - 1);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetTLCBound() {
+		return enumTLCBound.getRandomSubset(enumTLCBound.size() - 1);
+	}
+	
+	/* randomSubset with SetOfFcns */
+
+	@Benchmark
+	public Enumerable randomSubsetFcns008x008p16() {
+		return fcns008x008.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns008x008p08() {
+		return fcns008x008.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns008x008p12() {
+		return fcns008x008.getRandomSubset(twoPow12);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns011x011p16() {
+		return fcns008x008.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns011x011p08() {
+		return fcns011x011.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns011x011p12() {
+		return fcns011x011.getRandomSubset(twoPow12);
+	}
+	
+	@Benchmark
+	public Enumerable randomSubsetFcns016x008p16() {
+		return fcns011x011.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns016x008p08() {
+		return fcns016x008.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns016x008p12() {
+		return fcns016x008.getRandomSubset(twoPow12);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns016x016p16() {
+		return fcns016x016.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns016x016p08() {
+		return fcns016x016.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns016x016p12() {
+		return fcns016x016.getRandomSubset(twoPow12);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns032x016p16() {
+		return fcns032x016.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns032x016p08() {
+		return fcns032x016.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns032x016p12() {
+		return fcns032x016.getRandomSubset(twoPow12);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns032x032p16() {
+		return fcns032x032.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns032x032p08() {
+		return fcns032x032.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns032x032p12() {
+		return fcns032x032.getRandomSubset(twoPow12);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns048x048p16() {
+		return fcns048x048.getRandomSubset(twoPow16);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns048x048p08() {
+		return fcns048x048.getRandomSubset(twoPow08);
+	}
+
+	@Benchmark
+	public Enumerable randomSubsetFcns048x048p12() {
+		return fcns048x048.getRandomSubset(twoPow12);
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/impl/SetEnumValueBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SetEnumValueBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0b853c4585f1fe58ec2e8e0b373a635bb1933be
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/SetEnumValueBenchmark.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.SetEnumValue;
+
+@State(Scope.Benchmark)
+public class SetEnumValueBenchmark {
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+	}
+
+	private static ValueVec getValues(int from, int to) {
+		final ValueVec vec = new ValueVec(to - from);
+		for (int i = from; i <= to; i++) {
+			vec.addElement(IntValue.gen(i));
+		}
+		return vec;
+	}
+	
+	@Param({"10", "12", "14", "16"})
+	public int numOfElements;
+	
+	@Param({"16", "18", "20", "22"})
+	public int size;
+
+	public Enumerable setEnumValue;
+		
+	@Setup(Level.Invocation)
+	public void setup() {
+		setEnumValue = (Enumerable) new SetEnumValue(getValues(1, 1 << size), false).normalize();
+	}
+
+	@Benchmark
+	public Enumerable randomSubset() {
+		return setEnumValue.getRandomSubset(1 << numOfElements);
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/impl/SetOfFcnsBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SetOfFcnsBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..09ef16cd5b415dce356bf724bb3c8ed7d81a04ff
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/SetOfFcnsBenchmark.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+
+@State(Scope.Benchmark)
+public class SetOfFcnsBenchmark {
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+	}
+	
+	@Param({"10", "12", "14", "16"})
+	public int numOfElements;
+	
+	@Param({"16", "32", "46"})
+	public int sizeT;
+	
+	@Param({"8", "16", "46"})
+	public int sizeS;
+
+	// 08x16 ~= 2^32 = 4294967296
+	// 16x16 = 2^64  = 10^19
+	// 16x32 ~= 2^128 ~= 10^38
+	// 46x46 ~= 2^256 ~= 10^77
+	
+	public Enumerable setOfFcns;
+		
+	@Setup(Level.Invocation)
+	public void setup() {
+		if ((sizeS == 8 && sizeT == 16)
+				|| (sizeS == 16 && sizeT == 16)
+				|| (sizeS == 16 && sizeT == 32)
+				|| (sizeS == 46 && sizeT == 46)) {
+			final Value domain = new IntervalValue(1, sizeS);
+			final Value range = new IntervalValue(1, sizeT);
+			setOfFcns = (Enumerable) new SetOfFcnsValue(domain, range).normalize();
+		} else {
+			// This appears to be the only way to skip permutations from the parameter space
+			// sizeS X sizeT X numOfElements.
+			System.exit(0);
+		}
+	}
+
+	@Benchmark
+	public Enumerable randomSubset() {
+		return setOfFcns.getRandomSubset(1 << numOfElements);
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/impl/SubsetBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SubsetBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..54aa0060ae703eacdb29f86141dd141cf80b9abf
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/SubsetBenchmark.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SubsetValue;
+
+@State(Scope.Benchmark)
+public class SubsetBenchmark {
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+	}
+	
+	@Param({"10", "12", "14", "16", "20", "24"})
+	public int numOfElements;
+	
+	@Param({"32", "64", "128", "256"})
+	public int size;
+
+	public SubsetValue subset;
+		
+	@Setup(Level.Invocation)
+	public void setup() {
+		if (size < 128 || (size >= 128 && numOfElements <= 20)) {
+			subset = (SubsetValue) new SubsetValue(new IntervalValue(1, size)).normalize();
+		} else {
+			// This appears to be the only way to skip permutations from the parameter space
+			// size X numOfElements. These permutations will go OOM or reach GC overhad
+			// limit anyway.
+			System.exit(0);
+		}
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsets() {
+		return subset.getRandomSetOfSubsets(1 << numOfElements, .1d);
+	}
+
+	@Benchmark
+	public Enumerable randomSetOfSubsetsExact() {
+		return subset.getRandomSetOfSubsets(1 << numOfElements, 10);
+	}
+}
diff --git a/tlatools/test-benchmark/tlc2/value/impl/SubsetValueBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SubsetValueBenchmark.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2aecd266ce7a24a607468ffa8007b69adf12d40
--- /dev/null
+++ b/tlatools/test-benchmark/tlc2/value/impl/SubsetValueBenchmark.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SubsetValue;
+
+@State(Scope.Benchmark)
+public class SubsetValueBenchmark {
+
+	private static final SubsetValue subset35;
+	private static final SubsetValue subset60;
+	private static final SubsetValue subset100;
+	private static final SubsetValue subset200;
+	private static final SubsetValue subset300;
+	private static final SubsetValue subset400;
+
+	private static final int k = 80000;
+	private static final double d = 0.1d;
+	private static final int k2 = 160000;
+	private static final double d2 = 0.2d;
+
+	static {
+		RandomEnumerableValues.setSeed(15041980L);
+		RandomEnumerableValues.reset();
+
+		FP64.Init();
+
+		subset35 = new SubsetValue(new IntervalValue(1, 35));
+		subset35.normalize();
+		subset60 = new SubsetValue(new IntervalValue(1, 60));
+		subset60.normalize();
+		subset100 = new SubsetValue(new IntervalValue(1, 100));
+		subset100.normalize();
+		subset200 = new SubsetValue(new IntervalValue(1, 200));
+		subset200.normalize();
+		subset300 = new SubsetValue(new IntervalValue(1, 300));
+		subset300.normalize();
+		subset400 = new SubsetValue(new IntervalValue(1, 400));
+		subset400.normalize();
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN035k80d01() {
+		// ~49k subsets
+		return subset35.getRandomSetOfSubsets(k, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN060k80d01() {
+		// ~76500 subsets
+		return subset60.getRandomSetOfSubsets(k, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN100k80d01() {
+		// ~79900
+		return subset100.getRandomSetOfSubsets(k, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN200k80d01() {
+		// ~80k
+		return subset200.getRandomSetOfSubsets(k, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN300k80d01() {
+		// ~80k
+		return subset300.getRandomSetOfSubsets(k, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN400k80d01() {
+		// ~80k
+		return subset400.getRandomSetOfSubsets(k, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN035k16d01() {
+		// ~73500
+		return subset35.getRandomSetOfSubsets(k2, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN060k16d01() {
+		// ~150k
+		return subset60.getRandomSetOfSubsets(k2, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN100k16d01() {
+		// ~160k
+		return subset100.getRandomSetOfSubsets(k2, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN200k16d01() {
+		// 160k
+		return subset200.getRandomSetOfSubsets(k2, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN300k16d01() {
+		// 160k
+		return subset300.getRandomSetOfSubsets(k2, d);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN400k16d01() {
+		// 160k
+		return subset400.getRandomSetOfSubsets(k2, d);
+	}
+	
+	/* d2 */
+
+	@Benchmark
+	public Enumerable probabilisticN035k80d02() {
+		// ~77600
+		return subset35.getRandomSetOfSubsets(k, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN060k80d02() {
+		// 80k
+		return subset60.getRandomSetOfSubsets(k, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN100k80d02() {
+		// 80k
+		return subset100.getRandomSetOfSubsets(k, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN200k80d02() {
+		// 80k
+		return subset200.getRandomSetOfSubsets(k, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN300k80d02() {
+		// 80k
+		return subset300.getRandomSetOfSubsets(k, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN400k80d02() {
+		// 80k
+		return subset400.getRandomSetOfSubsets(k, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN035k16d02() {
+		// ~15200
+		return subset35.getRandomSetOfSubsets(k2, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN060k16d02() {
+		// 160k
+		return subset60.getRandomSetOfSubsets(k2, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN100k16d02() {
+		// 160k
+		return subset100.getRandomSetOfSubsets(k2, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN200k16d02() {
+		// 160k
+		return subset200.getRandomSetOfSubsets(k2, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN300k16d02() {
+		// 160k
+		return subset300.getRandomSetOfSubsets(k2, d2);
+	}
+
+	@Benchmark
+	public Enumerable probabilisticN400k16d02() {
+		// 160k
+		return subset400.getRandomSetOfSubsets(k2, d2);
+	}
+	
+	/* Exact getRandomSetOfSubsets */
+	
+	@Benchmark
+	public Enumerable exactN035K08() {
+		return subset35.getRandomSetOfSubsets(k, 8);
+	}
+
+	@Benchmark
+	public Enumerable exactN035K13() {
+		return subset35.getRandomSetOfSubsets(k, 13);
+	}
+
+	@Benchmark
+	public Enumerable exactN060K08() {
+		return subset60.getRandomSetOfSubsets(k, 8);
+	}
+
+	@Benchmark
+	public Enumerable exactN100K08() {
+		return subset100.getRandomSetOfSubsets(k, 8);
+	}
+
+	@Benchmark
+	public Enumerable exactN100K10() {
+		return subset100.getRandomSetOfSubsets(k, 10);
+	}
+
+	@Benchmark
+	public Enumerable exactN200K10() {
+		return subset200.getRandomSetOfSubsets(k, 10);
+	}
+
+	@Benchmark
+	public Enumerable exactN300K09() {
+		return subset300.getRandomSetOfSubsets(k, 9);
+	}
+
+	@Benchmark
+	public Enumerable exactN400K09() {
+		return subset400.getRandomSetOfSubsets(k, 9);
+	}
+	
+	/* k2 */
+	
+	@Benchmark
+	public Enumerable exactN035K208() {
+		return subset35.getRandomSetOfSubsets(k2, 8);
+	}
+
+	@Benchmark
+	public Enumerable exactN035K213() {
+		return subset35.getRandomSetOfSubsets(k2, 13);
+	}
+
+	@Benchmark
+	public Enumerable exactN060K208() {
+		return subset60.getRandomSetOfSubsets(k2, 8);
+	}
+
+	@Benchmark
+	public Enumerable exactN100K208() {
+		return subset100.getRandomSetOfSubsets(k2, 8);
+	}
+
+	@Benchmark
+	public Enumerable exactN100K210() {
+		return subset100.getRandomSetOfSubsets(k2, 10);
+	}
+
+	@Benchmark
+	public Enumerable exactN200K210() {
+		return subset200.getRandomSetOfSubsets(k2, 10);
+	}
+
+	@Benchmark
+	public Enumerable exactN300K209() {
+		return subset300.getRandomSetOfSubsets(k2, 9);
+	}
+
+	@Benchmark
+	public Enumerable exactN400K209() {
+		return subset400.getRandomSetOfSubsets(k2, 9);
+	}
+}
+
+/*
+ * Mode.Throughput: Calculate number of operations in a time unit. (Higher score
+ * better) Mode.AverageTime: Calculate an average running time. (Lower score
+ * better) Mode.SampleTime: Calculate how long does it take for a method to run
+ * (including percentiles). Mode.SingleShotTime: Just run a method once (useful
+ * for cold-testing mode). Or more than once if you have specified a batch size
+ * for your iterations (see @Measurement annotation below) - in this case JMH
+ * will calculate the batch running time (total time for all invocations in a
+ * batch). Any set of these modes You can specify any set of these modes - the
+ * test will be run several times (depending on number of requested modes).
+ * Mode.All: All these modes one after another.
+ */
\ No newline at end of file
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java b/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..804f6c81c18e13b610a378cbb67835f3ab4b3408
--- /dev/null
+++ b/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java
@@ -0,0 +1,227 @@
+// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved.
+package tlc2.tool.fp;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Test;
+
+import tlc2.util.BufferedRandomAccessFile;
+
+public class ConcurrentWriteTest {
+	@Test
+	public void test() throws IOException {
+		final File tempFile = File.createTempFile("ConcurrentWriteTest_test", ".bin");
+		
+		RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF0.setLength(4000L * Long.BYTES);
+		for(long i = 0L; i < 1000; i++) {
+			tmpRAF0.writeLong(i);
+		}
+		
+		for(long i = 1000L; i < 2000; i++) {
+			tmpRAF0.writeLong(i);
+		}
+		
+		for(long i = 2000L; i < 3000; i++) {
+			tmpRAF0.writeLong(i);
+		}
+		
+		for(long i = 3000L; i < 4000; i++) {
+			tmpRAF0.writeLong(i);
+		}
+		
+		tmpRAF0.close();
+		
+		final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r");
+		for(long i = 0L; i < 4000; i++) {
+			assertEquals(i, tmpRAF.readLong());
+		}
+		tmpRAF.close();
+	}
+
+	@Test
+	public void test1() throws IOException {
+		final File tempFile = File.createTempFile("ConcurrentWriteTest_test1", ".bin");
+		final long limit = 4000000L;
+		final long partition = limit / 4L;
+		
+		final RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF0.setLength(limit * Long.BYTES);
+		for(long i = 0L; i < partition; i++) {
+			tmpRAF0.writeLong(i);
+		}
+		
+		final RandomAccessFile tmpRAF1 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF1.setLength(limit * Long.BYTES);
+		tmpRAF1.seek(partition * Long.BYTES);
+		for (long i = partition; i < (2L * partition); i++) {
+			tmpRAF1.writeLong(i);
+		}
+
+		final RandomAccessFile tmpRAF2 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF2.setLength(limit * Long.BYTES);
+		tmpRAF2.seek((2L * partition) * Long.BYTES);
+		for (long i = (2L * partition); i < (3L * partition); i++) {
+			tmpRAF2.writeLong(i);
+		}
+
+		final RandomAccessFile tmpRAF3 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF3.setLength(limit * Long.BYTES);
+		tmpRAF3.seek((3L * partition) * Long.BYTES);
+		for (long i = (3L * partition); i < (4L * partition); i++) {
+			tmpRAF3.writeLong(i);
+		}
+		
+		tmpRAF0.close();
+		tmpRAF1.close();
+		tmpRAF2.close();
+		tmpRAF3.close();
+		
+		final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r");
+		for(long i = 0L; i < limit; i++) {
+			assertEquals(i, tmpRAF.readLong());
+		}
+		tmpRAF.close();
+	}
+	
+	@Test
+	public void test2() throws IOException {
+		final File tempFile = File.createTempFile("ConcurrentWriteTest_test2", ".bin");
+		final long limit = 4000000L;
+		final long partition = limit / 4L;
+		
+		final RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF0.setLength(limit * Long.BYTES);
+
+		final RandomAccessFile tmpRAF1 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF1.setLength(limit * Long.BYTES);
+		tmpRAF1.seek(partition * Long.BYTES);
+
+		final RandomAccessFile tmpRAF2 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF2.setLength(limit * Long.BYTES);
+		tmpRAF2.seek((2L * partition) * Long.BYTES);
+
+		final RandomAccessFile tmpRAF3 = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF3.setLength(limit * Long.BYTES);
+		tmpRAF3.seek((3L * partition) * Long.BYTES);
+
+		for(long i = 0L; i < (1L * partition); i++) {
+			tmpRAF0.writeLong(i);
+		}
+		
+		for(long i = (1L * partition); i < (2L * partition); i++) {
+			tmpRAF1.writeLong(i);
+		}
+		
+		for(long i = (2L * partition); i < (3L * partition); i++) {
+			tmpRAF2.writeLong(i);
+		}
+		
+		for(long i = (3L * partition); i < (4L * partition); i++) {
+			tmpRAF3.writeLong(i);
+		}
+		
+		tmpRAF0.close();
+		tmpRAF1.close();
+		tmpRAF2.close();
+		tmpRAF3.close();
+		
+		final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r");
+		for(long i = 0L; i < limit; i++) {
+			assertEquals(i, tmpRAF.readLong());
+		}
+		tmpRAF.close();
+	}
+	
+	@Test
+	public void test3() throws IOException, InterruptedException {
+		final File tempFile = File.createTempFile("ConcurrentWriteTest_test3", ".bin");
+		final long limit = 400000000L;
+		final long writers = 8L;
+		final long partition = limit / writers;
+
+		final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>((int) writers);
+		for (long i = 0L; i < writers; i++) {
+			final long id = i;
+			tasks.add(new Callable<Void>() {
+				public Void call() throws Exception {
+					final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "rw");
+					tmpRAF.setLength(limit * Long.BYTES);
+					tmpRAF.seek(id * partition * Long.BYTES);
+
+					for (long j = (id * partition); j < ((id + 1) * partition); j++) {
+						tmpRAF.writeLong(j);
+					}
+
+					tmpRAF.close();
+					return null;
+				}
+			});
+		}
+		
+		final ExecutorService executorService = Executors.newFixedThreadPool((int) writers);
+		executorService.invokeAll(tasks);
+		executorService.shutdown();
+
+		final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r");
+		for(long i = 0L; i < limit; i++) {
+			assertEquals(i, tmpRAF.readLong());
+		}
+		tmpRAF.close();
+	}
+
+	@Test
+	public void test4() throws IOException, InterruptedException {
+		final File tempFile = File.createTempFile("ConcurrentWriteTest_test4", ".bin");
+		final long limit = 400000000L;
+		final long writers = 8L;
+		final long partition = limit / writers;
+		final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "rw");
+		tmpRAF.setLength(limit * Long.BYTES);
+		final FileChannel channel = tmpRAF.getChannel();
+
+		final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>((int) writers);
+		for (long i = 0L; i < writers; i++) {
+			final long id = i;
+			tasks.add(new Callable<Void>() {
+				public Void call() throws Exception {
+					long position = id * partition * Long.BYTES;
+					final ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES/* * 1024*/);
+					for (long j = (id * partition); j < ((id + 1) * partition); j++/*j+=1024L*/) {
+//						for (int i = 0; i < buffer.capacity(); i++) {
+							buffer.putLong(j/* + i*/);
+							buffer.flip();
+//						}
+						channel.write(buffer, position + (j * Long.BYTES));
+						buffer.clear();
+					}
+					channel.force(false);
+					return null;
+				}
+			});
+		};
+		
+		final ExecutorService executorService = Executors.newFixedThreadPool((int) writers);
+		executorService.invokeAll(tasks);
+		executorService.shutdown();
+		
+		tmpRAF.close();	
+
+		final BufferedRandomAccessFile checkRaf = new BufferedRandomAccessFile(tempFile, "r");
+		for(long i = 0L; i < limit; i++) {
+			assertEquals(i, checkRaf.readLong());
+		}
+		checkRaf.close();
+	}
+}
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java
index d7d5a294223e33529afdb81c1dd05669f5f921a3..2959b88da91ea032e54c32b346187a2a32459125 100644
--- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java
+++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java
@@ -1,28 +1,41 @@
 // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
 
+import tlc2.TLCGlobals;
 import tlc2.tool.fp.generator.BatchedFingerPrintGenerator;
 import tlc2.tool.fp.generator.FingerPrintGenerator;
 import tlc2.tool.fp.generator.LongVecFingerPrintGenerator;
+import tlc2.tool.fp.generator.PartitionedFingerPrintGenerator;
+import tlc2.util.IdThread;
 
 public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest {
 
 	private static final int NUM_THREADS = Integer.getInteger(MultiThreadedFPSetTest.class.getName() + ".numThreads",
-			2);
+			Runtime.getRuntime().availableProcessors());
 	private static final long INSERTIONS = Long.getLong(MultiThreadedFPSetTest.class.getName() + ".insertions",
 			Integer.MAX_VALUE + 2L);
 
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#setUp()
 	 */
-	protected void setUp() throws Exception {
-		super.setUp();
+	@Before
+	public void printStats() throws Exception {
 		System.out.println("Insertions: " + df.format(INSERTIONS)
 				+ " (approx: " + df.format(INSERTIONS * FPSet.LongSize >> 20) + " GiB)");
 		System.out.println("Thread count: " + NUM_THREADS);
@@ -32,6 +45,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest {
 	 * Test filling a {@link FPSet} with random fingerprints using multiple
 	 * threads in ordered batches
 	 */
+	@Test
 	public void testMaxFPSetSizeRndBatched() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 		doTest(BatchedFingerPrintGenerator.class);
 	}
@@ -40,6 +54,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest {
 	 * Test filling a {@link FPSet} with random fingerprints using multiple
 	 * threads in ordered LongVecs using putBlock/containsBlock
 	 */
+	@Test
 	public void testMaxFPSetSizeRndBlock() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 		doTest(LongVecFingerPrintGenerator.class);
 	}
@@ -48,10 +63,22 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest {
 	 * Test filling a {@link FPSet} with max int + 2L random using multiple
 	 * threads
 	 */
+	@Test
 	public void testMaxFPSetSizeRnd() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 		doTest(FingerPrintGenerator.class);
 	}
 	
+	/**
+	 * Test filling a {@link FPSet} with multiple threads. Each thread accesses
+	 * a disjunct partition of the key space and fills it up linearly. This
+	 * prevents hash collisions as well as lock contention. Essentially, this is
+	 * the best case scenario. It ignores INSERTIONS for now.
+	 */
+	@Test
+	public void testMaxFPSetSizePartitioned() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		doTest(PartitionedFingerPrintGenerator.class);
+	}
+	
 	/**
 	 * @param fpgClass
 	 * @throws IOException
@@ -63,37 +90,86 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest {
 	 * @throws IllegalArgumentException
 	 * @throws InvocationTargetException
 	 */
-	private void doTest(Class<? extends FingerPrintGenerator> fpgClass) throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+	protected FPSet doTest(Class<? extends FingerPrintGenerator> fpgClass) throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		// Skip the test if the property
+		// -Dtlc2.tool.fp.MultiThreadedFPSetTest.excludes contains the simple
+		// name of the test. I.e.
+		// ...excludes=_BatchedFingerPrintGenerator_LongVecFingerPrintGenerator_PartitionedFingerPrintGenerator
+		// to skip all but FingerPrintGenerator test. Note that the name has to
+		// be prefixed with "_" to make it possible to skip
+		// "FingerPrintGenerator" itself.
+		Assume.assumeFalse(System.getProperty(MultiThreadedFPSetTest.class.getName() + ".excludes", "")
+				.contains("_" + fpgClass.getSimpleName()));
+		System.out.println("Running test: " + fpgClass.getSimpleName());
+		
+		TLCGlobals.setNumWorkers(NUM_THREADS);
 		final FPSet fpSet = getFPSetInitialized(NUM_THREADS);
+		fpSet.incWorkers(NUM_THREADS);
 		final CountDownLatch latch = new CountDownLatch(NUM_THREADS);
 
-		final Constructor<?> constructor = fpgClass
-				.getConstructor(new Class[] { MultiThreadedFPSetTest.class, int.class, FPSet.class, CountDownLatch.class, long.class, long.class });
+		final Constructor<?> constructor = fpgClass.getConstructor(new Class[] { MultiThreadedFPSetTest.class,
+				int.class, int.class, FPSet.class, CountDownLatch.class, long.class, long.class, CyclicBarrier.class });
+		
+		// Take timestamp after instantiating FPSet to not measure zero'ing/initializing FPSet.  
+		startTimestamp = System.currentTimeMillis();
+
+		final Timer timer = new Timer();
+		final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS, new Runnable() {
+			public void run() {
+				// Start a periodic task to report progress. Do not do it as part of the
+				// FPGs below. It can drastically slow down an FPG selected to be the
+				// reporter.
+				final TimerTask reporter = new TimerTask() {
+					public void run() {
+						final long currentSize = fpSet.size();
+						final long insertions = currentSize - previousSize;
+						if (fpSet instanceof FPSetStatistic) {
+							FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet;
+							System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor");
+						} else {
+							System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min");
+						}
+						previousSize = currentSize;
+					}
+				};
+				// Take timestamp after instantiating FPSet to not measure zero'ing/initializing FPSet.  
+				startTimestamp = System.currentTimeMillis();
+				timer.scheduleAtFixedRate(reporter, 1L, 60 * 1000);
+			}
+		});
 		
 		long seed = RNG_SEED;
 		final FingerPrintGenerator[] fpgs = new FingerPrintGenerator[NUM_THREADS];
 		for (int i = 0; i < fpgs.length; i++) {
 			fpgs[i] = (FingerPrintGenerator) constructor.newInstance(
-					this, i, fpSet, latch, seed++, INSERTIONS);
-			Thread thread = new Thread(fpgs[i], "Producer#" + i);
+					this, i, fpgs.length, fpSet, latch, seed++, INSERTIONS, barrier);
+			Thread thread = new IdThread(fpgs[i], "Producer#" + i, i);
 			thread.start();
 		}
 
 		// wait for runnables/fpg to tear down the latch
 		latch.await();
-
 		endTimeStamp = new Date();
 		
+		// Cancel reporting task.
+		timer.cancel();
+		
 		long overallPuts = 0L;
+		long overallCollisions = 0L;
 		
 		// print stats
 		for (int i = 0; i < fpgs.length; i++) {
 			final FingerPrintGenerator fpg = fpgs[i];
 			long puts = fpg.getPuts();
-			System.out.println("Producer: " + fpg.getId() + " puts: " + puts);
-			System.out.println("puts/collisions: " + (double) (puts / fpg.getCollisions()));
+			long collisions = fpg.getCollisions();
+			System.out.println(String.format("Producer: %s, puts: %s, puts/collisions: %s", fpg.getId(), puts,
+					(collisions == 0 ? "none" : (double) (puts / collisions))));
 			overallPuts += puts;
+			overallCollisions += collisions;
 		}
+		System.out.println(String.format("Total puts: %s, total collisions: %s, total load factor: %s, duration: %s ms.", overallPuts,
+				overallCollisions, df.format(((FPSetStatistic) fpSet).getLoadFactor()), endTimeStamp.getTime() - startTimestamp));
+		printInsertionSpeed(fpSet, startTimestamp, endTimeStamp.getTime());
 		
 		// Do not compare fpSet.size() to insertions as several FPGs might race
 		// with the FPG that inserts the INSERTIONS element. Hence we count the
@@ -108,5 +184,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest {
 		// Check a DiskFPSet's invariant that after flush all fingerprints in
 		// the file are a) monotonically increasing and b) there are no duplicates.
 		assertTrue(fpSet.checkInvariant());
+		
+		return fpSet;
 	}
 }
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedLSBDiskFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedLSBDiskFPSetTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b806e2fe6afd0b241621ffc54732c001c64cbef
--- /dev/null
+++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedLSBDiskFPSetTest.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved.
+package tlc2.tool.fp;
+
+import java.io.IOException;
+
+public class MultiThreadedLSBDiskFPSetTest extends MultiThreadedFPSetTest {
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.AbstractFPSetTest#getFPSet(long)
+	 */
+	@Override
+	protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException {
+		fpSetConfig.setRatio(1.0d); // Tests can consume all of the available VM memory
+		return new LSBDiskFPSet(fpSetConfig);
+	}
+}
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java
index 41701ea7e675d71085a55c193e593e18d76916dc..7d34c1eb752338da7a3eb0285b7a95fbecf53373 100644
--- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java
+++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java
@@ -10,6 +10,7 @@ public class MultiThreadedMSBDiskFPSetTest extends MultiThreadedFPSetTest {
 	 */
 	@Override
 	protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException {
+		fpSetConfig.setRatio(1.0d); // Tests can consume all of the available VM memory
 		return new MSBDiskFPSet(fpSetConfig);
 	}
 }
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java
index 452b01cb093a9552383c3d755e23ce5377dabaf4..a6dc15bdf0b46cac444b26b822073c4b12872872 100644
--- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java
+++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java
@@ -2,6 +2,11 @@
 package tlc2.tool.fp;
 
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Assert;
+
+import tlc2.tool.fp.generator.PartitionedFingerPrintGenerator;
 
 public class MultiThreadedOffHeapDiskFPSetTest extends MultiThreadedFPSetTest {
 
@@ -10,6 +15,17 @@ public class MultiThreadedOffHeapDiskFPSetTest extends MultiThreadedFPSetTest {
 	 */
 	@Override
 	protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException {
-		return new OffHeapDiskFPSet(new FPSetConfiguration(1.0d));
+		return new OffHeapDiskFPSet(new FPSetConfiguration(1.0d, OffHeapDiskFPSet.class.getName()));
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.fp.MultiThreadedFPSetTest#testMaxFPSetSizePartitioned()
+	 */
+	@Override
+	public void testMaxFPSetSizePartitioned()
+			throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException,
+			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		final OffHeapDiskFPSet fpSet = (OffHeapDiskFPSet) doTest(PartitionedFingerPrintGenerator.class);
+		Assert.assertEquals(0, fpSet.getBucketCapacity()); // bucket capacity is actually reprobe which expected to be zero.
 	}
 }
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java
index 9ebb8bf255fd76b9c40f0f24ae52ef0d208f665c..dd2b536878ef475095576f3d2d98b45a7d092ff5 100644
--- a/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java
+++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java
@@ -4,6 +4,9 @@ package tlc2.tool.fp.generator;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Assert;
 
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.MultiThreadedFPSetTest;
@@ -12,23 +15,28 @@ public class BatchedFingerPrintGenerator extends FingerPrintGenerator {
 
 	private static final int batch = 1024;
 	
-	public BatchedFingerPrintGenerator(MultiThreadedFPSetTest test, int id, FPSet fpSet, CountDownLatch latch, long seed, long insertions) {
-		super(test, id, fpSet, latch, seed, insertions);
+	public BatchedFingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch, long seed, long insertions, final CyclicBarrier barrier) {
+		super(test, id, numThreads, fpSet, latch, seed, insertions, barrier);
 	}
 	
 	/* (non-Javadoc)
 	 * @see java.lang.Runnable#run()
 	 */
 	public void run() {
+		waitForAllThreadsStarted();
+		
 		long predecessors[] = new long[batch];
 		boolean initialized = false;
-		while (fpSet.size() < insertions) {
+		// Reduce number of FPSet#size invocation by counting puts/collisions.
+		// FPSet#size can cause an FPSet to synchronize all its writers slowing
+		// down execution.
+		while (puts + collisions < perThreadInsertions || fpSet.size() < totalInsertions) {
 			try {
 				// Make sure set still contains predecessors
 				if (initialized) {
 					for (int i = 0; i < predecessors.length; i++) {
 						long predecessor = predecessors[i];
-						MultiThreadedFPSetTest.assertTrue(fpSet.contains(predecessor));
+						Assert.assertTrue(fpSet.contains(predecessor));
 					}
 				}
 
@@ -49,15 +57,9 @@ public class BatchedFingerPrintGenerator extends FingerPrintGenerator {
 						collisions++;
 					}
 				}
-
-				// First producer prints stats
-				if (id == 0) {
-					test.printInsertionSpeed(fpSet.size());
-				}
-
 			} catch (IOException e) {
 				e.printStackTrace();
-				MultiThreadedFPSetTest.fail("Unexpected");
+				Assert.fail("Unexpected");
 			}
 		}
 		latch.countDown();
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java
index 728c0bfdbf164c54e5f80b5d48345a46766a0d53..aa0db19ed87e79b675dca8252f54e86e0e3d6f58 100644
--- a/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java
+++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java
@@ -3,60 +3,83 @@ package tlc2.tool.fp.generator;
 
 import java.io.IOException;
 import java.util.Random;
+import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Assert;
 
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.MultiThreadedFPSetTest;
 
 public class FingerPrintGenerator implements Runnable {
 
-	protected final long insertions;
+	protected final long totalInsertions;
+	protected final long perThreadInsertions;
+	protected final long seed;
 	protected final Random rnd;
 	protected final FPSet fpSet;
 	protected final CountDownLatch latch;
+	protected final CyclicBarrier barrier;
 	protected final int id;
+	protected final int numThreads;
 	protected final MultiThreadedFPSetTest test;
 	protected long puts = 0L;
 	protected long collisions = 0L;
 
-	public FingerPrintGenerator(MultiThreadedFPSetTest test, int id, FPSet fpSet, CountDownLatch latch, long seed, long insertions) {
+	public FingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch,
+			long seed, long totalInsertions, final CyclicBarrier barrier) {
 		this.test = test;
 		this.id = id;
+		this.numThreads = numThreads;
 		this.fpSet = fpSet;
 		this.latch = latch;
+		this.barrier = barrier;
+		this.seed = seed;
 		this.rnd = new Random(seed);
-		this.insertions = insertions;
+		this.totalInsertions = totalInsertions;
+		this.perThreadInsertions = (long) Math.floor(totalInsertions / numThreads);
 	}
 
 	/* (non-Javadoc)
 	 * @see java.lang.Runnable#run()
 	 */
 	public void run() {
+		waitForAllThreadsStarted();
+		
 		long predecessor = 0L;
-		while (fpSet.size() < insertions) {
+		// Reduce number of FPSet#size invocation by counting puts/collisions.
+		// FPSet#size can cause an FPSet to synchronize all its writers slowing
+		// down execution.
+		while (puts + collisions < perThreadInsertions || fpSet.size() < totalInsertions) {
 			try {
 				// make sure set still contains predecessor
 				if (predecessor != 0L) {
-					MultiThreadedFPSetTest.assertTrue(fpSet.contains(predecessor));
+					Assert.assertTrue(fpSet.contains(predecessor));
 				}
 
 				predecessor = rnd.nextLong();
 
+				// Periodically verify the FPSet's content. This causes a
+				// drastic slow down.
+//				if (fpSet.size() % 10000 == 0) {
+//					final Random verify = new Random(seed);
+//					long fp = verify.nextLong();
+//					while (fp != predecessor) {
+//						Assert.assertTrue(fpSet.contains(fp));
+//						fp = verify.nextLong();
+//					}
+//				}
+//				
 				boolean put = fpSet.put(predecessor);
 				if (put == false) {
 					puts++;
 				} else {
 					collisions++;
 				}
-
-				// First producer prints stats
-				if (id == 0) {
-					test.printInsertionSpeed(fpSet.size());
-				}
-
 			} catch (IOException e) {
 				e.printStackTrace();
-				MultiThreadedFPSetTest.fail("Unexpected");
+				Assert.fail("Unexpected");
 			}
 		}
 		latch.countDown();
@@ -77,6 +100,16 @@ public class FingerPrintGenerator implements Runnable {
 	 * @return the collisions
 	 */
 	public long getCollisions() {
-		return collisions == 0 ? 1 : collisions;
+		return collisions;
+	}
+
+	protected void waitForAllThreadsStarted() {
+		try {
+			barrier.await();
+		} catch (InterruptedException e1) {
+			e1.printStackTrace();
+		} catch (BrokenBarrierException e1) {
+			e1.printStackTrace();
+		}
 	}
 }
\ No newline at end of file
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java
index 851a5a45ec627dee62401113e7ef03f8504a5f0d..fa38a0210b98938152daed9bc799b999c663029c 100644
--- a/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java
+++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java
@@ -4,6 +4,9 @@ package tlc2.tool.fp.generator;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Assert;
 
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.MultiThreadedFPSetTest;
@@ -14,22 +17,27 @@ public class LongVecFingerPrintGenerator extends FingerPrintGenerator {
 
 	private static final int batch = 1024;
 	
-	public LongVecFingerPrintGenerator(MultiThreadedFPSetTest test, int id, FPSet fpSet, CountDownLatch latch, long seed, long insertions) {
-		super(test, id, fpSet, latch, seed, insertions);
+	public LongVecFingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch, long seed, long insertions, final CyclicBarrier barrier) {
+		super(test, id, numThreads, fpSet, latch, seed, insertions, barrier);
 	}
 	
 	/* (non-Javadoc)
 	 * @see java.lang.Runnable#run()
 	 */
 	public void run() {
+		waitForAllThreadsStarted();
+		
 		TestLongVec predecessors = new TestLongVec(batch);
 		boolean initialized = false;
-		while (fpSet.size() < insertions) {
+		// Reduce number of FPSet#size invocation by counting puts/collisions.
+		// FPSet#size can cause an FPSet to synchronize all its writers slowing
+		// down execution.
+		while (puts + collisions < perThreadInsertions || fpSet.size() < totalInsertions) {
 			try {
 				// Make sure set still contains predecessors
 				if (initialized) {
 					final BitVector bitVector = fpSet.containsBlock(predecessors);
-					MultiThreadedFPSetTest.assertTrue(bitVector.trueCnt() == batch);
+					Assert.assertEquals(batch, batch - bitVector.trueCnt());
 				}
 
 				// Fill new fingerprints and sort them
@@ -43,15 +51,9 @@ public class LongVecFingerPrintGenerator extends FingerPrintGenerator {
 				final BitVector bitVector = fpSet.putBlock(predecessors);
 				puts += bitVector.trueCnt();
 				collisions += (batch - bitVector.trueCnt());
-
-				// First producer prints stats
-				if (id == 0) {
-					test.printInsertionSpeed(fpSet.size());
-				}
-
 			} catch (IOException e) {
 				e.printStackTrace();
-				MultiThreadedFPSetTest.fail("Unexpected");
+				Assert.fail("Unexpected");
 			}
 		}
 		latch.countDown();
diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/PartitionedFingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/PartitionedFingerPrintGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4deb32cc7aadfb9a23c11f4d78fa0428082b122
--- /dev/null
+++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/PartitionedFingerPrintGenerator.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.fp.generator;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Assert;
+
+import tlc2.tool.fp.FPSet;
+import tlc2.tool.fp.MultiThreadedFPSetTest;
+
+public class PartitionedFingerPrintGenerator extends FingerPrintGenerator {
+
+	private long fp;
+	
+	private final long numOfPerThreadBuckets;
+	private final long increment;
+
+	public PartitionedFingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch,
+			long seed, long insertions, final CyclicBarrier barrier) {
+		super(test, id, numThreads, fpSet, latch, seed, insertions, barrier);
+		
+		final long numOfTotalBuckets = fpSet.getConfiguration().getMemoryInFingerprintCnt();
+		numOfPerThreadBuckets = numOfTotalBuckets / (1L * numThreads);
+
+		final long perThreadStartBucket = numOfPerThreadBuckets * (1L * id);
+		increment = (long) Math.ceil((Long.MAX_VALUE - 1L) / (numOfTotalBuckets * 1d));
+		fp = increment * perThreadStartBucket;
+	}
+	
+	/* (non-Javadoc)
+	 * @see java.lang.Runnable#run()
+	 */
+	public void run() {
+		waitForAllThreadsStarted();
+		
+		long insertions = 0L;
+		
+		while (insertions++ < numOfPerThreadBuckets) {
+			try {
+				if (fp != 0L) {
+					if (fpSet.put(fp) != false) {
+						Assert.fail("Linear fill-up should not cause a collision");
+					}
+					// In case PartitionedFingerPrintGenerator and
+					// FingerPrintGenerator are used in performance tests, burn
+					// the same amount of cycles to obtain the next random like
+					// FPG does. puts is meaningless in the scope of PFPG
+					// anyway. It inserts up to a load factor of 1.
+					//puts += rnd.nextLong();
+					puts++;
+				}
+				fp += increment;
+			} catch (IOException e) {
+				e.printStackTrace();
+				Assert.fail("Unexpected");
+			}
+		}
+		latch.countDown();
+	}
+}
diff --git a/tlatools/test-long/tlc2/tool/fp/FPSetTest.java b/tlatools/test-long/tlc2/tool/fp/FPSetTest.java
index 8d758ddfe87384d9a811f682ee6dc5e6910ba488..fd48e05b5e8da50d417c38f75f7b5a1ee0d89cf5 100644
--- a/tlatools/test-long/tlc2/tool/fp/FPSetTest.java
+++ b/tlatools/test-long/tlc2/tool/fp/FPSetTest.java
@@ -1,15 +1,22 @@
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.util.Date;
 import java.util.Random;
 
+import org.junit.Test;
+
 public abstract class FPSetTest extends AbstractFPSetTest {
 
 	/**
 	 * Test filling a {@link FPSet} with four linearly incrementing values
 	 * @throws IOException
 	 */
+	@Test
 	public void testSimpleFill() throws IOException {
 		final FPSet fpSet = getFPSet(new FPSetConfiguration());
 		fpSet.init(1, tmpdir, filename);
@@ -29,6 +36,7 @@ public abstract class FPSetTest extends AbstractFPSetTest {
 	 * Test filling a {@link FPSet} with max int + 1 random
 	 * @throws IOException
 	 */
+	@Test
 	public void testMaxFPSetSizeRnd() throws IOException {
 		Random rnd = new Random(RNG_SEED);
 		
@@ -50,7 +58,7 @@ public abstract class FPSetTest extends AbstractFPSetTest {
 			long currentSize = fpSet.size();
 			assertTrue(i == currentSize);
 
-			printInsertionSpeed(currentSize);
+			printInsertionSpeed(fpSet);
 		}
 	
 		// try creating a check point
@@ -69,6 +77,7 @@ public abstract class FPSetTest extends AbstractFPSetTest {
 	 * Test filling a {@link FPSet} with max int + 1 
 	 * @throws IOException
 	 */
+	@Test
 	public void testMaxFPSetSize() throws IOException {
 	
 		//
@@ -92,7 +101,7 @@ public abstract class FPSetTest extends AbstractFPSetTest {
 			long currentSize = fpSet.size();
 			assertTrue(++counter == currentSize);
 			
-			printInsertionSpeed(currentSize);
+			printInsertionSpeed(fpSet);
 		}
 	
 		// try creating a check point
diff --git a/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java b/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java
similarity index 81%
rename from tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java
rename to tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java
index a619498f84f614c16e37c4be4a6a82d9f5b8947e..dd0667435682a0e3152c7069c833c537e7a432aa 100644
--- a/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java
+++ b/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java
@@ -1,15 +1,21 @@
 // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.util.Random;
 
+import org.junit.Test;
+
 import util.TLCRuntime;
 
-public class OffHeapDiskFPSetTest extends FPSetTest {
+public class OffHeapDiskFPSetLongTest extends FPSetTest {
 	
 	private static final int FLUSHES = 4;
 
+	@Test
 	public void testCollisionBucket() throws IOException {
 		final FPSet fpSet = getFPSet(new FPSetConfiguration());
 		fpSet.init(1, tmpdir, filename);
@@ -20,6 +26,7 @@ public class OffHeapDiskFPSetTest extends FPSetTest {
 		}
 	}
 
+	@Test
 	public void testPosition() throws IOException {
 		final FPSet fpSet = getFPSet(new FPSetConfiguration());
 		fpSet.init(1, tmpdir, filename);
@@ -36,6 +43,7 @@ public class OffHeapDiskFPSetTest extends FPSetTest {
 	/**
 	 * 
 	 */
+	@Test
 	public void testMultipleFlushes() throws IOException {
 		final FPSet fpSet = getFPSet(new FPSetConfiguration());
 		fpSet.init(1, tmpdir, filename);
@@ -61,7 +69,8 @@ public class OffHeapDiskFPSetTest extends FPSetTest {
 	 * @see tlc2.tool.fp.AbstractFPSetTest#getFPSet(long)
 	 */
 	@Override
-	protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException {
-		return new OffHeapDiskFPSet(new FPSetConfiguration(1.0d));
+	protected FPSet getFPSet(final FPSetConfiguration ignored) throws IOException {
+		final FPSetConfiguration config = new FPSetConfiguration(1.0d, OffHeapDiskFPSet.class.getName());
+		return new OffHeapDiskFPSet(config);
 	}
 }
diff --git a/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java b/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java
index f2e0f4dcf5db43857d0b3340afadda9b23bfae0a..095bd66b36d783f62f48b6e41ba0eb30a17bed65 100644
--- a/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java
+++ b/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java
@@ -26,6 +26,9 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
@@ -36,6 +39,9 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import org.junit.Before;
+import org.junit.Test;
+
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.tool.WorkerMonitor;
@@ -74,6 +80,7 @@ public abstract class MultiThreadedSpecTest extends ModelCheckerTestCase {
 		this.waitedRatio = waitedRatio;
 	}
 
+	@Test
 	public void testSpec() throws BrokenBarrierException, InterruptedException, TimeoutException {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
@@ -128,6 +135,7 @@ public abstract class MultiThreadedSpecTest extends ModelCheckerTestCase {
 		}
 	}
 	
+	@Before
 	public void setUp() {
 		// Set the threshold before TLC (periodically) checks liveness to
 		// the largest possible value. This essentially stops TLC from checking
@@ -155,8 +163,6 @@ public abstract class MultiThreadedSpecTest extends ModelCheckerTestCase {
 				latch.countDown();
 			}
 		});
-		
-		super.setUp();
 	}
 	
 	protected int getNumberOfThreads() {
diff --git a/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java b/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java
index 87779edbc369c33844c2c7f8da33fa437a98d23f..c4dd39a04c4bf1fc388abb9ea32d7c1c113a2ccb 100644
--- a/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java
+++ b/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java
@@ -25,9 +25,11 @@
  ******************************************************************************/
 package tlc2.tool.liveness;
 
+import util.TLAConstants;
+
 public class NoTableauSpecTest extends MultiThreadedSpecTest {
 
 	public NoTableauSpecTest() {
-		super("MC", "VoteProof", "137297983", "693930", 0.25d, 0.25d);
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "VoteProof", "137297983", "693930", 0.25d, 0.25d);
 	}
 }
diff --git a/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java b/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java
index a2789e1a5b8e91e542adddf8f6ac64fa0c3b58d5..332b7f4205975224cd3ad3525d44a244203b7ae7 100644
--- a/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java
+++ b/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java
@@ -25,9 +25,11 @@
  ******************************************************************************/
 package tlc2.tool.liveness;
 
+import util.TLAConstants;
+
 public class TableauSpecTest extends MultiThreadedSpecTest {
 
 	public TableauSpecTest() {
-		super("MC", "EWD840", new String[] {"-maxSetSize", "9000000"}, "540765192", "10487806", 0.3d, 0.3d);
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "EWD840", new String[] {"-maxSetSize", "9000000"}, "540765192", "10487806", 0.3d, 0.3d);
 	}
 }
diff --git a/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java b/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java
index f5fccba4fa918552c46cc34617cb80a11aa54b9c..7722fccb64b69d5b5a05044514d0b8dcd223602c 100644
--- a/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java
+++ b/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java
@@ -1,7 +1,13 @@
 package tlc2.tool.queue;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import tlc2.tool.TLCState;
 
 public class DiskStateQueueTest extends StateQueueTest {
@@ -11,9 +17,8 @@ public class DiskStateQueueTest extends StateQueueTest {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.queue.StateQueueTest#setUp()
 	 */
-	protected void setUp() throws Exception {
-		super.setUp();
-
+	@Before
+	public void setUp() throws Exception {
 		// create a temp folder in java.io.tmpdir and have it deleted on VM exit
 		final String diskdir = System.getProperty("java.io.tmpdir") + File.separator + "MultiDiskStateQueueTest_"
 				+ System.currentTimeMillis();
@@ -27,6 +32,7 @@ public class DiskStateQueueTest extends StateQueueTest {
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#tearDown()
 	 */
+	@After
 	public void tearDown() {
 		// delete all nested files
 		final File[] listFiles = file.listFiles();
@@ -39,6 +45,7 @@ public class DiskStateQueueTest extends StateQueueTest {
 	
 	// add Integer.MAX_VALUE states and check growth of MultiStateQueue. 
 	// Reuse the same state to speed up instantiation and space requirements
+	@Test
 	public void testGrowBeyondIntMaxValue() {
 		final TLCState state = new DummyTLCState();
 
diff --git a/tlatools/test-model/.gitattributes b/tlatools/test-model/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..8b8a4987707a99a70444d60d1bd5911155574261
--- /dev/null
+++ b/tlatools/test-model/.gitattributes
@@ -0,0 +1,2 @@
+* text eol=lf
+*.zip -text
\ No newline at end of file
diff --git a/tlatools/test-model/.gitignore b/tlatools/test-model/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2f100884b03c579eea1de3e29a003ea94ac86545
--- /dev/null
+++ b/tlatools/test-model/.gitignore
@@ -0,0 +1,2 @@
+/CallGotoTest.old
+/CallGotoUnlabeledTest.old
diff --git a/tlatools/test-model/AS/AS.cfg b/tlatools/test-model/AS/AS.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..2aacadae1f61d029d84eab28111b6caae7aabb1c
--- /dev/null
+++ b/tlatools/test-model/AS/AS.cfg
@@ -0,0 +1,6 @@
+\* CONSTANT definitions
+CONSTANT
+c = 4
+\* SPECIFICATION definition
+SPECIFICATION
+ASInit
diff --git a/tlatools/test-model/AS/AS.tla b/tlatools/test-model/AS/AS.tla
new file mode 100644
index 0000000000000000000000000000000000000000..20482a157b7c15884ae910d82a2ecdfe3539d682
--- /dev/null
+++ b/tlatools/test-model/AS/AS.tla
@@ -0,0 +1,30 @@
+--------------------------------- MODULE AS ---------------------------------
+EXTENDS Integers, FiniteSets
+
+CONSTANT c
+ASSUME c \in Nat \ {0}
+
+R == -42
+
+
+VARIABLES x, z
+
+ASTypeOK == /\ x \in 1..c \cup {R}
+            /\ z \subseteq 1..c
+
+ASInit == /\ x = R
+          /\ z = {}
+
+ASChoose == /\ Cardinality(z) # c 
+            /\ \E n \in 1..c \ z : /\ x' = n
+                                   /\ z' = z \cup {n}
+
+ASRest == /\ Cardinality(z) = c
+          /\ x' = R
+          /\ z' = {}
+
+ASNext == ASChoose \/ ASRest
+
+AS == ASInit /\ [][ASNext]_<<x, z>>
+
+=============================================================================
diff --git a/tlatools/test-model/AssertExpressionStack.cfg b/tlatools/test-model/AssertExpressionStack.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/AssertExpressionStack.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/AssertExpressionStack.tla b/tlatools/test-model/AssertExpressionStack.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5d344919bb01af72f67c5accf6653042d1524969
--- /dev/null
+++ b/tlatools/test-model/AssertExpressionStack.tla
@@ -0,0 +1,7 @@
+------------------------------ MODULE AssertExpressionStack ------------------------------
+EXTENDS Naturals, TLC
+
+VARIABLES x
+
+Spec == x = 0 /\ [][x'=1/\Assert(x=0, "Fails after initial state")]_x
+=============================================================================
diff --git a/tlatools/test-model/AssignmentInit.cfg b/tlatools/test-model/AssignmentInit.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..cea554fa6375ea5fbb9074b946f867a1bd02ac99
--- /dev/null
+++ b/tlatools/test-model/AssignmentInit.cfg
@@ -0,0 +1,7 @@
+SPECIFICATION
+Spec
+CONSTANT
+N = 42
+M = 42
+\*INVARIANT
+\*Inv
\ No newline at end of file
diff --git a/tlatools/test-model/AssignmentInit.tla b/tlatools/test-model/AssignmentInit.tla
new file mode 100644
index 0000000000000000000000000000000000000000..54c495e3c4a08c9665a035e5cc9b769c5bceced5
--- /dev/null
+++ b/tlatools/test-model/AssignmentInit.tla
@@ -0,0 +1,30 @@
+--------------------------- MODULE AssignmentInit ---------------------------
+EXTENDS Integers, TLC
+VARIABLE s
+
+min(S) == CHOOSE e \in S: \A a \in S: e <= a
+
+InitExit(var, S) == \E val \in S: (var = val /\ var > min(S))
+
+InitAll(var, S) == \A val \in S: (var = val /\ var \in S)
+
+InitIn(var, S) == var \in S /\ var > min(S)
+
+InitEq(var, S, val) == var \in S /\ var + 1 = val
+
+isOdd(n) == n % 2 = 1
+InitEven(var, S) == var \in S /\ isOdd(var)
+
+\* With this Init(var), the test does not fail without its fix.
+\*Init(ignored) == \E val \in {0,1}: (s = val /\ s > 0)
+
+\* Init1 one state + Init3 one state
+Spec == /\ \/ InitExit(s, {0,1}) \* 1 unique state
+           \/ InitAll(s, {2})    \* 1 unique state
+           \/ InitIn(s, {4,5})   \* 1 unique state
+           \/ InitAll(s, {6,7})  \* 0 unique states
+           \/ InitEq(s, {8,9}, 10) \* 1 unique states
+           \/ InitEven(s, {10,11}) \* 1 unique states
+        /\ [][InitExit(s, {0,1})/\UNCHANGED s]_s
+
+=============================================================================
diff --git a/tlatools/test-model/AssignmentInitExpensive.cfg b/tlatools/test-model/AssignmentInitExpensive.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..cea554fa6375ea5fbb9074b946f867a1bd02ac99
--- /dev/null
+++ b/tlatools/test-model/AssignmentInitExpensive.cfg
@@ -0,0 +1,7 @@
+SPECIFICATION
+Spec
+CONSTANT
+N = 42
+M = 42
+\*INVARIANT
+\*Inv
\ No newline at end of file
diff --git a/tlatools/test-model/AssignmentInitExpensive.tla b/tlatools/test-model/AssignmentInitExpensive.tla
new file mode 100644
index 0000000000000000000000000000000000000000..dc4091521a568de4fa2872183761c4392cdedc83
--- /dev/null
+++ b/tlatools/test-model/AssignmentInitExpensive.tla
@@ -0,0 +1,17 @@
+--------------------------- MODULE AssignmentInitExpensive ---------------------------
+EXTENDS Integers
+VARIABLE s
+
+\* expensiveOp takes ~10 seconds wallclock time on a modern day machine.
+\* The point is, that doing 10k times will make the test time out.
+expensiveOp == CHOOSE e \in SUBSET (1..18) : TRUE
+
+\* This variant (with an unused parameter) causes TLC to reevaluate 
+\* CHOOSE ... SUBSET 10k times.
+\*expensiveOpParam(ignored) == CHOOSE e \in SUBSET (1..18) : TRUE
+
+Init(var) == \E val \in 0..10000: var = expensiveOp
+
+Spec == Init(s) /\ [][UNCHANGED s]_s
+
+=============================================================================
diff --git a/tlatools/test-model/AssignmentInitNeg.cfg b/tlatools/test-model/AssignmentInitNeg.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/AssignmentInitNeg.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/AssignmentInitNeg.tla b/tlatools/test-model/AssignmentInitNeg.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8269be1a94fed8c333f1d2ee3e9bdb95ac0545b6
--- /dev/null
+++ b/tlatools/test-model/AssignmentInitNeg.tla
@@ -0,0 +1,10 @@
+--------------------------- MODULE AssignmentInitNeg ---------------------------
+EXTENDS Integers
+VARIABLE s
+
+Init(var) == \E val \in 0..1: (var = val /\ var < 1)
+
+Spec == Init(s) /\ [][UNCHANGED s]_s
+
+Inv == (s < 1)
+=============================================================================
diff --git a/tlatools/test-model/AssignmentNext.cfg b/tlatools/test-model/AssignmentNext.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/AssignmentNext.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/AssignmentNext.tla b/tlatools/test-model/AssignmentNext.tla
new file mode 100644
index 0000000000000000000000000000000000000000..75450d5ab5cfd5bb62dde3346e63959f5ecd0690
--- /dev/null
+++ b/tlatools/test-model/AssignmentNext.tla
@@ -0,0 +1,10 @@
+--------------------------- MODULE AssignmentNext ---------------------------
+EXTENDS Integers
+VARIABLE s
+
+Next(var) == \E val \in 0..1: (var' = val /\ var' > 0)
+
+Spec == s = 23 /\ [][Next(s)]_s
+
+Inv == s # 0
+=============================================================================
diff --git a/tlatools/test-model/AssignmentNext2.cfg b/tlatools/test-model/AssignmentNext2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/AssignmentNext2.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/AssignmentNext2.tla b/tlatools/test-model/AssignmentNext2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fa337732111cdd97afea05adc986bdfb5ebdfd63
--- /dev/null
+++ b/tlatools/test-model/AssignmentNext2.tla
@@ -0,0 +1,9 @@
+--------------------------- MODULE AssignmentNext2 ---------------------------
+EXTENDS Integers
+VARIABLE s
+
+Next(var) == \E val \in 0..1: (var = val /\ var > 0)
+
+Spec == s = "" /\ [][Next(s')]_s
+
+=============================================================================
diff --git a/tlatools/test-model/AssignmentNext3.cfg b/tlatools/test-model/AssignmentNext3.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/AssignmentNext3.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/AssignmentNext3.tla b/tlatools/test-model/AssignmentNext3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9843f1a755efb077eb20961bbd4b304583d2bef8
--- /dev/null
+++ b/tlatools/test-model/AssignmentNext3.tla
@@ -0,0 +1,9 @@
+--------------------------- MODULE AssignmentNext3 ---------------------------
+EXTENDS Integers
+VARIABLE s
+
+F(var) == (var \in 0..9 /\ (var % 2 = 0))
+
+Spec == s=0 /\ [][F(s')]_s
+
+=============================================================================
diff --git a/tlatools/test-model/BagsTest.cfg b/tlatools/test-model/BagsTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c03e566aa685ca74cda77763f103772eca0b2097
--- /dev/null
+++ b/tlatools/test-model/BagsTest.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+CONSTANT
+C = C
\ No newline at end of file
diff --git a/tlatools/test-model/BagsTest.tla b/tlatools/test-model/BagsTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..05dd61b84d5f28d64767b0da9a2b81c84c0ad831
--- /dev/null
+++ b/tlatools/test-model/BagsTest.tla
@@ -0,0 +1,199 @@
+---------------------------- MODULE BagsTest ----------------------------
+EXTENDS Bags, TLC, Integers
+
+CONSTANT C
+
+strings == {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o"}
+
+s == {"a","b","c"}
+
+a == <<>>
+b == <<1,2,3>>
+c == <<2,3,4>>
+d == <<1,1,1>>
+e == 1 :> 1
+f == 2 :> 1
+g == [x \in {1,2,3} |-> x * x]
+h == [a |-> 3, b |-> 2, c |-> 1]
+
+ASSUME(EmptyBag = <<>>)
+
+\* IsABag
+ASSUME(IsABag(a))
+ASSUME(IsABag(b))
+ASSUME(IsABag(c))
+ASSUME(IsABag(d))
+ASSUME(IsABag(e))
+ASSUME(IsABag(f))
+ASSUME(IsABag(g))
+ASSUME(IsABag(h))
+
+ASSUME(IsABag(SetToBag({})))
+
+t == <<0>>
+u == 2 :> -1
+v == <<1,0>>
+w == <<0,1>>
+x == <<0,0,0>>
+y == <<-1,-2,-3>>
+z == <<"a","b","c">>
+
+ASSUME(~IsABag(t))
+ASSUME(~IsABag(u))
+ASSUME(~IsABag(v))
+ASSUME(~IsABag(w))
+ASSUME(~IsABag(x))
+ASSUME(~IsABag(y))
+ASSUME(~IsABag(z)) \* With the standard module TLC fails with "Cannot decide if element "a" is element of Nat"
+
+ASSUME(\A bool \in BOOLEAN : ~IsABag(bool))
+ASSUME(~IsABag(C))	
+
+\* BagCardinality
+ASSUME(BagCardinality(a) = 0)
+ASSUME(BagCardinality(b) = 6)
+ASSUME(BagCardinality(c) = 9)
+ASSUME(BagCardinality(d) = 3)
+ASSUME(BagCardinality(e) = 1)
+ASSUME(BagCardinality(f) = 1)
+ASSUME(BagCardinality(g) = (1*1+2*2+3*3))
+ASSUME(BagCardinality(h) = 6)
+
+ASSUME(BagCardinality(<<1>>) = 1)
+ASSUME(BagCardinality(<<1,3>>) = 4)
+
+\* BagIn
+ASSUME(\A elem \in 1..10: ~BagIn(elem, a))
+ASSUME(\A elem \in 1..3: BagIn(elem, b))
+ASSUME(\A elem \in 4..10: ~BagIn(elem, b))
+ASSUME(\A dom \in DOMAIN c: BagIn(dom, c))
+ASSUME(\A dom \in DOMAIN d: BagIn(dom, d))
+ASSUME(\A dom \in DOMAIN e: BagIn(dom, e))
+ASSUME(\A dom \in DOMAIN f: BagIn(dom, f))
+ASSUME(\A dom \in DOMAIN f: BagIn(dom, f))
+ASSUME(\A dom \in DOMAIN g: BagIn(dom, g))
+ASSUME(\A dom \in DOMAIN h: BagIn(dom, h))
+ASSUME(\A str \in (strings \ DOMAIN h): ~BagIn(str, h))
+ASSUME(\A elem \in s : BagIn(elem, SetToBag(s)))
+
+
+\* SetToBag and BagToSet
+Codomain(fun) == {fun[i] : i \in DOMAIN fun}  
+ASSUME(SetToBag(s) = [elem \in s |-> 1])
+ASSUME(DOMAIN SetToBag(s) = s)
+ASSUME(Codomain(SetToBag(s)) = {1})
+ASSUME(BagToSet(SetToBag(s)) = s)
+
+ASSUME(BagToSet(a) = {})
+ASSUME(BagToSet(b) = DOMAIN b)
+ASSUME(BagToSet(c) = DOMAIN c)
+ASSUME(BagToSet(d) = DOMAIN d)
+ASSUME(BagToSet(e) = {1})
+ASSUME(BagToSet(f) = {2})
+ASSUME(BagToSet(g) = DOMAIN g)
+ASSUME(BagToSet(h) = s)
+
+
+\* CopiesIn
+ASSUME(\A str \in strings: CopiesIn(str, a) = 0)
+
+ASSUME(\A dom \in DOMAIN b: CopiesIn(dom, b) = b[dom])
+ASSUME(\A elem \in 5..10: CopiesIn(elem, b) = 0)
+ASSUME(\A dom \in DOMAIN c: CopiesIn(dom, c) = c[dom])
+ASSUME(\A elem \in 5..10: CopiesIn(elem, c) = 0)
+ASSUME(\A dom \in DOMAIN d: CopiesIn(dom, d) = d[dom])
+ASSUME(\A elem \in 5..10: CopiesIn(elem, d) = 0)
+
+ASSUME(CopiesIn(1, e) = 1)
+ASSUME(CopiesIn(2, f) = 1)
+ASSUME(CopiesIn(1, f) = 0)
+ASSUME(CopiesIn(1, 1 :> 0) = 0)
+ASSUME(CopiesIn(2, 2 :> -1) = -1)
+
+ASSUME(\A dom \in DOMAIN g: CopiesIn(dom, g) = dom * dom)
+
+ASSUME(CopiesIn("a", h) = 3)
+ASSUME(CopiesIn("b", h) = 2)
+ASSUME(CopiesIn("c", h) = 1)
+ASSUME(\A str \in (strings \ DOMAIN h): CopiesIn(str, h) = 0)
+
+ASSUME(\A aelem \in s: CopiesIn(aelem, [elem \in s |-> 42]) = 42)
+ASSUME(\A aelem \in s: CopiesIn(aelem, [elem \in s |-> 0]) = 0)
+
+
+\* (+)
+ASSUME(EmptyBag (+) EmptyBag = EmptyBag)
+ASSUME(a (+) a = a)
+ASSUME(b (+) b = <<2,4,6>>)
+ASSUME(b (+) EmptyBag = b)
+ASSUME(c (+) c = <<4,6,8>>)
+ASSUME(d (+) d = <<2,2,2>>)
+ASSUME(e (+) e = <<2>>)
+ASSUME(b (+) c = c (+) b)
+ASSUME(b (+) c = <<3,5,7>>)
+ASSUME(h (+) h = [a |-> 6, b |-> 4, c |-> 2])
+ASSUME([c |-> 3, d |-> 2, e |-> 1] (+) h = [a |-> 3, b |-> 2, c |-> 4, d |-> 2, e |-> 1])
+
+
+\* (-)
+ASSUME(a (-) a = a)
+ASSUME(b (-) b = EmptyBag)
+ASSUME(b (-) EmptyBag = b)
+ASSUME(c (-) c = EmptyBag)
+ASSUME(d (-) d = EmptyBag)
+ASSUME(e (-) e = EmptyBag)
+ASSUME(b (-) c = b (-) c)
+ASSUME(b (-) c = EmptyBag)
+ASSUME(c (-) b = <<1,1,1>>)
+ASSUME([c |-> 3, d |-> 2, e |-> 1] (-) h = [c |-> 2, d |-> 2, e |-> 1])
+ASSUME((1:>1) (-) EmptyBag = <<1>>)
+
+
+\* BagUnion
+ASSUME(BagUnion({a,a}) = a)
+ASSUME(BagUnion({b,b}) = b)
+ASSUME(BagUnion({c,c}) = c)
+ASSUME(BagUnion({d,d}) = d)
+ASSUME(BagUnion({e,e}) = e)
+ASSUME(BagUnion({f,f}) = f)
+ASSUME(BagUnion({g,g}) = g)
+ASSUME(BagUnion({h,h}) = h)
+ASSUME(BagUnion({h,h,[c |-> 3, d |-> 2, e |-> 1]}) = h (+) [c |-> 3, d |-> 2, e |-> 1])
+ASSUME(BagUnion({a,b,c,d}) = <<4,6,8>>)
+ASSUME(BagUnion({EmptyBag, EmptyBag}) = <<>>)
+ASSUME(BagUnion({[a |-> 3, b |-> 2, c |-> 1],[c |-> 3, d |-> 2, e |-> 1]}) = [a |-> 3, b |-> 2, c |-> 4, d |-> 2, e |-> 1])
+ASSUME(BagUnion({1 :> 2, 2 :> 2, 3 :> 3}) = (1 :> 2) (+) (2 :> 2) (+) (3 :> 3))
+ASSUME(BagUnion({<<>>, <<1,1,1>>, <<1,2,3,4>>}) = (1:>2 @@ 2:>3 @@ 3:>4 @@ 4:>4))
+
+
+\* \sqsubseteq
+ASSUME((a \sqsubseteq a) = TRUE)
+ASSUME((a \sqsubseteq b) = TRUE)
+ASSUME((a \sqsubseteq c) = TRUE)
+ASSUME((a \sqsubseteq d) = TRUE)
+ASSUME((b \sqsubseteq b) = TRUE)
+ASSUME((c \sqsubseteq c) = TRUE)
+ASSUME((h \sqsubseteq [a |-> 3]) = FALSE)
+ASSUME((h \sqsubseteq [a |-> 3, b |-> 2]) = FALSE)
+ASSUME(([a |-> 3] \sqsubseteq h) = TRUE)
+ASSUME(([a |-> 3, b |-> 2] \sqsubseteq h) = TRUE)
+ASSUME(([a |-> 3, c |-> 1] \sqsubseteq h) = TRUE)
+ASSUME(([b |-> 2, c |-> 1] \sqsubseteq h) = TRUE)
+ASSUME((h \sqsubseteq h) = TRUE)
+
+
+\* BagOfAll
+ASSUME(BagOfAll(LAMBDA elem : elem, a) = a)
+ASSUME(BagOfAll(LAMBDA elem : TRUE, a) = a)
+ASSUME(BagOfAll(LAMBDA elem : elem, h) = h)
+ASSUME(BagOfAll(LAMBDA elem : TRUE, h) = TRUE :> 6)
+ASSUME(BagOfAll(LAMBDA elem : IF elem = "a" THEN "b" ELSE elem, [a |-> 3, b |-> 2, c |-> 1]) = [b |-> 5, c |-> 1])
+
+
+\* SubBag
+\* Module overwrite does not overwrite SubBag
+
+\* A dummy spec to make TLC run model checking as required by BagsTest.java.
+Spec == [][TRUE]_<<>>
+
+=============================================================================
diff --git a/tlatools/test-model/BidirectionalTransitions.tla b/tlatools/test-model/BidirectionalTransitions.tla
new file mode 100644
index 0000000000000000000000000000000000000000..13692344091ab32bb16f8c9eddfa4340e1837986
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions.tla
@@ -0,0 +1,28 @@
+-------- MODULE BidirectionalTransitions --------
+EXTENDS Naturals
+VARIABLE x
+
+-------------------------------------------------
+
+A == \/ x' = (x + 1) % 3
+B == x' \in 0..2
+Spec1A == (x=0) /\ [][A \/ B]_x/\ WF_x(A)
+Prop1A == Spec1A /\ WF_x(A) /\ []<><<A>>_x
+
+Fair1B == WF_x(B)
+Spec1B == (x=0) /\ [][A \/ B]_x/\ Fair1B
+Prop1Bx == WF_x(A)
+Prop1By == []<><<A>>_x
+
+-------------------------------------------------
+
+C == x' = (x + 1) % 4
+D == IF x = 0 THEN x' = 3 ELSE  x' = x - 1 
+Spec2 == (x=0) /\ [][C \/ D]_x/\ WF_x(D)
+Prop2 == Spec2 /\ WF_x(D) /\ []<><<D>>_x
+
+Fair2C == WF_x(C)
+Spec2C == (x=0) /\ [][C \/ D]_x/\ Fair2C
+Prop2Cx == WF_x(D)
+Prop2Cy == []<><<D>>_x
+=================================================
diff --git a/tlatools/test-model/BidirectionalTransitions1.cfg b/tlatools/test-model/BidirectionalTransitions1.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4dfeb6f715ebb0c65e5dc6ea88e7e21bb111121c
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions1.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec1A
+PROPERTY Prop1A
\ No newline at end of file
diff --git a/tlatools/test-model/BidirectionalTransitions1Bx.cfg b/tlatools/test-model/BidirectionalTransitions1Bx.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e367efe8156a93bef21e9f069e2762959c275273
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions1Bx.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec1B
+PROPERTY Prop1Bx
\ No newline at end of file
diff --git a/tlatools/test-model/BidirectionalTransitions1By.cfg b/tlatools/test-model/BidirectionalTransitions1By.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5ea4ab4b101fc5cbd4f3e4960ce7762fae4793a8
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions1By.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec1B
+PROPERTY Prop1By
\ No newline at end of file
diff --git a/tlatools/test-model/BidirectionalTransitions2.cfg b/tlatools/test-model/BidirectionalTransitions2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..cf19795244dfcd43754275e2226f72f98622b353
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions2.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec2
+PROPERTY Prop2
\ No newline at end of file
diff --git a/tlatools/test-model/BidirectionalTransitions2Cx.cfg b/tlatools/test-model/BidirectionalTransitions2Cx.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..89d65691a97406e4ea87144e83ee11f266b746b0
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions2Cx.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec2C
+PROPERTY Prop2Cx
\ No newline at end of file
diff --git a/tlatools/test-model/BidirectionalTransitions2Cy.cfg b/tlatools/test-model/BidirectionalTransitions2Cy.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8fdea573a4265780e569292ab97742c5b3b99a7a
--- /dev/null
+++ b/tlatools/test-model/BidirectionalTransitions2Cy.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec2C
+PROPERTY Prop2Cy
\ No newline at end of file
diff --git a/tlatools/test-model/Bug156/FindOp.tla b/tlatools/test-model/Bug156/FindOp.tla
index 565d6a4166ec9be74f15724a037f9d77c2f84aeb..04f2226f73061387a7dff9a1088478688237375a 100644
--- a/tlatools/test-model/Bug156/FindOp.tla
+++ b/tlatools/test-model/Bug156/FindOp.tla
@@ -1,2474 +1,2474 @@
-Known Bugs:
-- The operator may not be found if you click on a space inside its name, as in 
-   "Bar   (x)  !  y"
-
-Known Features:
-- Detects the ">" and "<" in " <3> " as operators. 
-
-- If at a label that is also the name of an operator, then that operator 
-  will be found.
-
-- An operator appearing inside a comment or a string will be found.
-
-------------------------------- MODULE FindOpWithoutPrint -------------------------------
-EXTENDS Integers, Sequences, TLC
------------------------------------------------------------------------------
-(***************************************************************************)
-(* Operations for doing things in Java coordinates.                        *)
-(***************************************************************************)
-JavaSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> seq[i+1]]
-   \* Turns a TLA sequence into a Java array.
-   
-JavaSubseq(seq, b, e) == [i \in 0 .. (e-1-b) |-> seq[i+b]]
-  \* Corresponds to Java's Subseq String method.
-  
-JavaLen(seq) == IF DOMAIN seq = {}
-                  THEN 0
-                  ELSE 1 + 
-                         CHOOSE i \in DOMAIN seq : \A j \in DOMAIN seq : i >= j
-  \* Corresponds to Java's seq.size() for strings or seq.len for
-  \* arrays.  
-
-JavaIndex(jarray, elt, fromPos) == 
-  \* For jarray a Java array, it equals the smallest i >= fromPos such that
-  \* jarray[i] = elt, or -1 if there is no such i
-  LET Found(i) == jarray[i] = elt
-      lastIdx == JavaLen(jarray) - 1
-  IN  IF \E i \in fromPos .. lastIdx : Found(i)
-         THEN CHOOSE i \in fromPos .. lastIdx : /\ Found(i)
-                                                /\ \A j \in fromPos .. (i-1): ~Found(j)
-         ELSE -1
-  
-JavaSeqSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> JavaSeq(seq[i+1])]
-  \* Converts a TLA sequence of TLA sequences to the corresponding
-  \* array of Java arrays
-  
-JavaAppend(seq, elt) == LET len == JavaLen(seq) 
-                        IN  [i \in 0..len |-> IF i = len THEN elt ELSE seq[i]]
-  \* Appends an element to the end  of a Java sequence.
-
-JavaConcatenate(seq1, seq2) == LET len1 == JavaLen(seq1)
-                                   len2 == JavaLen(seq2)
-                               IN  [i \in 0..(len1 + len2-1) |->
-                                     IF i < len1 THEN seq1[i] 
-                                                 ELSE seq2[i-len1]]
-  \* Concatenates two Java arrays
-  
-IsJavaArray(array, S) == array \in [0..(JavaLen(array)-1) ->  S]
-  \* True iff array is a Java array of elements of "type" S
-  
-\* NullJavaArray == (-1 :> {})
------------------------------------------------------------------------------                      
-(***************************************************************************)
-(* The input.                                                              *)
-(***************************************************************************)
-
-(***************************************************************************)
-(* The contents of the current line of the module.                         *)
-(***************************************************************************)
-lineInput == JavaSeq(<< "f", "(" , "b", ")", \* " ", "!", " ", "a", "!" , " ", "g" , \* , " ", " ", " ", "(", "[", "x", "]", ")", " ",
-\*                     "(", "e", ")", " ",
-\*                     "<", "3", "4", ">", "a", "."
-" "
- >>)  
-
-               
-(***************************************************************************)
-(* The current position (in Java coordinates) of the cursor in the module. *)
-(***************************************************************************)
-currentPosition == 1 \* 7 
------------------------------------------------------------------------------
-Padding ==  JavaSeq(<<";", ";", ";", ";", ";", ";", ";", ";", ";", ";">>)
-  \* In an implementation ";" is replaced by an untypable character
-
-
-GoingLeft == FALSE
-  \* TRUE would mean that we continue searching for the symbol to the left if 
-  \* find that we're inside a subexpression selector.
-
-XSymbols == JavaSeqSeq(<< <<"(", "\\", "X", ")">>  >>)
-  \* These are the Operators that contain "X".
-  \*  Note that "\\X" is a TeXSymbol, not an Operator.
-
-Operators == JavaSeqSeq(<<
-\*   "-+->", "<=>", "...", "::=", "(+)", "(-)", "(.)", "(/)", "(\\X)",
-\*   "--", "**", "++", "<:", "<=", "<", ">=", "..", "||", "[]", "<>",
-\*   "/\\", "\\/", "//", "/", "/=", "~>", "=>", "=<", "=|", "^^", "##",
-\*   "|-", "|=", "&&", "$$", "??", "%%", "@@", "!!", ":>", ":=", "~", "=",
-\*   "#", "^", "-", "*", ">", "<", "+", "|", "&", "$", "%", "\\"
-<< "(", "+", ")" >>,
-<< "-", "+", "-", ">" >>,
-<< "<", "=", ">" >>,
-<< ".", ".", "." >>,
-<< ":", ":", "=" >>,
-<< "(", "-", ")" >>,
-<< "(", ".", ")" >>,
-<< "(", "/", ")" >>,
-<< "(", "\\", "X", ")" >>,
-<< "-",  "-" >>,
-<< "*",  "*" >>,
-<< "+",  "+" >>,
-<< "<",  ":" >>,
-<< "<",  "=" >>,
-<< "<", " " >>,
-<< ">",  "=" >>,
-<< ".",  "." >>,
-<< "|",  "|" >>,
-<< "[",  "]" >>,
-<< "<",  ">" >>,
-<< "/", "\\" >>,
-<< "\\", "/" >>,
-<< "/",  "/" >>,
-<< "/",  "=" >>,
-<< "~",  ">" >>,
-<< "=",  ">" >>,
-<< "=",  "<" >>,
-<< "=",  "|" >>,
-<< "^",  "^" >>,
-<< "#",  "#" >>,
-<< "|",  "-" >>,
-<< "|",  "=" >>,
-<< "&",  "&" >>,
-<< "$",  "$" >>,
-<< "?",  "?" >>,
-<< "%",  "%" >>,
-<< "@",  "@" >>,
-<< "!",  "!" >>,
-<< ":",  ">" >>,
-<< ":",  "=" >>,
-<<"^", "+">>,
-<<"^", "*">>,
-<<"^", "#">>,
-<<"~">>,
-<<"=">>,
-<<"#">>,
-<<"^">>,
-<<"-">>,
-<<"*">>,
-<<">">>,
-<<"<">>,
-<<"+">>,
-<<"|">>,
-<<"&">>,
-<<"$">>,
-<<"%">>,
-<<"\\">>,
-<<"'">>
->>)
-
-NonOperators == JavaSeqSeq( <<
-<<"=", "=">>, 
-<<"-", "-", "-">>,
-<<"-", ">">>,
-<<"<", "-">>,
-<<"*", "*", "*">>,
-<<"<", "<">>,
-<<">", ">">>
->> )
-
-\* ParenOperators == JavaSeqSeq( <<
-\* << "(", "+", ")" >>,
-\* << "(", "-", ")" >>,
-\* << "(", ".", ")" >>,
-\* << "(", "/", ")" >>,
-\* << "(", "\\", "X", ")" >>
-\* >> )
-
-TeXSymbols == JavaSeqSeq(<<
-\*   "\\neg", "\\lnot", "\\approx",
-\*   "\\asymp", "\\bigcirc", "\\bullet", "\\cap", "\\cdot", "\\circ",
-\*   "\\cong", "\\cup", "\\div", "\\doteq", "\\equiv", "\\geq", "\\gg",
-\*   "\\in", "\\intersect", "\\union", "\\land", "\\leq", "\\ll", "\\lor",
-\*   "\\mod", "\\o", "\\odot", "\\ominus", "\\oplus", "\\oslash",
-\*   "\\otimes", "\\prec", "\\preceq", "\\propto", "\\sim", "\\simeq",
-\*   "\\sqcap", "\\sqcup", "\\sqsubset", "\\sqsupset", "\\sqsubseteq",
-\*   "\\sqsupseteq", "\\star", "\\subset", "\\subseteq", "\\succ",
-\*   "\\succeq", "\\supset", "\\supseteq", "\\uplus", "\\wr", "\\notin",
-\*   "\\times", "\\X" 
-<<"\\", "n", "e", "g">>,
-<<"\\", "l", "n", "o", "t">>,
-<<"\\", "a", "p", "p", "r", "o", "x">>,
-<<"\\", "a", "s", "y", "m", "p">>,
-<<"\\", "b", "i", "g", "c", "i", "r", "c">>,
-<<"\\", "b", "u", "l", "l", "e", "t">>,
-<<"\\", "c", "a", "p">>,
-<<"\\", "c", "d", "o", "t">>,
-<<"\\", "c", "i", "r", "c">>,
-<<"\\", "c", "o", "n", "g">>,
-<<"\\", "c", "u", "p">>,
-<<"\\", "d", "i", "v">>,
-<<"\\", "d", "o", "t", "e", "q">>,
-<<"\\", "e", "q", "u", "i", "v">>,
-<<"\\", "g", "e", "q">>,
-<<"\\", "g", "g">>,
-<<"\\", "i", "n", "t", "e", "r", "s", "e", "c", "t">>,
-<<"\\", "u", "n", "i", "o", "n">>,
-<<"\\", "l", "a", "n", "d">>,
-<<"\\", "l", "e", "q">>,
-<<"\\", "l", "l">>,
-<<"\\", "l", "o", "r">>,
-<<"\\", "m", "o", "d">>,
-<<"\\", "o", "d", "o", "t">>,
-<<"\\", "o", "m", "i", "n", "u", "s">>,
-<<"\\", "o", "p", "l", "u", "s">>,
-<<"\\", "o", "s", "l", "a", "s", "h">>,
-<<"\\", "o", "t", "i", "m", "e", "s">>,
-<<"\\", "p", "r", "e", "c">>,
-<<"\\", "p", "r", "e", "c", "e", "q", "">>,
-<<"\\", "p", "r", "o", "p", "t", "o">>,
-<<"\\", "s", "i", "m">>,
-<<"\\", "s", "i", "m", "e", "q">>,
-<<"\\", "s", "q", "c", "a", "p">>,
-<<"\\", "s", "q", "c", "u", "p">>,
-<<"\\", "s", "q", "s", "u", "b", "s", "e", "t">>,
-<<"\\", "s", "q", "s", "u", "p", "s", "e", "t">>,
-<<"\\", "s", "q", "s", "u", "b", "s", "e", "t", "e", "q">>,
-<<"\\", "s", "q", "s", "u", "p", "s", "e", "t", "e", "q">>,
-<<"\\", "s", "t", "a", "r">>,
-<<"\\", "s", "u", "b", "s", "e", "t", "">>,
-<<"\\", "s", "u", "b", "s", "e", "t", "e", "q">>,
-<<"\\", "s", "u", "c", "c">>,
-<<"\\", "s", "u", "c", "c", "e", "q">>,
-<<"\\", "s", "u", "p", "s", "e", "t", "">>,
-<<"\\", "s", "u", "p", "s", "e", "t", "e", "q">>,
-<<"\\", "u", "p", "l", "u", "s">>,
-<<"\\", "w", "r">>,
-<<"\\", "n", "o", "t", "i", "n">>,
-<<"\\", "t", "i", "m", "e", "s">>,
-<<"\\", "o">>,
-<<"\\", "i", "n">>,
-<<"\\", "X">>
->>)
-
-IsNumber(seq) == /\ \A i \in DOMAIN seq : 
-                       seq[i] \in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
-                 /\ (DOMAIN seq) # {}
-IdChar == {"a", "b", "c", "d", "e", "f", "g", "h", "i", "n", (* ... *) "X", "Y", "Z",  
-           "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "_"}
-
-RightDelimiters == JavaSeqSeq(<< <<")">>, <<"]">>, <<"}">>, <<">", ">">> >>)
-LeftDelimiters == JavaSeqSeq(<< <<"(">>, <<"[">>, <<"{">>, <<"<", "<">> >>)
------------------------------------------------------------------------------
-TokenSpec == [token : STRING, leftPos : Nat, rightPos : Nat]
-NullTokenSpec == [null |-> "null"]
-(****************************************************************************
- 
---algorithm FindOp {
-variables 
-  line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding));
-  foundTokenSpecs = << >>  ; 
-   \* Array records of form [token   |-> string of a token,
-   \*                        leftPos |-> location in line of left of token,
-   \*                        rightPos   |-> location in line of just to the 
-   \*                                    right of token]
-  lastToken = FALSE; \* true if there can be no tokens to right of first
-                     \* one found
-  notLastToken = FALSE;  \* true if we found a "!" to the right of the
-                         \* first token found.
-  curPos = currentPosition  + JavaLen(Padding) ;
-  returnVal; \* Use for returning values from a procedure call.
-  tempVar1 ;
-  tempVar2 ;
-  tempVar3 ;
-  
-define 
-{ 
-  FixOrigin(tokarray) ==
-  [i \in DOMAIN tokarray |->
-           [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding), 
-            leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]]
-
-};
-
-macro vreturn(val)
-{ returnVal := val;
-  return;
-}
-
-\* Returns the TokenSpec for the token in the array tokArray of
-\* Java strings (Java array of 1-character strings) in the line
-\* containing the character at position pos, or NullTokenSpec
-\* if there is none.
-procedure findTokenIn(pos, tokArray) 
-  variables i ; tokIdx ;
-    { i := 0;
-      while (i < JavaLen(tokArray)) 
-       { tokIdx := JavaIndex(tokArray[i], line[pos], 0);
-         while (tokIdx # -1) 
-           { with (ftlft = pos - tokIdx,
-                   ftrt = pos - tokIdx + JavaLen(tokArray[i]))
-               { if (tokArray[i] = JavaSubseq(line, ftlft, ftrt))
-                  { vreturn([token |-> tokArray[i],
-                                  leftPos |-> ftlft, rightPos |-> ftrt])
-                  };
-               };
-             tokIdx := JavaIndex(tokArray[i], line[pos], tokIdx+1);
-           };
-           i := i+1; 
-        };
-      vreturn(NullTokenSpec);
-    }
-
-
-\* Returns the TokenSpec of the largest token containing digits,
-\* letters, and "_" characters containing position pos.  Returns
-\* NullTokenSpec if that would be an empty token.
-procedure findMaximalIdCharSeq(pos) 
-  variables token = << >>; left = pos; rt = pos;
-  { \* token := << >>;
-    \* left := pos;
-    while (line[left-1] \in IdChar)
-      { left := left-1; 
-      };
-    while (line[rt] \in IdChar)
-     { rt := rt+1
-     };
-   if (left = rt)
-     { vreturn(NullTokenSpec) }
-   else
-     { vreturn([token |-> JavaSubseq(line, left, rt), 
-                leftPos |-> left, rightPos |-> rt])
-     }
-  } 
-
-\* Returns the position of the left paren matching a ")" at pos-1.
-\* If there is none, it returns -1.
-procedure findMatchingLeftParen(pos) 
-{ if (line[pos-1] # ")") {vreturn(pos)};
-  call findMatchingLeftInner(pos, 0);
-  return;
- }
-
-procedure findMatchingLeftInner(pos, delim) 
- variables ipos = pos; jdelim; delimLen;
-{ delimLen := JavaLen(LeftDelimiters[delim]);
-  ipos := pos - delimLen;
-  while (JavaSubseq(line, ipos-delimLen, ipos) # LeftDelimiters[delim])
-   { if (line[ipos-1] = ";") {vreturn(-1)};
-     ipos := ipos-1;
-     jdelim := 0;
-     while (jdelim < JavaLen(LeftDelimiters))
-      { if (JavaSubseq(line, ipos-delimLen, ipos) = RightDelimiters[jdelim])
-         { call findMatchingLeftInner(ipos, jdelim);
-           ipos := returnVal;
-           if (ipos < 0) { vreturn(-1)};
-           jdelim := 99 ; \* exit while loop
-         };
-        jdelim := jdelim+1;
-      }
-   };
-   vreturn(ipos-delimLen);
- }
-
-\* Returns the position of the right paren matching a "(" at position pos.
-\* If there is none, it returns -1.
-procedure findMatchingRightParen(pos) 
-{ if (line[pos] # "(") {vreturn(pos)};
-  call findMatchingRightInner(pos, 0);
-  return;
- }
-
-procedure findMatchingRightInner(pos, delim) 
- variables ipos = pos; jdelim; delimLen; 
-{ delimLen := JavaLen(RightDelimiters[delim]);
-  ipos := pos + delimLen;
-  while (JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim])
-   { if (line[ipos] = ";") {vreturn(-1)};
-     ipos := ipos+1;
-     jdelim := 0;
-     while (jdelim < JavaLen(RightDelimiters))
-      { if (JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim])
-         { call findMatchingRightInner(ipos, jdelim);
-           ipos := returnVal;
-           if (ipos < 0) { vreturn(-1)};
-           jdelim := 99 ; \* exit while loop
-         };
-        jdelim := jdelim+1;
-      }
-   };
-   vreturn(ipos+delimLen);
- }
-
-\* Assumes that leftTok and rightTok are two TokenSpecs, 
-\* possibly NullTokenSpecs, separated by a ">".  Checks
-\* if they form a step name.  If so, it returns the
-\* TokenSpec for that step name; otherwise it returns
-\* NullTokenSpec.
-procedure checkIfStepName(leftTok, rightTok)
-{ if (leftTok = NullTokenSpec \/ rightTok = NullTokenSpec)
-    { vreturn(NullTokenSpec) };
-  if ( /\ IsNumber(leftTok.token)
-       /\ line[leftTok.leftPos-1] = "<"
-       /\ line[leftTok.leftPos-2] # "<" )
-     { vreturn( [token |-> JavaSubseq(line, 
-                                      leftTok.leftPos-1,
-                                      rightTok.rightPos),
-                 leftPos |-> leftTok.leftPos-1,
-                 rightPos |-> rightTok.rightPos] )
-     };
-   else { vreturn(NullTokenSpec) };
-}
-procedure skipLeftOverSpaces() 
-{ while (line[curPos-1] = " ") { curPos := curPos - 1; };
-  return;
-}
-
-procedure skipRightOverSpaces() 
-{ while (line[curPos] = " ") { curPos := curPos+1; };
-  return;
-}
-
-\* Returns a TokenSpec array of possible tokens to be selected
-\* when the cursor is at position curPos of line.  It 
-\* returns a zero-length array if there are none.
-\*
-\* Known Bug: This will not work right in some cases when curPos
-\* are spaces inside a name, as in
-\*
-\*    Foo   (x, y)!bar
-\*        ^
-procedure findTokenSpecs()
-variables currentToken; left; rt; notDone; foundBangToken;
-temp1; temp2; temp3;
-{ \* Try to find the current TokenSpec and put it in foundTokenSpecs, 
-  \* leaving curPos to the left of that token.  If the token is a
-  \* step name, we can return that single TokenSpec as the spec.  
-  \* If it is an unnamed step, or if it is not a possible identifier
-  \* or operator symbol, we return << >> (a zero-length array).
-  
-  \* First, we must check if we may be inside a string of form (" ")^* "!" (" ")^*
-  \* and, if so, move to its left, setting notLastToken to TRUE
-  if (line[curPos] = " ") 
-    { call skipLeftOverSpaces() ;
-      if (line[curPos-1] = "!"  /\ line[curPos-2] # "!")
-        { notLastToken := TRUE;
-          curPos := curPos - 1;
-          call skipLeftOverSpaces();
-        }
-    }
-  else if (line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!")
-    { notLastToken := TRUE;
-      call skipLeftOverSpaces()
-    };
-    
-  \* If we're to the right of a ")", we could be inside something like
-  \*
-  \*   Foo(42) ! bar
-  \*          ^
-  \* so we set notLastToken true and move to the left of the parentesized string.
-  if ( /\ \/ notLastToken
-          \/ line[curPos] = " "
-       /\ line[curPos-1] = ")")
-    { notLastToken := TRUE;
-      call findMatchingLeftParen(curPos);
-      if (returnVal < 0) {vreturn(<< >>)};
-      curPos := returnVal;
-      call skipLeftOverSpaces();
-    };
-  \* If notLastToken is true, then we should be just to the right
-  \* of the token we are expecting to find to the left of the "!" or the
-  \* parenthesized expression. 
-  \* Otherwise, if we're not at an IdChar or at an operator, then
-  \* let's try one character to the left.  
-  \* Note: this happens to work if we're right before "<4>7" or "\in"
-  \* because both "<" and "\\" are operators.
-
-  if (notLastToken) { curPos := curPos - 1; };
-  else 
-    { if (line[curPos] \notin IdChar) 
-        { call findTokenIn(curPos, Operators);
-          if ( returnVal = NullTokenSpec )
-            { curPos := curPos - 1
-            }
-        }
-
-    } ;
-\* \* Following code replaced by preceding "else" clause
-\*
-\*  \* If we didn't move left and we're at a "(" or "." that's not the beginning
-\*  \* of an operator, let's move left 1.
-\*  if (~ notLastToken /\ line[curPos] = "(")
-\*    { call findTokenIn(curPos, ParenOperators);
-\*      if (returnVal = NullTokenSpec) { curPos := curPos - 1 }
-\*    }
-\*  else if ( /\ ~ notLastToken 
-\*            /\ line[curPos] = "."
-\*            /\ line[curPos+1] # "." 
-\*            /\ line[curPos-1] # "." )
-\*         { curPos := curPos - 1 };
-\*  
-\*  \* If we're at a space (which means we haven't found a "!" or moved left
-\*  \* over a parenthesized string), then try as if the cursor were
-\*  \* one character to the left.
-\*  if (line[curPos] = " ") { curPos := curPos - 1};
-  
-  if (line[curPos] \in IdChar)
-    { \* Check if we're at an "X" that is inside an "\X" or "\X".
-      \* Note: this works because there are no TeXSymbols besides
-      if (line[curPos] = "X") { call findTokenIn(curPos, XSymbols); }
-      else {returnVal := NullTokenSpec} ;
-      if (returnVal # NullTokenSpec)
-        { foundTokenSpecs := JavaSeq(<<returnVal>>);
-          curPos := returnVal.leftPos;
-          lastToken := TRUE;
-         }
-      else 
-         { call findMaximalIdCharSeq(curPos);
-           currentToken := returnVal;
-           \* Check if token is part of step name.  If it is, we set
-           \* First, see if currentToken could be the token after the "<...>".
-           if ( line[currentToken.leftPos-1] = ">" )
-             { call findMaximalIdCharSeq(currentToken.leftPos - 1);
-               call checkIfStepName(returnVal, currentToken);
-               if (returnVal # NullTokenSpec)
-                 { foundTokenSpecs := JavaSeq(<<returnVal>>);
-                   vreturn(FixOrigin(foundTokenSpecs));
-                 }
-             };
-           \* Next see if it's the token to the left of the ">"
-           if (line[currentToken.rightPos] = ">" )
-             { call findMaximalIdCharSeq(currentToken.rightPos+1);
-               call checkIfStepName(currentToken, returnVal);
-               if (returnVal # NullTokenSpec)
-                 { foundTokenSpecs := JavaSeq(<<returnVal>>);
-                   vreturn(FixOrigin(foundTokenSpecs));
-                 }
-             };
-
-           \* Next, check if it's a number, and abort if it is.
-           if (IsNumber(currentToken.token)) 
-             { vreturn(<< >>)
-             };
-           
-           \* Next, check if the token appears as "\\"token, in
-           \* which case we adjust currentTok
-           left := currentToken.leftPos ;
-           rt   := currentToken.rightPos ;
-           if ( /\ line[left-1] = "\\" 
-                /\ line[left-2] # "\\"
-                /\ \E ii \in DOMAIN TeXSymbols :
-                         JavaSubseq(line, left-1, rt) = TeXSymbols[ii] )
-             { lastToken := TRUE;
-               currentToken.leftPos := left - 1 ||
-               currentToken.token := JavaSubseq(line, left-1, rt);
-             };
-           foundTokenSpecs := 
-                 JavaSeq( << currentToken >>);
-           curPos := left;
-         }
-    }
-  else 
-    { call findTokenIn(curPos, Operators);
-      currentToken  := returnVal;
-      if (currentToken = NullTokenSpec)
-        { vreturn(<< >>);
-        };
-      call findTokenIn(curPos, NonOperators);
-      if (returnVal # NullTokenSpec)
-        { vreturn(<< >>);
-        };
-
-      \* Need to check if beginning or end of level specifier
-      \* of step number.  
-      if ( currentToken.token = JavaSeq(<< "<" >>) )
-        { call findMaximalIdCharSeq(currentToken.rightPos);
-          temp1 := returnVal;
-          if (/\ temp1 # NullTokenSpec
-              /\ line[temp1.rightPos] = ">")
-            { call findMaximalIdCharSeq(temp1.rightPos + 1);
-              temp2 := returnVal;
-              call checkIfStepName(temp1, temp2);
-              if (returnVal # NullTokenSpec)
-               { foundTokenSpecs := JavaSeq(<<returnVal>>);
-                 vreturn(FixOrigin(foundTokenSpecs));
-               }
-            }
-          }
-       else if (currentToken.token = JavaSeq(<< ">" >>))
-         { call findMaximalIdCharSeq(currentToken.leftPos);
-           temp1 := returnVal;
-           call findMaximalIdCharSeq(currentToken.rightPos);
-           temp2 := returnVal;
-           call checkIfStepName(temp1, temp2);
-              if (returnVal # NullTokenSpec)
-               { foundTokenSpecs := JavaSeq(<<returnVal>>);
-                 vreturn(FixOrigin(foundTokenSpecs));
-               }
-         };
-
-      \* We check for the case of a "\\" starting a TeX token.
-      if (currentToken.token = JavaSeq(<<"\\">>))
-        { call findMaximalIdCharSeq(currentToken.rightPos);
-          if ( /\ returnVal # NullTokenSpec
-               /\ \E ii \in DOMAIN TeXSymbols :
-                         JavaSubseq(line, 
-                                    currentToken.leftPos, 
-                                    returnVal.rightPos) = TeXSymbols[ii] )
-            { currentToken.rightPos := returnVal.rightPos ||
-              currentToken.token := JavaSubseq(line, 
-                                    currentToken.leftPos, 
-                                    returnVal.rightPos) 
-            }
-        };
-
-      foundTokenSpecs := JavaSeq(<<currentToken>>);
-      curPos := currentToken.leftPos;
-      lastToken := TRUE;
-     };
-
-  assert JavaLen(foundTokenSpecs) = 1;
-
-\*print <<"1", foundTokenSpecs[0]>>;
-  \** We have now found the base token.  We now look to find extenders
-  \*  to the left of the form  idtoken "!".  These must be part of the
-  \*  name, so we prepend them to foundTokenSpecs[0].
-  curPos := foundTokenSpecs[0].leftPos;
-  notDone := TRUE;
-  while (notDone) 
-    { call skipLeftOverSpaces();
-      if (curPos <  0 \/ line[curPos-1] = ";") 
-        { notDone := FALSE; }
-      else
-        { if (line[curPos-1] = "!" /\ (line[curPos-2] # "!"))
-            { curPos := curPos - 1;
-              call skipLeftOverSpaces();
-              call findMatchingLeftParen(curPos);
-              curPos := returnVal;
-              if (curPos < 0) { notDone := FALSE }
-              else 
-                { call skipLeftOverSpaces();
-                  call findMaximalIdCharSeq(curPos);
-                  currentToken := returnVal ;
-                  if ( \/ currentToken = NullTokenSpec
-                       \/ IsNumber(currentToken.token) )
-                    { notDone := FALSE }
-                  else
-                    { curPos := currentToken.leftPos;
-                      foundTokenSpecs[0] := 
-                             [token |-> JavaConcatenate(
-                                            JavaAppend(currentToken.token, "!"),
-                                            foundTokenSpecs[0].token),
-                              leftPos |-> curPos, 
-                              rightPos |-> foundTokenSpecs[0].rightPos];
-                    }
-               }
-             }
-         else {notDone := FALSE;}
-       };
-       
-    };
-
-  assert foundTokenSpecs # << >> ;
-
-  if (lastToken = TRUE) {vreturn(FixOrigin(foundTokenSpecs))};
-  
-\*  print <<"2", foundTokenSpecs[0]>>;
-  
-  curPos := foundTokenSpecs[0].rightPos;
-  foundBangToken := FALSE;
-  notDone := TRUE;
-  while (notDone) 
-    { call skipRightOverSpaces();
-      call findMatchingRightParen(curPos);
-      curPos := returnVal;
-      if (curPos < 0) {notDone := FALSE}
-      else
-        { call skipRightOverSpaces();
-          if (line[curPos] # "!"  \/ line[curPos+1] = "!")
-            { notDone := FALSE } 
-          else
-            { curPos := curPos + 1;
-              call skipRightOverSpaces();
-              call findMaximalIdCharSeq(curPos);
-              currentToken := returnVal ;
-              if ( \/ currentToken = NullTokenSpec
-                   \/ IsNumber(currentToken.token) )
-                { notDone := FALSE }
-              else
-                { foundBangToken := TRUE;
-                  foundTokenSpecs :=
-                    JavaConcatenate(
-                      JavaSeq( 
-                        << [token |-> 
-                              JavaConcatenate(JavaAppend(foundTokenSpecs[0].token,
-                                                          "!"),
-                                              currentToken.token),
-                            leftPos |-> foundTokenSpecs[0].leftPos,
-                            rightPos |-> currentToken.rightPos]
-                         >> ) ,
-                        foundTokenSpecs); 
-                  curPos := currentToken.rightPos;
-                }
-            }
-        }
-    };
-
-  if (notLastToken /\ ~ foundBangToken) { vreturn (<< >>) };
-
-  vreturn(FixOrigin(foundTokenSpecs));
-} \* end findTokenSpecs
-
-\* Main Body:
-{ curPos := JavaLen(Padding);
-  tempVar1 := (-1 :> "");
-  tempVar2 := JavaLen(Padding);
-\*curPos := 15;
-\*call findTokenSpecs();
-\*print returnVal;
-\*  print line;
-  while (tempVar2 < JavaLen(lineInput) + JavaLen(Padding))
-    { \* print <<"curPos: ", tempVar2>>;
-      curPos := tempVar2;
-      foundTokenSpecs := << >>  ;
-      lastToken := FALSE;
-      notLastToken := FALSE;
-      call findTokenSpecs();
-\*      if (tempVar2 >= 0 + JavaLen(Padding))
-\*        { print "done with run" ;
-\*          assert FALSE
-\*        };
-\*      if (returnVal = tempVar1)
-\*        { print "same" \* ,"lastToken: ", lastToken, ", notLastToken: ", notLastToken>> 
-\*        }
-\*      else 
-\*        { print returnVal;
-   \*          print <<"lastToken: ", lastToken, ", notLastToken: ", notLastToken>>;
-\*        };
-      tempVar1 := returnVal;
-      tempVar2 := tempVar2 + 1;
-    };
-  print "goodby world";
-
-} \* end Main Body
-} \* End algorithm
-  
-*****************************************************************************************)
-\* BEGIN TRANSLATION
-\* Procedure variable left of procedure findMaximalIdCharSeq at line 317 col 28 changed to left_
-\* Procedure variable rt of procedure findMaximalIdCharSeq at line 317 col 40 changed to rt_
-\* Procedure variable ipos of procedure findMatchingLeftInner at line 343 col 12 changed to ipos_
-\* Procedure variable jdelim of procedure findMatchingLeftInner at line 343 col 24 changed to jdelim_
-\* Procedure variable delimLen of procedure findMatchingLeftInner at line 343 col 32 changed to delimLen_
-\* Parameter pos of procedure findTokenIn at line 292 col 23 changed to pos_
-\* Parameter pos of procedure findMaximalIdCharSeq at line 316 col 32 changed to pos_f
-\* Parameter pos of procedure findMatchingLeftParen at line 336 col 33 changed to pos_fi
-\* Parameter pos of procedure findMatchingLeftInner at line 342 col 33 changed to pos_fin
-\* Parameter delim of procedure findMatchingLeftInner at line 342 col 38 changed to delim_
-\* Parameter pos of procedure findMatchingRightParen at line 365 col 34 changed to pos_find
-CONSTANT defaultInitValue
+Known Bugs:
+- The operator may not be found if you click on a space inside its name, as in 
+   "Bar   (x)  !  y"
+
+Known Features:
+- Detects the ">" and "<" in " <3> " as operators. 
+
+- If at a label that is also the name of an operator, then that operator 
+  will be found.
+
+- An operator appearing inside a comment or a string will be found.
+
+------------------------------- MODULE FindOpWithoutPrint -------------------------------
+EXTENDS Integers, Sequences, TLC
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(* Operations for doing things in Java coordinates.                        *)
+(***************************************************************************)
+JavaSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> seq[i+1]]
+   \* Turns a TLA sequence into a Java array.
+   
+JavaSubseq(seq, b, e) == [i \in 0 .. (e-1-b) |-> seq[i+b]]
+  \* Corresponds to Java's Subseq String method.
+  
+JavaLen(seq) == IF DOMAIN seq = {}
+                  THEN 0
+                  ELSE 1 + 
+                         CHOOSE i \in DOMAIN seq : \A j \in DOMAIN seq : i >= j
+  \* Corresponds to Java's seq.size() for strings or seq.len for
+  \* arrays.  
+
+JavaIndex(jarray, elt, fromPos) == 
+  \* For jarray a Java array, it equals the smallest i >= fromPos such that
+  \* jarray[i] = elt, or -1 if there is no such i
+  LET Found(i) == jarray[i] = elt
+      lastIdx == JavaLen(jarray) - 1
+  IN  IF \E i \in fromPos .. lastIdx : Found(i)
+         THEN CHOOSE i \in fromPos .. lastIdx : /\ Found(i)
+                                                /\ \A j \in fromPos .. (i-1): ~Found(j)
+         ELSE -1
+  
+JavaSeqSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> JavaSeq(seq[i+1])]
+  \* Converts a TLA sequence of TLA sequences to the corresponding
+  \* array of Java arrays
+  
+JavaAppend(seq, elt) == LET len == JavaLen(seq) 
+                        IN  [i \in 0..len |-> IF i = len THEN elt ELSE seq[i]]
+  \* Appends an element to the end  of a Java sequence.
+
+JavaConcatenate(seq1, seq2) == LET len1 == JavaLen(seq1)
+                                   len2 == JavaLen(seq2)
+                               IN  [i \in 0..(len1 + len2-1) |->
+                                     IF i < len1 THEN seq1[i] 
+                                                 ELSE seq2[i-len1]]
+  \* Concatenates two Java arrays
+  
+IsJavaArray(array, S) == array \in [0..(JavaLen(array)-1) ->  S]
+  \* True iff array is a Java array of elements of "type" S
+  
+\* NullJavaArray == (-1 :> {})
+-----------------------------------------------------------------------------                      
+(***************************************************************************)
+(* The input.                                                              *)
+(***************************************************************************)
+
+(***************************************************************************)
+(* The contents of the current line of the module.                         *)
+(***************************************************************************)
+lineInput == JavaSeq(<< "f", "(" , "b", ")", \* " ", "!", " ", "a", "!" , " ", "g" , \* , " ", " ", " ", "(", "[", "x", "]", ")", " ",
+\*                     "(", "e", ")", " ",
+\*                     "<", "3", "4", ">", "a", "."
+" "
+ >>)  
+
+               
+(***************************************************************************)
+(* The current position (in Java coordinates) of the cursor in the module. *)
+(***************************************************************************)
+currentPosition == 1 \* 7 
+-----------------------------------------------------------------------------
+Padding ==  JavaSeq(<<";", ";", ";", ";", ";", ";", ";", ";", ";", ";">>)
+  \* In an implementation ";" is replaced by an untypable character
+
+
+GoingLeft == FALSE
+  \* TRUE would mean that we continue searching for the symbol to the left if 
+  \* find that we're inside a subexpression selector.
+
+XSymbols == JavaSeqSeq(<< <<"(", "\\", "X", ")">>  >>)
+  \* These are the Operators that contain "X".
+  \*  Note that "\\X" is a TeXSymbol, not an Operator.
+
+Operators == JavaSeqSeq(<<
+\*   "-+->", "<=>", "...", "::=", "(+)", "(-)", "(.)", "(/)", "(\\X)",
+\*   "--", "**", "++", "<:", "<=", "<", ">=", "..", "||", "[]", "<>",
+\*   "/\\", "\\/", "//", "/", "/=", "~>", "=>", "=<", "=|", "^^", "##",
+\*   "|-", "|=", "&&", "$$", "??", "%%", "@@", "!!", ":>", ":=", "~", "=",
+\*   "#", "^", "-", "*", ">", "<", "+", "|", "&", "$", "%", "\\"
+<< "(", "+", ")" >>,
+<< "-", "+", "-", ">" >>,
+<< "<", "=", ">" >>,
+<< ".", ".", "." >>,
+<< ":", ":", "=" >>,
+<< "(", "-", ")" >>,
+<< "(", ".", ")" >>,
+<< "(", "/", ")" >>,
+<< "(", "\\", "X", ")" >>,
+<< "-",  "-" >>,
+<< "*",  "*" >>,
+<< "+",  "+" >>,
+<< "<",  ":" >>,
+<< "<",  "=" >>,
+<< "<", " " >>,
+<< ">",  "=" >>,
+<< ".",  "." >>,
+<< "|",  "|" >>,
+<< "[",  "]" >>,
+<< "<",  ">" >>,
+<< "/", "\\" >>,
+<< "\\", "/" >>,
+<< "/",  "/" >>,
+<< "/",  "=" >>,
+<< "~",  ">" >>,
+<< "=",  ">" >>,
+<< "=",  "<" >>,
+<< "=",  "|" >>,
+<< "^",  "^" >>,
+<< "#",  "#" >>,
+<< "|",  "-" >>,
+<< "|",  "=" >>,
+<< "&",  "&" >>,
+<< "$",  "$" >>,
+<< "?",  "?" >>,
+<< "%",  "%" >>,
+<< "@",  "@" >>,
+<< "!",  "!" >>,
+<< ":",  ">" >>,
+<< ":",  "=" >>,
+<<"^", "+">>,
+<<"^", "*">>,
+<<"^", "#">>,
+<<"~">>,
+<<"=">>,
+<<"#">>,
+<<"^">>,
+<<"-">>,
+<<"*">>,
+<<">">>,
+<<"<">>,
+<<"+">>,
+<<"|">>,
+<<"&">>,
+<<"$">>,
+<<"%">>,
+<<"\\">>,
+<<"'">>
+>>)
+
+NonOperators == JavaSeqSeq( <<
+<<"=", "=">>, 
+<<"-", "-", "-">>,
+<<"-", ">">>,
+<<"<", "-">>,
+<<"*", "*", "*">>,
+<<"<", "<">>,
+<<">", ">">>
+>> )
+
+\* ParenOperators == JavaSeqSeq( <<
+\* << "(", "+", ")" >>,
+\* << "(", "-", ")" >>,
+\* << "(", ".", ")" >>,
+\* << "(", "/", ")" >>,
+\* << "(", "\\", "X", ")" >>
+\* >> )
+
+TeXSymbols == JavaSeqSeq(<<
+\*   "\\neg", "\\lnot", "\\approx",
+\*   "\\asymp", "\\bigcirc", "\\bullet", "\\cap", "\\cdot", "\\circ",
+\*   "\\cong", "\\cup", "\\div", "\\doteq", "\\equiv", "\\geq", "\\gg",
+\*   "\\in", "\\intersect", "\\union", "\\land", "\\leq", "\\ll", "\\lor",
+\*   "\\mod", "\\o", "\\odot", "\\ominus", "\\oplus", "\\oslash",
+\*   "\\otimes", "\\prec", "\\preceq", "\\propto", "\\sim", "\\simeq",
+\*   "\\sqcap", "\\sqcup", "\\sqsubset", "\\sqsupset", "\\sqsubseteq",
+\*   "\\sqsupseteq", "\\star", "\\subset", "\\subseteq", "\\succ",
+\*   "\\succeq", "\\supset", "\\supseteq", "\\uplus", "\\wr", "\\notin",
+\*   "\\times", "\\X" 
+<<"\\", "n", "e", "g">>,
+<<"\\", "l", "n", "o", "t">>,
+<<"\\", "a", "p", "p", "r", "o", "x">>,
+<<"\\", "a", "s", "y", "m", "p">>,
+<<"\\", "b", "i", "g", "c", "i", "r", "c">>,
+<<"\\", "b", "u", "l", "l", "e", "t">>,
+<<"\\", "c", "a", "p">>,
+<<"\\", "c", "d", "o", "t">>,
+<<"\\", "c", "i", "r", "c">>,
+<<"\\", "c", "o", "n", "g">>,
+<<"\\", "c", "u", "p">>,
+<<"\\", "d", "i", "v">>,
+<<"\\", "d", "o", "t", "e", "q">>,
+<<"\\", "e", "q", "u", "i", "v">>,
+<<"\\", "g", "e", "q">>,
+<<"\\", "g", "g">>,
+<<"\\", "i", "n", "t", "e", "r", "s", "e", "c", "t">>,
+<<"\\", "u", "n", "i", "o", "n">>,
+<<"\\", "l", "a", "n", "d">>,
+<<"\\", "l", "e", "q">>,
+<<"\\", "l", "l">>,
+<<"\\", "l", "o", "r">>,
+<<"\\", "m", "o", "d">>,
+<<"\\", "o", "d", "o", "t">>,
+<<"\\", "o", "m", "i", "n", "u", "s">>,
+<<"\\", "o", "p", "l", "u", "s">>,
+<<"\\", "o", "s", "l", "a", "s", "h">>,
+<<"\\", "o", "t", "i", "m", "e", "s">>,
+<<"\\", "p", "r", "e", "c">>,
+<<"\\", "p", "r", "e", "c", "e", "q", "">>,
+<<"\\", "p", "r", "o", "p", "t", "o">>,
+<<"\\", "s", "i", "m">>,
+<<"\\", "s", "i", "m", "e", "q">>,
+<<"\\", "s", "q", "c", "a", "p">>,
+<<"\\", "s", "q", "c", "u", "p">>,
+<<"\\", "s", "q", "s", "u", "b", "s", "e", "t">>,
+<<"\\", "s", "q", "s", "u", "p", "s", "e", "t">>,
+<<"\\", "s", "q", "s", "u", "b", "s", "e", "t", "e", "q">>,
+<<"\\", "s", "q", "s", "u", "p", "s", "e", "t", "e", "q">>,
+<<"\\", "s", "t", "a", "r">>,
+<<"\\", "s", "u", "b", "s", "e", "t", "">>,
+<<"\\", "s", "u", "b", "s", "e", "t", "e", "q">>,
+<<"\\", "s", "u", "c", "c">>,
+<<"\\", "s", "u", "c", "c", "e", "q">>,
+<<"\\", "s", "u", "p", "s", "e", "t", "">>,
+<<"\\", "s", "u", "p", "s", "e", "t", "e", "q">>,
+<<"\\", "u", "p", "l", "u", "s">>,
+<<"\\", "w", "r">>,
+<<"\\", "n", "o", "t", "i", "n">>,
+<<"\\", "t", "i", "m", "e", "s">>,
+<<"\\", "o">>,
+<<"\\", "i", "n">>,
+<<"\\", "X">>
+>>)
+
+IsNumber(seq) == /\ \A i \in DOMAIN seq : 
+                       seq[i] \in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
+                 /\ (DOMAIN seq) # {}
+IdChar == {"a", "b", "c", "d", "e", "f", "g", "h", "i", "n", (* ... *) "X", "Y", "Z",  
+           "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "_"}
+
+RightDelimiters == JavaSeqSeq(<< <<")">>, <<"]">>, <<"}">>, <<">", ">">> >>)
+LeftDelimiters == JavaSeqSeq(<< <<"(">>, <<"[">>, <<"{">>, <<"<", "<">> >>)
+-----------------------------------------------------------------------------
+TokenSpec == [token : STRING, leftPos : Nat, rightPos : Nat]
+NullTokenSpec == [null |-> "null"]
+(****************************************************************************
+ 
+--algorithm FindOp {
+variables 
+  line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding));
+  foundTokenSpecs = << >>  ; 
+   \* Array records of form [token   |-> string of a token,
+   \*                        leftPos |-> location in line of left of token,
+   \*                        rightPos   |-> location in line of just to the 
+   \*                                    right of token]
+  lastToken = FALSE; \* true if there can be no tokens to right of first
+                     \* one found
+  notLastToken = FALSE;  \* true if we found a "!" to the right of the
+                         \* first token found.
+  curPos = currentPosition  + JavaLen(Padding) ;
+  returnVal; \* Use for returning values from a procedure call.
+  tempVar1 ;
+  tempVar2 ;
+  tempVar3 ;
+  
+define 
+{ 
+  FixOrigin(tokarray) ==
+  [i \in DOMAIN tokarray |->
+           [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding), 
+            leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]]
+
+};
+
+macro vreturn(val)
+{ returnVal := val;
+  return;
+}
+
+\* Returns the TokenSpec for the token in the array tokArray of
+\* Java strings (Java array of 1-character strings) in the line
+\* containing the character at position pos, or NullTokenSpec
+\* if there is none.
+procedure findTokenIn(pos, tokArray) 
+  variables i ; tokIdx ;
+    { i := 0;
+      while (i < JavaLen(tokArray)) 
+       { tokIdx := JavaIndex(tokArray[i], line[pos], 0);
+         while (tokIdx # -1) 
+           { with (ftlft = pos - tokIdx,
+                   ftrt = pos - tokIdx + JavaLen(tokArray[i]))
+               { if (tokArray[i] = JavaSubseq(line, ftlft, ftrt))
+                  { vreturn([token |-> tokArray[i],
+                                  leftPos |-> ftlft, rightPos |-> ftrt])
+                  };
+               };
+             tokIdx := JavaIndex(tokArray[i], line[pos], tokIdx+1);
+           };
+           i := i+1; 
+        };
+      vreturn(NullTokenSpec);
+    }
+
+
+\* Returns the TokenSpec of the largest token containing digits,
+\* letters, and "_" characters containing position pos.  Returns
+\* NullTokenSpec if that would be an empty token.
+procedure findMaximalIdCharSeq(pos) 
+  variables token = << >>; left = pos; rt = pos;
+  { \* token := << >>;
+    \* left := pos;
+    while (line[left-1] \in IdChar)
+      { left := left-1; 
+      };
+    while (line[rt] \in IdChar)
+     { rt := rt+1
+     };
+   if (left = rt)
+     { vreturn(NullTokenSpec) }
+   else
+     { vreturn([token |-> JavaSubseq(line, left, rt), 
+                leftPos |-> left, rightPos |-> rt])
+     }
+  } 
+
+\* Returns the position of the left paren matching a ")" at pos-1.
+\* If there is none, it returns -1.
+procedure findMatchingLeftParen(pos) 
+{ if (line[pos-1] # ")") {vreturn(pos)};
+  call findMatchingLeftInner(pos, 0);
+  return;
+ }
+
+procedure findMatchingLeftInner(pos, delim) 
+ variables ipos = pos; jdelim; delimLen;
+{ delimLen := JavaLen(LeftDelimiters[delim]);
+  ipos := pos - delimLen;
+  while (JavaSubseq(line, ipos-delimLen, ipos) # LeftDelimiters[delim])
+   { if (line[ipos-1] = ";") {vreturn(-1)};
+     ipos := ipos-1;
+     jdelim := 0;
+     while (jdelim < JavaLen(LeftDelimiters))
+      { if (JavaSubseq(line, ipos-delimLen, ipos) = RightDelimiters[jdelim])
+         { call findMatchingLeftInner(ipos, jdelim);
+           ipos := returnVal;
+           if (ipos < 0) { vreturn(-1)};
+           jdelim := 99 ; \* exit while loop
+         };
+        jdelim := jdelim+1;
+      }
+   };
+   vreturn(ipos-delimLen);
+ }
+
+\* Returns the position of the right paren matching a "(" at position pos.
+\* If there is none, it returns -1.
+procedure findMatchingRightParen(pos) 
+{ if (line[pos] # "(") {vreturn(pos)};
+  call findMatchingRightInner(pos, 0);
+  return;
+ }
+
+procedure findMatchingRightInner(pos, delim) 
+ variables ipos = pos; jdelim; delimLen; 
+{ delimLen := JavaLen(RightDelimiters[delim]);
+  ipos := pos + delimLen;
+  while (JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim])
+   { if (line[ipos] = ";") {vreturn(-1)};
+     ipos := ipos+1;
+     jdelim := 0;
+     while (jdelim < JavaLen(RightDelimiters))
+      { if (JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim])
+         { call findMatchingRightInner(ipos, jdelim);
+           ipos := returnVal;
+           if (ipos < 0) { vreturn(-1)};
+           jdelim := 99 ; \* exit while loop
+         };
+        jdelim := jdelim+1;
+      }
+   };
+   vreturn(ipos+delimLen);
+ }
+
+\* Assumes that leftTok and rightTok are two TokenSpecs, 
+\* possibly NullTokenSpecs, separated by a ">".  Checks
+\* if they form a step name.  If so, it returns the
+\* TokenSpec for that step name; otherwise it returns
+\* NullTokenSpec.
+procedure checkIfStepName(leftTok, rightTok)
+{ if (leftTok = NullTokenSpec \/ rightTok = NullTokenSpec)
+    { vreturn(NullTokenSpec) };
+  if ( /\ IsNumber(leftTok.token)
+       /\ line[leftTok.leftPos-1] = "<"
+       /\ line[leftTok.leftPos-2] # "<" )
+     { vreturn( [token |-> JavaSubseq(line, 
+                                      leftTok.leftPos-1,
+                                      rightTok.rightPos),
+                 leftPos |-> leftTok.leftPos-1,
+                 rightPos |-> rightTok.rightPos] )
+     };
+   else { vreturn(NullTokenSpec) };
+}
+procedure skipLeftOverSpaces() 
+{ while (line[curPos-1] = " ") { curPos := curPos - 1; };
+  return;
+}
+
+procedure skipRightOverSpaces() 
+{ while (line[curPos] = " ") { curPos := curPos+1; };
+  return;
+}
+
+\* Returns a TokenSpec array of possible tokens to be selected
+\* when the cursor is at position curPos of line.  It 
+\* returns a zero-length array if there are none.
+\*
+\* Known Bug: This will not work right in some cases when curPos
+\* are spaces inside a name, as in
+\*
+\*    Foo   (x, y)!bar
+\*        ^
+procedure findTokenSpecs()
+variables currentToken; left; rt; notDone; foundBangToken;
+temp1; temp2; temp3;
+{ \* Try to find the current TokenSpec and put it in foundTokenSpecs, 
+  \* leaving curPos to the left of that token.  If the token is a
+  \* step name, we can return that single TokenSpec as the spec.  
+  \* If it is an unnamed step, or if it is not a possible identifier
+  \* or operator symbol, we return << >> (a zero-length array).
+  
+  \* First, we must check if we may be inside a string of form (" ")^* "!" (" ")^*
+  \* and, if so, move to its left, setting notLastToken to TRUE
+  if (line[curPos] = " ") 
+    { call skipLeftOverSpaces() ;
+      if (line[curPos-1] = "!"  /\ line[curPos-2] # "!")
+        { notLastToken := TRUE;
+          curPos := curPos - 1;
+          call skipLeftOverSpaces();
+        }
+    }
+  else if (line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!")
+    { notLastToken := TRUE;
+      call skipLeftOverSpaces()
+    };
+    
+  \* If we're to the right of a ")", we could be inside something like
+  \*
+  \*   Foo(42) ! bar
+  \*          ^
+  \* so we set notLastToken true and move to the left of the parentesized string.
+  if ( /\ \/ notLastToken
+          \/ line[curPos] = " "
+       /\ line[curPos-1] = ")")
+    { notLastToken := TRUE;
+      call findMatchingLeftParen(curPos);
+      if (returnVal < 0) {vreturn(<< >>)};
+      curPos := returnVal;
+      call skipLeftOverSpaces();
+    };
+  \* If notLastToken is true, then we should be just to the right
+  \* of the token we are expecting to find to the left of the "!" or the
+  \* parenthesized expression. 
+  \* Otherwise, if we're not at an IdChar or at an operator, then
+  \* let's try one character to the left.  
+  \* Note: this happens to work if we're right before "<4>7" or "\in"
+  \* because both "<" and "\\" are operators.
+
+  if (notLastToken) { curPos := curPos - 1; };
+  else 
+    { if (line[curPos] \notin IdChar) 
+        { call findTokenIn(curPos, Operators);
+          if ( returnVal = NullTokenSpec )
+            { curPos := curPos - 1
+            }
+        }
+
+    } ;
+\* \* Following code replaced by preceding "else" clause
+\*
+\*  \* If we didn't move left and we're at a "(" or "." that's not the beginning
+\*  \* of an operator, let's move left 1.
+\*  if (~ notLastToken /\ line[curPos] = "(")
+\*    { call findTokenIn(curPos, ParenOperators);
+\*      if (returnVal = NullTokenSpec) { curPos := curPos - 1 }
+\*    }
+\*  else if ( /\ ~ notLastToken 
+\*            /\ line[curPos] = "."
+\*            /\ line[curPos+1] # "." 
+\*            /\ line[curPos-1] # "." )
+\*         { curPos := curPos - 1 };
+\*  
+\*  \* If we're at a space (which means we haven't found a "!" or moved left
+\*  \* over a parenthesized string), then try as if the cursor were
+\*  \* one character to the left.
+\*  if (line[curPos] = " ") { curPos := curPos - 1};
+  
+  if (line[curPos] \in IdChar)
+    { \* Check if we're at an "X" that is inside an "\X" or "\X".
+      \* Note: this works because there are no TeXSymbols besides
+      if (line[curPos] = "X") { call findTokenIn(curPos, XSymbols); }
+      else {returnVal := NullTokenSpec} ;
+      if (returnVal # NullTokenSpec)
+        { foundTokenSpecs := JavaSeq(<<returnVal>>);
+          curPos := returnVal.leftPos;
+          lastToken := TRUE;
+         }
+      else 
+         { call findMaximalIdCharSeq(curPos);
+           currentToken := returnVal;
+           \* Check if token is part of step name.  If it is, we set
+           \* First, see if currentToken could be the token after the "<...>".
+           if ( line[currentToken.leftPos-1] = ">" )
+             { call findMaximalIdCharSeq(currentToken.leftPos - 1);
+               call checkIfStepName(returnVal, currentToken);
+               if (returnVal # NullTokenSpec)
+                 { foundTokenSpecs := JavaSeq(<<returnVal>>);
+                   vreturn(FixOrigin(foundTokenSpecs));
+                 }
+             };
+           \* Next see if it's the token to the left of the ">"
+           if (line[currentToken.rightPos] = ">" )
+             { call findMaximalIdCharSeq(currentToken.rightPos+1);
+               call checkIfStepName(currentToken, returnVal);
+               if (returnVal # NullTokenSpec)
+                 { foundTokenSpecs := JavaSeq(<<returnVal>>);
+                   vreturn(FixOrigin(foundTokenSpecs));
+                 }
+             };
+
+           \* Next, check if it's a number, and abort if it is.
+           if (IsNumber(currentToken.token)) 
+             { vreturn(<< >>)
+             };
+           
+           \* Next, check if the token appears as "\\"token, in
+           \* which case we adjust currentTok
+           left := currentToken.leftPos ;
+           rt   := currentToken.rightPos ;
+           if ( /\ line[left-1] = "\\" 
+                /\ line[left-2] # "\\"
+                /\ \E ii \in DOMAIN TeXSymbols :
+                         JavaSubseq(line, left-1, rt) = TeXSymbols[ii] )
+             { lastToken := TRUE;
+               currentToken.leftPos := left - 1 ||
+               currentToken.token := JavaSubseq(line, left-1, rt);
+             };
+           foundTokenSpecs := 
+                 JavaSeq( << currentToken >>);
+           curPos := left;
+         }
+    }
+  else 
+    { call findTokenIn(curPos, Operators);
+      currentToken  := returnVal;
+      if (currentToken = NullTokenSpec)
+        { vreturn(<< >>);
+        };
+      call findTokenIn(curPos, NonOperators);
+      if (returnVal # NullTokenSpec)
+        { vreturn(<< >>);
+        };
+
+      \* Need to check if beginning or end of level specifier
+      \* of step number.  
+      if ( currentToken.token = JavaSeq(<< "<" >>) )
+        { call findMaximalIdCharSeq(currentToken.rightPos);
+          temp1 := returnVal;
+          if (/\ temp1 # NullTokenSpec
+              /\ line[temp1.rightPos] = ">")
+            { call findMaximalIdCharSeq(temp1.rightPos + 1);
+              temp2 := returnVal;
+              call checkIfStepName(temp1, temp2);
+              if (returnVal # NullTokenSpec)
+               { foundTokenSpecs := JavaSeq(<<returnVal>>);
+                 vreturn(FixOrigin(foundTokenSpecs));
+               }
+            }
+          }
+       else if (currentToken.token = JavaSeq(<< ">" >>))
+         { call findMaximalIdCharSeq(currentToken.leftPos);
+           temp1 := returnVal;
+           call findMaximalIdCharSeq(currentToken.rightPos);
+           temp2 := returnVal;
+           call checkIfStepName(temp1, temp2);
+              if (returnVal # NullTokenSpec)
+               { foundTokenSpecs := JavaSeq(<<returnVal>>);
+                 vreturn(FixOrigin(foundTokenSpecs));
+               }
+         };
+
+      \* We check for the case of a "\\" starting a TeX token.
+      if (currentToken.token = JavaSeq(<<"\\">>))
+        { call findMaximalIdCharSeq(currentToken.rightPos);
+          if ( /\ returnVal # NullTokenSpec
+               /\ \E ii \in DOMAIN TeXSymbols :
+                         JavaSubseq(line, 
+                                    currentToken.leftPos, 
+                                    returnVal.rightPos) = TeXSymbols[ii] )
+            { currentToken.rightPos := returnVal.rightPos ||
+              currentToken.token := JavaSubseq(line, 
+                                    currentToken.leftPos, 
+                                    returnVal.rightPos) 
+            }
+        };
+
+      foundTokenSpecs := JavaSeq(<<currentToken>>);
+      curPos := currentToken.leftPos;
+      lastToken := TRUE;
+     };
+
+  assert JavaLen(foundTokenSpecs) = 1;
+
+\*print <<"1", foundTokenSpecs[0]>>;
+  \** We have now found the base token.  We now look to find extenders
+  \*  to the left of the form  idtoken "!".  These must be part of the
+  \*  name, so we prepend them to foundTokenSpecs[0].
+  curPos := foundTokenSpecs[0].leftPos;
+  notDone := TRUE;
+  while (notDone) 
+    { call skipLeftOverSpaces();
+      if (curPos <  0 \/ line[curPos-1] = ";") 
+        { notDone := FALSE; }
+      else
+        { if (line[curPos-1] = "!" /\ (line[curPos-2] # "!"))
+            { curPos := curPos - 1;
+              call skipLeftOverSpaces();
+              call findMatchingLeftParen(curPos);
+              curPos := returnVal;
+              if (curPos < 0) { notDone := FALSE }
+              else 
+                { call skipLeftOverSpaces();
+                  call findMaximalIdCharSeq(curPos);
+                  currentToken := returnVal ;
+                  if ( \/ currentToken = NullTokenSpec
+                       \/ IsNumber(currentToken.token) )
+                    { notDone := FALSE }
+                  else
+                    { curPos := currentToken.leftPos;
+                      foundTokenSpecs[0] := 
+                             [token |-> JavaConcatenate(
+                                            JavaAppend(currentToken.token, "!"),
+                                            foundTokenSpecs[0].token),
+                              leftPos |-> curPos, 
+                              rightPos |-> foundTokenSpecs[0].rightPos];
+                    }
+               }
+             }
+         else {notDone := FALSE;}
+       };
+       
+    };
+
+  assert foundTokenSpecs # << >> ;
+
+  if (lastToken = TRUE) {vreturn(FixOrigin(foundTokenSpecs))};
+  
+\*  print <<"2", foundTokenSpecs[0]>>;
+  
+  curPos := foundTokenSpecs[0].rightPos;
+  foundBangToken := FALSE;
+  notDone := TRUE;
+  while (notDone) 
+    { call skipRightOverSpaces();
+      call findMatchingRightParen(curPos);
+      curPos := returnVal;
+      if (curPos < 0) {notDone := FALSE}
+      else
+        { call skipRightOverSpaces();
+          if (line[curPos] # "!"  \/ line[curPos+1] = "!")
+            { notDone := FALSE } 
+          else
+            { curPos := curPos + 1;
+              call skipRightOverSpaces();
+              call findMaximalIdCharSeq(curPos);
+              currentToken := returnVal ;
+              if ( \/ currentToken = NullTokenSpec
+                   \/ IsNumber(currentToken.token) )
+                { notDone := FALSE }
+              else
+                { foundBangToken := TRUE;
+                  foundTokenSpecs :=
+                    JavaConcatenate(
+                      JavaSeq( 
+                        << [token |-> 
+                              JavaConcatenate(JavaAppend(foundTokenSpecs[0].token,
+                                                          "!"),
+                                              currentToken.token),
+                            leftPos |-> foundTokenSpecs[0].leftPos,
+                            rightPos |-> currentToken.rightPos]
+                         >> ) ,
+                        foundTokenSpecs); 
+                  curPos := currentToken.rightPos;
+                }
+            }
+        }
+    };
+
+  if (notLastToken /\ ~ foundBangToken) { vreturn (<< >>) };
+
+  vreturn(FixOrigin(foundTokenSpecs));
+} \* end findTokenSpecs
+
+\* Main Body:
+{ curPos := JavaLen(Padding);
+  tempVar1 := (-1 :> "");
+  tempVar2 := JavaLen(Padding);
+\*curPos := 15;
+\*call findTokenSpecs();
+\*print returnVal;
+\*  print line;
+  while (tempVar2 < JavaLen(lineInput) + JavaLen(Padding))
+    { \* print <<"curPos: ", tempVar2>>;
+      curPos := tempVar2;
+      foundTokenSpecs := << >>  ;
+      lastToken := FALSE;
+      notLastToken := FALSE;
+      call findTokenSpecs();
+\*      if (tempVar2 >= 0 + JavaLen(Padding))
+\*        { print "done with run" ;
+\*          assert FALSE
+\*        };
+\*      if (returnVal = tempVar1)
+\*        { print "same" \* ,"lastToken: ", lastToken, ", notLastToken: ", notLastToken>> 
+\*        }
+\*      else 
+\*        { print returnVal;
+   \*          print <<"lastToken: ", lastToken, ", notLastToken: ", notLastToken>>;
+\*        };
+      tempVar1 := returnVal;
+      tempVar2 := tempVar2 + 1;
+    };
+  print "goodby world";
+
+} \* end Main Body
+} \* End algorithm
+  
+*****************************************************************************************)
+\* BEGIN TRANSLATION
+\* Procedure variable left of procedure findMaximalIdCharSeq at line 317 col 28 changed to left_
+\* Procedure variable rt of procedure findMaximalIdCharSeq at line 317 col 40 changed to rt_
+\* Procedure variable ipos of procedure findMatchingLeftInner at line 343 col 12 changed to ipos_
+\* Procedure variable jdelim of procedure findMatchingLeftInner at line 343 col 24 changed to jdelim_
+\* Procedure variable delimLen of procedure findMatchingLeftInner at line 343 col 32 changed to delimLen_
+\* Parameter pos of procedure findTokenIn at line 292 col 23 changed to pos_
+\* Parameter pos of procedure findMaximalIdCharSeq at line 316 col 32 changed to pos_f
+\* Parameter pos of procedure findMatchingLeftParen at line 336 col 33 changed to pos_fi
+\* Parameter pos of procedure findMatchingLeftInner at line 342 col 33 changed to pos_fin
+\* Parameter delim of procedure findMatchingLeftInner at line 342 col 38 changed to delim_
+\* Parameter pos of procedure findMatchingRightParen at line 365 col 34 changed to pos_find
+CONSTANT defaultInitValue
 VARIABLES line, foundTokenSpecs, lastToken, notLastToken, curPos, returnVal, 
-          tempVar1, tempVar2, tempVar3, pc, stack
-
-(* define statement *)
-FixOrigin(tokarray) ==
-[i \in DOMAIN tokarray |->
-         [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding),
-          leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]]
-
+          tempVar1, tempVar2, tempVar3, pc, stack
+
+(* define statement *)
+FixOrigin(tokarray) ==
+[i \in DOMAIN tokarray |->
+         [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding),
+          leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]]
+
 VARIABLES pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, 
           pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
           ipos, jdelim, delimLen, leftTok, rightTok, currentToken, left, rt, 
-          notDone, foundBangToken, temp1, temp2, temp3
-
+          notDone, foundBangToken, temp1, temp2, temp3
+
 vars == << line, foundTokenSpecs, lastToken, notLastToken, curPos, returnVal, 
            tempVar1, tempVar2, tempVar3, pc, stack, pos_, tokArray, i, tokIdx, 
            pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
            delimLen_, pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
            rightTok, currentToken, left, rt, notDone, foundBangToken, temp1, 
-           temp2, temp3 >>
-
-Init == (* Global variables *)
-        /\ line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding))
-        /\ foundTokenSpecs = << >>
-        /\ lastToken = FALSE
-        /\ notLastToken = FALSE
-        /\ curPos = currentPosition  + JavaLen(Padding)
-        /\ returnVal = defaultInitValue
-        /\ tempVar1 = defaultInitValue
-        /\ tempVar2 = defaultInitValue
-        /\ tempVar3 = defaultInitValue
-        (* Procedure findTokenIn *)
-        /\ pos_ = defaultInitValue
-        /\ tokArray = defaultInitValue
-        /\ i = defaultInitValue
-        /\ tokIdx = defaultInitValue
-        (* Procedure findMaximalIdCharSeq *)
-        /\ pos_f = defaultInitValue
-        /\ token = << >>
-        /\ left_ = pos_f
-        /\ rt_ = pos_f
-        (* Procedure findMatchingLeftParen *)
-        /\ pos_fi = defaultInitValue
-        (* Procedure findMatchingLeftInner *)
-        /\ pos_fin = defaultInitValue
-        /\ delim_ = defaultInitValue
-        /\ ipos_ = pos_fin
-        /\ jdelim_ = defaultInitValue
-        /\ delimLen_ = defaultInitValue
-        (* Procedure findMatchingRightParen *)
-        /\ pos_find = defaultInitValue
-        (* Procedure findMatchingRightInner *)
-        /\ pos = defaultInitValue
-        /\ delim = defaultInitValue
-        /\ ipos = pos
-        /\ jdelim = defaultInitValue
-        /\ delimLen = defaultInitValue
-        (* Procedure checkIfStepName *)
-        /\ leftTok = defaultInitValue
-        /\ rightTok = defaultInitValue
-        (* Procedure findTokenSpecs *)
-        /\ currentToken = defaultInitValue
-        /\ left = defaultInitValue
-        /\ rt = defaultInitValue
-        /\ notDone = defaultInitValue
-        /\ foundBangToken = defaultInitValue
-        /\ temp1 = defaultInitValue
-        /\ temp2 = defaultInitValue
-        /\ temp3 = defaultInitValue
-        /\ stack = << >>
-        /\ pc = "Lbl_77"
-
-Lbl_1 == /\ pc = "Lbl_1"
-         /\ i' = 0
-         /\ pc' = "Lbl_2"
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                         stack, pos_, tokArray, tokIdx, pos_f, token, left_, 
-                         rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                         delimLen, leftTok, rightTok, currentToken, left, rt, 
-                         notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_2 == /\ pc = "Lbl_2"
-         /\ IF i < JavaLen(tokArray)
-               THEN /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], 0)
-                    /\ pc' = "Lbl_3"
-                    /\ UNCHANGED << returnVal, stack, pos_, tokArray, i >>
-               ELSE /\ returnVal' = NullTokenSpec
-                    /\ pc' = Head(stack).pc
-                    /\ i' = Head(stack).i
-                    /\ tokIdx' = Head(stack).tokIdx
-                    /\ pos_' = Head(stack).pos_
-                    /\ tokArray' = Head(stack).tokArray
-                    /\ stack' = Tail(stack)
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, tempVar1, tempVar2, tempVar3, pos_f, token, 
-                         left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                         delimLen, leftTok, rightTok, currentToken, left, rt, 
-                         notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_3 == /\ pc = "Lbl_3"
-         /\ IF tokIdx # -1
-               THEN /\ LET ftlft == pos_ - tokIdx IN
-                         LET ftrt == pos_ - tokIdx + JavaLen(tokArray[i]) IN
-                           IF tokArray[i] = JavaSubseq(line, ftlft, ftrt)
-                              THEN /\ returnVal' = [token |-> tokArray[i],
-                                                         leftPos |-> ftlft, rightPos |-> ftrt]
-                                   /\ pc' = Head(stack).pc
-                                   /\ i' = Head(stack).i
-                                   /\ tokIdx' = Head(stack).tokIdx
-                                   /\ pos_' = Head(stack).pos_
-                                   /\ tokArray' = Head(stack).tokArray
-                                   /\ stack' = Tail(stack)
-                              ELSE /\ pc' = "Lbl_4"
-                                   /\ UNCHANGED << returnVal, stack, pos_, 
-                                                   tokArray, i, tokIdx >>
-               ELSE /\ i' = i+1
-                    /\ pc' = "Lbl_2"
-                    /\ UNCHANGED << returnVal, stack, pos_, tokArray, tokIdx >>
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, tempVar1, tempVar2, tempVar3, pos_f, token, 
-                         left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                         delimLen, leftTok, rightTok, currentToken, left, rt, 
-                         notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_4 == /\ pc = "Lbl_4"
-         /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], tokIdx+1)
-         /\ pc' = "Lbl_3"
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                         stack, pos_, tokArray, i, pos_f, token, left_, rt_, 
-                         pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                         pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
-                         rightTok, currentToken, left, rt, notDone, 
-                         foundBangToken, temp1, temp2, temp3 >>
-
-findTokenIn == Lbl_1 \/ Lbl_2 \/ Lbl_3 \/ Lbl_4
-
-Lbl_5 == /\ pc = "Lbl_5"
-         /\ IF line[left_-1] \in IdChar
-               THEN /\ left_' = left_-1
-                    /\ pc' = "Lbl_5"
-               ELSE /\ pc' = "Lbl_6"
-                    /\ left_' = left_
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                         stack, pos_, tokArray, i, tokIdx, pos_f, token, rt_, 
-                         pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                         pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
-                         rightTok, currentToken, left, rt, notDone, 
-                         foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_6 == /\ pc = "Lbl_6"
-         /\ IF line[rt_] \in IdChar
-               THEN /\ rt_' = rt_+1
-                    /\ pc' = "Lbl_6"
-                    /\ UNCHANGED << returnVal, stack, pos_f, token, left_ >>
-               ELSE /\ IF left_ = rt_
-                          THEN /\ returnVal' = NullTokenSpec
-                               /\ pc' = Head(stack).pc
-                               /\ token' = Head(stack).token
-                               /\ left_' = Head(stack).left_
-                               /\ rt_' = Head(stack).rt_
-                               /\ pos_f' = Head(stack).pos_f
-                               /\ stack' = Tail(stack)
-                          ELSE /\ returnVal' = [token |-> JavaSubseq(line, left_, rt_),
-                                                leftPos |-> left_, rightPos |-> rt_]
-                               /\ pc' = Head(stack).pc
-                               /\ token' = Head(stack).token
-                               /\ left_' = Head(stack).left_
-                               /\ rt_' = Head(stack).rt_
-                               /\ pos_f' = Head(stack).pos_f
-                               /\ stack' = Tail(stack)
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                         i, tokIdx, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                         delimLen, leftTok, rightTok, currentToken, left, rt, 
-                         notDone, foundBangToken, temp1, temp2, temp3 >>
-
-findMaximalIdCharSeq == Lbl_5 \/ Lbl_6
-
-Lbl_7 == /\ pc = "Lbl_7"
-         /\ IF line[pos_fi-1] # ")"
-               THEN /\ returnVal' = pos_fi
-                    /\ pc' = Head(stack).pc
-                    /\ pos_fi' = Head(stack).pos_fi
-                    /\ stack' = Tail(stack)
-               ELSE /\ pc' = "Lbl_8"
-                    /\ UNCHANGED << returnVal, stack, pos_fi >>
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                         i, tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, 
-                         ipos_, jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                         jdelim, delimLen, leftTok, rightTok, currentToken, 
-                         left, rt, notDone, foundBangToken, temp1, temp2, 
-                         temp3 >>
-
-Lbl_8 == /\ pc = "Lbl_8"
-         /\ /\ delim_' = 0
-            /\ pos_fin' = pos_fi
-            /\ stack' = << [ procedure |->  "findMatchingLeftInner",
-                             pc        |->  Head(stack).pc,
-                             ipos_     |->  ipos_,
-                             jdelim_   |->  jdelim_,
-                             delimLen_ |->  delimLen_,
-                             pos_fin   |->  pos_fin,
-                             delim_    |->  delim_ ] >>
-                         \o Tail(stack)
-         /\ ipos_' = pos_fin'
-         /\ jdelim_' = defaultInitValue
-         /\ delimLen_' = defaultInitValue
-         /\ pc' = "Lbl_9"
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                         tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, 
-                         pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
-                         rightTok, currentToken, left, rt, notDone, 
-                         foundBangToken, temp1, temp2, temp3 >>
-
-findMatchingLeftParen == Lbl_7 \/ Lbl_8
-
-Lbl_9 == /\ pc = "Lbl_9"
-         /\ delimLen_' = JavaLen(LeftDelimiters[delim_])
-         /\ ipos_' = pos_fin - delimLen_'
-         /\ pc' = "Lbl_10"
-         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                         stack, pos_, tokArray, i, tokIdx, pos_f, token, left_, 
-                         rt_, pos_fi, pos_fin, delim_, jdelim_, pos_find, pos, 
-                         delim, ipos, jdelim, delimLen, leftTok, rightTok, 
-                         currentToken, left, rt, notDone, foundBangToken, 
-                         temp1, temp2, temp3 >>
-
-Lbl_10 == /\ pc = "Lbl_10"
-          /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) # LeftDelimiters[delim_]
-                THEN /\ IF line[ipos_-1] = ";"
-                           THEN /\ returnVal' = -1
-                                /\ pc' = Head(stack).pc
-                                /\ ipos_' = Head(stack).ipos_
-                                /\ jdelim_' = Head(stack).jdelim_
-                                /\ delimLen_' = Head(stack).delimLen_
-                                /\ pos_fin' = Head(stack).pos_fin
-                                /\ delim_' = Head(stack).delim_
-                                /\ stack' = Tail(stack)
-                           ELSE /\ pc' = "Lbl_11"
-                                /\ UNCHANGED << returnVal, stack, pos_fin, 
-                                                delim_, ipos_, jdelim_, 
-                                                delimLen_ >>
-                ELSE /\ returnVal' = ipos_-delimLen_
-                     /\ pc' = Head(stack).pc
-                     /\ ipos_' = Head(stack).ipos_
-                     /\ jdelim_' = Head(stack).jdelim_
-                     /\ delimLen_' = Head(stack).delimLen_
-                     /\ pos_fin' = Head(stack).pos_fin
-                     /\ delim_' = Head(stack).delim_
-                     /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_11 == /\ pc = "Lbl_11"
-          /\ ipos_' = ipos_-1
-          /\ jdelim_' = 0
-          /\ pc' = "Lbl_12"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_12 == /\ pc = "Lbl_12"
-          /\ IF jdelim_ < JavaLen(LeftDelimiters)
-                THEN /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) = RightDelimiters[jdelim_]
-                           THEN /\ /\ delim_' = jdelim_
-                                   /\ pos_fin' = ipos_
-                                   /\ stack' = << [ procedure |->  "findMatchingLeftInner",
-                                                    pc        |->  "Lbl_13",
-                                                    ipos_     |->  ipos_,
-                                                    jdelim_   |->  jdelim_,
-                                                    delimLen_ |->  delimLen_,
-                                                    pos_fin   |->  pos_fin,
-                                                    delim_    |->  delim_ ] >>
-                                                \o stack
-                                /\ ipos_' = pos_fin'
-                                /\ jdelim_' = defaultInitValue
-                                /\ delimLen_' = defaultInitValue
-                                /\ pc' = "Lbl_9"
-                           ELSE /\ pc' = "Lbl_16"
-                                /\ UNCHANGED << stack, pos_fin, delim_, ipos_, 
-                                                jdelim_, delimLen_ >>
-                ELSE /\ pc' = "Lbl_10"
-                     /\ UNCHANGED << stack, pos_fin, delim_, ipos_, jdelim_, 
-                                     delimLen_ >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_16 == /\ pc = "Lbl_16"
-          /\ jdelim_' = jdelim_+1
-          /\ pc' = "Lbl_12"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                          delimLen, leftTok, rightTok, currentToken, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_13 == /\ pc = "Lbl_13"
-          /\ ipos_' = returnVal
-          /\ IF ipos_' < 0
-                THEN /\ returnVal' = -1
-                     /\ pc' = "Lbl_14"
-                ELSE /\ pc' = "Lbl_15"
-                     /\ UNCHANGED returnVal
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, stack, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_14 == /\ pc = "Lbl_14"
-          /\ pc' = Head(stack).pc
-          /\ ipos_' = Head(stack).ipos_
-          /\ jdelim_' = Head(stack).jdelim_
-          /\ delimLen_' = Head(stack).delimLen_
-          /\ pos_fin' = Head(stack).pos_fin
-          /\ delim_' = Head(stack).delim_
-          /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_15 == /\ pc = "Lbl_15"
-          /\ jdelim_' = 99
-          /\ pc' = "Lbl_16"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                          delimLen, leftTok, rightTok, currentToken, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-findMatchingLeftInner == Lbl_9 \/ Lbl_10 \/ Lbl_11 \/ Lbl_12 \/ Lbl_16
-                            \/ Lbl_13 \/ Lbl_14 \/ Lbl_15
-
-Lbl_17 == /\ pc = "Lbl_17"
-          /\ IF line[pos_find] # "("
-                THEN /\ returnVal' = pos_find
-                     /\ pc' = Head(stack).pc
-                     /\ pos_find' = Head(stack).pos_find
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_18"
-                     /\ UNCHANGED << returnVal, stack, pos_find >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok, currentToken, 
-                          left, rt, notDone, foundBangToken, temp1, temp2, 
-                          temp3 >>
-
-Lbl_18 == /\ pc = "Lbl_18"
-          /\ /\ delim' = 0
-             /\ pos' = pos_find
-             /\ stack' = << [ procedure |->  "findMatchingRightInner",
-                              pc        |->  Head(stack).pc,
-                              ipos      |->  ipos,
-                              jdelim    |->  jdelim,
-                              delimLen  |->  delimLen,
-                              pos       |->  pos,
-                              delim     |->  delim ] >>
-                          \o Tail(stack)
-          /\ ipos' = pos'
-          /\ jdelim' = defaultInitValue
-          /\ delimLen' = defaultInitValue
-          /\ pc' = "Lbl_19"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, leftTok, rightTok, currentToken, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-findMatchingRightParen == Lbl_17 \/ Lbl_18
-
-Lbl_19 == /\ pc = "Lbl_19"
-          /\ delimLen' = JavaLen(RightDelimiters[delim])
-          /\ ipos' = pos + delimLen'
-          /\ pc' = "Lbl_20"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                          delimLen_, pos_find, pos, delim, jdelim, leftTok, 
-                          rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_20 == /\ pc = "Lbl_20"
-          /\ IF JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim]
-                THEN /\ IF line[ipos] = ";"
-                           THEN /\ returnVal' = -1
-                                /\ pc' = Head(stack).pc
-                                /\ ipos' = Head(stack).ipos
-                                /\ jdelim' = Head(stack).jdelim
-                                /\ delimLen' = Head(stack).delimLen
-                                /\ pos' = Head(stack).pos
-                                /\ delim' = Head(stack).delim
-                                /\ stack' = Tail(stack)
-                           ELSE /\ pc' = "Lbl_21"
-                                /\ UNCHANGED << returnVal, stack, pos, delim, 
-                                                ipos, jdelim, delimLen >>
-                ELSE /\ returnVal' = ipos+delimLen
-                     /\ pc' = Head(stack).pc
-                     /\ ipos' = Head(stack).ipos
-                     /\ jdelim' = Head(stack).jdelim
-                     /\ delimLen' = Head(stack).delimLen
-                     /\ pos' = Head(stack).pos
-                     /\ delim' = Head(stack).delim
-                     /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, leftTok, 
-                          rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_21 == /\ pc = "Lbl_21"
-          /\ ipos' = ipos+1
-          /\ jdelim' = 0
-          /\ pc' = "Lbl_22"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                          delimLen_, pos_find, pos, delim, delimLen, leftTok, 
-                          rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_22 == /\ pc = "Lbl_22"
-          /\ IF jdelim < JavaLen(RightDelimiters)
-                THEN /\ IF JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim]
-                           THEN /\ /\ delim' = jdelim
-                                   /\ pos' = ipos
-                                   /\ stack' = << [ procedure |->  "findMatchingRightInner",
-                                                    pc        |->  "Lbl_23",
-                                                    ipos      |->  ipos,
-                                                    jdelim    |->  jdelim,
-                                                    delimLen  |->  delimLen,
-                                                    pos       |->  pos,
-                                                    delim     |->  delim ] >>
-                                                \o stack
-                                /\ ipos' = pos'
-                                /\ jdelim' = defaultInitValue
-                                /\ delimLen' = defaultInitValue
-                                /\ pc' = "Lbl_19"
-                           ELSE /\ pc' = "Lbl_26"
-                                /\ UNCHANGED << stack, pos, delim, ipos, 
-                                                jdelim, delimLen >>
-                ELSE /\ pc' = "Lbl_20"
-                     /\ UNCHANGED << stack, pos, delim, ipos, jdelim, delimLen >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, leftTok, rightTok, currentToken, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_26 == /\ pc = "Lbl_26"
-          /\ jdelim' = jdelim+1
-          /\ pc' = "Lbl_22"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                          delimLen_, pos_find, pos, delim, ipos, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_23 == /\ pc = "Lbl_23"
-          /\ ipos' = returnVal
-          /\ IF ipos' < 0
-                THEN /\ returnVal' = -1
-                     /\ pc' = "Lbl_24"
-                ELSE /\ pc' = "Lbl_25"
-                     /\ UNCHANGED returnVal
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, stack, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, jdelim, delimLen, leftTok, 
-                          rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_24 == /\ pc = "Lbl_24"
-          /\ pc' = Head(stack).pc
-          /\ ipos' = Head(stack).ipos
-          /\ jdelim' = Head(stack).jdelim
-          /\ delimLen' = Head(stack).delimLen
-          /\ pos' = Head(stack).pos
-          /\ delim' = Head(stack).delim
-          /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, leftTok, rightTok, currentToken, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_25 == /\ pc = "Lbl_25"
-          /\ jdelim' = 99
-          /\ pc' = "Lbl_26"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                          delimLen_, pos_find, pos, delim, ipos, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-findMatchingRightInner == Lbl_19 \/ Lbl_20 \/ Lbl_21 \/ Lbl_22 \/ Lbl_26
-                             \/ Lbl_23 \/ Lbl_24 \/ Lbl_25
-
-Lbl_27 == /\ pc = "Lbl_27"
-          /\ IF leftTok = NullTokenSpec \/ rightTok = NullTokenSpec
-                THEN /\ returnVal' = NullTokenSpec
-                     /\ pc' = Head(stack).pc
-                     /\ leftTok' = Head(stack).leftTok
-                     /\ rightTok' = Head(stack).rightTok
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_28"
-                     /\ UNCHANGED << returnVal, stack, leftTok, rightTok >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, currentToken, left, 
-                          rt, notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_28 == /\ pc = "Lbl_28"
-          /\ IF /\ IsNumber(leftTok.token)
-                /\ line[leftTok.leftPos-1] = "<"
-                /\ line[leftTok.leftPos-2] # "<"
-                THEN /\ returnVal' = [token |-> JavaSubseq(line,
-                                                           leftTok.leftPos-1,
-                                                           rightTok.rightPos),
-                                      leftPos |-> leftTok.leftPos-1,
-                                      rightPos |-> rightTok.rightPos]
-                     /\ pc' = Head(stack).pc
-                     /\ leftTok' = Head(stack).leftTok
-                     /\ rightTok' = Head(stack).rightTok
-                     /\ stack' = Tail(stack)
-                ELSE /\ returnVal' = NullTokenSpec
-                     /\ pc' = Head(stack).pc
-                     /\ leftTok' = Head(stack).leftTok
-                     /\ rightTok' = Head(stack).rightTok
-                     /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, currentToken, left, 
-                          rt, notDone, foundBangToken, temp1, temp2, temp3 >>
-
-checkIfStepName == Lbl_27 \/ Lbl_28
-
-Lbl_29 == /\ pc = "Lbl_29"
-          /\ IF line[curPos-1] = " "
-                THEN /\ curPos' = curPos - 1
-                     /\ pc' = "Lbl_29"
-                     /\ stack' = stack
-                ELSE /\ pc' = Head(stack).pc
-                     /\ stack' = Tail(stack)
-                     /\ UNCHANGED curPos
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-skipLeftOverSpaces == Lbl_29
-
-Lbl_30 == /\ pc = "Lbl_30"
-          /\ IF line[curPos] = " "
-                THEN /\ curPos' = curPos+1
-                     /\ pc' = "Lbl_30"
-                     /\ stack' = stack
-                ELSE /\ pc' = Head(stack).pc
-                     /\ stack' = Tail(stack)
-                     /\ UNCHANGED curPos
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-skipRightOverSpaces == Lbl_30
-
-Lbl_31 == /\ pc = "Lbl_31"
-          /\ IF line[curPos] = " "
-                THEN /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                                      pc        |->  "Lbl_32" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_29"
-                     /\ UNCHANGED notLastToken
-                ELSE /\ IF line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!"
-                           THEN /\ notLastToken' = TRUE
-                                /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                                                 pc        |->  "Lbl_33" ] >>
-                                             \o stack
-                                /\ pc' = "Lbl_29"
-                           ELSE /\ pc' = "Lbl_33"
-                                /\ UNCHANGED << notLastToken, stack >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, 
-                          tempVar1, tempVar2, tempVar3, pos_, tokArray, i, 
-                          tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_32 == /\ pc = "Lbl_32"
-          /\ IF line[curPos-1] = "!"  /\ line[curPos-2] # "!"
-                THEN /\ notLastToken' = TRUE
-                     /\ curPos' = curPos - 1
-                     /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                                      pc        |->  "Lbl_33" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_29"
-                ELSE /\ pc' = "Lbl_33"
-                     /\ UNCHANGED << notLastToken, curPos, stack >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, returnVal, 
-                          tempVar1, tempVar2, tempVar3, pos_, tokArray, i, 
-                          tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_33 == /\ pc = "Lbl_33"
-          /\ IF /\ \/ notLastToken
-                   \/ line[curPos] = " "
-                /\ line[curPos-1] = ")"
-                THEN /\ notLastToken' = TRUE
-                     /\ /\ pos_fi' = curPos
-                        /\ stack' = << [ procedure |->  "findMatchingLeftParen",
-                                         pc        |->  "Lbl_34",
-                                         pos_fi    |->  pos_fi ] >>
-                                     \o stack
-                     /\ pc' = "Lbl_7"
-                ELSE /\ pc' = "Lbl_36"
-                     /\ UNCHANGED << notLastToken, stack, pos_fi >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, 
-                          tempVar1, tempVar2, tempVar3, pos_, tokArray, i, 
-                          tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_34 == /\ pc = "Lbl_34"
-          /\ IF returnVal < 0
-                THEN /\ returnVal' = << >>
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_35"
-                     /\ UNCHANGED << returnVal, stack, currentToken, left, rt, 
-                                     notDone, foundBangToken, temp1, temp2, 
-                                     temp3 >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_35 == /\ pc = "Lbl_35"
-          /\ curPos' = returnVal
-          /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                           pc        |->  "Lbl_36" ] >>
-                       \o stack
-          /\ pc' = "Lbl_29"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_36 == /\ pc = "Lbl_36"
-          /\ IF notLastToken
-                THEN /\ curPos' = curPos - 1
-                     /\ pc' = "Lbl_38"
-                     /\ UNCHANGED << stack, pos_, tokArray, i, tokIdx >>
-                ELSE /\ IF line[curPos] \notin IdChar
-                           THEN /\ /\ pos_' = curPos
-                                   /\ stack' = << [ procedure |->  "findTokenIn",
-                                                    pc        |->  "Lbl_37",
-                                                    i         |->  i,
-                                                    tokIdx    |->  tokIdx,
-                                                    pos_      |->  pos_,
-                                                    tokArray  |->  tokArray ] >>
-                                                \o stack
-                                   /\ tokArray' = Operators
-                                /\ i' = defaultInitValue
-                                /\ tokIdx' = defaultInitValue
-                                /\ pc' = "Lbl_1"
-                           ELSE /\ pc' = "Lbl_38"
-                                /\ UNCHANGED << stack, pos_, tokArray, i, 
-                                                tokIdx >>
-                     /\ UNCHANGED curPos
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok, currentToken, 
-                          left, rt, notDone, foundBangToken, temp1, temp2, 
-                          temp3 >>
-
-Lbl_37 == /\ pc = "Lbl_37"
-          /\ IF returnVal = NullTokenSpec
-                THEN /\ curPos' = curPos - 1
-                ELSE /\ TRUE
-                     /\ UNCHANGED curPos
-          /\ pc' = "Lbl_38"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_38 == /\ pc = "Lbl_38"
-          /\ IF line[curPos] \in IdChar
-                THEN /\ IF line[curPos] = "X"
-                           THEN /\ /\ pos_' = curPos
-                                   /\ stack' = << [ procedure |->  "findTokenIn",
-                                                    pc        |->  "Lbl_39",
-                                                    i         |->  i,
-                                                    tokIdx    |->  tokIdx,
-                                                    pos_      |->  pos_,
-                                                    tokArray  |->  tokArray ] >>
-                                                \o stack
-                                   /\ tokArray' = XSymbols
-                                /\ i' = defaultInitValue
-                                /\ tokIdx' = defaultInitValue
-                                /\ pc' = "Lbl_1"
-                                /\ UNCHANGED returnVal
-                           ELSE /\ returnVal' = NullTokenSpec
-                                /\ pc' = "Lbl_39"
-                                /\ UNCHANGED << stack, pos_, tokArray, i, 
-                                                tokIdx >>
-                ELSE /\ /\ pos_' = curPos
-                        /\ stack' = << [ procedure |->  "findTokenIn",
-                                         pc        |->  "Lbl_48",
-                                         i         |->  i,
-                                         tokIdx    |->  tokIdx,
-                                         pos_      |->  pos_,
-                                         tokArray  |->  tokArray ] >>
-                                     \o stack
-                        /\ tokArray' = Operators
-                     /\ i' = defaultInitValue
-                     /\ tokIdx' = defaultInitValue
-                     /\ pc' = "Lbl_1"
-                     /\ UNCHANGED returnVal
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                          delimLen, leftTok, rightTok, currentToken, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_39 == /\ pc = "Lbl_39"
-          /\ IF returnVal # NullTokenSpec
-                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
-                     /\ curPos' = returnVal.leftPos
-                     /\ lastToken' = TRUE
-                     /\ pc' = "Lbl_62"
-                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
-                ELSE /\ /\ pos_f' = curPos
-                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                         pc        |->  "Lbl_40",
-                                         token     |->  token,
-                                         left_     |->  left_,
-                                         rt_       |->  rt_,
-                                         pos_f     |->  pos_f ] >>
-                                     \o stack
-                     /\ token' = << >>
-                     /\ left_' = pos_f'
-                     /\ rt_' = pos_f'
-                     /\ pc' = "Lbl_5"
-                     /\ UNCHANGED << foundTokenSpecs, lastToken, curPos >>
-          /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, 
-                          tempVar3, pos_, tokArray, i, tokIdx, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_40 == /\ pc = "Lbl_40"
-          /\ currentToken' = returnVal
-          /\ IF line[currentToken'.leftPos-1] = ">"
-                THEN /\ /\ pos_f' = currentToken'.leftPos - 1
-                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                         pc        |->  "Lbl_41",
-                                         token     |->  token,
-                                         left_     |->  left_,
-                                         rt_       |->  rt_,
-                                         pos_f     |->  pos_f ] >>
-                                     \o stack
-                     /\ token' = << >>
-                     /\ left_' = pos_f'
-                     /\ rt_' = pos_f'
-                     /\ pc' = "Lbl_5"
-                ELSE /\ pc' = "Lbl_43"
-                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, left, rt, 
-                          notDone, foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_41 == /\ pc = "Lbl_41"
-          /\ /\ leftTok' = returnVal
-             /\ rightTok' = currentToken
-             /\ stack' = << [ procedure |->  "checkIfStepName",
-                              pc        |->  "Lbl_42",
-                              leftTok   |->  leftTok,
-                              rightTok  |->  rightTok ] >>
-                          \o stack
-          /\ pc' = "Lbl_27"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_42 == /\ pc = "Lbl_42"
-          /\ IF returnVal # NullTokenSpec
-                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
-                     /\ returnVal' = FixOrigin(foundTokenSpecs')
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_43"
-                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
-                                     currentToken, left, rt, notDone, 
-                                     foundBangToken, temp1, temp2, temp3 >>
-          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
-                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_43 == /\ pc = "Lbl_43"
-          /\ IF line[currentToken.rightPos] = ">"
-                THEN /\ /\ pos_f' = currentToken.rightPos+1
-                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                         pc        |->  "Lbl_44",
-                                         token     |->  token,
-                                         left_     |->  left_,
-                                         rt_       |->  rt_,
-                                         pos_f     |->  pos_f ] >>
-                                     \o stack
-                     /\ token' = << >>
-                     /\ left_' = pos_f'
-                     /\ rt_' = pos_f'
-                     /\ pc' = "Lbl_5"
-                ELSE /\ pc' = "Lbl_46"
-                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_44 == /\ pc = "Lbl_44"
-          /\ /\ leftTok' = currentToken
-             /\ rightTok' = returnVal
-             /\ stack' = << [ procedure |->  "checkIfStepName",
-                              pc        |->  "Lbl_45",
-                              leftTok   |->  leftTok,
-                              rightTok  |->  rightTok ] >>
-                          \o stack
-          /\ pc' = "Lbl_27"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_45 == /\ pc = "Lbl_45"
-          /\ IF returnVal # NullTokenSpec
-                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
-                     /\ returnVal' = FixOrigin(foundTokenSpecs')
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_46"
-                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
-                                     currentToken, left, rt, notDone, 
-                                     foundBangToken, temp1, temp2, temp3 >>
-          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
-                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_46 == /\ pc = "Lbl_46"
-          /\ IF IsNumber(currentToken.token)
-                THEN /\ returnVal' = << >>
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_47"
-                     /\ UNCHANGED << returnVal, stack, currentToken, left, rt, 
-                                     notDone, foundBangToken, temp1, temp2, 
-                                     temp3 >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_47 == /\ pc = "Lbl_47"
-          /\ left' = currentToken.leftPos
-          /\ rt' = currentToken.rightPos
-          /\ IF /\ line[left'-1] = "\\"
-                /\ line[left'-2] # "\\"
-                /\ \E ii \in DOMAIN TeXSymbols :
-                         JavaSubseq(line, left'-1, rt') = TeXSymbols[ii]
-                THEN /\ lastToken' = TRUE
-                     /\ currentToken' = [currentToken EXCEPT !.leftPos = left' - 1,
-                                                             !.token = JavaSubseq(line, left'-1, rt')]
-                ELSE /\ TRUE
-                     /\ UNCHANGED << lastToken, currentToken >>
-          /\ foundTokenSpecs' = JavaSeq( << currentToken' >>)
-          /\ curPos' = left'
-          /\ pc' = "Lbl_62"
-          /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, 
-                          tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_48 == /\ pc = "Lbl_48"
-          /\ currentToken' = returnVal
-          /\ IF currentToken' = NullTokenSpec
-                THEN /\ returnVal' = << >>
-                     /\ pc' = "Lbl_49"
-                ELSE /\ pc' = "Lbl_50"
-                     /\ UNCHANGED returnVal
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, stack, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_49 == /\ pc = "Lbl_49"
-          /\ pc' = Head(stack).pc
-          /\ currentToken' = Head(stack).currentToken
-          /\ left' = Head(stack).left
-          /\ rt' = Head(stack).rt
-          /\ notDone' = Head(stack).notDone
-          /\ foundBangToken' = Head(stack).foundBangToken
-          /\ temp1' = Head(stack).temp1
-          /\ temp2' = Head(stack).temp2
-          /\ temp3' = Head(stack).temp3
-          /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok >>
-
-Lbl_50 == /\ pc = "Lbl_50"
-          /\ /\ pos_' = curPos
-             /\ stack' = << [ procedure |->  "findTokenIn",
-                              pc        |->  "Lbl_51",
-                              i         |->  i,
-                              tokIdx    |->  tokIdx,
-                              pos_      |->  pos_,
-                              tokArray  |->  tokArray ] >>
-                          \o stack
-             /\ tokArray' = NonOperators
-          /\ i' = defaultInitValue
-          /\ tokIdx' = defaultInitValue
-          /\ pc' = "Lbl_1"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_51 == /\ pc = "Lbl_51"
-          /\ IF returnVal # NullTokenSpec
-                THEN /\ returnVal' = << >>
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_52"
-                     /\ UNCHANGED << returnVal, stack, currentToken, left, rt, 
-                                     notDone, foundBangToken, temp1, temp2, 
-                                     temp3 >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_52 == /\ pc = "Lbl_52"
-          /\ IF currentToken.token = JavaSeq(<< "<" >>)
-                THEN /\ /\ pos_f' = currentToken.rightPos
-                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                         pc        |->  "Lbl_53",
-                                         token     |->  token,
-                                         left_     |->  left_,
-                                         rt_       |->  rt_,
-                                         pos_f     |->  pos_f ] >>
-                                     \o stack
-                     /\ token' = << >>
-                     /\ left_' = pos_f'
-                     /\ rt_' = pos_f'
-                     /\ pc' = "Lbl_5"
-                ELSE /\ IF currentToken.token = JavaSeq(<< ">" >>)
-                           THEN /\ /\ pos_f' = currentToken.leftPos
-                                   /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                                    pc        |->  "Lbl_56",
-                                                    token     |->  token,
-                                                    left_     |->  left_,
-                                                    rt_       |->  rt_,
-                                                    pos_f     |->  pos_f ] >>
-                                                \o stack
-                                /\ token' = << >>
-                                /\ left_' = pos_f'
-                                /\ rt_' = pos_f'
-                                /\ pc' = "Lbl_5"
-                           ELSE /\ pc' = "Lbl_59"
-                                /\ UNCHANGED << stack, pos_f, token, left_, 
-                                                rt_ >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_53 == /\ pc = "Lbl_53"
-          /\ temp1' = returnVal
-          /\ IF /\ temp1' # NullTokenSpec
-                /\ line[temp1'.rightPos] = ">"
-                THEN /\ /\ pos_f' = temp1'.rightPos + 1
-                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                         pc        |->  "Lbl_54",
-                                         token     |->  token,
-                                         left_     |->  left_,
-                                         rt_       |->  rt_,
-                                         pos_f     |->  pos_f ] >>
-                                     \o stack
-                     /\ token' = << >>
-                     /\ left_' = pos_f'
-                     /\ rt_' = pos_f'
-                     /\ pc' = "Lbl_5"
-                ELSE /\ pc' = "Lbl_59"
-                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp2, temp3 >>
-
-Lbl_54 == /\ pc = "Lbl_54"
-          /\ temp2' = returnVal
-          /\ /\ leftTok' = temp1
-             /\ rightTok' = temp2'
-             /\ stack' = << [ procedure |->  "checkIfStepName",
-                              pc        |->  "Lbl_55",
-                              leftTok   |->  leftTok,
-                              rightTok  |->  rightTok ] >>
-                          \o stack
-          /\ pc' = "Lbl_27"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp3 >>
-
-Lbl_55 == /\ pc = "Lbl_55"
-          /\ IF returnVal # NullTokenSpec
-                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
-                     /\ returnVal' = FixOrigin(foundTokenSpecs')
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_59"
-                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
-                                     currentToken, left, rt, notDone, 
-                                     foundBangToken, temp1, temp2, temp3 >>
-          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
-                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_56 == /\ pc = "Lbl_56"
-          /\ temp1' = returnVal
-          /\ /\ pos_f' = currentToken.rightPos
-             /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                              pc        |->  "Lbl_57",
-                              token     |->  token,
-                              left_     |->  left_,
-                              rt_       |->  rt_,
-                              pos_f     |->  pos_f ] >>
-                          \o stack
-          /\ token' = << >>
-          /\ left_' = pos_f'
-          /\ rt_' = pos_f'
-          /\ pc' = "Lbl_5"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp2, temp3 >>
-
-Lbl_57 == /\ pc = "Lbl_57"
-          /\ temp2' = returnVal
-          /\ /\ leftTok' = temp1
-             /\ rightTok' = temp2'
-             /\ stack' = << [ procedure |->  "checkIfStepName",
-                              pc        |->  "Lbl_58",
-                              leftTok   |->  leftTok,
-                              rightTok  |->  rightTok ] >>
-                          \o stack
-          /\ pc' = "Lbl_27"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp3 >>
-
-Lbl_58 == /\ pc = "Lbl_58"
-          /\ IF returnVal # NullTokenSpec
-                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
-                     /\ returnVal' = FixOrigin(foundTokenSpecs')
-                     /\ pc' = Head(stack).pc
-                     /\ currentToken' = Head(stack).currentToken
-                     /\ left' = Head(stack).left
-                     /\ rt' = Head(stack).rt
-                     /\ notDone' = Head(stack).notDone
-                     /\ foundBangToken' = Head(stack).foundBangToken
-                     /\ temp1' = Head(stack).temp1
-                     /\ temp2' = Head(stack).temp2
-                     /\ temp3' = Head(stack).temp3
-                     /\ stack' = Tail(stack)
-                ELSE /\ pc' = "Lbl_59"
-                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
-                                     currentToken, left, rt, notDone, 
-                                     foundBangToken, temp1, temp2, temp3 >>
-          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
-                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_59 == /\ pc = "Lbl_59"
-          /\ IF currentToken.token = JavaSeq(<<"\\">>)
-                THEN /\ /\ pos_f' = currentToken.rightPos
-                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                                         pc        |->  "Lbl_60",
-                                         token     |->  token,
-                                         left_     |->  left_,
-                                         rt_       |->  rt_,
-                                         pos_f     |->  pos_f ] >>
-                                     \o stack
-                     /\ token' = << >>
-                     /\ left_' = pos_f'
-                     /\ rt_' = pos_f'
-                     /\ pc' = "Lbl_5"
-                ELSE /\ pc' = "Lbl_61"
-                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_60 == /\ pc = "Lbl_60"
-          /\ IF /\ returnVal # NullTokenSpec
-                /\ \E ii \in DOMAIN TeXSymbols :
-                          JavaSubseq(line,
-                                     currentToken.leftPos,
-                                     returnVal.rightPos) = TeXSymbols[ii]
-                THEN /\ currentToken' = [currentToken EXCEPT !.rightPos = returnVal.rightPos,
-                                                             !.token = JavaSubseq(line,
-                                                                       currentToken.leftPos,
-                                                                       returnVal.rightPos)]
-                ELSE /\ TRUE
-                     /\ UNCHANGED currentToken
-          /\ pc' = "Lbl_61"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
-                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
-                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
-                          delimLen, leftTok, rightTok, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_61 == /\ pc = "Lbl_61"
-          /\ foundTokenSpecs' = JavaSeq(<<currentToken>>)
-          /\ curPos' = currentToken.leftPos
-          /\ lastToken' = TRUE
-          /\ pc' = "Lbl_62"
-          /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, 
-                          tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, 
-                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
-                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
-                          jdelim, delimLen, leftTok, rightTok, currentToken, 
-                          left, rt, notDone, foundBangToken, temp1, temp2, 
-                          temp3 >>
-
-Lbl_62 == /\ pc = "Lbl_62"
-          /\ Assert(JavaLen(foundTokenSpecs) = 1, 
-                    "Failure of assertion at line 619, column 3.")
-          /\ curPos' = foundTokenSpecs[0].leftPos
-          /\ notDone' = TRUE
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_63 == /\ pc = "Lbl_63"
-          /\ IF notDone
-                THEN /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                                      pc        |->  "Lbl_64" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_29"
-                     /\ UNCHANGED << returnVal, currentToken, left, rt, 
-                                     notDone, foundBangToken, temp1, temp2, 
-                                     temp3 >>
-                ELSE /\ Assert(foundTokenSpecs # << >>, 
-                               "Failure of assertion at line 661, column 3.")
-                     /\ IF lastToken = TRUE
-                           THEN /\ returnVal' = FixOrigin(foundTokenSpecs)
-                                /\ pc' = Head(stack).pc
-                                /\ currentToken' = Head(stack).currentToken
-                                /\ left' = Head(stack).left
-                                /\ rt' = Head(stack).rt
-                                /\ notDone' = Head(stack).notDone
-                                /\ foundBangToken' = Head(stack).foundBangToken
-                                /\ temp1' = Head(stack).temp1
-                                /\ temp2' = Head(stack).temp2
-                                /\ temp3' = Head(stack).temp3
-                                /\ stack' = Tail(stack)
-                           ELSE /\ pc' = "Lbl_69"
-                                /\ UNCHANGED << returnVal, stack, currentToken, 
-                                                left, rt, notDone, 
-                                                foundBangToken, temp1, temp2, 
-                                                temp3 >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_64 == /\ pc = "Lbl_64"
-          /\ IF curPos <  0 \/ line[curPos-1] = ";"
-                THEN /\ notDone' = FALSE
-                     /\ pc' = "Lbl_63"
-                     /\ UNCHANGED << curPos, stack >>
-                ELSE /\ IF line[curPos-1] = "!" /\ (line[curPos-2] # "!")
-                           THEN /\ curPos' = curPos - 1
-                                /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                                                 pc        |->  "Lbl_65" ] >>
-                                             \o stack
-                                /\ pc' = "Lbl_29"
-                                /\ UNCHANGED notDone
-                           ELSE /\ notDone' = FALSE
-                                /\ pc' = "Lbl_63"
-                                /\ UNCHANGED << curPos, stack >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_65 == /\ pc = "Lbl_65"
-          /\ /\ pos_fi' = curPos
-             /\ stack' = << [ procedure |->  "findMatchingLeftParen",
-                              pc        |->  "Lbl_66",
-                              pos_fi    |->  pos_fi ] >>
-                          \o stack
-          /\ pc' = "Lbl_7"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, 
-                          pos, delim, ipos, jdelim, delimLen, leftTok, 
-                          rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_66 == /\ pc = "Lbl_66"
-          /\ curPos' = returnVal
-          /\ IF curPos' < 0
-                THEN /\ notDone' = FALSE
-                     /\ pc' = "Lbl_63"
-                     /\ stack' = stack
-                ELSE /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
-                                      pc        |->  "Lbl_67" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_29"
-                     /\ UNCHANGED notDone
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_67 == /\ pc = "Lbl_67"
-          /\ /\ pos_f' = curPos
-             /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                              pc        |->  "Lbl_68",
-                              token     |->  token,
-                              left_     |->  left_,
-                              rt_       |->  rt_,
-                              pos_f     |->  pos_f ] >>
-                          \o stack
-          /\ token' = << >>
-          /\ left_' = pos_f'
-          /\ rt_' = pos_f'
-          /\ pc' = "Lbl_5"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_68 == /\ pc = "Lbl_68"
-          /\ currentToken' = returnVal
-          /\ IF \/ currentToken' = NullTokenSpec
-                \/ IsNumber(currentToken'.token)
-                THEN /\ notDone' = FALSE
-                     /\ UNCHANGED << foundTokenSpecs, curPos >>
-                ELSE /\ curPos' = currentToken'.leftPos
-                     /\ foundTokenSpecs' = [foundTokenSpecs EXCEPT ![0] = [token |-> JavaConcatenate(
-                                                                                         JavaAppend(currentToken'.token, "!"),
-                                                                                         foundTokenSpecs[0].token),
-                                                                           leftPos |-> curPos',
-                                                                           rightPos |-> foundTokenSpecs[0].rightPos]]
-                     /\ UNCHANGED notDone
-          /\ pc' = "Lbl_63"
-          /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, 
-                          tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, 
-                          pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, left, rt, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_69 == /\ pc = "Lbl_69"
-          /\ curPos' = foundTokenSpecs[0].rightPos
-          /\ foundBangToken' = FALSE
-          /\ notDone' = TRUE
-          /\ pc' = "Lbl_70"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, temp1, 
-                          temp2, temp3 >>
-
-Lbl_70 == /\ pc = "Lbl_70"
-          /\ IF notDone
-                THEN /\ stack' = << [ procedure |->  "skipRightOverSpaces",
-                                      pc        |->  "Lbl_71" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_30"
-                     /\ UNCHANGED << returnVal, currentToken, left, rt, 
-                                     notDone, foundBangToken, temp1, temp2, 
-                                     temp3 >>
-                ELSE /\ IF notLastToken /\ ~ foundBangToken
-                           THEN /\ returnVal' = << >>
-                                /\ pc' = Head(stack).pc
-                                /\ currentToken' = Head(stack).currentToken
-                                /\ left' = Head(stack).left
-                                /\ rt' = Head(stack).rt
-                                /\ notDone' = Head(stack).notDone
-                                /\ foundBangToken' = Head(stack).foundBangToken
-                                /\ temp1' = Head(stack).temp1
-                                /\ temp2' = Head(stack).temp2
-                                /\ temp3' = Head(stack).temp3
-                                /\ stack' = Tail(stack)
-                           ELSE /\ pc' = "Lbl_76"
-                                /\ UNCHANGED << returnVal, stack, currentToken, 
-                                                left, rt, notDone, 
-                                                foundBangToken, temp1, temp2, 
-                                                temp3 >>
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
-
-Lbl_71 == /\ pc = "Lbl_71"
-          /\ /\ pos_find' = curPos
-             /\ stack' = << [ procedure |->  "findMatchingRightParen",
-                              pc        |->  "Lbl_72",
-                              pos_find  |->  pos_find ] >>
-                          \o stack
-          /\ pc' = "Lbl_17"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos, delim, ipos, jdelim, delimLen, leftTok, 
-                          rightTok, currentToken, left, rt, notDone, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_72 == /\ pc = "Lbl_72"
-          /\ curPos' = returnVal
-          /\ IF curPos' < 0
-                THEN /\ notDone' = FALSE
-                     /\ pc' = "Lbl_70"
-                     /\ stack' = stack
-                ELSE /\ stack' = << [ procedure |->  "skipRightOverSpaces",
-                                      pc        |->  "Lbl_73" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_30"
-                     /\ UNCHANGED notDone
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_73 == /\ pc = "Lbl_73"
-          /\ IF line[curPos] # "!"  \/ line[curPos+1] = "!"
-                THEN /\ notDone' = FALSE
-                     /\ pc' = "Lbl_70"
-                     /\ UNCHANGED << curPos, stack >>
-                ELSE /\ curPos' = curPos + 1
-                     /\ stack' = << [ procedure |->  "skipRightOverSpaces",
-                                      pc        |->  "Lbl_74" ] >>
-                                  \o stack
-                     /\ pc' = "Lbl_30"
-                     /\ UNCHANGED notDone
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok, currentToken, left, rt, 
-                          foundBangToken, temp1, temp2, temp3 >>
-
-Lbl_74 == /\ pc = "Lbl_74"
-          /\ /\ pos_f' = curPos
-             /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
-                              pc        |->  "Lbl_75",
-                              token     |->  token,
-                              left_     |->  left_,
-                              rt_       |->  rt_,
-                              pos_f     |->  pos_f ] >>
-                          \o stack
-          /\ token' = << >>
-          /\ left_' = pos_f'
-          /\ rt_' = pos_f'
-          /\ pc' = "Lbl_5"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
-                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_75 == /\ pc = "Lbl_75"
-          /\ currentToken' = returnVal
-          /\ IF \/ currentToken' = NullTokenSpec
-                \/ IsNumber(currentToken'.token)
-                THEN /\ notDone' = FALSE
-                     /\ UNCHANGED << foundTokenSpecs, curPos, foundBangToken >>
-                ELSE /\ foundBangToken' = TRUE
-                     /\ foundTokenSpecs' = JavaConcatenate(
-                                             JavaSeq(
-                                               << [token |->
-                                                     JavaConcatenate(JavaAppend(foundTokenSpecs[0].token,
-                                                                                 "!"),
-                                                                     currentToken'.token),
-                                                   leftPos |-> foundTokenSpecs[0].leftPos,
-                                                   rightPos |-> currentToken'.rightPos]
-                                                >> ) ,
-                                               foundTokenSpecs)
-                     /\ curPos' = currentToken'.rightPos
-                     /\ UNCHANGED notDone
-          /\ pc' = "Lbl_70"
-          /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, 
-                          tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, 
-                          pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, 
-                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
-                          ipos, jdelim, delimLen, leftTok, rightTok, left, rt, 
-                          temp1, temp2, temp3 >>
-
-Lbl_76 == /\ pc = "Lbl_76"
-          /\ returnVal' = FixOrigin(foundTokenSpecs)
-          /\ pc' = Head(stack).pc
-          /\ currentToken' = Head(stack).currentToken
-          /\ left' = Head(stack).left
-          /\ rt' = Head(stack).rt
-          /\ notDone' = Head(stack).notDone
-          /\ foundBangToken' = Head(stack).foundBangToken
-          /\ temp1' = Head(stack).temp1
-          /\ temp2' = Head(stack).temp2
-          /\ temp3' = Head(stack).temp3
-          /\ stack' = Tail(stack)
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
-
-findTokenSpecs == Lbl_31 \/ Lbl_32 \/ Lbl_33 \/ Lbl_34 \/ Lbl_35 \/ Lbl_36
-                     \/ Lbl_37 \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 \/ Lbl_42
-                     \/ Lbl_43 \/ Lbl_44 \/ Lbl_45 \/ Lbl_46 \/ Lbl_47 \/ Lbl_48
-                     \/ Lbl_49 \/ Lbl_50 \/ Lbl_51 \/ Lbl_52 \/ Lbl_53 \/ Lbl_54
-                     \/ Lbl_55 \/ Lbl_56 \/ Lbl_57 \/ Lbl_58 \/ Lbl_59 \/ Lbl_60
-                     \/ Lbl_61 \/ Lbl_62 \/ Lbl_63 \/ Lbl_64 \/ Lbl_65 \/ Lbl_66
-                     \/ Lbl_67 \/ Lbl_68 \/ Lbl_69 \/ Lbl_70 \/ Lbl_71 \/ Lbl_72
-                     \/ Lbl_73 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76
-
-Lbl_77 == /\ pc = "Lbl_77"
-          /\ curPos' = JavaLen(Padding)
-          /\ tempVar1' = (-1 :> "")
-          /\ tempVar2' = JavaLen(Padding)
-          /\ pc' = "Lbl_78"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          returnVal, tempVar3, stack, pos_, tokArray, i, 
-                          tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Lbl_78 == /\ pc = "Lbl_78"
-          /\ IF tempVar2 < JavaLen(lineInput) + JavaLen(Padding)
-                THEN /\ curPos' = tempVar2
-                     /\ foundTokenSpecs' = << >>
-                     /\ lastToken' = FALSE
-                     /\ notLastToken' = FALSE
-                     /\ stack' = << [ procedure |->  "findTokenSpecs",
-                                      pc        |->  "Lbl_79",
-                                      currentToken |->  currentToken,
-                                      left      |->  left,
-                                      rt        |->  rt,
-                                      notDone   |->  notDone,
-                                      foundBangToken |->  foundBangToken,
-                                      temp1     |->  temp1,
-                                      temp2     |->  temp2,
-                                      temp3     |->  temp3 ] >>
-                                  \o stack
-                     /\ currentToken' = defaultInitValue
-                     /\ left' = defaultInitValue
-                     /\ rt' = defaultInitValue
-                     /\ notDone' = defaultInitValue
-                     /\ foundBangToken' = defaultInitValue
-                     /\ temp1' = defaultInitValue
-                     /\ temp2' = defaultInitValue
-                     /\ temp3' = defaultInitValue
-                     /\ pc' = "Lbl_31"
-                ELSE /\ PrintT("goodby world")
-                     /\ pc' = "Done"
-                     /\ UNCHANGED << foundTokenSpecs, lastToken, notLastToken, 
-                                     curPos, stack, currentToken, left, rt, 
-                                     notDone, foundBangToken, temp1, temp2, 
-                                     temp3 >>
-          /\ UNCHANGED << line, returnVal, tempVar1, tempVar2, tempVar3, pos_, 
-                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
-                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
-                          pos_find, pos, delim, ipos, jdelim, delimLen, 
-                          leftTok, rightTok >>
-
-Lbl_79 == /\ pc = "Lbl_79"
-          /\ tempVar1' = returnVal
-          /\ tempVar2' = tempVar2 + 1
-          /\ pc' = "Lbl_78"
-          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
-                          curPos, returnVal, tempVar3, stack, pos_, tokArray, 
-                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
-                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
-                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
-                          currentToken, left, rt, notDone, foundBangToken, 
-                          temp1, temp2, temp3 >>
-
-Next == findTokenIn \/ findMaximalIdCharSeq \/ findMatchingLeftParen
-           \/ findMatchingLeftInner \/ findMatchingRightParen
-           \/ findMatchingRightInner \/ checkIfStepName \/ skipLeftOverSpaces
-           \/ skipRightOverSpaces \/ findTokenSpecs \/ Lbl_77 \/ Lbl_78 \/ Lbl_79
-           \/ (* Disjunct to prevent deadlock on termination *)
-              (pc = "Done" /\ UNCHANGED vars)
-
-Spec == Init /\ [][Next]_vars
-
-Termination == <>(pc = "Done")
-
-\* END TRANSLATION
-=============================================================================
+           temp2, temp3 >>
+
+Init == (* Global variables *)
+        /\ line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding))
+        /\ foundTokenSpecs = << >>
+        /\ lastToken = FALSE
+        /\ notLastToken = FALSE
+        /\ curPos = currentPosition  + JavaLen(Padding)
+        /\ returnVal = defaultInitValue
+        /\ tempVar1 = defaultInitValue
+        /\ tempVar2 = defaultInitValue
+        /\ tempVar3 = defaultInitValue
+        (* Procedure findTokenIn *)
+        /\ pos_ = defaultInitValue
+        /\ tokArray = defaultInitValue
+        /\ i = defaultInitValue
+        /\ tokIdx = defaultInitValue
+        (* Procedure findMaximalIdCharSeq *)
+        /\ pos_f = defaultInitValue
+        /\ token = << >>
+        /\ left_ = pos_f
+        /\ rt_ = pos_f
+        (* Procedure findMatchingLeftParen *)
+        /\ pos_fi = defaultInitValue
+        (* Procedure findMatchingLeftInner *)
+        /\ pos_fin = defaultInitValue
+        /\ delim_ = defaultInitValue
+        /\ ipos_ = pos_fin
+        /\ jdelim_ = defaultInitValue
+        /\ delimLen_ = defaultInitValue
+        (* Procedure findMatchingRightParen *)
+        /\ pos_find = defaultInitValue
+        (* Procedure findMatchingRightInner *)
+        /\ pos = defaultInitValue
+        /\ delim = defaultInitValue
+        /\ ipos = pos
+        /\ jdelim = defaultInitValue
+        /\ delimLen = defaultInitValue
+        (* Procedure checkIfStepName *)
+        /\ leftTok = defaultInitValue
+        /\ rightTok = defaultInitValue
+        (* Procedure findTokenSpecs *)
+        /\ currentToken = defaultInitValue
+        /\ left = defaultInitValue
+        /\ rt = defaultInitValue
+        /\ notDone = defaultInitValue
+        /\ foundBangToken = defaultInitValue
+        /\ temp1 = defaultInitValue
+        /\ temp2 = defaultInitValue
+        /\ temp3 = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "Lbl_77"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ i' = 0
+         /\ pc' = "Lbl_2"
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                         stack, pos_, tokArray, tokIdx, pos_f, token, left_, 
+                         rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                         delimLen, leftTok, rightTok, currentToken, left, rt, 
+                         notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ IF i < JavaLen(tokArray)
+               THEN /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], 0)
+                    /\ pc' = "Lbl_3"
+                    /\ UNCHANGED << returnVal, stack, pos_, tokArray, i >>
+               ELSE /\ returnVal' = NullTokenSpec
+                    /\ pc' = Head(stack).pc
+                    /\ i' = Head(stack).i
+                    /\ tokIdx' = Head(stack).tokIdx
+                    /\ pos_' = Head(stack).pos_
+                    /\ tokArray' = Head(stack).tokArray
+                    /\ stack' = Tail(stack)
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, tempVar1, tempVar2, tempVar3, pos_f, token, 
+                         left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                         delimLen, leftTok, rightTok, currentToken, left, rt, 
+                         notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_3 == /\ pc = "Lbl_3"
+         /\ IF tokIdx # -1
+               THEN /\ LET ftlft == pos_ - tokIdx IN
+                         LET ftrt == pos_ - tokIdx + JavaLen(tokArray[i]) IN
+                           IF tokArray[i] = JavaSubseq(line, ftlft, ftrt)
+                              THEN /\ returnVal' = [token |-> tokArray[i],
+                                                         leftPos |-> ftlft, rightPos |-> ftrt]
+                                   /\ pc' = Head(stack).pc
+                                   /\ i' = Head(stack).i
+                                   /\ tokIdx' = Head(stack).tokIdx
+                                   /\ pos_' = Head(stack).pos_
+                                   /\ tokArray' = Head(stack).tokArray
+                                   /\ stack' = Tail(stack)
+                              ELSE /\ pc' = "Lbl_4"
+                                   /\ UNCHANGED << returnVal, stack, pos_, 
+                                                   tokArray, i, tokIdx >>
+               ELSE /\ i' = i+1
+                    /\ pc' = "Lbl_2"
+                    /\ UNCHANGED << returnVal, stack, pos_, tokArray, tokIdx >>
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, tempVar1, tempVar2, tempVar3, pos_f, token, 
+                         left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                         delimLen, leftTok, rightTok, currentToken, left, rt, 
+                         notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_4 == /\ pc = "Lbl_4"
+         /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], tokIdx+1)
+         /\ pc' = "Lbl_3"
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                         stack, pos_, tokArray, i, pos_f, token, left_, rt_, 
+                         pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                         pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
+                         rightTok, currentToken, left, rt, notDone, 
+                         foundBangToken, temp1, temp2, temp3 >>
+
+findTokenIn == Lbl_1 \/ Lbl_2 \/ Lbl_3 \/ Lbl_4
+
+Lbl_5 == /\ pc = "Lbl_5"
+         /\ IF line[left_-1] \in IdChar
+               THEN /\ left_' = left_-1
+                    /\ pc' = "Lbl_5"
+               ELSE /\ pc' = "Lbl_6"
+                    /\ left_' = left_
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                         stack, pos_, tokArray, i, tokIdx, pos_f, token, rt_, 
+                         pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                         pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
+                         rightTok, currentToken, left, rt, notDone, 
+                         foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_6 == /\ pc = "Lbl_6"
+         /\ IF line[rt_] \in IdChar
+               THEN /\ rt_' = rt_+1
+                    /\ pc' = "Lbl_6"
+                    /\ UNCHANGED << returnVal, stack, pos_f, token, left_ >>
+               ELSE /\ IF left_ = rt_
+                          THEN /\ returnVal' = NullTokenSpec
+                               /\ pc' = Head(stack).pc
+                               /\ token' = Head(stack).token
+                               /\ left_' = Head(stack).left_
+                               /\ rt_' = Head(stack).rt_
+                               /\ pos_f' = Head(stack).pos_f
+                               /\ stack' = Tail(stack)
+                          ELSE /\ returnVal' = [token |-> JavaSubseq(line, left_, rt_),
+                                                leftPos |-> left_, rightPos |-> rt_]
+                               /\ pc' = Head(stack).pc
+                               /\ token' = Head(stack).token
+                               /\ left_' = Head(stack).left_
+                               /\ rt_' = Head(stack).rt_
+                               /\ pos_f' = Head(stack).pos_f
+                               /\ stack' = Tail(stack)
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                         i, tokIdx, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                         delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                         delimLen, leftTok, rightTok, currentToken, left, rt, 
+                         notDone, foundBangToken, temp1, temp2, temp3 >>
+
+findMaximalIdCharSeq == Lbl_5 \/ Lbl_6
+
+Lbl_7 == /\ pc = "Lbl_7"
+         /\ IF line[pos_fi-1] # ")"
+               THEN /\ returnVal' = pos_fi
+                    /\ pc' = Head(stack).pc
+                    /\ pos_fi' = Head(stack).pos_fi
+                    /\ stack' = Tail(stack)
+               ELSE /\ pc' = "Lbl_8"
+                    /\ UNCHANGED << returnVal, stack, pos_fi >>
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                         i, tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, 
+                         ipos_, jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                         jdelim, delimLen, leftTok, rightTok, currentToken, 
+                         left, rt, notDone, foundBangToken, temp1, temp2, 
+                         temp3 >>
+
+Lbl_8 == /\ pc = "Lbl_8"
+         /\ /\ delim_' = 0
+            /\ pos_fin' = pos_fi
+            /\ stack' = << [ procedure |->  "findMatchingLeftInner",
+                             pc        |->  Head(stack).pc,
+                             ipos_     |->  ipos_,
+                             jdelim_   |->  jdelim_,
+                             delimLen_ |->  delimLen_,
+                             pos_fin   |->  pos_fin,
+                             delim_    |->  delim_ ] >>
+                         \o Tail(stack)
+         /\ ipos_' = pos_fin'
+         /\ jdelim_' = defaultInitValue
+         /\ delimLen_' = defaultInitValue
+         /\ pc' = "Lbl_9"
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                         tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, 
+                         pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, 
+                         rightTok, currentToken, left, rt, notDone, 
+                         foundBangToken, temp1, temp2, temp3 >>
+
+findMatchingLeftParen == Lbl_7 \/ Lbl_8
+
+Lbl_9 == /\ pc = "Lbl_9"
+         /\ delimLen_' = JavaLen(LeftDelimiters[delim_])
+         /\ ipos_' = pos_fin - delimLen_'
+         /\ pc' = "Lbl_10"
+         /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                         curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                         stack, pos_, tokArray, i, tokIdx, pos_f, token, left_, 
+                         rt_, pos_fi, pos_fin, delim_, jdelim_, pos_find, pos, 
+                         delim, ipos, jdelim, delimLen, leftTok, rightTok, 
+                         currentToken, left, rt, notDone, foundBangToken, 
+                         temp1, temp2, temp3 >>
+
+Lbl_10 == /\ pc = "Lbl_10"
+          /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) # LeftDelimiters[delim_]
+                THEN /\ IF line[ipos_-1] = ";"
+                           THEN /\ returnVal' = -1
+                                /\ pc' = Head(stack).pc
+                                /\ ipos_' = Head(stack).ipos_
+                                /\ jdelim_' = Head(stack).jdelim_
+                                /\ delimLen_' = Head(stack).delimLen_
+                                /\ pos_fin' = Head(stack).pos_fin
+                                /\ delim_' = Head(stack).delim_
+                                /\ stack' = Tail(stack)
+                           ELSE /\ pc' = "Lbl_11"
+                                /\ UNCHANGED << returnVal, stack, pos_fin, 
+                                                delim_, ipos_, jdelim_, 
+                                                delimLen_ >>
+                ELSE /\ returnVal' = ipos_-delimLen_
+                     /\ pc' = Head(stack).pc
+                     /\ ipos_' = Head(stack).ipos_
+                     /\ jdelim_' = Head(stack).jdelim_
+                     /\ delimLen_' = Head(stack).delimLen_
+                     /\ pos_fin' = Head(stack).pos_fin
+                     /\ delim_' = Head(stack).delim_
+                     /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_11 == /\ pc = "Lbl_11"
+          /\ ipos_' = ipos_-1
+          /\ jdelim_' = 0
+          /\ pc' = "Lbl_12"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_12 == /\ pc = "Lbl_12"
+          /\ IF jdelim_ < JavaLen(LeftDelimiters)
+                THEN /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) = RightDelimiters[jdelim_]
+                           THEN /\ /\ delim_' = jdelim_
+                                   /\ pos_fin' = ipos_
+                                   /\ stack' = << [ procedure |->  "findMatchingLeftInner",
+                                                    pc        |->  "Lbl_13",
+                                                    ipos_     |->  ipos_,
+                                                    jdelim_   |->  jdelim_,
+                                                    delimLen_ |->  delimLen_,
+                                                    pos_fin   |->  pos_fin,
+                                                    delim_    |->  delim_ ] >>
+                                                \o stack
+                                /\ ipos_' = pos_fin'
+                                /\ jdelim_' = defaultInitValue
+                                /\ delimLen_' = defaultInitValue
+                                /\ pc' = "Lbl_9"
+                           ELSE /\ pc' = "Lbl_16"
+                                /\ UNCHANGED << stack, pos_fin, delim_, ipos_, 
+                                                jdelim_, delimLen_ >>
+                ELSE /\ pc' = "Lbl_10"
+                     /\ UNCHANGED << stack, pos_fin, delim_, ipos_, jdelim_, 
+                                     delimLen_ >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_16 == /\ pc = "Lbl_16"
+          /\ jdelim_' = jdelim_+1
+          /\ pc' = "Lbl_12"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                          delimLen, leftTok, rightTok, currentToken, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_13 == /\ pc = "Lbl_13"
+          /\ ipos_' = returnVal
+          /\ IF ipos_' < 0
+                THEN /\ returnVal' = -1
+                     /\ pc' = "Lbl_14"
+                ELSE /\ pc' = "Lbl_15"
+                     /\ UNCHANGED returnVal
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, stack, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_14 == /\ pc = "Lbl_14"
+          /\ pc' = Head(stack).pc
+          /\ ipos_' = Head(stack).ipos_
+          /\ jdelim_' = Head(stack).jdelim_
+          /\ delimLen_' = Head(stack).delimLen_
+          /\ pos_fin' = Head(stack).pos_fin
+          /\ delim_' = Head(stack).delim_
+          /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_15 == /\ pc = "Lbl_15"
+          /\ jdelim_' = 99
+          /\ pc' = "Lbl_16"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                          delimLen, leftTok, rightTok, currentToken, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+findMatchingLeftInner == Lbl_9 \/ Lbl_10 \/ Lbl_11 \/ Lbl_12 \/ Lbl_16
+                            \/ Lbl_13 \/ Lbl_14 \/ Lbl_15
+
+Lbl_17 == /\ pc = "Lbl_17"
+          /\ IF line[pos_find] # "("
+                THEN /\ returnVal' = pos_find
+                     /\ pc' = Head(stack).pc
+                     /\ pos_find' = Head(stack).pos_find
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_18"
+                     /\ UNCHANGED << returnVal, stack, pos_find >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok, currentToken, 
+                          left, rt, notDone, foundBangToken, temp1, temp2, 
+                          temp3 >>
+
+Lbl_18 == /\ pc = "Lbl_18"
+          /\ /\ delim' = 0
+             /\ pos' = pos_find
+             /\ stack' = << [ procedure |->  "findMatchingRightInner",
+                              pc        |->  Head(stack).pc,
+                              ipos      |->  ipos,
+                              jdelim    |->  jdelim,
+                              delimLen  |->  delimLen,
+                              pos       |->  pos,
+                              delim     |->  delim ] >>
+                          \o Tail(stack)
+          /\ ipos' = pos'
+          /\ jdelim' = defaultInitValue
+          /\ delimLen' = defaultInitValue
+          /\ pc' = "Lbl_19"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, leftTok, rightTok, currentToken, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+findMatchingRightParen == Lbl_17 \/ Lbl_18
+
+Lbl_19 == /\ pc = "Lbl_19"
+          /\ delimLen' = JavaLen(RightDelimiters[delim])
+          /\ ipos' = pos + delimLen'
+          /\ pc' = "Lbl_20"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                          delimLen_, pos_find, pos, delim, jdelim, leftTok, 
+                          rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_20 == /\ pc = "Lbl_20"
+          /\ IF JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim]
+                THEN /\ IF line[ipos] = ";"
+                           THEN /\ returnVal' = -1
+                                /\ pc' = Head(stack).pc
+                                /\ ipos' = Head(stack).ipos
+                                /\ jdelim' = Head(stack).jdelim
+                                /\ delimLen' = Head(stack).delimLen
+                                /\ pos' = Head(stack).pos
+                                /\ delim' = Head(stack).delim
+                                /\ stack' = Tail(stack)
+                           ELSE /\ pc' = "Lbl_21"
+                                /\ UNCHANGED << returnVal, stack, pos, delim, 
+                                                ipos, jdelim, delimLen >>
+                ELSE /\ returnVal' = ipos+delimLen
+                     /\ pc' = Head(stack).pc
+                     /\ ipos' = Head(stack).ipos
+                     /\ jdelim' = Head(stack).jdelim
+                     /\ delimLen' = Head(stack).delimLen
+                     /\ pos' = Head(stack).pos
+                     /\ delim' = Head(stack).delim
+                     /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, leftTok, 
+                          rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_21 == /\ pc = "Lbl_21"
+          /\ ipos' = ipos+1
+          /\ jdelim' = 0
+          /\ pc' = "Lbl_22"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                          delimLen_, pos_find, pos, delim, delimLen, leftTok, 
+                          rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_22 == /\ pc = "Lbl_22"
+          /\ IF jdelim < JavaLen(RightDelimiters)
+                THEN /\ IF JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim]
+                           THEN /\ /\ delim' = jdelim
+                                   /\ pos' = ipos
+                                   /\ stack' = << [ procedure |->  "findMatchingRightInner",
+                                                    pc        |->  "Lbl_23",
+                                                    ipos      |->  ipos,
+                                                    jdelim    |->  jdelim,
+                                                    delimLen  |->  delimLen,
+                                                    pos       |->  pos,
+                                                    delim     |->  delim ] >>
+                                                \o stack
+                                /\ ipos' = pos'
+                                /\ jdelim' = defaultInitValue
+                                /\ delimLen' = defaultInitValue
+                                /\ pc' = "Lbl_19"
+                           ELSE /\ pc' = "Lbl_26"
+                                /\ UNCHANGED << stack, pos, delim, ipos, 
+                                                jdelim, delimLen >>
+                ELSE /\ pc' = "Lbl_20"
+                     /\ UNCHANGED << stack, pos, delim, ipos, jdelim, delimLen >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, leftTok, rightTok, currentToken, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_26 == /\ pc = "Lbl_26"
+          /\ jdelim' = jdelim+1
+          /\ pc' = "Lbl_22"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                          delimLen_, pos_find, pos, delim, ipos, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_23 == /\ pc = "Lbl_23"
+          /\ ipos' = returnVal
+          /\ IF ipos' < 0
+                THEN /\ returnVal' = -1
+                     /\ pc' = "Lbl_24"
+                ELSE /\ pc' = "Lbl_25"
+                     /\ UNCHANGED returnVal
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, stack, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, jdelim, delimLen, leftTok, 
+                          rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_24 == /\ pc = "Lbl_24"
+          /\ pc' = Head(stack).pc
+          /\ ipos' = Head(stack).ipos
+          /\ jdelim' = Head(stack).jdelim
+          /\ delimLen' = Head(stack).delimLen
+          /\ pos' = Head(stack).pos
+          /\ delim' = Head(stack).delim
+          /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, leftTok, rightTok, currentToken, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_25 == /\ pc = "Lbl_25"
+          /\ jdelim' = 99
+          /\ pc' = "Lbl_26"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                          delimLen_, pos_find, pos, delim, ipos, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+findMatchingRightInner == Lbl_19 \/ Lbl_20 \/ Lbl_21 \/ Lbl_22 \/ Lbl_26
+                             \/ Lbl_23 \/ Lbl_24 \/ Lbl_25
+
+Lbl_27 == /\ pc = "Lbl_27"
+          /\ IF leftTok = NullTokenSpec \/ rightTok = NullTokenSpec
+                THEN /\ returnVal' = NullTokenSpec
+                     /\ pc' = Head(stack).pc
+                     /\ leftTok' = Head(stack).leftTok
+                     /\ rightTok' = Head(stack).rightTok
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_28"
+                     /\ UNCHANGED << returnVal, stack, leftTok, rightTok >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, currentToken, left, 
+                          rt, notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_28 == /\ pc = "Lbl_28"
+          /\ IF /\ IsNumber(leftTok.token)
+                /\ line[leftTok.leftPos-1] = "<"
+                /\ line[leftTok.leftPos-2] # "<"
+                THEN /\ returnVal' = [token |-> JavaSubseq(line,
+                                                           leftTok.leftPos-1,
+                                                           rightTok.rightPos),
+                                      leftPos |-> leftTok.leftPos-1,
+                                      rightPos |-> rightTok.rightPos]
+                     /\ pc' = Head(stack).pc
+                     /\ leftTok' = Head(stack).leftTok
+                     /\ rightTok' = Head(stack).rightTok
+                     /\ stack' = Tail(stack)
+                ELSE /\ returnVal' = NullTokenSpec
+                     /\ pc' = Head(stack).pc
+                     /\ leftTok' = Head(stack).leftTok
+                     /\ rightTok' = Head(stack).rightTok
+                     /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, currentToken, left, 
+                          rt, notDone, foundBangToken, temp1, temp2, temp3 >>
+
+checkIfStepName == Lbl_27 \/ Lbl_28
+
+Lbl_29 == /\ pc = "Lbl_29"
+          /\ IF line[curPos-1] = " "
+                THEN /\ curPos' = curPos - 1
+                     /\ pc' = "Lbl_29"
+                     /\ stack' = stack
+                ELSE /\ pc' = Head(stack).pc
+                     /\ stack' = Tail(stack)
+                     /\ UNCHANGED curPos
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+skipLeftOverSpaces == Lbl_29
+
+Lbl_30 == /\ pc = "Lbl_30"
+          /\ IF line[curPos] = " "
+                THEN /\ curPos' = curPos+1
+                     /\ pc' = "Lbl_30"
+                     /\ stack' = stack
+                ELSE /\ pc' = Head(stack).pc
+                     /\ stack' = Tail(stack)
+                     /\ UNCHANGED curPos
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+skipRightOverSpaces == Lbl_30
+
+Lbl_31 == /\ pc = "Lbl_31"
+          /\ IF line[curPos] = " "
+                THEN /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                                      pc        |->  "Lbl_32" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_29"
+                     /\ UNCHANGED notLastToken
+                ELSE /\ IF line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!"
+                           THEN /\ notLastToken' = TRUE
+                                /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                                                 pc        |->  "Lbl_33" ] >>
+                                             \o stack
+                                /\ pc' = "Lbl_29"
+                           ELSE /\ pc' = "Lbl_33"
+                                /\ UNCHANGED << notLastToken, stack >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, 
+                          tempVar1, tempVar2, tempVar3, pos_, tokArray, i, 
+                          tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_32 == /\ pc = "Lbl_32"
+          /\ IF line[curPos-1] = "!"  /\ line[curPos-2] # "!"
+                THEN /\ notLastToken' = TRUE
+                     /\ curPos' = curPos - 1
+                     /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                                      pc        |->  "Lbl_33" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_29"
+                ELSE /\ pc' = "Lbl_33"
+                     /\ UNCHANGED << notLastToken, curPos, stack >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, returnVal, 
+                          tempVar1, tempVar2, tempVar3, pos_, tokArray, i, 
+                          tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_33 == /\ pc = "Lbl_33"
+          /\ IF /\ \/ notLastToken
+                   \/ line[curPos] = " "
+                /\ line[curPos-1] = ")"
+                THEN /\ notLastToken' = TRUE
+                     /\ /\ pos_fi' = curPos
+                        /\ stack' = << [ procedure |->  "findMatchingLeftParen",
+                                         pc        |->  "Lbl_34",
+                                         pos_fi    |->  pos_fi ] >>
+                                     \o stack
+                     /\ pc' = "Lbl_7"
+                ELSE /\ pc' = "Lbl_36"
+                     /\ UNCHANGED << notLastToken, stack, pos_fi >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, 
+                          tempVar1, tempVar2, tempVar3, pos_, tokArray, i, 
+                          tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_34 == /\ pc = "Lbl_34"
+          /\ IF returnVal < 0
+                THEN /\ returnVal' = << >>
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_35"
+                     /\ UNCHANGED << returnVal, stack, currentToken, left, rt, 
+                                     notDone, foundBangToken, temp1, temp2, 
+                                     temp3 >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_35 == /\ pc = "Lbl_35"
+          /\ curPos' = returnVal
+          /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                           pc        |->  "Lbl_36" ] >>
+                       \o stack
+          /\ pc' = "Lbl_29"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_36 == /\ pc = "Lbl_36"
+          /\ IF notLastToken
+                THEN /\ curPos' = curPos - 1
+                     /\ pc' = "Lbl_38"
+                     /\ UNCHANGED << stack, pos_, tokArray, i, tokIdx >>
+                ELSE /\ IF line[curPos] \notin IdChar
+                           THEN /\ /\ pos_' = curPos
+                                   /\ stack' = << [ procedure |->  "findTokenIn",
+                                                    pc        |->  "Lbl_37",
+                                                    i         |->  i,
+                                                    tokIdx    |->  tokIdx,
+                                                    pos_      |->  pos_,
+                                                    tokArray  |->  tokArray ] >>
+                                                \o stack
+                                   /\ tokArray' = Operators
+                                /\ i' = defaultInitValue
+                                /\ tokIdx' = defaultInitValue
+                                /\ pc' = "Lbl_1"
+                           ELSE /\ pc' = "Lbl_38"
+                                /\ UNCHANGED << stack, pos_, tokArray, i, 
+                                                tokIdx >>
+                     /\ UNCHANGED curPos
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok, currentToken, 
+                          left, rt, notDone, foundBangToken, temp1, temp2, 
+                          temp3 >>
+
+Lbl_37 == /\ pc = "Lbl_37"
+          /\ IF returnVal = NullTokenSpec
+                THEN /\ curPos' = curPos - 1
+                ELSE /\ TRUE
+                     /\ UNCHANGED curPos
+          /\ pc' = "Lbl_38"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_38 == /\ pc = "Lbl_38"
+          /\ IF line[curPos] \in IdChar
+                THEN /\ IF line[curPos] = "X"
+                           THEN /\ /\ pos_' = curPos
+                                   /\ stack' = << [ procedure |->  "findTokenIn",
+                                                    pc        |->  "Lbl_39",
+                                                    i         |->  i,
+                                                    tokIdx    |->  tokIdx,
+                                                    pos_      |->  pos_,
+                                                    tokArray  |->  tokArray ] >>
+                                                \o stack
+                                   /\ tokArray' = XSymbols
+                                /\ i' = defaultInitValue
+                                /\ tokIdx' = defaultInitValue
+                                /\ pc' = "Lbl_1"
+                                /\ UNCHANGED returnVal
+                           ELSE /\ returnVal' = NullTokenSpec
+                                /\ pc' = "Lbl_39"
+                                /\ UNCHANGED << stack, pos_, tokArray, i, 
+                                                tokIdx >>
+                ELSE /\ /\ pos_' = curPos
+                        /\ stack' = << [ procedure |->  "findTokenIn",
+                                         pc        |->  "Lbl_48",
+                                         i         |->  i,
+                                         tokIdx    |->  tokIdx,
+                                         pos_      |->  pos_,
+                                         tokArray  |->  tokArray ] >>
+                                     \o stack
+                        /\ tokArray' = Operators
+                     /\ i' = defaultInitValue
+                     /\ tokIdx' = defaultInitValue
+                     /\ pc' = "Lbl_1"
+                     /\ UNCHANGED returnVal
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                          delimLen, leftTok, rightTok, currentToken, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_39 == /\ pc = "Lbl_39"
+          /\ IF returnVal # NullTokenSpec
+                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
+                     /\ curPos' = returnVal.leftPos
+                     /\ lastToken' = TRUE
+                     /\ pc' = "Lbl_62"
+                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
+                ELSE /\ /\ pos_f' = curPos
+                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                         pc        |->  "Lbl_40",
+                                         token     |->  token,
+                                         left_     |->  left_,
+                                         rt_       |->  rt_,
+                                         pos_f     |->  pos_f ] >>
+                                     \o stack
+                     /\ token' = << >>
+                     /\ left_' = pos_f'
+                     /\ rt_' = pos_f'
+                     /\ pc' = "Lbl_5"
+                     /\ UNCHANGED << foundTokenSpecs, lastToken, curPos >>
+          /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, 
+                          tempVar3, pos_, tokArray, i, tokIdx, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_40 == /\ pc = "Lbl_40"
+          /\ currentToken' = returnVal
+          /\ IF line[currentToken'.leftPos-1] = ">"
+                THEN /\ /\ pos_f' = currentToken'.leftPos - 1
+                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                         pc        |->  "Lbl_41",
+                                         token     |->  token,
+                                         left_     |->  left_,
+                                         rt_       |->  rt_,
+                                         pos_f     |->  pos_f ] >>
+                                     \o stack
+                     /\ token' = << >>
+                     /\ left_' = pos_f'
+                     /\ rt_' = pos_f'
+                     /\ pc' = "Lbl_5"
+                ELSE /\ pc' = "Lbl_43"
+                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, left, rt, 
+                          notDone, foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_41 == /\ pc = "Lbl_41"
+          /\ /\ leftTok' = returnVal
+             /\ rightTok' = currentToken
+             /\ stack' = << [ procedure |->  "checkIfStepName",
+                              pc        |->  "Lbl_42",
+                              leftTok   |->  leftTok,
+                              rightTok  |->  rightTok ] >>
+                          \o stack
+          /\ pc' = "Lbl_27"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_42 == /\ pc = "Lbl_42"
+          /\ IF returnVal # NullTokenSpec
+                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
+                     /\ returnVal' = FixOrigin(foundTokenSpecs')
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_43"
+                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
+                                     currentToken, left, rt, notDone, 
+                                     foundBangToken, temp1, temp2, temp3 >>
+          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
+                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_43 == /\ pc = "Lbl_43"
+          /\ IF line[currentToken.rightPos] = ">"
+                THEN /\ /\ pos_f' = currentToken.rightPos+1
+                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                         pc        |->  "Lbl_44",
+                                         token     |->  token,
+                                         left_     |->  left_,
+                                         rt_       |->  rt_,
+                                         pos_f     |->  pos_f ] >>
+                                     \o stack
+                     /\ token' = << >>
+                     /\ left_' = pos_f'
+                     /\ rt_' = pos_f'
+                     /\ pc' = "Lbl_5"
+                ELSE /\ pc' = "Lbl_46"
+                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_44 == /\ pc = "Lbl_44"
+          /\ /\ leftTok' = currentToken
+             /\ rightTok' = returnVal
+             /\ stack' = << [ procedure |->  "checkIfStepName",
+                              pc        |->  "Lbl_45",
+                              leftTok   |->  leftTok,
+                              rightTok  |->  rightTok ] >>
+                          \o stack
+          /\ pc' = "Lbl_27"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_45 == /\ pc = "Lbl_45"
+          /\ IF returnVal # NullTokenSpec
+                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
+                     /\ returnVal' = FixOrigin(foundTokenSpecs')
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_46"
+                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
+                                     currentToken, left, rt, notDone, 
+                                     foundBangToken, temp1, temp2, temp3 >>
+          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
+                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_46 == /\ pc = "Lbl_46"
+          /\ IF IsNumber(currentToken.token)
+                THEN /\ returnVal' = << >>
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_47"
+                     /\ UNCHANGED << returnVal, stack, currentToken, left, rt, 
+                                     notDone, foundBangToken, temp1, temp2, 
+                                     temp3 >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_47 == /\ pc = "Lbl_47"
+          /\ left' = currentToken.leftPos
+          /\ rt' = currentToken.rightPos
+          /\ IF /\ line[left'-1] = "\\"
+                /\ line[left'-2] # "\\"
+                /\ \E ii \in DOMAIN TeXSymbols :
+                         JavaSubseq(line, left'-1, rt') = TeXSymbols[ii]
+                THEN /\ lastToken' = TRUE
+                     /\ currentToken' = [currentToken EXCEPT !.leftPos = left' - 1,
+                                                             !.token = JavaSubseq(line, left'-1, rt')]
+                ELSE /\ TRUE
+                     /\ UNCHANGED << lastToken, currentToken >>
+          /\ foundTokenSpecs' = JavaSeq( << currentToken' >>)
+          /\ curPos' = left'
+          /\ pc' = "Lbl_62"
+          /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, 
+                          tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_48 == /\ pc = "Lbl_48"
+          /\ currentToken' = returnVal
+          /\ IF currentToken' = NullTokenSpec
+                THEN /\ returnVal' = << >>
+                     /\ pc' = "Lbl_49"
+                ELSE /\ pc' = "Lbl_50"
+                     /\ UNCHANGED returnVal
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, stack, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_49 == /\ pc = "Lbl_49"
+          /\ pc' = Head(stack).pc
+          /\ currentToken' = Head(stack).currentToken
+          /\ left' = Head(stack).left
+          /\ rt' = Head(stack).rt
+          /\ notDone' = Head(stack).notDone
+          /\ foundBangToken' = Head(stack).foundBangToken
+          /\ temp1' = Head(stack).temp1
+          /\ temp2' = Head(stack).temp2
+          /\ temp3' = Head(stack).temp3
+          /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok >>
+
+Lbl_50 == /\ pc = "Lbl_50"
+          /\ /\ pos_' = curPos
+             /\ stack' = << [ procedure |->  "findTokenIn",
+                              pc        |->  "Lbl_51",
+                              i         |->  i,
+                              tokIdx    |->  tokIdx,
+                              pos_      |->  pos_,
+                              tokArray  |->  tokArray ] >>
+                          \o stack
+             /\ tokArray' = NonOperators
+          /\ i' = defaultInitValue
+          /\ tokIdx' = defaultInitValue
+          /\ pc' = "Lbl_1"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_51 == /\ pc = "Lbl_51"
+          /\ IF returnVal # NullTokenSpec
+                THEN /\ returnVal' = << >>
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_52"
+                     /\ UNCHANGED << returnVal, stack, currentToken, left, rt, 
+                                     notDone, foundBangToken, temp1, temp2, 
+                                     temp3 >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_52 == /\ pc = "Lbl_52"
+          /\ IF currentToken.token = JavaSeq(<< "<" >>)
+                THEN /\ /\ pos_f' = currentToken.rightPos
+                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                         pc        |->  "Lbl_53",
+                                         token     |->  token,
+                                         left_     |->  left_,
+                                         rt_       |->  rt_,
+                                         pos_f     |->  pos_f ] >>
+                                     \o stack
+                     /\ token' = << >>
+                     /\ left_' = pos_f'
+                     /\ rt_' = pos_f'
+                     /\ pc' = "Lbl_5"
+                ELSE /\ IF currentToken.token = JavaSeq(<< ">" >>)
+                           THEN /\ /\ pos_f' = currentToken.leftPos
+                                   /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                                    pc        |->  "Lbl_56",
+                                                    token     |->  token,
+                                                    left_     |->  left_,
+                                                    rt_       |->  rt_,
+                                                    pos_f     |->  pos_f ] >>
+                                                \o stack
+                                /\ token' = << >>
+                                /\ left_' = pos_f'
+                                /\ rt_' = pos_f'
+                                /\ pc' = "Lbl_5"
+                           ELSE /\ pc' = "Lbl_59"
+                                /\ UNCHANGED << stack, pos_f, token, left_, 
+                                                rt_ >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_53 == /\ pc = "Lbl_53"
+          /\ temp1' = returnVal
+          /\ IF /\ temp1' # NullTokenSpec
+                /\ line[temp1'.rightPos] = ">"
+                THEN /\ /\ pos_f' = temp1'.rightPos + 1
+                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                         pc        |->  "Lbl_54",
+                                         token     |->  token,
+                                         left_     |->  left_,
+                                         rt_       |->  rt_,
+                                         pos_f     |->  pos_f ] >>
+                                     \o stack
+                     /\ token' = << >>
+                     /\ left_' = pos_f'
+                     /\ rt_' = pos_f'
+                     /\ pc' = "Lbl_5"
+                ELSE /\ pc' = "Lbl_59"
+                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp2, temp3 >>
+
+Lbl_54 == /\ pc = "Lbl_54"
+          /\ temp2' = returnVal
+          /\ /\ leftTok' = temp1
+             /\ rightTok' = temp2'
+             /\ stack' = << [ procedure |->  "checkIfStepName",
+                              pc        |->  "Lbl_55",
+                              leftTok   |->  leftTok,
+                              rightTok  |->  rightTok ] >>
+                          \o stack
+          /\ pc' = "Lbl_27"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp3 >>
+
+Lbl_55 == /\ pc = "Lbl_55"
+          /\ IF returnVal # NullTokenSpec
+                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
+                     /\ returnVal' = FixOrigin(foundTokenSpecs')
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_59"
+                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
+                                     currentToken, left, rt, notDone, 
+                                     foundBangToken, temp1, temp2, temp3 >>
+          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
+                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_56 == /\ pc = "Lbl_56"
+          /\ temp1' = returnVal
+          /\ /\ pos_f' = currentToken.rightPos
+             /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                              pc        |->  "Lbl_57",
+                              token     |->  token,
+                              left_     |->  left_,
+                              rt_       |->  rt_,
+                              pos_f     |->  pos_f ] >>
+                          \o stack
+          /\ token' = << >>
+          /\ left_' = pos_f'
+          /\ rt_' = pos_f'
+          /\ pc' = "Lbl_5"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp2, temp3 >>
+
+Lbl_57 == /\ pc = "Lbl_57"
+          /\ temp2' = returnVal
+          /\ /\ leftTok' = temp1
+             /\ rightTok' = temp2'
+             /\ stack' = << [ procedure |->  "checkIfStepName",
+                              pc        |->  "Lbl_58",
+                              leftTok   |->  leftTok,
+                              rightTok  |->  rightTok ] >>
+                          \o stack
+          /\ pc' = "Lbl_27"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp3 >>
+
+Lbl_58 == /\ pc = "Lbl_58"
+          /\ IF returnVal # NullTokenSpec
+                THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>)
+                     /\ returnVal' = FixOrigin(foundTokenSpecs')
+                     /\ pc' = Head(stack).pc
+                     /\ currentToken' = Head(stack).currentToken
+                     /\ left' = Head(stack).left
+                     /\ rt' = Head(stack).rt
+                     /\ notDone' = Head(stack).notDone
+                     /\ foundBangToken' = Head(stack).foundBangToken
+                     /\ temp1' = Head(stack).temp1
+                     /\ temp2' = Head(stack).temp2
+                     /\ temp3' = Head(stack).temp3
+                     /\ stack' = Tail(stack)
+                ELSE /\ pc' = "Lbl_59"
+                     /\ UNCHANGED << foundTokenSpecs, returnVal, stack, 
+                                     currentToken, left, rt, notDone, 
+                                     foundBangToken, temp1, temp2, temp3 >>
+          /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, 
+                          tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_59 == /\ pc = "Lbl_59"
+          /\ IF currentToken.token = JavaSeq(<<"\\">>)
+                THEN /\ /\ pos_f' = currentToken.rightPos
+                        /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                                         pc        |->  "Lbl_60",
+                                         token     |->  token,
+                                         left_     |->  left_,
+                                         rt_       |->  rt_,
+                                         pos_f     |->  pos_f ] >>
+                                     \o stack
+                     /\ token' = << >>
+                     /\ left_' = pos_f'
+                     /\ rt_' = pos_f'
+                     /\ pc' = "Lbl_5"
+                ELSE /\ pc' = "Lbl_61"
+                     /\ UNCHANGED << stack, pos_f, token, left_, rt_ >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_60 == /\ pc = "Lbl_60"
+          /\ IF /\ returnVal # NullTokenSpec
+                /\ \E ii \in DOMAIN TeXSymbols :
+                          JavaSubseq(line,
+                                     currentToken.leftPos,
+                                     returnVal.rightPos) = TeXSymbols[ii]
+                THEN /\ currentToken' = [currentToken EXCEPT !.rightPos = returnVal.rightPos,
+                                                             !.token = JavaSubseq(line,
+                                                                       currentToken.leftPos,
+                                                                       returnVal.rightPos)]
+                ELSE /\ TRUE
+                     /\ UNCHANGED currentToken
+          /\ pc' = "Lbl_61"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          stack, pos_, tokArray, i, tokIdx, pos_f, token, 
+                          left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, 
+                          delimLen_, pos_find, pos, delim, ipos, jdelim, 
+                          delimLen, leftTok, rightTok, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_61 == /\ pc = "Lbl_61"
+          /\ foundTokenSpecs' = JavaSeq(<<currentToken>>)
+          /\ curPos' = currentToken.leftPos
+          /\ lastToken' = TRUE
+          /\ pc' = "Lbl_62"
+          /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, 
+                          tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, 
+                          token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, 
+                          jdelim_, delimLen_, pos_find, pos, delim, ipos, 
+                          jdelim, delimLen, leftTok, rightTok, currentToken, 
+                          left, rt, notDone, foundBangToken, temp1, temp2, 
+                          temp3 >>
+
+Lbl_62 == /\ pc = "Lbl_62"
+          /\ Assert(JavaLen(foundTokenSpecs) = 1, 
+                    "Failure of assertion at line 619, column 3.")
+          /\ curPos' = foundTokenSpecs[0].leftPos
+          /\ notDone' = TRUE
+          /\ pc' = "Lbl_63"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_63 == /\ pc = "Lbl_63"
+          /\ IF notDone
+                THEN /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                                      pc        |->  "Lbl_64" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_29"
+                     /\ UNCHANGED << returnVal, currentToken, left, rt, 
+                                     notDone, foundBangToken, temp1, temp2, 
+                                     temp3 >>
+                ELSE /\ Assert(foundTokenSpecs # << >>, 
+                               "Failure of assertion at line 661, column 3.")
+                     /\ IF lastToken = TRUE
+                           THEN /\ returnVal' = FixOrigin(foundTokenSpecs)
+                                /\ pc' = Head(stack).pc
+                                /\ currentToken' = Head(stack).currentToken
+                                /\ left' = Head(stack).left
+                                /\ rt' = Head(stack).rt
+                                /\ notDone' = Head(stack).notDone
+                                /\ foundBangToken' = Head(stack).foundBangToken
+                                /\ temp1' = Head(stack).temp1
+                                /\ temp2' = Head(stack).temp2
+                                /\ temp3' = Head(stack).temp3
+                                /\ stack' = Tail(stack)
+                           ELSE /\ pc' = "Lbl_69"
+                                /\ UNCHANGED << returnVal, stack, currentToken, 
+                                                left, rt, notDone, 
+                                                foundBangToken, temp1, temp2, 
+                                                temp3 >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_64 == /\ pc = "Lbl_64"
+          /\ IF curPos <  0 \/ line[curPos-1] = ";"
+                THEN /\ notDone' = FALSE
+                     /\ pc' = "Lbl_63"
+                     /\ UNCHANGED << curPos, stack >>
+                ELSE /\ IF line[curPos-1] = "!" /\ (line[curPos-2] # "!")
+                           THEN /\ curPos' = curPos - 1
+                                /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                                                 pc        |->  "Lbl_65" ] >>
+                                             \o stack
+                                /\ pc' = "Lbl_29"
+                                /\ UNCHANGED notDone
+                           ELSE /\ notDone' = FALSE
+                                /\ pc' = "Lbl_63"
+                                /\ UNCHANGED << curPos, stack >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_65 == /\ pc = "Lbl_65"
+          /\ /\ pos_fi' = curPos
+             /\ stack' = << [ procedure |->  "findMatchingLeftParen",
+                              pc        |->  "Lbl_66",
+                              pos_fi    |->  pos_fi ] >>
+                          \o stack
+          /\ pc' = "Lbl_7"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, 
+                          pos, delim, ipos, jdelim, delimLen, leftTok, 
+                          rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_66 == /\ pc = "Lbl_66"
+          /\ curPos' = returnVal
+          /\ IF curPos' < 0
+                THEN /\ notDone' = FALSE
+                     /\ pc' = "Lbl_63"
+                     /\ stack' = stack
+                ELSE /\ stack' = << [ procedure |->  "skipLeftOverSpaces",
+                                      pc        |->  "Lbl_67" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_29"
+                     /\ UNCHANGED notDone
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_67 == /\ pc = "Lbl_67"
+          /\ /\ pos_f' = curPos
+             /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                              pc        |->  "Lbl_68",
+                              token     |->  token,
+                              left_     |->  left_,
+                              rt_       |->  rt_,
+                              pos_f     |->  pos_f ] >>
+                          \o stack
+          /\ token' = << >>
+          /\ left_' = pos_f'
+          /\ rt_' = pos_f'
+          /\ pc' = "Lbl_5"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_68 == /\ pc = "Lbl_68"
+          /\ currentToken' = returnVal
+          /\ IF \/ currentToken' = NullTokenSpec
+                \/ IsNumber(currentToken'.token)
+                THEN /\ notDone' = FALSE
+                     /\ UNCHANGED << foundTokenSpecs, curPos >>
+                ELSE /\ curPos' = currentToken'.leftPos
+                     /\ foundTokenSpecs' = [foundTokenSpecs EXCEPT ![0] = [token |-> JavaConcatenate(
+                                                                                         JavaAppend(currentToken'.token, "!"),
+                                                                                         foundTokenSpecs[0].token),
+                                                                           leftPos |-> curPos',
+                                                                           rightPos |-> foundTokenSpecs[0].rightPos]]
+                     /\ UNCHANGED notDone
+          /\ pc' = "Lbl_63"
+          /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, 
+                          tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, 
+                          pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, left, rt, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_69 == /\ pc = "Lbl_69"
+          /\ curPos' = foundTokenSpecs[0].rightPos
+          /\ foundBangToken' = FALSE
+          /\ notDone' = TRUE
+          /\ pc' = "Lbl_70"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, temp1, 
+                          temp2, temp3 >>
+
+Lbl_70 == /\ pc = "Lbl_70"
+          /\ IF notDone
+                THEN /\ stack' = << [ procedure |->  "skipRightOverSpaces",
+                                      pc        |->  "Lbl_71" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_30"
+                     /\ UNCHANGED << returnVal, currentToken, left, rt, 
+                                     notDone, foundBangToken, temp1, temp2, 
+                                     temp3 >>
+                ELSE /\ IF notLastToken /\ ~ foundBangToken
+                           THEN /\ returnVal' = << >>
+                                /\ pc' = Head(stack).pc
+                                /\ currentToken' = Head(stack).currentToken
+                                /\ left' = Head(stack).left
+                                /\ rt' = Head(stack).rt
+                                /\ notDone' = Head(stack).notDone
+                                /\ foundBangToken' = Head(stack).foundBangToken
+                                /\ temp1' = Head(stack).temp1
+                                /\ temp2' = Head(stack).temp2
+                                /\ temp3' = Head(stack).temp3
+                                /\ stack' = Tail(stack)
+                           ELSE /\ pc' = "Lbl_76"
+                                /\ UNCHANGED << returnVal, stack, currentToken, 
+                                                left, rt, notDone, 
+                                                foundBangToken, temp1, temp2, 
+                                                temp3 >>
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
+
+Lbl_71 == /\ pc = "Lbl_71"
+          /\ /\ pos_find' = curPos
+             /\ stack' = << [ procedure |->  "findMatchingRightParen",
+                              pc        |->  "Lbl_72",
+                              pos_find  |->  pos_find ] >>
+                          \o stack
+          /\ pc' = "Lbl_17"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos, delim, ipos, jdelim, delimLen, leftTok, 
+                          rightTok, currentToken, left, rt, notDone, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_72 == /\ pc = "Lbl_72"
+          /\ curPos' = returnVal
+          /\ IF curPos' < 0
+                THEN /\ notDone' = FALSE
+                     /\ pc' = "Lbl_70"
+                     /\ stack' = stack
+                ELSE /\ stack' = << [ procedure |->  "skipRightOverSpaces",
+                                      pc        |->  "Lbl_73" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_30"
+                     /\ UNCHANGED notDone
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_73 == /\ pc = "Lbl_73"
+          /\ IF line[curPos] # "!"  \/ line[curPos+1] = "!"
+                THEN /\ notDone' = FALSE
+                     /\ pc' = "Lbl_70"
+                     /\ UNCHANGED << curPos, stack >>
+                ELSE /\ curPos' = curPos + 1
+                     /\ stack' = << [ procedure |->  "skipRightOverSpaces",
+                                      pc        |->  "Lbl_74" ] >>
+                                  \o stack
+                     /\ pc' = "Lbl_30"
+                     /\ UNCHANGED notDone
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok, currentToken, left, rt, 
+                          foundBangToken, temp1, temp2, temp3 >>
+
+Lbl_74 == /\ pc = "Lbl_74"
+          /\ /\ pos_f' = curPos
+             /\ stack' = << [ procedure |->  "findMaximalIdCharSeq",
+                              pc        |->  "Lbl_75",
+                              token     |->  token,
+                              left_     |->  left_,
+                              rt_       |->  rt_,
+                              pos_f     |->  pos_f ] >>
+                          \o stack
+          /\ token' = << >>
+          /\ left_' = pos_f'
+          /\ rt_' = pos_f'
+          /\ pc' = "Lbl_5"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar1, tempVar2, tempVar3, 
+                          pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_75 == /\ pc = "Lbl_75"
+          /\ currentToken' = returnVal
+          /\ IF \/ currentToken' = NullTokenSpec
+                \/ IsNumber(currentToken'.token)
+                THEN /\ notDone' = FALSE
+                     /\ UNCHANGED << foundTokenSpecs, curPos, foundBangToken >>
+                ELSE /\ foundBangToken' = TRUE
+                     /\ foundTokenSpecs' = JavaConcatenate(
+                                             JavaSeq(
+                                               << [token |->
+                                                     JavaConcatenate(JavaAppend(foundTokenSpecs[0].token,
+                                                                                 "!"),
+                                                                     currentToken'.token),
+                                                   leftPos |-> foundTokenSpecs[0].leftPos,
+                                                   rightPos |-> currentToken'.rightPos]
+                                                >> ) ,
+                                               foundTokenSpecs)
+                     /\ curPos' = currentToken'.rightPos
+                     /\ UNCHANGED notDone
+          /\ pc' = "Lbl_70"
+          /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, 
+                          tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, 
+                          pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, 
+                          ipos_, jdelim_, delimLen_, pos_find, pos, delim, 
+                          ipos, jdelim, delimLen, leftTok, rightTok, left, rt, 
+                          temp1, temp2, temp3 >>
+
+Lbl_76 == /\ pc = "Lbl_76"
+          /\ returnVal' = FixOrigin(foundTokenSpecs)
+          /\ pc' = Head(stack).pc
+          /\ currentToken' = Head(stack).currentToken
+          /\ left' = Head(stack).left
+          /\ rt' = Head(stack).rt
+          /\ notDone' = Head(stack).notDone
+          /\ foundBangToken' = Head(stack).foundBangToken
+          /\ temp1' = Head(stack).temp1
+          /\ temp2' = Head(stack).temp2
+          /\ temp3' = Head(stack).temp3
+          /\ stack' = Tail(stack)
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok >>
+
+findTokenSpecs == Lbl_31 \/ Lbl_32 \/ Lbl_33 \/ Lbl_34 \/ Lbl_35 \/ Lbl_36
+                     \/ Lbl_37 \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 \/ Lbl_42
+                     \/ Lbl_43 \/ Lbl_44 \/ Lbl_45 \/ Lbl_46 \/ Lbl_47 \/ Lbl_48
+                     \/ Lbl_49 \/ Lbl_50 \/ Lbl_51 \/ Lbl_52 \/ Lbl_53 \/ Lbl_54
+                     \/ Lbl_55 \/ Lbl_56 \/ Lbl_57 \/ Lbl_58 \/ Lbl_59 \/ Lbl_60
+                     \/ Lbl_61 \/ Lbl_62 \/ Lbl_63 \/ Lbl_64 \/ Lbl_65 \/ Lbl_66
+                     \/ Lbl_67 \/ Lbl_68 \/ Lbl_69 \/ Lbl_70 \/ Lbl_71 \/ Lbl_72
+                     \/ Lbl_73 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76
+
+Lbl_77 == /\ pc = "Lbl_77"
+          /\ curPos' = JavaLen(Padding)
+          /\ tempVar1' = (-1 :> "")
+          /\ tempVar2' = JavaLen(Padding)
+          /\ pc' = "Lbl_78"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          returnVal, tempVar3, stack, pos_, tokArray, i, 
+                          tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Lbl_78 == /\ pc = "Lbl_78"
+          /\ IF tempVar2 < JavaLen(lineInput) + JavaLen(Padding)
+                THEN /\ curPos' = tempVar2
+                     /\ foundTokenSpecs' = << >>
+                     /\ lastToken' = FALSE
+                     /\ notLastToken' = FALSE
+                     /\ stack' = << [ procedure |->  "findTokenSpecs",
+                                      pc        |->  "Lbl_79",
+                                      currentToken |->  currentToken,
+                                      left      |->  left,
+                                      rt        |->  rt,
+                                      notDone   |->  notDone,
+                                      foundBangToken |->  foundBangToken,
+                                      temp1     |->  temp1,
+                                      temp2     |->  temp2,
+                                      temp3     |->  temp3 ] >>
+                                  \o stack
+                     /\ currentToken' = defaultInitValue
+                     /\ left' = defaultInitValue
+                     /\ rt' = defaultInitValue
+                     /\ notDone' = defaultInitValue
+                     /\ foundBangToken' = defaultInitValue
+                     /\ temp1' = defaultInitValue
+                     /\ temp2' = defaultInitValue
+                     /\ temp3' = defaultInitValue
+                     /\ pc' = "Lbl_31"
+                ELSE /\ PrintT("goodby world")
+                     /\ pc' = "Done"
+                     /\ UNCHANGED << foundTokenSpecs, lastToken, notLastToken, 
+                                     curPos, stack, currentToken, left, rt, 
+                                     notDone, foundBangToken, temp1, temp2, 
+                                     temp3 >>
+          /\ UNCHANGED << line, returnVal, tempVar1, tempVar2, tempVar3, pos_, 
+                          tokArray, i, tokIdx, pos_f, token, left_, rt_, 
+                          pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, 
+                          pos_find, pos, delim, ipos, jdelim, delimLen, 
+                          leftTok, rightTok >>
+
+Lbl_79 == /\ pc = "Lbl_79"
+          /\ tempVar1' = returnVal
+          /\ tempVar2' = tempVar2 + 1
+          /\ pc' = "Lbl_78"
+          /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, 
+                          curPos, returnVal, tempVar3, stack, pos_, tokArray, 
+                          i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, 
+                          delim_, ipos_, jdelim_, delimLen_, pos_find, pos, 
+                          delim, ipos, jdelim, delimLen, leftTok, rightTok, 
+                          currentToken, left, rt, notDone, foundBangToken, 
+                          temp1, temp2, temp3 >>
+
+Next == findTokenIn \/ findMaximalIdCharSeq \/ findMatchingLeftParen
+           \/ findMatchingLeftInner \/ findMatchingRightParen
+           \/ findMatchingRightInner \/ checkIfStepName \/ skipLeftOverSpaces
+           \/ skipRightOverSpaces \/ findTokenSpecs \/ Lbl_77 \/ Lbl_78 \/ Lbl_79
+           \/ (* Disjunct to prevent deadlock on termination *)
+              (pc = "Done" /\ UNCHANGED vars)
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION
+=============================================================================
 \* Modification History
-\* Last modified Tue Jun 21 11:33:07 CEST 2011 by markus
-\* Last modified Tue Jun 21 02:08:39 PDT 2011 by lamport
-\* Created Mon Sep 20 06:42:12 PDT 2010 by lamport
-
-c: if (lastToken = TRUE) {return FixOrigin(foundTokens);}
-   while   
-} \* end algorithm   
-****************************************************************************)
-
+\* Last modified Tue Jun 21 11:33:07 CEST 2011 by markus
+\* Last modified Tue Jun 21 02:08:39 PDT 2011 by lamport
+\* Created Mon Sep 20 06:42:12 PDT 2010 by lamport
+
+c: if (lastToken = TRUE) {return FixOrigin(foundTokens);}
+   while   
+} \* end algorithm   
+****************************************************************************)
+
diff --git a/tlatools/test-model/Bug279/InitStateBug.cfg b/tlatools/test-model/Bug279/InitStateBug.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5db1b74d8239cc161ffbc357c088cdbc8f41f02c
--- /dev/null
+++ b/tlatools/test-model/Bug279/InitStateBug.cfg
@@ -0,0 +1,4 @@
+\* INIT definition
+INIT Init
+NEXT Next
+\* Generated on Wed Mar 07 12:58:05 PST 2012
\ No newline at end of file
diff --git a/tlatools/test-model/Bug279/InitStateBug.tla b/tlatools/test-model/Bug279/InitStateBug.tla
new file mode 100644
index 0000000000000000000000000000000000000000..53c2c4226d146f1163a7f8b4ea6948b6215d6ed7
--- /dev/null
+++ b/tlatools/test-model/Bug279/InitStateBug.tla
@@ -0,0 +1,39 @@
+---------------------------- MODULE InitStateBug ----------------------------
+EXTENDS Naturals
+
+RECURSIVE AllSubs(_)
+AllSubs(S) ==
+  IF S = {} THEN {{}} ELSE
+  LET
+    c  == CHOOSE c \in S : TRUE
+    T  == S \ {c}
+    TT == AllSubs(T)
+  IN
+    TT \cup { t \cup {c} : t \in TT }
+
+RECURSIVE Sum(_)
+Sum(S) ==
+  IF S = {} THEN 0 ELSE
+  LET c == CHOOSE c \in S : TRUE
+  IN c + Sum(S \ {c})
+
+Ch(S) == CHOOSE v \in S : Sum(v) > 4
+
+S1 == SUBSET (1..20) \* Increased from 1..8 to 1..20 to also test performance with this spec.
+S2 == AllSubs(1..8)
+
+VARIABLE pc, set, fun
+
+Init == pc = 0 /\ set = {} /\ fun = {}
+
+Next ==
+  CASE
+  pc = 0 -> pc' = 1 /\ set' = S1 /\ fun' = Ch(set')
+  []
+  pc = 1 -> pc' = 2 /\ set' = S2 /\ fun' = Ch(set')
+  []
+  OTHER -> FALSE
+=============================================================================
+\* Modification History
+\* Last modified Wed Mar 07 10:27:18 PST 2012 by tomr
+\* Created Wed Mar 07 10:24:35 PST 2012 by tomr
diff --git a/tlatools/test-model/CallGotoUnlabeledTest.tla b/tlatools/test-model/CallGotoUnlabeledTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9fe0642ff8ab380047d4ae69554e538e56fd8cd7
--- /dev/null
+++ b/tlatools/test-model/CallGotoUnlabeledTest.tla
@@ -0,0 +1,71 @@
+---- MODULE CallGotoUnlabeledTest ----
+
+(* --algorithm CallGotoUnlabeledTest
+
+variable x = 0;
+
+procedure Foo()
+begin
+\*Q: \* Not needed anymore.
+  x := x + 1;
+  return;
+end procedure;
+
+begin
+A:
+  call Foo();
+  goto C;
+\*B: \* Not needed anymore.
+  x := 13;
+C:
+  x := x - 1;
+  goto A;
+
+end algorithm *)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-4786c31224fe8555dc7ad50128a92fc6
+VARIABLES x, pc, stack
+
+vars == << x, pc, stack >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ stack = << >>
+        /\ pc = "A"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ x' = x + 1
+         /\ pc' = Head(stack).pc
+         /\ stack' = Tail(stack)
+
+Foo == Lbl_1
+
+A == /\ pc = "A"
+     /\ stack' = << [ procedure |->  "Foo",
+                      pc        |->  "C" ] >>
+                  \o stack
+     /\ pc' = "Lbl_1"
+     /\ x' = x
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ x' = 13
+         /\ pc' = "C"
+         /\ stack' = stack
+
+C == /\ pc = "C"
+     /\ x' = x - 1
+     /\ pc' = "A"
+     /\ stack' = stack
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Foo \/ A \/ Lbl_2 \/ C
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8ca0b91b1573d7c7ae4c4135afa5f61b
+
+====
diff --git a/tlatools/test-model/CodePlexBug08/AgentRingMC.tla b/tlatools/test-model/CodePlexBug08/AgentRingMC.tla
index acd6fc07679129f8295e00f3e36e0b31f561a952..04ff75f3250fe770d575baa2dd06bba3aacffa4d 100644
--- a/tlatools/test-model/CodePlexBug08/AgentRingMC.tla
+++ b/tlatools/test-model/CodePlexBug08/AgentRingMC.tla
@@ -23,5 +23,5 @@ prop_142745588773041000 ==
 Test
 ----
 =============================================================================
-\* Modification History
-\* Created Fri Mar 27 12:31:27 CET 2015 by makuppe
+\* Modification History
+\* Created Fri Mar 27 12:31:27 CET 2015 by makuppe
diff --git a/tlatools/test-model/CodePlexBug08/InlineMC.cfg b/tlatools/test-model/CodePlexBug08/InlineMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a2b7e06f96db60091276304ced5553107b06b3ee
--- /dev/null
+++ b/tlatools/test-model/CodePlexBug08/InlineMC.cfg
@@ -0,0 +1,4 @@
+CONSTANT
+N = 6
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/CodePlexBug08/InlineMC.tla b/tlatools/test-model/CodePlexBug08/InlineMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e9ff6750e51ebce262d9b91c52fa7e2c2df41324
--- /dev/null
+++ b/tlatools/test-model/CodePlexBug08/InlineMC.tla
@@ -0,0 +1,4 @@
+---- MODULE InlineMC ----
+EXTENDS EWD840, TLC
+
+===================
\ No newline at end of file
diff --git a/tlatools/test-model/CodePlexBug08/checkpoint.zip b/tlatools/test-model/CodePlexBug08/checkpoint.zip
new file mode 100644
index 0000000000000000000000000000000000000000..b9c9d7d520634a13a9fe457bddf49cb78247b532
Binary files /dev/null and b/tlatools/test-model/CodePlexBug08/checkpoint.zip differ
diff --git a/tlatools/test-model/CodePlexBug21.cfg b/tlatools/test-model/CodePlexBug21.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/CodePlexBug21.tla b/tlatools/test-model/CodePlexBug21.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3df19a634c534e57d12049cd9d5429fbb0304df2
--- /dev/null
+++ b/tlatools/test-model/CodePlexBug21.tla
@@ -0,0 +1,13 @@
+--------------------------- MODULE CodePlexBug21 ---------------------------
+EXTENDS Naturals, TLC
+
+func1 == [x \in 1..3 |-> x]
+func2 == <<1,2,3>>
+
+ASSUME func1 = func2 \* evaluated to TRUE
+ASSUME 3 :> 11 @@ func1 = <<1,2,11>> \* evaluated to TRUE
+
+ASSUME 3 :> 11 @@ func2 = <<1,2,11>> \* erroneously evaluated to FALSE
+
+ASSUME 4 :> 11 @@ func2 = <<1,2,3,11>> \* throws a TLC error
+=============================================================================
diff --git a/tlatools/test-model/Continue.cfg b/tlatools/test-model/Continue.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b7702f14d2ece8edb2bf761738836a80877cd133
--- /dev/null
+++ b/tlatools/test-model/Continue.cfg
@@ -0,0 +1,7 @@
+INIT Init
+NEXT Next
+
+CONSTRAINT Constraint
+
+INVARIANT Inv1
+INVARIANT Inv2
\ No newline at end of file
diff --git a/tlatools/test-model/Continue.tla b/tlatools/test-model/Continue.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c5330b9ccbe3159e083403d444ec771c0a179105
--- /dev/null
+++ b/tlatools/test-model/Continue.tla
@@ -0,0 +1,20 @@
+---- MODULE Continue ----
+EXTENDS Naturals
+
+VARIABLES x,y
+
+Init == x = 1 /\ y = 1
+
+Next == /\ x' = x + 1 
+        /\ \/ /\ x = 1
+              /\ y' \in {1,2}
+           \/ /\ UNCHANGED y
+
+\* For the test to meaningful, it is vital that workers
+\* actually continue state space exploration after the
+\* first invariant violation (for trace files to be written).
+Inv1 == y < 2
+Inv2 == x < 30
+
+Constraint == Inv1 /\ Inv2
+====
diff --git a/tlatools/test-model/DepthFirstErrorTrace.cfg b/tlatools/test-model/DepthFirstErrorTrace.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/DepthFirstErrorTrace.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/DepthFirstErrorTrace.tla b/tlatools/test-model/DepthFirstErrorTrace.tla
new file mode 100644
index 0000000000000000000000000000000000000000..af057477a9bd4c3362a915f8fe67da96df904752
--- /dev/null
+++ b/tlatools/test-model/DepthFirstErrorTrace.tla
@@ -0,0 +1,9 @@
+--------------------------- MODULE DepthFirstErrorTrace ---------------------------
+EXTENDS Integers
+
+VARIABLES x
+
+Spec == (x=0) /\ [][x'=x+1]_<<x>>
+
+Inv == x < 7
+=============================================================================
diff --git a/tlatools/test-model/DepthFirstTerminate.cfg b/tlatools/test-model/DepthFirstTerminate.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/DepthFirstTerminate.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/DepthFirstTerminate.tla b/tlatools/test-model/DepthFirstTerminate.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d7193b8a66cd0df78a0847a70974f22bc664a477
--- /dev/null
+++ b/tlatools/test-model/DepthFirstTerminate.tla
@@ -0,0 +1,59 @@
+---------------------------- MODULE DepthFirstTerminate ----------------------------
+EXTENDS Integers
+
+(*
+
+--algorithm Untitled
+{
+    variables u = 24 ; v \in 1 .. 50;
+    {
+        print <<u, v>>;
+        while (u # 0) {
+            if (u < v) {
+                u := v || v := u
+            };
+            u := u - v ;
+        };
+        print <<"have gcd", v>>;
+    }
+}
+*)
+\* BEGIN TRANSLATION
+VARIABLES u, v, pc
+
+vars == << u, v, pc >>
+
+Init == (* Global variables *)
+        /\ u = 24
+        /\ v \in 1 .. 50
+        /\ pc = "Lbl_1"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ pc' = "Lbl_2"
+         /\ UNCHANGED << u, v >>
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ IF u # 0
+               THEN /\ IF u < v
+                          THEN /\ /\ u' = v
+                                  /\ v' = u
+                          ELSE /\ TRUE
+                               /\ UNCHANGED << u, v >>
+                    /\ pc' = "Lbl_3"
+               ELSE /\ pc' = "Done"
+                    /\ UNCHANGED << u, v >>
+
+Lbl_3 == /\ pc = "Lbl_3"
+         /\ u' = u - v
+         /\ pc' = "Lbl_2"
+         /\ v' = v
+
+Next == Lbl_1 \/ Lbl_2 \/ Lbl_3
+           \/ (* Disjunct to prevent deadlock on termination *)
+              (pc = "Done" /\ UNCHANGED vars)
+
+Spec == /\ Init /\ [][Next]_vars
+
+\* END TRANSLATION
+
+=============================================================================
diff --git a/tlatools/test-model/DieHard.cfg b/tlatools/test-model/DieHard.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/DieHard.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/DieHard.tla b/tlatools/test-model/DieHard.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e35c24c86f88dcda5eedc753dab7d8915a5dcc7f
--- /dev/null
+++ b/tlatools/test-model/DieHard.tla
@@ -0,0 +1,87 @@
+---------------------------- MODULE DieHard ----------------------------
+EXTENDS TLC, Integers
+
+Min(m, n) == IF m < n THEN m ELSE n
+
+NONDET == "nondet"
+FILL_BIG == "fill big"
+EMPTY_BIG == "empty big"
+FILL_SMALL == "fill small"
+EMPTY_SMALL == "empty small"
+BIG_TO_SMALL == "pour big to small"
+SMALL_TO_BIG == "pour small to big"
+
+(***************************************************************************
+--algorithm DieHardDemo {
+    variables bigBucket = 0, smallBucket = 0, action = NONDET;
+      
+    process (FixWater = 1)
+      variables water_to_pour = 0;
+      {
+        LB1: while (TRUE) 
+          {
+            either { action := FILL_BIG; bigBucket := 5; }
+                or { action := EMPTY_BIG; bigBucket := 0; }
+                or { action := FILL_SMALL; smallBucket := 3; } 
+                or { action := EMPTY_SMALL; smallBucket := 0; } 
+                or { 
+                     action := BIG_TO_SMALL; 
+                     water_to_pour := Min(3 - smallBucket, bigBucket); 
+                     smallBucket := smallBucket + water_to_pour;
+                     bigBucket := bigBucket - water_to_pour;
+                   }
+                or { 
+                     action := SMALL_TO_BIG; 
+                     water_to_pour := Min(5 - bigBucket, smallBucket); 
+                     smallBucket := smallBucket - water_to_pour;
+                     bigBucket := bigBucket + water_to_pour;
+                   }
+          }
+      }
+}
+
+***************************************************************************)
+\* BEGIN TRANSLATION
+VARIABLES bigBucket, smallBucket, action, water_to_pour
+
+vars == << bigBucket, smallBucket, action, water_to_pour >>
+
+ProcSet == {1}
+
+Init == (* Global variables *)
+        /\ bigBucket = 0
+        /\ smallBucket = 0
+        /\ action = NONDET
+        (* Process FixWater *)
+        /\ water_to_pour = 0
+
+FixWater == \/ /\ action' = FILL_BIG
+               /\ bigBucket' = 5
+               /\ UNCHANGED <<smallBucket, water_to_pour>>
+            \/ /\ action' = EMPTY_BIG
+               /\ bigBucket' = 0
+               /\ UNCHANGED <<smallBucket, water_to_pour>>
+            \/ /\ action' = FILL_SMALL
+               /\ smallBucket' = 3
+               /\ UNCHANGED <<bigBucket, water_to_pour>>
+            \/ /\ action' = EMPTY_SMALL
+               /\ smallBucket' = 0
+               /\ UNCHANGED <<bigBucket, water_to_pour>>
+            \/ /\ action' = BIG_TO_SMALL
+               /\ water_to_pour' = Min(3 - smallBucket, bigBucket)
+               /\ smallBucket' = smallBucket + water_to_pour'
+               /\ bigBucket' = bigBucket - water_to_pour'
+            \/ /\ action' = SMALL_TO_BIG
+               /\ water_to_pour' = Min(5 - bigBucket, smallBucket)
+               /\ smallBucket' = smallBucket - water_to_pour'
+               /\ bigBucket' = bigBucket + water_to_pour'
+
+Next == FixWater
+
+Spec == Init /\ [][Next]_vars
+
+Inv == bigBucket # 4
+
+\* END TRANSLATION
+
+=============================================================================
diff --git a/tlatools/test-model/DieHardTLA.cfg b/tlatools/test-model/DieHardTLA.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bb7713361d8dc3c0f1759e274a1b41aaacc3411a
--- /dev/null
+++ b/tlatools/test-model/DieHardTLA.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+TypeOK
\ No newline at end of file
diff --git a/tlatools/test-model/DieHardTLA.tla b/tlatools/test-model/DieHardTLA.tla
new file mode 100644
index 0000000000000000000000000000000000000000..78d44d483e7540b4de6476503dd9a8c316c66eba
--- /dev/null
+++ b/tlatools/test-model/DieHardTLA.tla
@@ -0,0 +1,44 @@
+------------------------------ MODULE DieHardTLA ------------------------------
+EXTENDS Integers
+
+VARIABLES small, big   
+          
+TypeOK == /\ small \in 0..3 
+          /\ big   \in 0..5
+
+Init == /\ big   = 0 
+        /\ small = 0
+
+FillSmall == /\ small' = 3 
+             /\ big'   = big
+
+FillBig == /\ big'   = 5 
+           /\ small' = small
+
+EmptySmall == /\ small' = 0 
+              /\ big'   = big
+
+EmptyBig == /\ big'   = 0 
+            /\ small' = small
+
+SmallToBig == IF big + small =< 5
+               THEN /\ big'   = big + small
+                    /\ small' = 0
+               ELSE /\ big'   = 5
+                    /\ small' = small - (5 - big)
+ 
+BigToSmall == IF big + small =< 3
+               THEN /\ big'   = 0 
+                    /\ small' = big + small
+               ELSE /\ big'   = small - (3 - big)
+                    /\ small' = 3
+
+Next == \/ FillSmall 
+        \/ FillBig    
+        \/ EmptySmall 
+        \/ EmptyBig    
+        \/ SmallToBig    
+        \/ BigToSmall    
+        
+Spec == Init /\ [][Next]_<<small, big>>        
+=============================================================================
diff --git a/tlatools/test-model/DistributedTrace.cfg b/tlatools/test-model/DistributedTrace.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/DistributedTrace.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/DistributedTrace.tla b/tlatools/test-model/DistributedTrace.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6149b74a924ac27de52a3461e0124a21b13eabf5
--- /dev/null
+++ b/tlatools/test-model/DistributedTrace.tla
@@ -0,0 +1,14 @@
+-------------------------- MODULE DistributedTrace --------------------------
+EXTENDS Integers, Sequences
+
+VARIABLES x
+vars == <<x>>
+
+Init == x \in 1..10
+
+Next == x' = x + 1
+
+Spec == /\ Init /\ [][Next]_vars
+
+Inv == x < 20
+=============================================================================
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..00592a934cbecee867aa47abbdde199473d8fd74
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.cfg
@@ -0,0 +1,6 @@
+CONSTRAINT
+const_4711
+SPECIFICATION
+Spec
+INVARIANT
+NotNine
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c71fb286d50ae1b6eddeba21be20e72cc613ea33
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.tla
@@ -0,0 +1,19 @@
+--------------------------- MODULE DoInitFunctorEvalException ---------------------------
+EXTENDS Integers
+
+VARIABLES x
+vars == <<x>>
+
+Init == x \in 1..10
+
+Next == x' = 0
+
+Spec == Init /\ [][Next]_vars
+
+(* This function is used as a model constraint to trigger an EvalException (because it doesn't return Bool) *)
+F(a) == {}
+const_4711 == F(x)
+
+(* This property is violated by one of the init states *)
+NotNine == x # 9 
+=============================================================================
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5fd6a98a18bd4dcdd7201895f3989c0a7a472a0c
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+NotNine
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.tla
new file mode 100644
index 0000000000000000000000000000000000000000..73c16c5702db1d15d92a4e20a20f69e6dddb84e4
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.tla
@@ -0,0 +1,15 @@
+--------------------------- MODULE DoInitFunctorInvariant ---------------------------
+EXTENDS Integers
+
+VARIABLES x
+vars == <<x>>
+
+Init == x \in 1..10
+
+Next == x' = 0
+
+Spec == Init /\ [][Next]_vars
+
+(* This property is violated by one of the init states *)
+NotNine == x # 9 
+=============================================================================
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6a89e0901c7fa1bcc62bf2ffba508d385d572ca4
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.tla
new file mode 100644
index 0000000000000000000000000000000000000000..df785bda8dfb7ca8df064c132e22a5c5826513e5
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.tla
@@ -0,0 +1,15 @@
+--------------------------- MODULE DoInitFunctorInvariantContinue ---------------------------
+EXTENDS Integers
+
+VARIABLES x
+vars == <<x>>
+
+Init == x \in 1..10
+
+Next == x' = 0
+
+Spec == Init /\ [][Next]_vars
+
+(* This property is violated by all of the init states *)
+Inv == x \notin Nat 
+=============================================================================
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6a89e0901c7fa1bcc62bf2ffba508d385d572ca4
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f29ac16460c7768c18b783e9386086be4a05250c
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.tla
@@ -0,0 +1,15 @@
+------------------ MODULE DoInitFunctorMinimalErrorStack --------------------
+EXTENDS Naturals
+
+VARIABLES a, b
+vars == <<a, b>>
+
+Init == 
+  /\ a \in [1..2 -> 1..3]
+  /\ b = TRUE
+
+Spec == Init /\ [][UNCHANGED vars]_vars
+
+(* This property is violated by all of the init states *)
+Inv == a >= 0
+=============================================================================
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b92bb43293dbff997539b4da5c9374702f3e0507
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+NotNine
diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d1acd346b9b89ce6607e41148ea71b277946ccbd
--- /dev/null
+++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.tla
@@ -0,0 +1,15 @@
+--------------------------- MODULE DoInitFunctorProperty ---------------------------
+EXTENDS Integers
+
+VARIABLES x
+vars == <<x>>
+
+Init == x \in 1..10
+
+Next == x' = 0
+
+Spec == Init /\ [][Next]_vars
+
+(* This property is violated by one of the init states *)
+NotNine == x # 9 
+=============================================================================
diff --git a/tlatools/test-model/EWD840/MC06.cfg b/tlatools/test-model/EWD840/MC06.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ed1d175d3106a5e3cf5d65bf42ace5b386eb145c
--- /dev/null
+++ b/tlatools/test-model/EWD840/MC06.cfg
@@ -0,0 +1,9 @@
+\* CONSTANT definitions
+CONSTANT
+N <- const_143073460396411000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_143073460397412000
+\* PROPERTY definition
+PROPERTY
+prop_143073460398413000
diff --git a/tlatools/test-model/EWD840/MC06.tla b/tlatools/test-model/EWD840/MC06.tla
new file mode 100644
index 0000000000000000000000000000000000000000..71a018b16aeb0327558fb1052a57350ed3dff85d
--- /dev/null
+++ b/tlatools/test-model/EWD840/MC06.tla
@@ -0,0 +1,17 @@
+---- MODULE MC06 ----
+EXTENDS EWD840, TLC
+
+\* CONSTANT definitions @modelParameterConstants:0N
+const_143073460396411000 == 
+7
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_143073460397412000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_143073460398413000 ==
+Liveness
+----
+=============================================================================
diff --git a/tlatools/test-model/Empty.cfg b/tlatools/test-model/Empty.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/Empty.tla b/tlatools/test-model/Empty.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3e6c3492f671ebc8e947dc4fcecc22505bf3bb71
--- /dev/null
+++ b/tlatools/test-model/Empty.tla
@@ -0,0 +1,2 @@
+------------------------------- MODULE Empty -------------------------------
+============================================================================
diff --git a/tlatools/test-model/EmptyOrderOfSolutions.cfg b/tlatools/test-model/EmptyOrderOfSolutions.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca
--- /dev/null
+++ b/tlatools/test-model/EmptyOrderOfSolutions.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+Prop
diff --git a/tlatools/test-model/EmptyOrderOfSolutions.tla b/tlatools/test-model/EmptyOrderOfSolutions.tla
new file mode 100644
index 0000000000000000000000000000000000000000..96d3acd32389749608f70073e1fcf0132d6d3291
--- /dev/null
+++ b/tlatools/test-model/EmptyOrderOfSolutions.tla
@@ -0,0 +1,11 @@
+----- MODULE EmptyOrderOfSolutions -----
+VARIABLES x
+
+Init == x = 0
+
+Next == x' = 1
+
+Spec == Init /\ [][Next]_x
+
+Prop == <>[]TRUE => TRUE
+=====
diff --git a/tlatools/test-model/EmptySubsetEq.cfg b/tlatools/test-model/EmptySubsetEq.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/EmptySubsetEq.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/EmptySubsetEq.tla b/tlatools/test-model/EmptySubsetEq.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d951e633b1d5853aec2aa1a9bb0f9846201fab3f
--- /dev/null
+++ b/tlatools/test-model/EmptySubsetEq.tla
@@ -0,0 +1,13 @@
+--------------------------- MODULE EmptySubsetEq --------------------------
+EXTENDS Integers, FiniteSets
+
+VARIABLE b
+
+Init == b = TRUE
+
+Next == b' = /\ (SUBSET (1..3)) \subseteq (1..4)
+
+Spec == Init /\ [][Next]_<<b>>
+
+Inv == b = TRUE
+=============================================================================
diff --git a/tlatools/test-model/EvalOrder/Base.tla b/tlatools/test-model/EvalOrder/Base.tla
new file mode 100644
index 0000000000000000000000000000000000000000..28ae14504aa4588c69bd526ba3bb137a6418bcc6
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/Base.tla
@@ -0,0 +1,9 @@
+-------------------------------- MODULE Base --------------------------------
+VARIABLES clk
+vars == <<clk>>
+
+Init == clk = 0
+        
+Spec == Init /\ [][UNCHANGED vars]_vars
+
+=============================================================================
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder.tla b/tlatools/test-model/EvalOrder/InitEvalOrder.tla
new file mode 100644
index 0000000000000000000000000000000000000000..098bc1a98d2ceb693bb9edd3e7cd7f408cdb8bf1
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrder.tla
@@ -0,0 +1,23 @@
+--------------------------- MODULE InitEvalOrder ---------------------------
+VARIABLE Clk1
+S1 == INSTANCE Base  WITH  clk <- Clk1
+
+VARIABLE Clk2
+S2 == INSTANCE Base  WITH  clk <- Clk2
+
+Spec1 == 
+	   /\ S1!Init /\ S2!Init /\ (Clk2 = Clk1)
+       /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>>
+
+Spec2 == 
+	   /\ S2!Init /\ S1!Init /\ (Clk1 = Clk2)
+       /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>>
+
+Spec3 == 
+	   /\ S1!Init /\ S2!Init /\ (Clk1 = Clk2)
+       /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>>
+
+Spec4 == 
+	   /\ S2!Init /\ S1!Init /\ (Clk2 = Clk1)
+       /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>>
+=============================================================================
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder1.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder1.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d1f34ad6b4d496bb2024748004055359877a09ef
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrder1.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec1
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder2.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e041393416d454a2bdd87c66b69feebaf31ddbd9
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrder2.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec2
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder3.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder3.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..da6984d2a15785661ab45dfbce82aefb482c1c8a
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrder3.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec3
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder4.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder4.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..789c3de3bc109d466cec77824bda13ad9b4b1da8
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrder4.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec4
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrderBasic.cfg b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/EvalOrder/InitEvalOrderBasic.tla b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9e70f772e449733b9a29de075dd99c9dd36a9c82
--- /dev/null
+++ b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.tla
@@ -0,0 +1,6 @@
+--------------------------- MODULE InitEvalOrderBasic ---------------------------
+VARIABLES x, y
+
+Spec == /\ (y=0) /\ (x=0) /\ (y = x)
+        /\ [][UNCHANGED <<x,y>>]_<<x,y>>
+=============================================================================
diff --git a/tlatools/test-model/EvaluatingValueTest.cfg b/tlatools/test-model/EvaluatingValueTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..f78adb5fb4413592b83a20ec7c7738dcc9cfcef4
--- /dev/null
+++ b/tlatools/test-model/EvaluatingValueTest.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION Spec
+INVARIANT Inv
\ No newline at end of file
diff --git a/tlatools/test-model/EvaluatingValueTest.tla b/tlatools/test-model/EvaluatingValueTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c4c3bb24a9ce7b16a853ef740459bb1666e33361
--- /dev/null
+++ b/tlatools/test-model/EvaluatingValueTest.tla
@@ -0,0 +1,18 @@
+------------------------ MODULE EvaluatingValueTest ------------------------
+EXTENDS Naturals
+
+VARIABLES x
+
+Init == x = 1
+
+A == x' = FALSE \* This action is replaced by the @Evaluation in EvaluatingValueTest.java
+
+Spec == (x = 1) /\ [][A]_x
+
+Inv == x \in Nat
+
+THEOREM Spec => []Inv
+
+Prop == <>(x = 42)
+
+============================================================================
diff --git a/tlatools/test-model/FingerprintExceptionInit.cfg b/tlatools/test-model/FingerprintExceptionInit.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3de9a85ec4e2058d21906ad4967bf50084684036
--- /dev/null
+++ b/tlatools/test-model/FingerprintExceptionInit.cfg
@@ -0,0 +1,5 @@
+INIT
+Init
+
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/FingerprintExceptionInit.tla b/tlatools/test-model/FingerprintExceptionInit.tla
new file mode 100644
index 0000000000000000000000000000000000000000..647fc4e6fd198e673d6b3945c9b1a7fe7c2b1a39
--- /dev/null
+++ b/tlatools/test-model/FingerprintExceptionInit.tla
@@ -0,0 +1,11 @@
+------------------------ MODULE FingerprintExceptionInit --------------------
+
+EXTENDS Integers
+
+VARIABLE x
+
+Init == x = SUBSET(SUBSET(1..36))
+Next == IF {1} \in SUBSET(1..31) THEN x' = SUBSET(SUBSET(1..32)) ELSE x' = 2
+Spec == Init /\ [][Next]_x
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/FingerprintExceptionNext.cfg b/tlatools/test-model/FingerprintExceptionNext.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3de9a85ec4e2058d21906ad4967bf50084684036
--- /dev/null
+++ b/tlatools/test-model/FingerprintExceptionNext.cfg
@@ -0,0 +1,5 @@
+INIT
+Init
+
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/FingerprintExceptionNext.tla b/tlatools/test-model/FingerprintExceptionNext.tla
new file mode 100644
index 0000000000000000000000000000000000000000..009b6d79491a586a770fa3ee754afd832edae9f2
--- /dev/null
+++ b/tlatools/test-model/FingerprintExceptionNext.tla
@@ -0,0 +1,11 @@
+------------------------ MODULE FingerprintExceptionNext --------------------
+
+EXTENDS Integers
+
+VARIABLE x
+
+Init == x = 0
+Next == IF {1} \in SUBSET(1..31) THEN x' = SUBSET(SUBSET(1..32)) ELSE x' = 2
+Spec == Init /\ [][Next]_x
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/Github179a.cfg b/tlatools/test-model/Github179a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36
--- /dev/null
+++ b/tlatools/test-model/Github179a.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/Github179a.tla b/tlatools/test-model/Github179a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fd046e45a32ead359ea53c1cf490f8db1303aa0f
--- /dev/null
+++ b/tlatools/test-model/Github179a.tla
@@ -0,0 +1,25 @@
+---- MODULE Github179a ----
+EXTENDS TLC, Naturals
+
+VARIABLE v
+
+Max(s) == { x \in s : \A y \in s : x >= y}
+
+(* Attempted to check equality of integer 1 with non-integer: {1} *)
+sillyExpr == 
+   {s \in SUBSET {1,2,3} : 
+               \A x \in s : IF x = Max(s) 
+                            THEN TRUE
+                            ELSE (x+1) \in s}
+----
+
+ASSUME PrintT(<<"$!@$!@$!@$!@$!", sillyExpr>>)
+----
+
+Init ==
+FALSE/\v = 0
+----
+Next ==
+FALSE/\ v'= v
+----
+=============================================================================
diff --git a/tlatools/test-model/Github179b.cfg b/tlatools/test-model/Github179b.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36
--- /dev/null
+++ b/tlatools/test-model/Github179b.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/Github179b.tla b/tlatools/test-model/Github179b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..adcd7ea71a7ad30334038fe0fe6dab8f5b1b9c3c
--- /dev/null
+++ b/tlatools/test-model/Github179b.tla
@@ -0,0 +1,18 @@
+---- MODULE Github179b ----
+EXTENDS TLC, Naturals
+
+VARIABLE v
+
+Max(s) == { x \in s : \A y \in s : x >= y}
+
+sillyExpr == 
+{s \in SUBSET {1,2,3,4} : \A x \in s : IF x = Max(s) THEN TRUE ELSE (x+1) \in s}
+----
+
+Init ==
+Print(sillyExpr, FALSE)/\v = 0
+----
+Next ==
+FALSE/\ v'= v
+----
+=============================================================================
diff --git a/tlatools/test-model/Github179c.cfg b/tlatools/test-model/Github179c.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36
--- /dev/null
+++ b/tlatools/test-model/Github179c.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/Github179c.tla b/tlatools/test-model/Github179c.tla
new file mode 100644
index 0000000000000000000000000000000000000000..37a89a6e2d0c1a9f51725b77fb7d768c7c8b1d7a
--- /dev/null
+++ b/tlatools/test-model/Github179c.tla
@@ -0,0 +1,17 @@
+---- MODULE Github179c ----
+EXTENDS TLC, Naturals
+
+VARIABLE v
+
+Max(s) == { x \in s : \A y \in s : x >= y}
+
+sillyExpr == 
+{s \in SUBSET {1,2,3,4} : 
+          \A x \in s : IF x = Max(s) 
+                       THEN TRUE 
+                       ELSE (x+1) \in s}
+
+Init == v = 0
+
+Next == PrintT(sillyExpr) = FALSE /\ v'= v
+=====
diff --git a/tlatools/test-model/Github358.tla b/tlatools/test-model/Github358.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6ff29f99fc11a89475e8cc951347f1fd56c87c33
--- /dev/null
+++ b/tlatools/test-model/Github358.tla
@@ -0,0 +1,7 @@
+------------------------------ MODULE Github358 ----------------------------- 
+(*--algorithm Github358
+begin
+    A::
+      skip;
+end algorithm; *)
+=============================================================================
diff --git a/tlatools/test-model/Github361.cfg b/tlatools/test-model/Github361.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36
--- /dev/null
+++ b/tlatools/test-model/Github361.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/Github361.tla b/tlatools/test-model/Github361.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ec06c2f5c467632330b2f8c671301d81f8186624
--- /dev/null
+++ b/tlatools/test-model/Github361.tla
@@ -0,0 +1,24 @@
+---- MODULE Github361 ----
+EXTENDS TLC, Naturals
+
+Nodes == {1, 2, 3, 4, 5}
+null == 6 \* not in Nodes
+
+Vertices == [nodes : SUBSET Nodes, succ : Nodes \cup {null}, dead : BOOLEAN]
+
+Partitions == {P \in SUBSET Vertices :
+                 /\ \A v1, v2 \in P : 
+                       (v1 # v2) => ((v1.nodes \cap v2.nodes) = {})
+                 /\ \A v \in P : /\ v.nodes # {}
+                                 /\ \E w \in P : \/ v.succ = null
+                                                 \/ v.succ \in w.nodes}     
+
+VARIABLES partition
+
+TypeOK == partition \in Partitions
+
+Init == partition = {}
+
+Next == UNCHANGED partition
+
+=============================================================================
diff --git a/tlatools/test-model/Github362.cfg b/tlatools/test-model/Github362.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..dbbd7f2a293042e29cff5cca9741e11fa1cbe4f3
--- /dev/null
+++ b/tlatools/test-model/Github362.cfg
@@ -0,0 +1,4 @@
+INIT Init
+NEXT Next
+PROPERTY Correct
+CONSTANT overloadedConst = 4711
diff --git a/tlatools/test-model/Github362.tla b/tlatools/test-model/Github362.tla
new file mode 100644
index 0000000000000000000000000000000000000000..058242a52c374c77939eb7488c25482bde189be3
--- /dev/null
+++ b/tlatools/test-model/Github362.tla
@@ -0,0 +1,34 @@
+---- CONFIG Github362 ----
+INIT Init
+NEXT Next
+PROPERTY Correct
+CONSTANT overloadedConst = 4711
+====
+
+---- MODULE Github362 ----
+
+EXTENDS TLC
+
+CONSTANT overloadedConst
+
+VARIABLES overloadedName
+
+\* Github362B.tla contains the definition "overloadedName == x".  It is
+\* unrelated to the variable "overloadedName" defined above.
+Abstract == INSTANCE Github362B WITH
+    x <- "x",
+    C <- 42
+
+Init ==
+    /\ overloadedName = "fizzbuzz"
+    /\ overloadedConst = 4711
+    /\ Print(<<"Evaluated initial state in A; overloadedName is: ", overloadedName>>, TRUE)        \* fizzbuzz
+    /\ Print(<<"From A's perspective, B's overloadedName is: ", Abstract!overloadedName>>, TRUE)   \* x
+    /\ Print(<<"Evaluated initial state in A; overloadedConst is: ", overloadedConst>>, TRUE)      \* 4711
+    /\ Print(<<"From A's perspective, B's overloadedConst is: ", Abstract!overloadedConst>>, TRUE) \* 42
+
+Next == UNCHANGED overloadedName
+
+Correct == Abstract!Spec
+
+==================
diff --git a/tlatools/test-model/Github362B.tla b/tlatools/test-model/Github362B.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9a5e0e20870709f16145a4ce9a2bd4f6a8a83748
--- /dev/null
+++ b/tlatools/test-model/Github362B.tla
@@ -0,0 +1,29 @@
+---- MODULE Github362B ----
+
+\* Auxiliary spec instantiated by Github362.tla.
+
+EXTENDS TLC
+
+CONSTANT C
+
+VARIABLES x
+
+\* This name also exists in Github362.tla with a different definition (it is a
+\* variable rather than a user-defined operator).  The definition in Github362
+\* should not conflict with this one.
+overloadedName == x
+
+overloadedConst == C
+
+Init ==
+    /\ Print(<<"Evaluating initial state in B; overloadedName is ", overloadedName>>, TRUE) \* x
+    /\ overloadedName = "x" \* overloadedName -> variable x -> value "x"
+    /\ x = "x"
+    /\ Print(<<"Evaluating initial state in B; overloadedConst is ", overloadedConst>>, TRUE) \* 42
+    /\ overloadedConst = 42 \* overloadedConst -> const C -> 42
+    /\ C = 42
+
+Next == UNCHANGED x
+Spec == Init /\ [][Next]_x
+
+==================
diff --git a/tlatools/test-model/Github391.cfg b/tlatools/test-model/Github391.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/Github391.tla b/tlatools/test-model/Github391.tla
new file mode 100644
index 0000000000000000000000000000000000000000..896130f8d9e5fb75ef86f4318c5eb95f916bbbec
--- /dev/null
+++ b/tlatools/test-model/Github391.tla
@@ -0,0 +1,10 @@
+---- MODULE Github391 ----
+EXTENDS Integers
+
+\* IntValue
+x == {1157660672, -989822976}
+y == {-989822976, 1157660672}
+
+ASSUME x = y
+
+=============================================================================
diff --git a/tlatools/test-model/Github407.cfg b/tlatools/test-model/Github407.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36
--- /dev/null
+++ b/tlatools/test-model/Github407.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
\ No newline at end of file
diff --git a/tlatools/test-model/Github407.tla b/tlatools/test-model/Github407.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9e46fcdabd7f28edff63943883f0a1f1397f3b83
--- /dev/null
+++ b/tlatools/test-model/Github407.tla
@@ -0,0 +1,9 @@
+---- MODULE Github407 ----
+Data == {"d1", "d2"}
+
+VARIABLES state
+
+Init == state = {}
+
+Next == \E e \in Data: state' = state \union {e}
+=============================================================================
diff --git a/tlatools/test-model/Github429.tla b/tlatools/test-model/Github429.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8ad0cd5b51170b7d00c1998a5979b9f1e1957d3c
--- /dev/null
+++ b/tlatools/test-model/Github429.tla
@@ -0,0 +1,14 @@
+---- MODULE Github429 ----
+
+---- MODULE Alpha ----
+
+======================
+
+---- MODULE Beta ----
+EXTENDS Alpha
+
+
+VARIABLE y
+======================
+
+==================================
diff --git a/tlatools/test-model/Github432.cfg b/tlatools/test-model/Github432.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..f7daf023446246482952679ff0fba218fdae092f
--- /dev/null
+++ b/tlatools/test-model/Github432.cfg
@@ -0,0 +1,8 @@
+CONSTANT Humans = {%1%}
+CONSTANT Others = {%2%}
+
+INIT Init
+NEXT Next
+SYMMETRY symmetry
+INVARIANT TypeOK
+
diff --git a/tlatools/test-model/Github432.tla b/tlatools/test-model/Github432.tla
new file mode 100644
index 0000000000000000000000000000000000000000..33e33e33fe7db82d0b0f7f9a6ab3c06605d98202
--- /dev/null
+++ b/tlatools/test-model/Github432.tla
@@ -0,0 +1,22 @@
+---- MODULE Github432 ----
+
+EXTENDS TLC
+
+CONSTANT Humans, Others
+
+symmetry == Permutations(Humans) \union Permutations(Others)
+
+VARIABLES set
+
+TypeOK ==
+    /\ set \subseteq Humans
+
+Init ==
+    /\ set = {}
+
+Next ==
+    \E h \in Humans:
+        set' = set \union {h}
+
+==================================
+
diff --git a/tlatools/test-model/Github461.cfg b/tlatools/test-model/Github461.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e5a3896997d57e27da8ca619e152212920e137d1
--- /dev/null
+++ b/tlatools/test-model/Github461.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
diff --git a/tlatools/test-model/Github461.tla b/tlatools/test-model/Github461.tla
new file mode 100644
index 0000000000000000000000000000000000000000..58beab5b7e764bbfe3bbae7bf902923eec77a7b8
--- /dev/null
+++ b/tlatools/test-model/Github461.tla
@@ -0,0 +1,12 @@
+----------------------------- MODULE Github461 -----------------------------
+EXTENDS Integers, TLC
+
+VARIABLES x
+
+Init == x = 0
+
+Next == 
+    /\ Assert(x < 4, "Failure of assertion at line 8, column 4.")
+    /\ x' = x + 1
+
+====
diff --git a/tlatools/test-model/Github798I.tla b/tlatools/test-model/Github798I.tla
new file mode 100644
index 0000000000000000000000000000000000000000..91cf9e095a5a0141aea4b8d053f21fc03e608669
--- /dev/null
+++ b/tlatools/test-model/Github798I.tla
@@ -0,0 +1,31 @@
+---------------------------- MODULE Github798I ----------------------------
+S == {"n1", "n2"}
+
+Direct(xx, yy) ==
+    /\ xx \in [S -> {"a"}]
+    /\ yy \in [S -> S]
+    /\ \A s \in S : 
+        xx[s] = "a" => yy[s] = s
+
+Indirect(xx, yy) == 
+    /\ Direct(xx, yy)
+
+InIndirect(xx, yy) ==
+	LET zz == xx IN
+    Indirect(zz, yy)
+
+VARIABLE x, y
+
+vars == <<x, y>>
+
+Init ==
+    InIndirect(x, y)
+   
+Spec == 
+    Init /\ [][InIndirect(x', y')]_vars
+
+==========================================================================
+---------------------------- CONFIG Github798I -----------------------------
+SPECIFICATION
+    Spec
+===========================================================================
diff --git a/tlatools/test-model/Github798N.tla b/tlatools/test-model/Github798N.tla
new file mode 100644
index 0000000000000000000000000000000000000000..08171ccc799143aabeac22a0eb09eca0b6dbb636
--- /dev/null
+++ b/tlatools/test-model/Github798N.tla
@@ -0,0 +1,35 @@
+---------------------------- MODULE Github798N ---------------------------
+S == {"n1", "n2"}
+
+Direct(xx, yy) ==
+    /\ xx \in [S -> {"a"}]
+    /\ yy \in [S -> S]
+    /\ \A s \in S : 
+        xx[s] = "a" => yy[s] = s
+
+Indirect(xx, yy) == 
+    /\ Direct(xx, yy)
+
+InIndirect(xx, yy) ==
+	LET zz == xx IN
+    Indirect(zz, yy)
+
+VARIABLE x, y
+
+vars == <<x, y>>
+
+Init == 
+	/\ x = {}
+	/\ y = {}
+
+Next ==
+    InIndirect(x', y')
+   
+Spec == 
+    Init /\ [][Next]_vars
+
+==========================================================================
+---------------------------- CONFIG Github798N ----------------------------
+SPECIFICATION
+    Spec
+===========================================================================
diff --git a/tlatools/test-model/IllegalOperatorTest.tla b/tlatools/test-model/IllegalOperatorTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d1748f8cbd4c046eb8acf5e4ed6f6b7d94559ce5
--- /dev/null
+++ b/tlatools/test-model/IllegalOperatorTest.tla
@@ -0,0 +1,4 @@
+------------------------- MODULE IllegalOperatorTest ------------------------
+D(Op(_)) == Op(42)
+Foo == D(D)
+=============================================================================
diff --git a/tlatools/test-model/IncompleteNext.cfg b/tlatools/test-model/IncompleteNext.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/IncompleteNext.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/IncompleteNext.tla b/tlatools/test-model/IncompleteNext.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8ce0581a7d9a03619b0a8c51c4f78386fbc94c84
--- /dev/null
+++ b/tlatools/test-model/IncompleteNext.tla
@@ -0,0 +1,7 @@
+--------------------------- MODULE IncompleteNext ---------------------------
+EXTENDS Integers, TLC
+
+VARIABLES x, y
+
+Spec == (x=0) /\ (y=0) /\ [][x'=x+1]_<<x,y>>
+=============================================================================
diff --git a/tlatools/test-model/IncompleteNextMultipleActions.cfg b/tlatools/test-model/IncompleteNextMultipleActions.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/IncompleteNextMultipleActions.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/IncompleteNextMultipleActions.tla b/tlatools/test-model/IncompleteNextMultipleActions.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d39ea6a603226b3bc571f83b2004712c08a49472
--- /dev/null
+++ b/tlatools/test-model/IncompleteNextMultipleActions.tla
@@ -0,0 +1,11 @@
+--------------------------- MODULE IncompleteNextMultipleActions ---------------------------
+EXTENDS Integers, TLC
+
+VARIABLES x, y, z
+
+A1 == x'= x + 1
+
+A2 == x'= 1 /\ y' = 1 /\ z' = 1
+
+Spec == (x=0) /\ (y=0) /\ (z=0) /\ [][A1 \/ A2]_<<x,y,z>>
+=============================================================================
diff --git a/tlatools/test-model/LivenessConstraintWarning.cfg b/tlatools/test-model/LivenessConstraintWarning.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b5dd13e7e5bd487b690bde48224c3aa13061a285
--- /dev/null
+++ b/tlatools/test-model/LivenessConstraintWarning.cfg
@@ -0,0 +1,8 @@
+CONSTRAINT
+Constraint
+
+SPECIFICATION
+Spec
+
+PROPERTY
+Prop
\ No newline at end of file
diff --git a/tlatools/test-model/LivenessConstraintWarning.tla b/tlatools/test-model/LivenessConstraintWarning.tla
new file mode 100644
index 0000000000000000000000000000000000000000..69a8e6a3071d8ce2ba0b9b442924d6f54719abc7
--- /dev/null
+++ b/tlatools/test-model/LivenessConstraintWarning.tla
@@ -0,0 +1,24 @@
+----------------------------- MODULE LivenessConstraintWarning -----------------------------
+EXTENDS Integers, Sequences
+
+VARIABLES pc, history
+vars == <<pc, history>>
+
+Init == /\ pc = "A" 
+        /\ history = <<>>
+
+A == /\ pc  = "A"
+     /\ pc' = "B"
+     /\ history' = history \o <<pc>>
+
+B == /\ pc  = "B"
+     /\ pc' = "A"
+     /\ UNCHANGED history 
+
+Spec == Init /\ [][A \/ B]_vars /\ WF_vars(A \/ B)
+
+Constraint == Len(history) < 3
+
+Prop == <>(pc = "Done")
+
+==============================================================================
diff --git a/tlatools/test-model/Loop/SystemLoop.cfg b/tlatools/test-model/Loop/SystemLoop.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc6955541b374c7b1e9d24f2f1380e98a172e650
--- /dev/null
+++ b/tlatools/test-model/Loop/SystemLoop.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+Liveness
\ No newline at end of file
diff --git a/tlatools/test-model/Loop/SystemLoop.tla b/tlatools/test-model/Loop/SystemLoop.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cdb0b7a89aa75c55e659be950b49ddfbd29ab819
--- /dev/null
+++ b/tlatools/test-model/Loop/SystemLoop.tla
@@ -0,0 +1,23 @@
+----------------------------- MODULE SystemLoop -----------------------------
+
+(* System LOOP as described by Manna & Pneuli on page 423ff *)
+
+VARIABLES x
+vars == <<x>>
+
+Init == x = 0
+
+One == x = 0 /\ x' = 1
+Two == x = 1 /\ x' = 2
+Three == x = 2 /\ x' = 3
+Back == x = 3 /\ x' = 0
+
+Next == One \/ Two \/ Three \/ Back
+
+Spec == Init /\ [][Next]_vars
+
+SpecWeakFair == Spec /\ WF_vars(Next)
+
+Liveness == []<>(x=3)
+
+=============================================================================
diff --git a/tlatools/test-model/Loop/SystemLoopWeakFair.cfg b/tlatools/test-model/Loop/SystemLoopWeakFair.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b8fb187f59c22cd4118b7eac56293c021598a9ca
--- /dev/null
+++ b/tlatools/test-model/Loop/SystemLoopWeakFair.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+SpecWeakFair
+PROPERTY
+Liveness
\ No newline at end of file
diff --git a/tlatools/test-model/MinimalSetOfInitStates.cfg b/tlatools/test-model/MinimalSetOfInitStates.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/MinimalSetOfInitStates.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/MinimalSetOfInitStates.tla b/tlatools/test-model/MinimalSetOfInitStates.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cd0480fa0da8661b7d71c1267dd4311172ab8110
--- /dev/null
+++ b/tlatools/test-model/MinimalSetOfInitStates.tla
@@ -0,0 +1,62 @@
+--------- MODULE MinimalSetOfInitStates ------------
+VARIABLE x
+
+\* x = 0 true because _second_ conjunct is true.
+\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor)
+\* prevents TLC from generating three states with x = 0.  
+I1a == /\ x = 0
+       /\ \/ 1 = 1
+          \/ 2 = 2
+          \/ 3 = 3
+         
+\* x = 1 true because _first_ conjunct is true.
+\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor)
+\* _does not_ prevents TLC from generating three states with x = 1.
+I1b == /\ \/ 1 = 1
+          \/ 2 = 2
+          \/ 3 = 3
+       /\ x = 1
+
+\* x = 2 true because _first_ conjunct is true.
+\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor)
+\* _does not_ prevents TLC from generating three states with x = 2. The "~~" trick prevents
+\* TLC from generating three states with x = 2 because the default case in Tool line 729
+\* simply evaluates the first conjunct without generating states.
+I1c == /\ ~~(\/ 1 = 1
+             \/ 2 = 2
+             \/ 3 = 3)
+       /\ x = 2
+       
+\* Doesn't assign 1 bc pred is false, thus not counted as init.
+I2 == /\ x = 3 
+      /\ \/ 1 = 2
+         \/ 2 = 3
+         \/ 3 = 1
+
+\* Doesn't assign 1 because pred is false,
+\* but counted as init because pred comes first.
+I3 == /\ \/ 1 = 2
+         \/ 2 = 3
+         \/ 3 = 1
+      /\ x = 4
+
+I4 == \/ x = 5
+      \/ x = 6
+        /\ \/ \/ 1 = 2
+              \/ 1 = 1 
+           \/ 2 = 3
+           \/ 3 = 2
+
+I5 == /\ x = 7
+
+Init == \/ I1a
+        \/ I1b
+        \/ I1c
+        \/ I2
+        \/ I3
+        \/ I4
+        \/ I5
+           
+
+Next == UNCHANGED x
+====================================================
diff --git a/tlatools/test-model/MinimalSetOfNextStates.cfg b/tlatools/test-model/MinimalSetOfNextStates.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fd959cf27ec2f77af7c36fb2497c44c0c88b6d0e
--- /dev/null
+++ b/tlatools/test-model/MinimalSetOfNextStates.cfg
@@ -0,0 +1,3 @@
+INIT Init
+NEXT Next
+INVARIANT Inv
diff --git a/tlatools/test-model/MinimalSetOfNextStates.tla b/tlatools/test-model/MinimalSetOfNextStates.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d211a935d3b78beab2c57a059fe3a253eed9a9f2
--- /dev/null
+++ b/tlatools/test-model/MinimalSetOfNextStates.tla
@@ -0,0 +1,61 @@
+--------- MODULE MinimalSetOfNextStates ------------
+VARIABLE x
+
+Init == x = 42
+
+
+\* x = 0 true because _second_ conjunct is true.
+\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor)
+\* prevents TLC from generating duplicate states with x = 0.  
+N1a == /\ x' = 0
+       /\ \/ 1 = 1
+          \/ 2 = 2
+          \/ 3 = 3
+         
+\* x = 1 true because _first_ conjunct is true.
+\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor)
+\* _does not_ prevents TLC from generating duplicate states with x = 1.
+N1b == /\ \/ 1 = 1
+          \/ 2 = 2
+          \/ 3 = 3
+       /\ x' = 1
+
+\* x = 2 true because _first_ conjunct is true.
+\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor)
+\* _does not_ prevents TLC from generating three states with x = 2. The "~~" trick prevents
+\* TLC from generating duplicate states with x = 2 because Tool simply evaluates the first
+\* conjunct without generating states.
+N1c == /\ ~~(\/ 1 = 1
+             \/ 2 = 2
+             \/ 3 = 3)
+       /\ x' = 2
+       
+N2 == /\ x' = 3 \* Pred FALSE
+      /\ \/ 1 = 2
+         \/ 2 = 3
+         \/ 3 = 1
+
+N3 == /\ \/ 1 = 2 \* Pred FALSE
+         \/ 2 = 3
+         \/ 3 = 1
+      /\ x' = 4
+
+N4 == \/ x' = 5
+      \/ x' = 6
+        /\ \/ \/ 1 = 2
+              \/ 1 = 1 
+           \/ 2 = 3
+           \/ 3 = 2
+
+N5 == /\ x' = 7
+
+Next == \/ N1a
+        \/ N1b
+        \/ N1c
+        \/ N2
+        \/ N3
+        \/ N4
+        \/ N5
+           
+Inv == x \in {42,0,1,2,5,6,7}
+====================================================
diff --git a/tlatools/test-model/MinimumDiameter.cfg b/tlatools/test-model/MinimumDiameter.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/MinimumDiameter.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/MinimumDiameter.tla b/tlatools/test-model/MinimumDiameter.tla
new file mode 100644
index 0000000000000000000000000000000000000000..470e11f3c8194fdfb3dd450a9de3f58773009471
--- /dev/null
+++ b/tlatools/test-model/MinimumDiameter.tla
@@ -0,0 +1,5 @@
+--------------------------- MODULE MinimumDiameter ---------------------------
+VARIABLES x
+
+Spec == (x = 0) /\ [][UNCHANGED x]_x
+=============================================================================
diff --git a/tlatools/test-model/MissingBodyInWhile.tla b/tlatools/test-model/MissingBodyInWhile.tla
new file mode 100644
index 0000000000000000000000000000000000000000..12427132e914c5a82ba8057f220f3eb0af5b9dd8
--- /dev/null
+++ b/tlatools/test-model/MissingBodyInWhile.tla
@@ -0,0 +1,10 @@
+------------------------------ MODULE MissingBodyInWhile ------------------------------
+(***************************************************************************
+--algorithm MissingBodyInWhile
+begin
+    while TRUE do
+    end while;
+end algorithm
+ ***************************************************************************)
+
+=============================================================================
diff --git a/tlatools/test-model/MissingBodyInWith.tla b/tlatools/test-model/MissingBodyInWith.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e674a3270aac01eb55c69c8975e79d4118509f8a
--- /dev/null
+++ b/tlatools/test-model/MissingBodyInWith.tla
@@ -0,0 +1,10 @@
+------------------------------ MODULE MissingBodyInWith ------------------------------
+(***************************************************************************
+--algorithm MissingBodyInWith
+begin
+    with x = 0 do
+    end with;
+end algorithm
+ ***************************************************************************)
+
+=============================================================================
diff --git a/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.cfg b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..07b4e01c6a7aa827151c2f43bac3e72657abe857
--- /dev/null
+++ b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.cfg
@@ -0,0 +1,6 @@
+INIT Init
+NEXT Next
+INVARIANT
+InvNoModuleOverwrite
+InvAsModuleOverwrite
+InvAsModuleOverwriteLinear
\ No newline at end of file
diff --git a/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.tla b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6995b9fcce0c7d5351787ea27271fd623e1adbd6
--- /dev/null
+++ b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.tla
@@ -0,0 +1,32 @@
+-------------------------- MODULE ModuleOverwrites --------------------------
+EXTENDS Integers, Sequences
+
+VARIABLES t
+
+Init == t = [ i \in 1..10 |-> i ]
+
+Next == t' = t
+
+-----------------------------------------------------------------------------
+
+noDupesOverwrite(arr, emp) == FALSE
+
+InvAsModuleOverwrite == noDupesOverwrite(t, -1)
+
+-----------------------------------------------------------------------------
+
+noDupesOverwriteLinear(arr, emp) == FALSE
+
+InvAsModuleOverwriteLinear == noDupesOverwriteLinear(t, -1)
+
+-----------------------------------------------------------------------------
+
+noDupes(arr, emp) == LET sub == SelectSeq(t, LAMBDA e: e # -1)
+                         abs(number) == IF number < 0 THEN -1 * number ELSE number
+                     IN IF Len(sub) < 2 THEN TRUE
+                        ELSE \A i \in 1..(Len(sub) - 1):
+                                \A j \in (i+1)..Len(sub):
+                                   abs(sub[i]) # abs(sub[j]) 
+
+InvNoModuleOverwrite == noDupes(t, -1)
+=============================================================================
diff --git a/tlatools/test-model/PlusCalOptions.cfg b/tlatools/test-model/PlusCalOptions.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..79662a1bb6a95c9fe61024917806a8412009a671
--- /dev/null
+++ b/tlatools/test-model/PlusCalOptions.cfg
@@ -0,0 +1,23 @@
+\* CONSTANT definitions
+CONSTANT
+N <- const_158025887640018000
+\* INVARIANT definition
+INVARIANT
+TypeInvariant
+Invariant
+\* PROPERTY definition
+PROPERTY
+Termination
+\* INIT definition
+INIT
+_SpecTEInit
+\* NEXT definition
+NEXT
+_SpecTENext
+\* Action Constraint definition
+\* ACTION_CONSTRAINT
+\* _SpecTEActionConstraint
+CONSTANT
+_TETrace <- def_ov_15802590929252000
+
+\* Generated on Tue Jan 28 16:51:32 PST 2020
diff --git a/tlatools/test-model/PlusCalOptions.tla b/tlatools/test-model/PlusCalOptions.tla
new file mode 100644
index 0000000000000000000000000000000000000000..30c8db874ee13bdd0b2c3488456448a58a8f8540
--- /dev/null
+++ b/tlatools/test-model/PlusCalOptions.tla
@@ -0,0 +1,597 @@
+@!@!@STARTMSG 1000:0 @!@!@
+Will generate a SpecTE file pair if error states are encountered.
+@!@!@ENDMSG 1000 @!@!@
+@!@!@STARTMSG 2262:0 @!@!@
+TLC2 Version 2.15 of Day Month 20??
+@!@!@ENDMSG 2262 @!@!@
+@!@!@STARTMSG 2187:0 @!@!@
+Running breadth-first search Model-Checking with fp 4 and seed 5688400317107100584 with 1 worker on 12 cores with 2428MB heap and 5460MB offheap memory [pid: 81433] (Mac OS X 10.13.6 x86_64, AdoptOpenJDK 13.0.1 x86_64, OffHeapDiskFPSet, DiskStateQueue).
+@!@!@ENDMSG 2187 @!@!@
+@!@!@STARTMSG 2220:0 @!@!@
+Starting SANY...
+@!@!@ENDMSG 2220 @!@!@
+Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/MC.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/QueensPluscal.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/TLC.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Naturals.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Sequences.tla
+Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/FiniteSets.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module QueensPluscal
+Semantic processing of module FiniteSets
+Semantic processing of module TLC
+Semantic processing of module MC
+@!@!@STARTMSG 2219:0 @!@!@
+SANY finished.
+@!@!@ENDMSG 2219 @!@!@
+@!@!@STARTMSG 2185:0 @!@!@
+Starting... (2020-01-28 16:51:31)
+@!@!@ENDMSG 2185 @!@!@
+@!@!@STARTMSG 2212:0 @!@!@
+Implied-temporal checking--satisfiability problem has 1 branches.
+@!@!@ENDMSG 2212 @!@!@
+@!@!@STARTMSG 2189:0 @!@!@
+Computing initial states...
+@!@!@ENDMSG 2189 @!@!@
+@!@!@STARTMSG 2190:0 @!@!@
+Finished computing initial states: 1 distinct state generated at 2020-01-28 16:51:32.
+@!@!@ENDMSG 2190 @!@!@
+@!@!@STARTMSG 2111:1 @!@!@
+Evaluating invariant Invariant failed.
+Attempted to apply function:
+<<1, 1, 1>>
+to argument 0, which is not in the domain of the function.
+@!@!@ENDMSG 2111 @!@!@
+@!@!@STARTMSG 2121:1 @!@!@
+The behavior up to this point is:
+@!@!@ENDMSG 2121 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+1: <Initial predicate>
+/\ todo = {<<>>}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+2: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal>
+/\ todo = {<<1>>, <<2>>, <<3>>}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+3: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal>
+/\ todo = {<<2>>, <<3>>, <<1, 3>>}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+4: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal>
+/\ todo = {<<3>>, <<1, 3>>}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+5: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal>
+/\ todo = {<<1, 3>>, <<3, 1>>}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+6: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal>
+/\ todo = {<<3, 1>>}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2217:4 @!@!@
+7: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal>
+/\ todo = {}
+/\ sols = {}
+/\ pc = "nxtQ"
+
+@!@!@ENDMSG 2217 @!@!@
+@!@!@STARTMSG 2201:0 @!@!@
+The coverage statistics at 2020-01-28 16:51:32
+@!@!@ENDMSG 2201 @!@!@
+@!@!@STARTMSG 2773:0 @!@!@
+<Init line 72, col 1 to line 72, col 4 of module QueensPluscal>: 2:2
+@!@!@ENDMSG 2773 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 73, col 9 to line 75, col 22 of module QueensPluscal: 2
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2772:0 @!@!@
+<nxtQ line 77, col 1 to line 77, col 4 of module QueensPluscal>: 18:45
+@!@!@ENDMSG 2772 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 77, col 12 to line 77, col 22 of module QueensPluscal: 45
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 77, col 12 to line 77, col 13 of module QueensPluscal: 23
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 78, col 15 to line 78, col 23 of module QueensPluscal: 23
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 84, col 35 to line 84, col 42 of module QueensPluscal: 45
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 85, col 42 to line 85, col 64 of module QueensPluscal: 16
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 85, col 50 to line 85, col 64 of module QueensPluscal: 17
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 86, col 42 to line 86, col 67 of module QueensPluscal: 16
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 86, col 51 to line 86, col 66 of module QueensPluscal: 17
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 86, col 51 to line 86, col 54 of module QueensPluscal: 17
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 86, col 63 to line 86, col 66 of module QueensPluscal: 17
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2775:0 @!@!@
+  |||line 83, col 41 to line 83, col 73 of module QueensPluscal: 17:30
+@!@!@ENDMSG 2775 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||line 83, col 68 to line 83, col 71 of module QueensPluscal: 17
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2775:0 @!@!@
+  |||||line 81, col 39 to line 82, col 94 of module QueensPluscal: 17:17
+@!@!@ENDMSG 2775 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||line 81, col 54 to line 82, col 92 of module QueensPluscal: 51
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||line 81, col 56 to line 82, col 92 of module QueensPluscal: 51
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||line 82, col 56 to line 82, col 92 of module QueensPluscal: 68
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 20, col 3 to line 22, col 34 of module QueensPluscal: 68
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||||line 20, col 6 to line 20, col 26 of module QueensPluscal: 68
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||||line 21, col 6 to line 21, col 34 of module QueensPluscal: 51
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||||line 22, col 6 to line 22, col 34 of module QueensPluscal: 34
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 82, col 65 to line 82, col 81 of module QueensPluscal: 68
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 82, col 84 to line 82, col 84 of module QueensPluscal: 68
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 82, col 87 to line 82, col 90 of module QueensPluscal: 68
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||line 81, col 65 to line 81, col 80 of module QueensPluscal: 51
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||line 81, col 47 to line 81, col 50 of module QueensPluscal: 17
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 87, col 42 to line 87, col 80 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 87, col 51 to line 87, col 79 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 87, col 52 to line 87, col 66 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 87, col 76 to line 87, col 79 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2775:0 @!@!@
+  |||line 83, col 41 to line 83, col 73 of module QueensPluscal: 28:71
+@!@!@ENDMSG 2775 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||line 83, col 43 to line 83, col 58 of module QueensPluscal: 22
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||line 83, col 68 to line 83, col 71 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2775:0 @!@!@
+  |||||line 81, col 39 to line 82, col 94 of module QueensPluscal: 28:50
+@!@!@ENDMSG 2775 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||line 81, col 54 to line 82, col 92 of module QueensPluscal: 84
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||line 81, col 56 to line 82, col 92 of module QueensPluscal: 84
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||line 82, col 56 to line 82, col 92 of module QueensPluscal: 78
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 20, col 3 to line 22, col 34 of module QueensPluscal: 78
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||||line 20, col 6 to line 20, col 26 of module QueensPluscal: 78
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||||line 21, col 6 to line 21, col 34 of module QueensPluscal: 52
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||||line 22, col 6 to line 22, col 34 of module QueensPluscal: 35
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 82, col 65 to line 82, col 81 of module QueensPluscal: 78
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 82, col 84 to line 82, col 84 of module QueensPluscal: 78
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||||||||line 82, col 87 to line 82, col 90 of module QueensPluscal: 78
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||||line 81, col 65 to line 81, col 80 of module QueensPluscal: 84
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||||||line 81, col 47 to line 81, col 50 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 88, col 42 to line 88, col 53 of module QueensPluscal: 28
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 79, col 37 to line 79, col 40 of module QueensPluscal: 23
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 89, col 23 to line 89, col 34 of module QueensPluscal: 44
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 90, col 23 to line 90, col 34 of module QueensPluscal: 0
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 91, col 23 to line 91, col 48 of module QueensPluscal: 0
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2772:0 @!@!@
+<Terminating line 94, col 1 to line 94, col 11 of module QueensPluscal>: 0:0
+@!@!@ENDMSG 2772 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 94, col 16 to line 94, col 26 of module QueensPluscal: 16
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 94, col 31 to line 94, col 44 of module QueensPluscal: 0
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2774:0 @!@!@
+<TypeInvariant line 105, col 1 to line 105, col 13 of module QueensPluscal>
+@!@!@ENDMSG 2774 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 106, col 3 to line 107, col 62 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 106, col 6 to line 106, col 62 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 106, col 6 to line 106, col 32 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 106, col 37 to line 106, col 62 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||line 106, col 53 to line 106, col 62 of module QueensPluscal: 34
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |||line 106, col 46 to line 106, col 49 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 107, col 6 to line 107, col 62 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2774:0 @!@!@
+<Invariant line 111, col 1 to line 111, col 9 of module QueensPluscal>
+@!@!@ENDMSG 2774 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  line 112, col 3 to line 113, col 42 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 112, col 6 to line 112, col 29 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  |line 113, col 6 to line 113, col 42 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 113, col 6 to line 113, col 14 of module QueensPluscal: 19
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2221:0 @!@!@
+  ||line 113, col 19 to line 113, col 42 of module QueensPluscal: 1
+@!@!@ENDMSG 2221 @!@!@
+@!@!@STARTMSG 2202:0 @!@!@
+End of statistics.
+@!@!@ENDMSG 2202 @!@!@
+@!@!@STARTMSG 2200:0 @!@!@
+Progress(7) at 2020-01-28 16:51:32: 33 states generated (968 s/min), 19 distinct states found (557 ds/min), 2 states left on queue.
+@!@!@ENDMSG 2200 @!@!@
+@!@!@STARTMSG 2199:0 @!@!@
+33 states generated, 19 distinct states found, 2 states left on queue.
+@!@!@ENDMSG 2199 @!@!@
+@!@!@STARTMSG 2194:0 @!@!@
+The depth of the complete state graph search is 7.
+@!@!@ENDMSG 2194 @!@!@
+@!@!@STARTMSG 2268:0 @!@!@
+The average outdegree of the complete state graph is 1 (minimum is 0, the maximum 3 and the 95th percentile is 3).
+@!@!@ENDMSG 2268 @!@!@
+@!@!@STARTMSG 1000:0 @!@!@
+The model check run produced error-states - we will generate the SpecTE files now.
+@!@!@ENDMSG 1000 @!@!@
+@!@!@STARTMSG 1000:0 @!@!@
+The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/SpecTE.tla has been created.
+@!@!@ENDMSG 1000 @!@!@
+@!@!@STARTMSG 1000:0 @!@!@
+The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/SpecTE.cfg has been created.
+@!@!@ENDMSG 1000 @!@!@
+@!@!@STARTMSG 2186:0 @!@!@
+Finished in 2069ms at (2020-01-28 16:51:32)
+@!@!@ENDMSG 2186 @!@!@
+---- MODULE SpecTE ----
+EXTENDS Sequences, Toolbox, TLC, Naturals
+
+\* 
+\*  QueensPluscal follows
+\* 
+
+(***************************************************************************)
+(* Formulation of the N-queens problem and an iterative algorithm to solve *)
+(* the problem in TLA+. Since there must be exactly one queen in every row *)
+(* we represent placements of queens as functions of the form              *)
+(*    queens \in [ 1..N -> 1..N ]                                          *)
+(* where queens[i] gives the column of the queen in row i. Note that such  *)
+(* a function is just a sequence of length N.                              *)
+(* We will also consider partial solutions, also represented as sequences  *)
+(* of length \leq N.                                                       *)
+(***************************************************************************)
+
+CONSTANT N             \** number of queens and size of the board
+ASSUME N \in Nat \ {0}
+
+(* The following predicate determines if queens i and j attack each other
+   in a placement of queens (represented by a sequence as above). *)
+Attacks(queens,i,j) ==
+  \/ queens[i] = queens[j]                 \** same column
+  \/ queens[i] - queens[j] = i - j         \** first diagonal
+  \/ queens[j] - queens[i] = i - j         \** second diagonal
+
+(* A placement represents a (partial) solution if no two different queens
+   attack each other in it. *)
+IsSolution(queens) ==
+  \A i \in 1 .. Len(queens)-1 : \A j \in (i - 1) .. Len(queens) : 
+       ~ Attacks(queens,i,j) 
+
+(* Compute the set of solutions of the N-queens problem. *)
+Solutions == { queens \in [1..N -> 1..N] : IsSolution(queens) }
+
+(***************************************************************************)
+(* We now describe an algorithm that iteratively computes the set of       *)
+(* solutions of the N-queens problem by successively placing queens.       *)
+(* The current state of the algorithm is given by two variables:           *)
+(* - todo contains a set of partial solutions,                             *)
+(* - sols contains the set of full solutions found so far.                 *)
+(* At every step, the algorithm picks some partial solution and computes   *)
+(* all possible extensions by the next queen. If N queens have been placed *)
+(* these extensions are in fact full solutions, otherwise they are added   *)
+(* to the set todo.                                                        *)
+(***************************************************************************)
+
+(* --algorithm Queens
+     variables
+       todo = { << >> };
+       sols = {};
+
+     begin
+nxtQ:  while todo # {}
+       do
+         with queens \in todo,
+              nxtQ = Len(queens) + 1,
+              cols = { c \in 1..N : ~ \E i \in 1 .. Len(queens) :
+                                      Attacks( Append(queens, c), i, nxtQ ) },
+              exts = { Append(queens,q) : q \in cols }
+         do
+           if (nxtQ = N)
+           then todo := todo \ {queens}; sols := sols \union exts;
+           else todo := (todo \ {queens}) \union exts;
+           end if;
+         end with;
+       end while;
+     end algorithm
+*)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-2ed860b26a15ec8be61ed184eb8c9ae0
+VARIABLES todo, sols, pc
+
+vars == << todo, sols, pc >>
+
+Init == (* Global variables *)
+        /\ todo = { << >> }
+        /\ sols = {}
+        /\ pc = "nxtQ"
+
+nxtQ == /\ pc = "nxtQ"
+        /\ IF todo # {}
+              THEN /\ \E queens \in todo:
+                        LET nxtQ == Len(queens) + 1 IN
+                          LET cols == { c \in 1..N : ~ \E i \in 1 .. Len(queens) :
+                                                       Attacks( Append(queens, c), i, nxtQ ) } IN
+                            LET exts == { Append(queens,q) : q \in cols } IN
+                              IF (nxtQ = N)
+                                 THEN /\ todo' = todo \ {queens}
+                                      /\ sols' = (sols \union exts)
+                                 ELSE /\ todo' = ((todo \ {queens}) \union exts)
+                                      /\ sols' = sols
+                   /\ pc' = "nxtQ"
+              ELSE /\ pc' = "Done"
+                   /\ UNCHANGED << todo, sols >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == nxtQ
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-fcc4c3104f960f6e4881fc76793c3cb5
+
+TypeInvariant ==
+  /\ todo \in SUBSET Seq(1 .. N) /\ \A s \in todo : Len(s) < N
+  /\ sols \in SUBSET Seq(1 .. N) /\ \A s \in sols : Len(s) = N
+
+(* The set of sols contains only solutions, and contains all solutions
+   when todo is empty. *)
+Invariant ==
+  /\ sols \subseteq Solutions
+  /\ todo = {} => Solutions \subseteq sols
+
+(* Assert that no solutions are ever computed so that TLC displays one *)
+NoSolutions == sols = {}
+
+(* Add a fairness condition to ensure progress as long as todo is nonempty *)
+Liveness == WF_vars(nxtQ)
+LiveSpec == Spec /\ Liveness
+
+
+\* 
+\*  SpecTE follows
+\* 
+
+
+\* CONSTANT definitions @modelParameterConstants:0N
+const_158025887640018000 == 
+3
+----
+
+\* TRACE Sub-Action definition 0
+LiveSpec_sa_0 ==
+    (
+        /\ todo = ({<<>>})
+        /\ sols = ({})
+        /\ pc = ("nxtQ")
+        /\ todo' = ({<<1>>, <<2>>, <<3>>})
+        /\ sols' = ({})
+        /\ pc' = ("nxtQ")
+    )
+
+\* TRACE Sub-Action definition 1
+LiveSpec_sa_1 ==
+    (
+        /\ todo = ({<<1>>, <<2>>, <<3>>})
+        /\ sols = ({})
+        /\ pc = ("nxtQ")
+        /\ todo' = ({<<2>>, <<3>>, <<1, 3>>})
+        /\ sols' = ({})
+        /\ pc' = ("nxtQ")
+    )
+
+\* TRACE Sub-Action definition 2
+LiveSpec_sa_2 ==
+    (
+        /\ todo = ({<<2>>, <<3>>, <<1, 3>>})
+        /\ sols = ({})
+        /\ pc = ("nxtQ")
+        /\ todo' = ({<<3>>, <<1, 3>>})
+        /\ sols' = ({})
+        /\ pc' = ("nxtQ")
+    )
+
+\* TRACE Sub-Action definition 3
+LiveSpec_sa_3 ==
+    (
+        /\ todo = ({<<3>>, <<1, 3>>})
+        /\ sols = ({})
+        /\ pc = ("nxtQ")
+        /\ todo' = ({<<1, 3>>, <<3, 1>>})
+        /\ sols' = ({})
+        /\ pc' = ("nxtQ")
+    )
+
+\* TRACE Sub-Action definition 4
+LiveSpec_sa_4 ==
+    (
+        /\ todo = ({<<1, 3>>, <<3, 1>>})
+        /\ sols = ({})
+        /\ pc = ("nxtQ")
+        /\ todo' = ({<<3, 1>>})
+        /\ sols' = ({})
+        /\ pc' = ("nxtQ")
+    )
+
+\* TRACE Sub-Action definition 5
+LiveSpec_sa_5 ==
+    (
+        /\ todo = ({<<3, 1>>})
+        /\ sols = ({})
+        /\ pc = ("nxtQ")
+        /\ todo' = ({})
+        /\ sols' = ({})
+        /\ pc' = ("nxtQ")
+    )
+
+\* TRACE Action Constraint definition traceExploreActionConstraint
+_SpecTEActionConstraint ==
+<<
+LiveSpec_sa_0,
+LiveSpec_sa_1,
+LiveSpec_sa_2,
+LiveSpec_sa_3,
+LiveSpec_sa_4,
+LiveSpec_sa_5
+>>[TLCGet("level")]
+----
+
+def_ov_15802590929252000 == 
+<<
+([todo |-> {<<>>},sols |-> {},pc |-> "nxtQ"]),
+([todo |-> {<<1>>, <<2>>, <<3>>},sols |-> {},pc |-> "nxtQ"]),
+([todo |-> {<<2>>, <<3>>, <<1, 3>>},sols |-> {},pc |-> "nxtQ"]),
+([todo |-> {<<3>>, <<1, 3>>},sols |-> {},pc |-> "nxtQ"]),
+([todo |-> {<<1, 3>>, <<3, 1>>},sols |-> {},pc |-> "nxtQ"]),
+([todo |-> {<<3, 1>>},sols |-> {},pc |-> "nxtQ"]),
+([todo |-> {},sols |-> {},pc |-> "nxtQ"])
+>>
+----
+
+
+\* VARIABLE TraceExp
+
+\* TRACE INIT definition traceExploreInit
+_SpecTEInit ==
+    /\ todo = (
+            {<<>>}
+        )
+    /\ sols = (
+            {}
+        )
+    /\ pc = (
+            "nxtQ"
+        )
+\*     /\ TraceExp = TRUE
+
+----
+
+\* TRACE NEXT definition traceExploreNext
+_SpecTENext ==
+/\  \/ LiveSpec_sa_0
+    \/ LiveSpec_sa_1
+    \/ LiveSpec_sa_2
+    \/ LiveSpec_sa_3
+    \/ LiveSpec_sa_4
+    \/ LiveSpec_sa_5
+\* /\ TraceExp' = TraceExp
+
+
+
+=============================================================================
+\* Modification History
+\* Created Tue Jan 28 16:47:56 PST 2020 by loki
diff --git a/tlatools/test-model/PrintTraceRace/MC.cfg b/tlatools/test-model/PrintTraceRace/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..99575a812c2dc63dbacd75840a606638206d29f9
--- /dev/null
+++ b/tlatools/test-model/PrintTraceRace/MC.cfg
@@ -0,0 +1,10 @@
+\* INIT definition
+INIT
+init_143958263805435000
+\* NEXT definition
+NEXT
+next_143958263806436000
+\* INVARIANT definition
+INVARIANT
+inv_143958263807437000
+\* Generated on Fri Aug 14 13:03:58 PDT 2015
\ No newline at end of file
diff --git a/tlatools/test-model/PrintTraceRace/MC.tla b/tlatools/test-model/PrintTraceRace/MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d2d57c2754978f29f3831b7ac0977c7d043294cc
--- /dev/null
+++ b/tlatools/test-model/PrintTraceRace/MC.tla
@@ -0,0 +1,16 @@
+---- MODULE MC ----
+EXTENDS PrintTraceRace, TLC
+
+\* INIT definition @modelBehaviorInit:0
+init_143958263805435000 ==
+TestInit
+----
+\* NEXT definition @modelBehaviorNext:0
+next_143958263806436000 ==
+TestNext
+----
+\* INVARIANT definition @modelCorrectnessInvariants:0
+inv_143958263807437000 ==
+TestTypeInv
+----
+=============================================================================
diff --git a/tlatools/test-model/PrintTraceRace/PrintTraceRace.tla b/tlatools/test-model/PrintTraceRace/PrintTraceRace.tla
new file mode 100644
index 0000000000000000000000000000000000000000..671671164094477aa0481a60d452b820f7f13826
--- /dev/null
+++ b/tlatools/test-model/PrintTraceRace/PrintTraceRace.tla
@@ -0,0 +1,19 @@
+---------------------- MODULE PrintTraceRace -------------------------
+EXTENDS Naturals, Sequences
+VARIABLES S
+
+TestTypeInv == S \in [ q: Seq({1..2}),
+                       i: 0..3]
+
+TestInit == S = [q |->  << >>,
+                 i |-> 1]
+
+Send == /\ (S.i \leq 2)
+        /\ (S' = [S EXCEPT !.i = (S.i + 1),!.q = Append(S.q,S.i)])
+
+Done == /\ S.i > 2
+        /\ UNCHANGED << S >>
+
+TestNext == \/ Send
+            \/ Done
+============================================================
\ No newline at end of file
diff --git a/tlatools/test-model/RandomElement.cfg b/tlatools/test-model/RandomElement.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/RandomElement.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/RandomElement.tla b/tlatools/test-model/RandomElement.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a6f235186e58fc39f7a4fc638b64fcfb33d2a624
--- /dev/null
+++ b/tlatools/test-model/RandomElement.tla
@@ -0,0 +1,17 @@
+--------------------------- MODULE RandomElement ---------------------------
+EXTENDS Integers, TLC
+
+VARIABLE x, y
+
+Init == /\ x = RandomElement(1..1000)
+        /\ y = 0
+
+Next == /\ \/ x' = RandomElement(1..1000)
+           \/ x' = x
+        /\ y' = y + 1
+        
+
+Spec == Init /\ [][Next]_<<x,y>>
+
+Inv == y < 10
+=============================================================================
diff --git a/tlatools/test-model/RandomElementXandY.cfg b/tlatools/test-model/RandomElementXandY.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/RandomElementXandY.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/RandomElementXandY.tla b/tlatools/test-model/RandomElementXandY.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c804ce1cfa05ed5c002bfe8f7ef388aae6bc25c5
--- /dev/null
+++ b/tlatools/test-model/RandomElementXandY.tla
@@ -0,0 +1,16 @@
+--------------------------- MODULE RandomElementXandY ---------------------------
+EXTENDS Integers, TLC
+
+VARIABLE x, y
+
+Init == /\ x = 0
+        /\ y = 0
+
+Next == /\ x' = RandomElement(0..1)
+        /\ y' = RandomElement(0..1)
+        
+
+Spec == Init /\ [][Next]_<<x,y>>
+
+Inv == x = y
+=============================================================================
diff --git a/tlatools/test-model/RandomSubset.cfg b/tlatools/test-model/RandomSubset.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/RandomSubset.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/RandomSubset.tla b/tlatools/test-model/RandomSubset.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b9def6f097273dfe0999f1ca51e8065a5f8082a8
--- /dev/null
+++ b/tlatools/test-model/RandomSubset.tla
@@ -0,0 +1,17 @@
+--------------------------- MODULE RandomSubset ---------------------------
+EXTENDS Integers, Randomization
+
+VARIABLE x, y, z
+
+Init == /\ x \in RandomSubset(1001, 1..100000000)
+        /\ y \in RandomSubset(2, 100000000..100000010)
+        /\ z = TRUE
+
+Next == /\ UNCHANGED <<x, y>>
+        /\ z' = FALSE
+        
+
+Spec == Init /\ [][Next]_<<x,y,z>>
+
+Inv == z = TRUE
+=============================================================================
diff --git a/tlatools/test-model/RandomSubsetNext.cfg b/tlatools/test-model/RandomSubsetNext.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/RandomSubsetNext.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/RandomSubsetNext.tla b/tlatools/test-model/RandomSubsetNext.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0feeb377e3e9287307aec25b2e898572024cbb0e
--- /dev/null
+++ b/tlatools/test-model/RandomSubsetNext.tla
@@ -0,0 +1,15 @@
+------------------------- MODULE RandomSubsetNext -------------------------
+EXTENDS Integers, Randomization
+
+VARIABLE x, y
+
+Init == /\ x \in RandomSubset(10, 1..1000)
+        /\ y = 0
+
+Next == /\ x'\in RandomSubset(10, 1..1000)
+        /\ y' = y + 1
+        
+Spec == Init /\ [][Next]_<<x,y>>
+
+Inv == y < 10
+=============================================================================
diff --git a/tlatools/test-model/RandomSubsetSetOfFcns.cfg b/tlatools/test-model/RandomSubsetSetOfFcns.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/RandomSubsetSetOfFcns.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/RandomSubsetSetOfFcns.tla b/tlatools/test-model/RandomSubsetSetOfFcns.tla
new file mode 100644
index 0000000000000000000000000000000000000000..968b8467522724e0fa79b7504444686758a79309
--- /dev/null
+++ b/tlatools/test-model/RandomSubsetSetOfFcns.tla
@@ -0,0 +1,21 @@
+--------------------------- MODULE RandomSubsetSetOfFcns ---------------------------
+EXTENDS Integers, Randomization
+
+VARIABLE x
+
+S == {1,2,3,4,5,6,7,8,9}
+T == 1..10
+
+\* [S->T] has 10^8 elements of which we want 1k.
+\* Explicitly enumerating all 10^8 elements will
+\* definitely timeout the tests. |[S -> T]| of
+\* sizes exceeding Integer.MAX_VALUE are not yet
+\* supported.
+Init == /\ x \in RandomSubset(1000, [ S -> T ])
+
+Next == /\ UNCHANGED <<x>>
+
+Spec == Init /\ [][Next]_<<x>>
+
+Inv == TRUE
+=============================================================================
diff --git a/tlatools/test-model/SetPredValue.cfg b/tlatools/test-model/SetPredValue.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/SetPredValue.tla b/tlatools/test-model/SetPredValue.tla
new file mode 100644
index 0000000000000000000000000000000000000000..dbdd30c5d8965f9a09a9ec0777c3e724b0f837a3
--- /dev/null
+++ b/tlatools/test-model/SetPredValue.tla
@@ -0,0 +1,13 @@
+---- MODULE SetPredValue ----
+EXTENDS TLC, FiniteSets, Integers
+
+Range(F) == { F[x] : x \in DOMAIN F }
+Min(n, m) == IF n < m THEN n ELSE m
+
+LP(S, T, p) == LET ss == { s \in SUBSET S : Cardinality(s) = p}
+                          IN { e \in [ T -> ss ] : 
+                                Cardinality(Range(e)) = Min(Cardinality(T), Cardinality(ss)) }
+
+ASSUME PrintT(LP(1..4, {"a","b","c"}, 2))
+
+=============================
diff --git a/tlatools/test-model/StandardModules.cfg b/tlatools/test-model/StandardModules.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..647db03515f896fda377c6760f9db2bb57cc380c
--- /dev/null
+++ b/tlatools/test-model/StandardModules.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
\ No newline at end of file
diff --git a/tlatools/test-model/StandardModules.tla b/tlatools/test-model/StandardModules.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b1316d0c1e7521d9eb90b9ac3cb6409fd5c3bcfa
--- /dev/null
+++ b/tlatools/test-model/StandardModules.tla
@@ -0,0 +1,6 @@
+------------------------------- MODULE StandardModules -------------------------------
+EXTENDS Bags, Sequences, FiniteSets, TLC, RealTime, Naturals, Integers, Randomization
+VARIABLES x
+Init == FALSE /\ x = 0
+Next == [][FALSE /\ x' = x]_<<x>>
+============================================================================
diff --git a/tlatools/test-model/SubsetEq.cfg b/tlatools/test-model/SubsetEq.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/SubsetEq.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/SubsetEq.tla b/tlatools/test-model/SubsetEq.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8f92096de78df8f2ea787cf481ef2c98248b88f4
--- /dev/null
+++ b/tlatools/test-model/SubsetEq.tla
@@ -0,0 +1,22 @@
+------------------------------ MODULE SubsetEq ------------------------------
+EXTENDS Integers, FiniteSets
+
+VARIABLE b
+
+Init == b = TRUE
+
+Next == b' = /\ (SUBSET (1..23) \subseteq SUBSET (1..42))
+             /\ ~(SUBSET (1..42) \subseteq SUBSET (1..23))
+             /\ ~(SUBSET (1..42) \subseteq SUBSET (2..42))
+             /\ (SUBSET (2..42) \subseteq SUBSET (1..42))
+             /\ (SUBSET {1,2,3} \subseteq SUBSET {1,2,3,4})
+             /\ (SUBSET {} \subseteq SUBSET {})
+             /\ (SUBSET {} \subseteq SUBSET {1})
+             /\ ~(SUBSET {1} \subseteq SUBSET {})
+             /\ (SUBSET {1} \subseteq SUBSET Int)
+             /\ (SUBSET {1} \subseteq SUBSET Nat)
+
+Spec == Init /\ [][Next]_<<b>>
+
+Inv == b = TRUE /\ b \in BOOLEAN
+=============================================================================
diff --git a/tlatools/test-model/TLCGetLevel.cfg b/tlatools/test-model/TLCGetLevel.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..774452b7125ec39c1e777f0bb0bb59fa54527b34
--- /dev/null
+++ b/tlatools/test-model/TLCGetLevel.cfg
@@ -0,0 +1,6 @@
+INIT
+Init
+NEXT
+Next
+PROPERTY
+Prop
diff --git a/tlatools/test-model/TLCGetLevel.tla b/tlatools/test-model/TLCGetLevel.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e2522d3c6d17fb40bf8d27a81b8c4f276f171292
--- /dev/null
+++ b/tlatools/test-model/TLCGetLevel.tla
@@ -0,0 +1,16 @@
+--------------------------- MODULE TLCGetLevel ----------------------------
+EXTENDS Integers, TLC
+
+VARIABLES x, y
+
+ASSUME(TLCGet("level") = 0)
+
+Init == /\ x = 0
+        /\ y = TLCGet("level")
+
+Next == /\ x < 3 
+        /\ x' = x + 1
+        /\ y' = TLCGet("level")
+        
+Prop == <>[](x = 2)
+=============================================================================
diff --git a/tlatools/test-model/TLCGetNamedUndefined.cfg b/tlatools/test-model/TLCGetNamedUndefined.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d4ca8dc341ceb40f8d5582f9bc0e52ff7ef99ce3
--- /dev/null
+++ b/tlatools/test-model/TLCGetNamedUndefined.cfg
@@ -0,0 +1,5 @@
+CONSTANTS
+A <- diameter
+B <- distinct
+C <- queue
+D <- level
diff --git a/tlatools/test-model/TLCGetNamedUndefined.tla b/tlatools/test-model/TLCGetNamedUndefined.tla
new file mode 100644
index 0000000000000000000000000000000000000000..854da116c482dac2e94ac6d2cf9f7c340963c4be
--- /dev/null
+++ b/tlatools/test-model/TLCGetNamedUndefined.tla
@@ -0,0 +1,13 @@
+------------------------ MODULE TLCGetNamedUndefined ------------------------
+EXTENDS TLC
+
+diameter == TLCGet("diameter")
+distinct == TLCGet("distinct")
+queue == TLCGet("queue")
+level == TLCGet("level")
+
+CONSTANT A,B,C,D
+
+ASSUME(TRUE)
+
+=============================================================================
diff --git a/tlatools/test-model/TLCGetNonDeterminism.cfg b/tlatools/test-model/TLCGetNonDeterminism.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..774452b7125ec39c1e777f0bb0bb59fa54527b34
--- /dev/null
+++ b/tlatools/test-model/TLCGetNonDeterminism.cfg
@@ -0,0 +1,6 @@
+INIT
+Init
+NEXT
+Next
+PROPERTY
+Prop
diff --git a/tlatools/test-model/TLCGetNonDeterminism.tla b/tlatools/test-model/TLCGetNonDeterminism.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5dcd2adcd3e17249022b33e045120fb64648e206
--- /dev/null
+++ b/tlatools/test-model/TLCGetNonDeterminism.tla
@@ -0,0 +1,48 @@
+--------------------------- MODULE TLCGetNonDeterminism ----------------------------
+EXTENDS Integers, TLC
+
+\* A monotonic counter:
+
+VARIABLES x,y
+
+Init == /\ TLCSet(1, 0)
+        /\ x = 0
+        /\ y = TLCGet(1)
+
+\* Replacing "TLCGet(1) + 1" with "x'" does not trigger the TLC bug.
+\* "x'" is clearly defined but TLCGet(1) returns the local value of 
+\* a non-deterministically chosen worker from the set of TLC workers
+\* (the local values are inconsistent and depend on worker threading).
+\*
+\* In other words, this next-state relation does not correspond to a
+\* single behavior <x=1,y=1>, <x=2,y=2>, <x=3,y=3> but to a set of
+\* behaviors where the value of y \in 1..3 depends on the actual
+\* worker schedule. E.g. with three workers, the behavior might be:
+\* <x=1,y=1-a>, <x=2,y=1-b>, <x=3,y=1-c> with 1-a being the local value
+\* of worker a, 1-b the local value of worker b, ...
+Next == /\ x < 3
+        /\ TLCSet(1, TLCGet(1) + 1) 
+        /\ x' = x + 1
+        /\ y' = TLCGet(1)
+
+\* This bug surfaces at a later stage, when TLC re-creates the error
+\* trace for the violation of Prop below:
+\*   Error: TLC threw an unexpected exception.
+\*   This was probably caused by an error in the spec or model.
+\*   The error occurred when TLC was checking liveness.
+\*   The exception was a tlc2.tool.EvalException
+\*   : Failed to recover the next state from its fingerprint.
+\*
+\* During error trace re-creation (where TLC re-creates the actual states
+\* from a path in the fingerprint graph, TLC always runs with a single worker.
+\* Consequently, it always create the expected <x=1,y=1>, <x=2,y=2>, <x=3,y=3>
+\* behavior.  This behavior however gets rejected because the fingerprints
+\* in the path do not match the fingerprint of the re-created states.
+
+------------------------------------------------------------------------------
+
+\* Force an arbitrary liveness violation by allowing
+\* x to go up to 3 but expect x to remain -lt 3 forever.
+Prop == <>[](x < 3)
+
+=============================================================================
diff --git a/tlatools/test-model/TLCSet.cfg b/tlatools/test-model/TLCSet.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/TLCSet.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/TLCSet.tla b/tlatools/test-model/TLCSet.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f9008c735ddf4dbe5ac5c7fc397f89b82722035f
--- /dev/null
+++ b/tlatools/test-model/TLCSet.tla
@@ -0,0 +1,20 @@
+--------------------------- MODULE TLCSet ---------------------------
+EXTENDS Integers, TLC
+
+VARIABLES x
+
+Init == /\ x = 0
+        /\ TLCGet("diameter") = 1
+        /\ TLCGet("queue") = 0
+        /\ TLCGet("distinct") = 0
+        /\ TLCGet("duration") >= 0 \* Init might take more than a seconds since startup.
+
+Next == /\ x' = x + 1
+        /\ TLCGet("distinct") = x + 1
+		/\ TLCGet("diameter") = x + 1 \* As byproduct check that trace is strictly monotonically increasing.
+        /\ TLCGet("queue") = 0 \* queue is always empty because spec is a single behavior.
+        /\ TLCGet("duration") >= 0 \* Next might evaluate within the first second of model checking.
+        /\ TLCSet("exit", x = 4223)
+
+Spec == Init /\ [][Next]_x
+=============================================================================
diff --git a/tlatools/test-model/TLCSetInit.cfg b/tlatools/test-model/TLCSetInit.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e5a3896997d57e27da8ca619e152212920e137d1
--- /dev/null
+++ b/tlatools/test-model/TLCSetInit.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
diff --git a/tlatools/test-model/TLCSetInit.tla b/tlatools/test-model/TLCSetInit.tla
new file mode 100644
index 0000000000000000000000000000000000000000..20fd8cd5bb4379ba5960037ea42b0e734d7ebb5f
--- /dev/null
+++ b/tlatools/test-model/TLCSetInit.tla
@@ -0,0 +1,9 @@
+----- MODULE TLCSetInit -----
+EXTENDS Integers, TLC
+
+VARIABLES x
+
+Init == x = 1 /\ TLCSet(1,"TLCSet works.")
+Next == x < 10 /\ x' = x + 1 /\ PrintT(TLCGet(1))
+
+=====
\ No newline at end of file
diff --git a/tlatools/test-model/TSnapShot/MC.cfg b/tlatools/test-model/TSnapShot/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..445d96f9cfbddc7d9ae4d51d67bff194b463e7e8
--- /dev/null
+++ b/tlatools/test-model/TSnapShot/MC.cfg
@@ -0,0 +1,25 @@
+\* MV CONSTANT declarations
+CONSTANTS
+w1 = w1
+\* MV CONSTANT declarations
+CONSTANTS
+r1 = r1
+\* MV CONSTANT definitions
+CONSTANT
+Writer <- const_1446808092434108000
+\* MV CONSTANT definitions
+CONSTANT
+Reader <- const_1446808092444109000
+\* CONSTANT definition
+CONSTANT
+Nat <- def_ov_1446808092454110000
+\* CONSTRAINT definition
+CONSTRAINT
+constr_1446808092464111000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_1446808092484113000
+\* INVARIANT definition
+INVARIANT
+inv_1446808092494114000
+\* Generated on Fri Nov 06 12:08:12 CET 2015
\ No newline at end of file
diff --git a/tlatools/test-model/TSnapShot/MC.tla b/tlatools/test-model/TSnapShot/MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c1ac31b94e748c5dddf4b79cef44230878e12558
--- /dev/null
+++ b/tlatools/test-model/TSnapShot/MC.tla
@@ -0,0 +1,52 @@
+---- MODULE MC ----
+EXTENDS TSnapShot, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+w1
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+r1
+----
+
+\* MV CONSTANT definitions Writer
+const_1446808092434108000 == 
+{w1}
+----
+
+\* MV CONSTANT definitions Reader
+const_1446808092444109000 == 
+{r1}
+----
+
+\* CONSTANT definition @modelParameterDefinitions:0
+def_ov_1446808092454110000 ==
+0..2
+----
+\* CONSTRAINT definition @modelParameterContraint:0
+constr_1446808092464111000 ==
+LET maxNat == CHOOSE n \in Nat : \A m \in Nat : n >= m IN
+\A w \in Writer : iwriter[w] < maxNat
+----
+\* Constant expression definition @modelExpressionEval
+const_expr_1446808092474112000 == 
+(w1 :> (w1 :> (w1 :> 0)) @@ r1 :> (w1 :> (w1 :> 0))) \in [Process -> [Writer -> Memory]]
+----
+
+\* Constant expression ASSUME statement @modelExpressionEval
+ASSUME PrintT(<<"$!@$!@$!@$!@$!",const_expr_1446808092474112000>>)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_1446808092484113000 ==
+Spec
+----
+\* INVARIANT definition @modelCorrectnessInvariants:0
+inv_1446808092494114000 ==
+TypeOK2
+----
+=============================================================================
+\* Modification History
+\* Created Fri Nov 06 12:08:12 CET 2015 by markus
diff --git a/tlatools/test-model/TSnapShot/TSnapShot.tla b/tlatools/test-model/TSnapShot/TSnapShot.tla
new file mode 100644
index 0000000000000000000000000000000000000000..79e914c22c23ad5daf27907f7812e591214342a8
--- /dev/null
+++ b/tlatools/test-model/TSnapShot/TSnapShot.tla
@@ -0,0 +1,170 @@
+----------------------------- MODULE TSnapShot -----------------------------
+(****************************************************************************
+This spec describes an algorithm by Afek et al. described in
+
+   https://en.wikipedia.org/wiki/Shared_snapshot_objects
+   
+This is the unbounded memory algorithm described there.
+
+This implementation satisfies the TSnapSpec spec, with internal
+variables hidden.  
+****************************************************************************)
+EXTENDS Integers, TLC
+
+CONSTANTS Reader, Writer
+
+ASSUME Reader \cap Writer = { }
+
+Process == Reader \cup Writer
+(***************************************************************************)
+(* To make it easier to modify the spec to one in which processes write    *)
+(* values in some arbitrary set, we write `Value' instead of `Nat'.        *)
+(***************************************************************************)
+Value == Nat
+Memory == [Writer -> Value]
+InitMemory == [w \in Writer |-> 0]
+NotAMemory ==  [w \in Writer |-> -1]
+
+VARIABLES 
+  rstate,  wstate,  \* These variables are the same as in TSnapSpec, since
+                    \* they describe the externally visible state.
+  rwstate,          \* Like rstate, except has a copy for the writer's state
+                    \* of reading as well
+  mem, iwriter,     \* The same as in TSnapSpec
+  snap,             \* snap[w] is the last snapshot value obtained by
+                    \* writer w during its read. 
+  \* The following are arrays indexed by Reader \cup Writer, since
+  \* both readers and writers perform a read operation.
+  rd1, rd2,         \* Two arrays of arrays holding the values obtained in 
+                    \* the two reads.
+  (*view1,*) view2, \* Two arrays of arrays holding the snap obtained in 
+                    \* the second reads.
+  notRd1, notRd2,   \* Two arrays holding the sets of writers whose values
+                    \* haven't yet been read by each of the reads.
+  moved             \* moved[r][w] is the number of times different
+                    \* values of mem[w] have been read by r during its
+                    \* current read operation.
+ 
+vars ==  <<rstate, wstate, rwstate, mem, iwriter, rd1, rd2, 
+           (*view1,*) view2, notRd1, notRd2, moved, snap>>
+            
+TypeOK == (* /\ rstate  \in [Reader -> Memory \cup {NotAMemory}]
+          /\ wstate  \in [Writer -> {"idle", "busy"}]
+          /\ rwstate \in [Process -> Memory \cup {NotAMemory}]
+          /\ mem     \in Memory
+          /\ iwriter \in [Writer -> Nat]
+          /\ snap    \in [Writer -> Memory]
+          /\ rd1     \in [Process -> Memory]
+          /\ rd2     \in [Process -> Memory] *)
+\*          /\ view1   \in [Process -> [Writer -> Memory]]
+          /\ view2   \in [Process -> [Writer -> Memory]]
+(*          /\ notRd1  \in [Process -> SUBSET Writer]
+          /\ notRd2  \in [Process -> SUBSET Writer]
+          /\ moved   \in [Process -> [Writer -> BOOLEAN]] *)
+TypeOK2 == view2   \in [Process -> [Writer -> Memory]]
+Init == /\ rstate  = [r \in Reader |-> InitMemory]
+        /\ rwstate = [r \in Process |-> InitMemory]
+        /\ wstate  = [r \in Writer |-> "idle"]
+        /\ mem     = InitMemory
+        /\ iwriter = [r \in Writer |-> 0]
+        /\ rd1     = [r \in Process |-> InitMemory]
+        /\ rd2     = [r \in Process |-> InitMemory]
+\*        /\ view1   = [r \in Process |-> [w \in Writer |-> Memory]]
+        /\ view2   = [r \in Process |-> [w \in Writer |-> InitMemory]]
+        /\ notRd1  = [r \in Process |-> Writer]
+        /\ notRd2  = [r \in Process |-> Writer]
+        /\ moved   = [r \in Process |-> [w \in Writer |-> FALSE]]
+        /\ snap    = [r \in Writer |-> InitMemory]
+
+TypeOK3 == PrintT(TypeOK2)
+(***************************************************************************)
+(* Define Assign(array, idx, val) to essentially mean                      *)
+(*                                                                         *)
+(*   array[idx] := val                                                     *)
+(*                                                                         *)
+(* so the reader of the spec doesn't have to deal with EXCEPT.             *)
+(***************************************************************************)
+Assign(array, idx, val) == array' = [array EXCEPT ![idx] = val]
+
+(***************************************************************************)
+(* Define Assign(array, idx1, idx2, val) to essentially mean               *)
+(*                                                                         *)
+(*   array[idx1][idx2] := val                                              *)
+(*                                                                         *)
+(* so the reader of the spec doesn't have to deal with EXCEPT.             *)
+(***************************************************************************)
+AAssign(array, idx1, idx2, val) == array' = [array EXCEPT ![idx1][idx2] = val]
+
+ 
+BeginRead(r) == /\ rstate[r] # NotAMemory
+                /\ Assign(rstate, r, NotAMemory)
+                /\ Assign(rwstate, r, NotAMemory)
+                /\ UNCHANGED <<wstate, mem, iwriter, rd1, rd2, 
+                               view2, notRd1, notRd2, moved, snap>>
+                    
+\*vars ==  <<rstate, wstate, rwstate, mem, iwriter, rd1, rd2, 
+\*           (*view1,*) view2, notRd1, notRd2, moved, snap>>
+DoRd1(r) == /\ rwstate[r] = NotAMemory
+            /\ \E w \in notRd1[r] : /\ Assign(notRd1, r, notRd1[r] \ {w})
+                                    /\ AAssign(rd1, r, w, mem[w])
+\*                                    /\ AAssign(view1, r, w, scan[w])
+            /\ UNCHANGED <<rstate, rwstate, wstate, mem, iwriter, rd2, view2, notRd2,
+                           moved, snap>>
+            
+DoRd2(r) == /\ (rwstate[r] = NotAMemory) /\ (notRd1[r] = {})
+            /\ \E w \in notRd2[r] : /\ Assign(notRd2, r, notRd2[r] \ {w})
+                                    /\ AAssign(rd2, r, w, mem[w])
+                                    /\ AAssign(view2, r, w, snap[w])
+            /\ UNCHANGED <<rstate, rwstate, wstate, mem, iwriter, rd1, notRd1,
+                           moved, snap>>
+
+TryEnding(r) == /\ (rwstate[r] = NotAMemory) /\ (notRd1[r] = {})
+                /\ IF rd1[r] = rd2[r] 
+                     THEN Assign(rwstate, r, rd1[r]) 
+                     ELSE LET F(w) == /\ rd1[r][w] # rd2[r][w]
+                                      /\ moved[r][w]
+                          IN  IF \E w \in Writer : F(w)
+                                THEN Assign(rwstate,r, 
+                                            view2[CHOOSE w \in Writer : F(w)])
+                                ELSE /\ UNCHANGED rwstate
+                                     /\ moved[r] = [w \in Writer |-> 
+                                                     moved[r] \/ (rd1[r][w] # rd2[r][w])]
+                /\ IF r \in Reader
+                     THEN Assign(rstate, r, rwstate'[r])
+                     ELSE UNCHANGED rstate
+                /\ Assign(moved, r, [w \in Writer |-> FALSE])            
+                /\ Assign(notRd1, r, Writer)  
+                /\ Assign(notRd2, r, Writer)    
+                /\ Assign(rd1, r, InitMemory)   \* To reduce state space
+                /\ Assign(rd2, r, InitMemory)   \* To reduce state space
+                /\ Assign(view2, r, InitMemory) \* To reduce state space     
+                /\ UNCHANGED <<wstate, mem, snap, iwriter>>
+
+
+BeginWrite(w) == /\ wstate[w] = "idle"
+                 /\ Assign(wstate, w, "busy")
+                 /\ Assign(iwriter, w, iwriter[w] + 1)
+                 /\ Assign(rwstate, w, NotAMemory)
+                 /\ UNCHANGED <<rstate, mem, rd1, rd2, view2, notRd1, notRd2, moved, snap>>
+                 
+DoWrite(w) == /\ iwriter[w] # mem[w]
+              /\ rwstate[w] # NotAMemory
+              /\ Assign(mem, w, mem[w]+1)
+              /\ Assign(snap, w, rwstate[w])
+              /\ UNCHANGED <<rstate, wstate, iwriter, rd1, rd2, view2, notRd1, 
+                             notRd2, moved>>
+              
+EndWrite(w) == /\ wstate[w] = "busy"
+               /\ Assign(wstate, w, "idle")
+               /\ UNCHANGED <<rstate, rwstate, mem, snap, iwriter, rd1, rd2, 
+                              view2, notRd1, notRd2, moved>>
+
+Next == \/ \E w \in Writer : BeginWrite(w) \/ DoWrite(w) \/ EndWrite(w)
+        \/ \E r \in Reader : 
+              BeginRead(r) \/ DoRd1(r) \/ DoRd2(r) \/ TryEnding(r)
+
+Spec == Init /\ [][Next]_vars                     
+=============================================================================
+\* Modification History
+\* Last modified Mon Nov 02 18:16:48 PST 2015 by lamport
+\* Created Mon Nov 02 13:33:53 PST 2015 by lamport
diff --git a/tlatools/test-model/Test4.cfg b/tlatools/test-model/Test4.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..81bbfad60f51f7a0061d092d12270f050a4ec618
--- /dev/null
+++ b/tlatools/test-model/Test4.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
\ No newline at end of file
diff --git a/tlatools/test-model/Test4.tla b/tlatools/test-model/Test4.tla
new file mode 100644
index 0000000000000000000000000000000000000000..15affe77f9f9ac7f57fda1f6555a7aff3df30cec
--- /dev/null
+++ b/tlatools/test-model/Test4.tla
@@ -0,0 +1,8 @@
+---------- MODULE Test4 -----------
+VARIABLE x
+
+ASSUME TRUE = FALSE \* No, it's not!
+
+Spec == x = 0 /\ [][UNCHANGED x]_x
+
+==============================================
diff --git a/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f9a90034431352c6bccbbc1544fbcec7b99f6eb2
--- /dev/null
+++ b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla
@@ -0,0 +1,9 @@
+---------------- MODULE TraceWithLargeSetOfInitialStatesTest ----------------
+EXTENDS Integers
+
+VARIABLES x,y
+
+Spec == (x \in 1..11 /\ y = FALSE) /\ [][x' = x /\ y' = TRUE]_<<x,y>>
+
+Inv == y = FALSE
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/UserModuleOverride.cfg b/tlatools/test-model/UserModuleOverride.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverride.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+Prop
diff --git a/tlatools/test-model/UserModuleOverride.class b/tlatools/test-model/UserModuleOverride.class
new file mode 100644
index 0000000000000000000000000000000000000000..64e8fae620e506c2d8b882ee131ce78afcf729c1
Binary files /dev/null and b/tlatools/test-model/UserModuleOverride.class differ
diff --git a/tlatools/test-model/UserModuleOverride.java b/tlatools/test-model/UserModuleOverride.java
new file mode 100644
index 0000000000000000000000000000000000000000..6cbac2ff78480e399a26c0b66c2ddb937ef74ea3
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverride.java
@@ -0,0 +1,18 @@
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.Value;
+
+// manually compiled with Java 1.8
+public class UserModuleOverride {
+    public static Value Get() {
+        return BoolValue.ValTrue;
+    }
+
+    public static Value Get2(Value v1) {
+        return BoolValue.ValFalse;
+    }
+
+
+    public static Value Get3() {
+        return BoolValue.ValFalse;
+    }
+}
diff --git a/tlatools/test-model/UserModuleOverride.tla b/tlatools/test-model/UserModuleOverride.tla
new file mode 100644
index 0000000000000000000000000000000000000000..258e5c7efc6a204aa5dc5f861cbfef0baed1d08f
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverride.tla
@@ -0,0 +1,16 @@
+--------------------------- MODULE UserModuleOverride -----------------------
+EXTENDS Naturals
+VARIABLES x
+
+Get == FALSE
+
+Get2 == TRUE
+
+Init == x = Get
+
+Next == x' = Get2
+
+Spec == Init /\ [][Next]_<<x>>
+
+Prop == [](x = TRUE)
+=============================================================================
diff --git a/tlatools/test-model/UserModuleOverrideAnnotation.cfg b/tlatools/test-model/UserModuleOverrideAnnotation.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverrideAnnotation.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+Prop
diff --git a/tlatools/test-model/UserModuleOverrideAnnotation.tla b/tlatools/test-model/UserModuleOverrideAnnotation.tla
new file mode 100644
index 0000000000000000000000000000000000000000..7e85bed2def057647cb62fe79593959cddb3be7a
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverrideAnnotation.tla
@@ -0,0 +1,15 @@
+--------------------------- MODULE UserModuleOverrideAnnotation -----------------------
+EXTENDS Naturals
+VARIABLES x
+
+Get == FALSE
+Get2 == FALSE
+
+Init == x = Get
+
+Next == x' = Get2
+
+Spec == Init /\ [][Next]_<<x>>
+
+Prop == [](x = TRUE)
+=============================================================================
diff --git a/tlatools/test-model/UserModuleOverrideBase.cfg b/tlatools/test-model/UserModuleOverrideBase.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverrideBase.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+Prop
diff --git a/tlatools/test-model/UserModuleOverrideBase.tla b/tlatools/test-model/UserModuleOverrideBase.tla
new file mode 100644
index 0000000000000000000000000000000000000000..77b33caa16d9017b53e11b7bf81204f32d8b0bde
--- /dev/null
+++ b/tlatools/test-model/UserModuleOverrideBase.tla
@@ -0,0 +1,12 @@
+--------------------------- MODULE UserModuleOverrideBase -----------------------
+EXTENDS Naturals, UserModuleOverrideFromJar
+VARIABLES x
+
+Init == x = Get
+
+Next == x' = Get2
+
+Spec == Init /\ [][Next]_<<x>>
+
+Prop == [](x = TRUE)
+=============================================================================
diff --git a/tlatools/test-model/ViewMap.cfg b/tlatools/test-model/ViewMap.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..96d6a36718587ae6ccaab59c90e98f3b9b069693
--- /dev/null
+++ b/tlatools/test-model/ViewMap.cfg
@@ -0,0 +1,24 @@
+\* MV CONSTANT declarations
+CONSTANTS
+c1 = c1
+c2 = c2
+\* MV CONSTANT declarations
+CONSTANTS
+p1 = p1
+\* MV CONSTANT definitions
+CONSTANT
+C = {c1, c2}
+\* MV CONSTANT definitions
+CONSTANT
+P = {p1}
+\* CONSTANT definitions
+CONSTANT
+K = 1
+\* VIEW definition
+VIEW
+view
+\* SPECIFICATION definition
+SPECIFICATION
+Spec
+INVARIANT
+Inv
diff --git a/tlatools/test-model/ViewMap.tla b/tlatools/test-model/ViewMap.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ec4eccb93d6c20e274b6699ac30fa8f5fa2e45f5
--- /dev/null
+++ b/tlatools/test-model/ViewMap.tla
@@ -0,0 +1,102 @@
+--------------------------- MODULE ViewMap ---------------------------
+EXTENDS Integers, FiniteSets, Sequences
+
+CONSTANT P, \* Set of Producers
+         C, \* Set of Consumers
+         K  \* Capacity of buffer/queue 
+
+RemoveLast(seq) == [ i \in 1..(Len(seq) - 1) |-> seq[i] ]
+
+(*
+--algorithm JBuffer01Pcal {
+   variables buffer = <<>>, waitset = {};
+
+   process (p \in P) {
+      lbp: while (Len(buffer) >= K) {
+               (* wait *)
+               waitset := waitset \cup {self};
+          };
+          buffer := Append(buffer, "d");
+          (* notify *)
+          if (waitset # {}) {
+            with (x \in waitset) {
+              waitset := waitset \ {x};
+            }
+          };
+          goto lbp;
+   }
+
+   process (c \in C) {
+      lbc:  while (Len(buffer) = 0) {
+               (* wait *)
+               waitset := waitset \cup {self};
+          };
+          buffer := RemoveLast(buffer);
+          (* notify *)
+          if (waitset # {}) {
+            with (x \in waitset) {
+              waitset := waitset \ {x};
+            }
+          };
+          goto lbc;
+   }
+}
+*)
+\* BEGIN TRANSLATION
+VARIABLES buffer, waitset, pc
+
+vars == << buffer, waitset, pc >>
+
+ProcSet == (P) \cup (C)
+
+Init == (* Global variables *)
+        /\ buffer = <<>>
+        /\ waitset = {}
+        /\ pc = [self \in ProcSet |-> CASE self \in P -> "lbp"
+                                        [] self \in C -> "lbc"]
+
+lbp(self) == /\ pc[self] = "lbp"
+             /\ IF Len(buffer) >= K
+                   THEN /\ waitset' = (waitset \cup {self})
+                        /\ pc' = [pc EXCEPT ![self] = "lbp"]
+                        /\ UNCHANGED buffer
+                   ELSE /\ buffer' = Append(buffer, "d")
+                        /\ IF waitset # {}
+                              THEN /\ \E x \in waitset:
+                                        waitset' = waitset \ {x}
+                              ELSE /\ TRUE
+                                   /\ UNCHANGED waitset
+                        /\ pc' = [pc EXCEPT ![self] = "lbp"]
+
+p(self) == lbp(self)
+
+lbc(self) == /\ pc[self] = "lbc"
+             /\ IF Len(buffer) = 0
+                   THEN /\ waitset' = (waitset \cup {self})
+                        /\ pc' = [pc EXCEPT ![self] = "lbc"]
+                        /\ UNCHANGED buffer
+                   ELSE /\ buffer' = RemoveLast(buffer)
+                        /\ IF waitset # {}
+                              THEN /\ \E x \in waitset:
+                                        waitset' = waitset \ {x}
+                              ELSE /\ TRUE
+                                   /\ UNCHANGED waitset
+                        /\ pc' = [pc EXCEPT ![self] = "lbc"]
+
+c(self) == lbc(self)
+
+Next == (\E self \in P: p(self))
+           \/ (\E self \in C: c(self))
+           \/ (* Disjunct to prevent deadlock on termination *)
+              ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION
+
+Inv == waitset # ProcSet
+
+view == <<buffer, waitset>>
+=============================================================================
diff --git a/tlatools/test-model/VoteProof/VoteProof.tla b/tlatools/test-model/VoteProof/VoteProof.tla
index 75a9c636b5215607e5c6947c1795b6bc985b4628..518f983d1fcd66ad0caff9a504ab45eaed5c28f7 100644
--- a/tlatools/test-model/VoteProof/VoteProof.tla
+++ b/tlatools/test-model/VoteProof/VoteProof.tla
@@ -1,785 +1,785 @@
------------------------------ MODULE VoteProof ------------------------------ 
-
-(*************************************************************************)
-(*                     !!!! REGRESSION TESTS ONLY !!!!                   *)
-(*                                                                       *)
-(* This file is not meant as a reference to TLA+ in general, nor for     *)
-(* VoteProof in particular. Please search the web for an official        *)
-(* version of the VoteProof spec.                                        *)
-(*                                                                       *)
-(*************************************************************************)
-
-EXTENDS Integers , FiniteSets, TLC, TLAPS 
-
-CONSTANT Value,     \* As in module Consensus, the set of choosable values.
-         Acceptor,  \* The set of all acceptors.
-         Quorum     \* The set of all quorums.
-
-ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor
-             /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {}  
- 
-THEOREM QuorumNonEmpty == \A Q \in Quorum : Q # {}
-PROOF BY QA
-
-Ballot == Nat
- 
-(***************************
---algorithm Voting {
-  variables votes = [a \in Acceptor |-> {}],
-            maxBal = [a \in Acceptor |-> -1];
-  define {
-   VotedFor(a, b, v) == <<b, v>> \in votes[a]
-   DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) 
-
-   SafeAt(b, v) ==
-     LET SA[bb \in Ballot] ==
-           \/ bb = 0
-           \/ \E Q \in Quorum :
-                /\ \A a \in Q : maxBal[a] \geq bb
-                /\ \E c \in -1..(bb-1) :
-                     /\ (c # -1) => /\ SA[c]
-                                    /\ \A a \in Q :
-                                         \A w \in Value :
-                                            VotedFor(a, c, w) => (w = v)
-                     /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
-     IN  SA[b]
-    }
-  macro IncreaseMaxBal(b) {
-    when b > maxBal[self] ;
-    maxBal[self] := b
-    }
-    
-  macro VoteFor(b, v) {
-    when /\ maxBal[self] \leq b
-         /\ DidNotVoteIn(self, b)
-         /\ \A p \in Acceptor \ {self} : 
-               \A w \in Value : VotedFor(p, b, w) => (w = v)
-         /\ SafeAt(b, v) ;
-    votes[self]  := votes[self] \cup {<<b, v>>};
-    maxBal[self] := b 
-    }
-    
-  process (acceptor \in Acceptor) {
-    acc : while (TRUE) {
-           with (b \in Ballot) {
-             either IncreaseMaxBal(b)
-             or     with (v \in Value) { VoteFor(b, v) }
-       }
-     }
-    }
-}
-
-The following is the TLA+ specification produced by the translation.
-Blank lines, produced by the translation because of the comments, have
-been deleted.
-****************************)
-\* BEGIN TRANSLATION
-VARIABLES votes, maxBal
-
-(* define statement *)
-VotedFor(a, b, v) == <<b, v>> \in votes[a]
-
-
-
-DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-SafeAt(b, v) ==
-  LET SA[bb \in Ballot] ==
-
-
-
-        \/ bb = 0
-        \/ \E Q \in Quorum :
-             /\ \A a \in Q : maxBal[a] \geq bb
-             /\ \E c \in -1..(bb-1) :
-                  /\ (c # -1) => /\ SA[c]
-                                 /\ \A a \in Q :
-                                      \A w \in Value :
-                                         VotedFor(a, c, w) => (w = v)
-                  /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
-  IN  SA[b]
-
-
-vars == << votes, maxBal >>
-
-ProcSet == (Acceptor)
-
-Init == (* Global variables *)
-        /\ votes = [a \in Acceptor |-> {}]
-        /\ maxBal = [a \in Acceptor |-> -1]
-
-acceptor(self) == \E b \in Ballot:
-                    \/ /\ b > maxBal[self]
-                       /\ maxBal' = [maxBal EXCEPT ![self] = b]
-                       /\ votes' = votes
-                    \/ /\ \E v \in Value:
-                            /\ /\ maxBal[self] \leq b
-                               /\ DidNotVoteIn(self, b)
-                               /\ \A p \in Acceptor \ {self} :
-                                     \A w \in Value : VotedFor(p, b, w) => (w = v)
-                               /\ SafeAt(b, v)
-                            /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}]
-                            /\ maxBal' = [maxBal EXCEPT ![self] = b]
-
-Next == (\E self \in Acceptor: acceptor(self))
-
-Spec == Init /\ [][Next]_vars
-
-\* END TRANSLATION
-
-THEOREM RecursiveFcnOfNat ==
-          ASSUME NEW Def(_,_), 
-                 \A n \in Nat : 
-                    \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n))
-          PROVE  LET f[n \in Nat] == Def(f, n)
-                 IN  f = [n \in Nat |-> Def(f, n)]
-PROOF OMITTED
-
-THEOREM SafeAtProp ==
-  \A b \in Ballot, v \in Value :
-    SafeAt(b, v) =
-      \/ b = 0
-      \/ \E Q \in Quorum :
-           /\ \A a \in Q : maxBal[a] \geq b
-           /\ \E c \in -1..(b-1) :
-                /\ (c # -1) => /\ SafeAt(c, v)
-                               /\ \A a \in Q :
-                                    \A w \in Value :
-                                        VotedFor(a, c, w) => (w = v)
-                /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
-<1>1. SUFFICES ASSUME NEW v \in Value
-               PROVE  \A b \in Ballot : SafeAtProp!(b, v)
-  OBVIOUS
-<1> USE DEF Ballot
-<1> DEFINE Def(SA, bb) ==
-        \/ bb = 0
-        \/ \E Q \in Quorum :
-             /\ \A a \in Q : maxBal[a] \geq bb
-             /\ \E c \in -1..(bb-1) :
-                  /\ (c # -1) => /\ SA[c]
-                                 /\ \A a \in Q :
-                                      \A w \in Value :
-                                         VotedFor(a, c, w) => (w = v)
-                  /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
-      SA[bb \in Ballot] == Def(SA, bb)
-<1>2. \A b : SafeAt(b, v) = SA[b]
-  BY DEF SafeAt
-<1>3. \A n \in Nat : 
-         \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n))
-  BY SMT
-<1> HIDE DEF Def
-<1>4. SA = [b \in Ballot |-> Def(SA, b)]
-  BY ONLY <1>3, RecursiveFcnOfNat    
-<1>5. \A b \in Ballot : SA[b] = Def(SA, b)
-  BY <1>4
-<1>6. QED
-  BY <1>2, <1>5 DEF SafeAt, Def
-
-TypeOK == /\ votes \in [Acceptor -> SUBSET (Ballot \X Value)]
-          /\ maxBal \in [Acceptor -> Ballot \cup {-1}]
-
-ChosenIn(b, v) == \E Q \in Quorum : \A a \in Q : VotedFor(a, b, v)
-
-chosen == {v \in Value : \E b \in Ballot : ChosenIn(b, v)}
-
-AXIOM SimpleNatInduction == \A f : /\ f[0]
-                                   /\ \A n \in Nat : f[n] => f[n+1]
-                                   => \A n \in Nat : f[n]
-
-THEOREM GeneralNatInduction == 
-         \A f : /\ f[0]
-                /\ \A n \in Nat : (\A j \in 0..n : f[j]) => f[n+1]
-                => \A n \in Nat : f[n]
-<1>1. SUFFICES ASSUME NEW f,
-                      f[0],
-                      \A m \in Nat : (\A j \in 0..m : f[j]) => f[m+1],
-                      NEW n \in Nat
-               PROVE  f[n]
-  OBVIOUS
-<1> DEFINE g == [m \in Nat |-> \A j \in 0..m : f[j]]
-<1>2. g[0]
-  BY <1>1, SMT
-<1>3. ASSUME NEW k \in Nat,  g[k]
-      PROVE  g[k+1]
-  BY <1>1, <1>3, SMT
-<1>4. \A k \in Nat : g[k]
-  BY <1>2, <1>3, SimpleNatInduction
-<1>5. QED  
-  BY <1>4, SMT                         
-
-LEMMA SafeLemma == 
-       TypeOK => 
-         \A b \in Ballot :
-           \A v \in Value :
-              SafeAt(b, v) => 
-                \A c \in 0..(b-1) :
-                  \E Q \in Quorum :
-                    \A a \in Q : /\ maxBal[a] >= c
-                                 /\ \/ DidNotVoteIn(a, c)
-                                    \/ VotedFor(a, c, v)
-<1> SUFFICES ASSUME TypeOK
-             PROVE  SafeLemma!2
-  OBVIOUS
-<1> DEFINE P[b \in Ballot] == \A c \in 0..b : SafeLemma!2!(c)
-<1>1. P[0]
-  BY SMT DEF Ballot  
-<1>2. ASSUME NEW b \in Ballot, P[b]
-      PROVE  P[b+1]
-  <2>1. /\ b+1 \in Ballot 
-        /\ (b+1) - 1 = b
-    BY SMT DEF Ballot
-  <2>2. 0..(b+1) = (0..b) \cup {b+1}
-    BY SMT DEF Ballot
-  <2>3. SUFFICES ASSUME NEW v \in Value,
-                        SafeAt(b+1, v),
-                        NEW c \in 0..b
-                 PROVE  \E Q \in Quorum :
-                           \A a \in Q : /\ maxBal[a] >= c
-                                        /\ \/ DidNotVoteIn(a, c)
-                                           \/ VotedFor(a, c, v)
-    BY <1>2, <2>1, <2>2  
-  <2>4. PICK Q \in Quorum : 
-               /\ \A a \in Q : maxBal[a] \geq (b+1)
-               /\ \E cc \in -1..b :
-                    /\ (cc # -1) => /\ SafeAt(cc, v)
-                                    /\ \A a \in Q :
-                                         \A w \in Value :
-                                            VotedFor(a, cc, w) => (w = v)
-                    /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
-    <3>1. b+1 # 0
-      BY SMT DEF Ballot
-    <3>2. SafeAt(b+1,v) = SafeAtProp!(b+1,v)!2
-       BY SafeAtProp, <2>1
-    <3>3. @ = SafeAtProp!(b+1,v)!2!2
-      BY <3>1
-    <3>4. @ = \E Q \in Quorum : 
-               /\ \A a \in Q : maxBal[a] \geq (b+1)
-               /\ \E cc \in -1..b :
-                    /\ (cc # -1) => /\ SafeAt(cc, v)
-                                    /\ \A a \in Q :
-                                         \A w \in Value :
-                                            VotedFor(a, cc, w) => (w = v)
-                    /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
-      BY <2>1
-    <3>5. QED
-      BY <3>2, <3>3, <3>4, <2>3 
-  <2>5. PICK cc \in -1..b : 
-               /\ (cc # -1) => /\ SafeAt(cc, v)
-                               /\ \A a \in Q :
-                                     \A w \in Value :
-                                        VotedFor(a, cc, w) => (w = v)
-               /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
-    BY <2>4
-  <2>6. CASE c > cc
-    BY <2>6, <2>5, <2>4, QA, SMT DEF TypeOK, Ballot
-  <2>7. CASE c =< cc
-    <3>1. /\ cc \in 0..b
-          /\ cc # -1
-      BY <2>7, SMT DEF Ballot
-    <3>2. SafeLemma!2!(cc)!(v)
-      BY <1>2, <3>1
-    <3>3. SafeAt(cc, v)
-      BY <2>5, <3>1
-    <3>4. CASE c = cc
-      BY <3>4, <3>1, <2>5, <2>4, SMT DEF Ballot, TypeOK, DidNotVoteIn
-    <3>5. CASE c < cc
-      <4>1. c \in 0..(cc-1)
-        BY <3>1, <3>5, SMT
-      <4>2. SafeLemma!2!(cc)
-        BY <3>1, <1>2
-      <4>3. QED
-        BY <4>1,<4>2, <4>2, <3>3
-    <3>6. QED
-      BY <2>7, <3>4, <3>5, SMT DEF Ballot
-  <2>8. QED
-    BY <2>6, <2>7, SMT DEF Ballot
-<1>3. \A b \in Ballot : P[b]
-  BY <1>1, <1>2, SimpleNatInduction DEF Ballot
-<1>4. QED
-  BY <1>3, Z3 DEF Ballot \* SMT fails
-
-VInv1 == \A a \in Acceptor, b \in Ballot, v, w \in Value : 
-           VotedFor(a, b, v) /\ VotedFor(a, b, w) => (v = w)
-
-VInv2 == \A a \in Acceptor, b \in Ballot, v \in Value :
-                  VotedFor(a, b, v) => SafeAt(b, v)
-
-VInv3 ==  \A a1, a2 \in Acceptor, b \in Ballot, v1, v2 \in Value : 
-                VotedFor(a1, b, v1) /\ VotedFor(a2, b, v2) => (v1 = v2)
-
-THEOREM VInv3 => VInv1
-BY DEF VInv1, VInv3
-
-LEMMA VT0 == /\ TypeOK
-             /\ VInv1
-             /\ VInv2
-             => \A v, w \in Value, b, c \in Ballot : 
-                   (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
-<1> SUFFICES ASSUME TypeOK, VInv1, VInv2,
-                    NEW v \in Value, NEW w \in Value 
-             PROVE  \A b, c \in Ballot : 
-                      (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
-  OBVIOUS
-<1> P == [b \in Ballot |-> 
-            \A c \in Ballot : 
-            (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)]
-
-<1>1. P[0]
-  BY SMT DEF Ballot (* This works *)
-<1>2. ASSUME NEW b \in Ballot, \A i \in 0..b : P[i]
-      PROVE  P[b+1]
-  <2>1. b+1 \in Ballot
-    BY SMT DEF Ballot
-  <2>2. SUFFICES ASSUME NEW c \in Ballot, b+1 > c, SafeAt(b+1, v), ChosenIn(c, w)
-                 PROVE  v=w
-    BY <2>1
-  <2>3. PICK Q \in Quorum : \A a \in Q : VotedFor(a, c, w)
-    BY <2>2 DEF ChosenIn
-  <2>4. b+1 # 0 /\ ((b+1)-1 = b) 
-    BY SMT DEF Ballot
-  <2>5. PICK QQ \in Quorum, 
-              d \in -1..((b+1)-1) :
-                /\ (d # -1) => /\ SafeAt(d, v)
-                               /\ \A a \in QQ :
-                                    \A x \in Value :
-                                        VotedFor(a, d, x) => (x = v)
-                /\ \A e \in (d+1)..((b+1)-1), a \in QQ : DidNotVoteIn(a, e)
-   BY <2>1, <2>2, <2>4, SafeAtProp
-  <2> PICK aa \in QQ \cap Q : TRUE
-    BY QA
-  <2>6. c \leq d 
-    BY <2>2, <2>3, <2>5, SMT DEF DidNotVoteIn, Ballot 
-  <2>7. d # -1
-    BY <2>6, SMT DEF Ballot
-  <2>8. CASE c = d
-    BY <2>3, <2>5, <2>7, <2>8
-  <2>9. CASE d > c
-    <3>1. SafeAt(d, v)
-      BY <2>5, <2>7
-    <3>2. d \in Ballot /\ d \in 0..b 
-      BY <2>6, SMT DEF Ballot
-    <3>3. P[d]
-      BY <1>2, <3>2
-    <3>4. QED
-      BY <2>2, <2>9, <3>1,  <3>2, <3>3
-  <2>10. QED
-    BY <2>3, <2>6, <2>8, <2>9, SMT DEF Ballot 
-<1>3. \A b \in Ballot : P[b]
-  BY <1>1, <1>2, GeneralNatInduction DEF Ballot
-
-<1>4. QED
-  BY <1>3
- 
-THEOREM VT1 == /\ TypeOK 
-               /\ VInv1
-               /\ VInv2
-               => \A v, w : 
-                    (v \in chosen) /\ (w \in chosen) => (v = w)
-<1>1. SUFFICES ASSUME TypeOK, VInv1, VInv2,
-                      NEW v, NEW w, 
-                      v \in chosen, w \in chosen
-               PROVE  v = w
-  OBVIOUS
-<1>2. v \in Value /\ w \in Value
-  BY <1>1 DEF chosen
-<1>3. PICK b \in Ballot, c \in Ballot : ChosenIn(b, v) /\ ChosenIn(c, w)
-  BY <1>1 DEF chosen
-<1>4. PICK Q \in Quorum, R \in Quorum : 
-         /\ \A a \in Q : VotedFor(a, b, v)
-         /\ \A a \in R : VotedFor(a, c, w)
-  BY <1>3 DEF ChosenIn
-<1>5. PICK av \in Q, aw \in R: /\ VotedFor(av, b, v)
-                               /\ VotedFor(aw, c, w)
-  BY <1>4, QuorumNonEmpty
-<1>6. SafeAt(b, v) /\ SafeAt(c, w)
-  BY <1>1, <1>2, <1>5, QA DEF VInv2
-<1>7. CASE b = c
-  <2> PICK a \in Q \cap R : TRUE
-    BY QA
-  <2>1. /\ VotedFor(a, b, v)
-        /\ VotedFor(a, c, w)
-    BY <1>4
-  <2>2. QED
-    BY <1>1, <1>2, <1>7, <2>1, QA DEF VInv1
-<1>8. CASE b > c
-  BY <1>1, <1>6, <1>3, <1>8, VT0, <1>2 \* <2>1
-<1>9. CASE c > b
-    BY <1>1, <1>6, <1>3, <1>9, VT0, <1>2 \* <2>1
-<1>10. QED
-  BY <1>7, <1>8, <1>9 DEF Ballot
-
-VInv4 == \A a \in Acceptor, b \in Ballot : 
-            maxBal[a] < b => DidNotVoteIn(a, b)
-             
-VInv == TypeOK /\ VInv2 /\ VInv3 /\ VInv4
-
-IncreaseMaxBal(self, b) ==  
-  /\ b > maxBal[self]
-  /\ maxBal' = [maxBal EXCEPT ![self] = b]
-  /\ UNCHANGED votes
-
-VoteFor(self, b, v) == 
-  /\ maxBal[self] \leq b
-  /\ DidNotVoteIn(self, b)
-  /\ \A p \in Acceptor \ {self} :
-        \A w \in Value : VotedFor(p, b, w) => (w = v)
-  /\ SafeAt(b, v)
-  /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}]
-  /\ maxBal' = [maxBal EXCEPT ![self] = b]
-
-BallotAction(self, b) ==
-  \/ IncreaseMaxBal(self, b)
-  \/ \E v \in Value : VoteFor(self, b, v)
-
-ASSUME AcceptorNonempty == Acceptor # {}
-
-LEMMA NextDef ==
-  TypeOK => 
-   (Next =  \E self \in Acceptor :
-                 \E b \in Ballot : BallotAction(self, b) )
-<1> HAVE TypeOK
-<1>2. Next = \E self \in Acceptor: acceptor(self)
- BY AcceptorNonempty DEF Next, ProcSet
-<1>3. @ = NextDef!2!2
-  BY DEF Next, BallotAction, IncreaseMaxBal, VoteFor, ProcSet, acceptor
-<1>4. QED  
-  BY <1>2, <1>3
-
-THEOREM InductiveInvariance == VInv /\ [Next]_vars => VInv'
-<1>1. VInv /\ (vars' = vars) => VInv'
-  \* SMT can't do this because it requires the definition of SafeAt,
-  \* which uses a recursive function definition.
-  <2> SUFFICES ASSUME VInv, vars' = vars
-               PROVE  VInv'
-    OBVIOUS
-  <2> USE DEF vars, VInv
-  <2>1. TypeOK'
-    BY DEF TypeOK
-  <2>2. VInv2'
-    BY DEF VInv2, VotedFor, SafeAt, DidNotVoteIn
-  <2>3. VInv3'
-    BY DEF VInv3, VotedFor
-  <2>4. VInv4'
-    BY DEF VInv4, DidNotVoteIn, VotedFor
-  <2>5. QED
-    BY <2>1, <2>2, <2>3, <2>4 
-
-<1> SUFFICES ASSUME VInv, 
-                    NEW self \in Acceptor,
-                    NEW b \in Ballot, 
-                    BallotAction(self, b)
-             PROVE  VInv'
-  BY <1>1, NextDef DEF VInv
-
-<1>2. TypeOK'
-  <2>1. CASE IncreaseMaxBal(self, b)
-    BY <2>1 DEF IncreaseMaxBal, VInv, TypeOK
-  <2>2. CASE \E v \in Value : VoteFor(self, b, v)
-    BY <2>2 DEF VInv, TypeOK, VoteFor  \* SMT and Zenon failed
-  <2>3. QED
-    BY <2>1, <2>2 DEF BallotAction
-
-<1>3. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value,
-             ~VotedFor(a, c, w), VotedFor(a, c, w)'
-      PROVE  (a = self) /\ (c = b) /\ VoteFor(self, b, w) 
-  BY <1>3, SMT DEF IncreaseMaxBal, VInv, TypeOK, VotedFor, VoteFor, BallotAction
-    
-<1>4. ASSUME NEW a \in Acceptor
-      PROVE  /\ maxBal[a] \in Ballot \cup {-1}
-             /\ maxBal'[a] \in Ballot \cup {-1}
-             /\ maxBal'[a] >= maxBal[a]
-  BY SMT DEF VInv, TypeOK, IncreaseMaxBal, VInv, VoteFor, BallotAction, 
-             DidNotVoteIn, VotedFor, Ballot  
-    
-<1>5. ASSUME NEW c \in Ballot, NEW w \in Value,
-             SafeAt(c, w)
-      PROVE  SafeAt(c, w)'
-  <2>SafeAtPropPrime. SafeAtProp'
-    BY SafeAtProp, PTL
-  <2> DEFINE P[i \in Ballot] == \A j \in 0..i : SafeAt(j, w) => SafeAt(j, w)'
-  <2>1. P[0]
-    <3>1. 0 \in Ballot /\ \A i \in 0..0 : i = 0
-      BY SMT DEF Ballot
-    <3>2. QED
-      BY <2>SafeAtPropPrime, <3>1
-  <2>2. ASSUME NEW d \in Ballot, P[d]
-        PROVE  P[d+1]
-    <3>1. d+1 \in Ballot /\ d+1 # 0
-      BY SMT DEF Ballot
-    <3>2. SUFFICES ASSUME NEW e \in 0..(d+1), SafeAt(e, w)
-                   PROVE  SafeAt(e, w)'
-      BY <3>1
-    <3>3. e \in 0..d \/ e = d+1
-      BY SMT DEF Ballot
-    <3>4. CASE e \in 0..d
-      BY <2>2, <3>2, <3>4
-    <3>5. CASE e = d+1      
-      <4>1. PICK Q \in Quorum : SafeAtProp!(e, w)!2!2!(Q)
-        BY <3>1, <3>2, <3>5, SafeAtProp
-      <4>2. \A aa \in Q : maxBal'[aa] \geq e
-        BY <1>4, <4>1, QA, SMT DEF Ballot
-      <4>3. \E cc \in -1..(e-1) : 
-               /\ (cc # -1) => /\ SafeAt(cc, w)'
-                               /\ \A ax \in Q :
-                                    \A z \in Value :
-                                        VotedFor(ax, cc, z)' => (z = w)
-               /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)'
-        <5>1. PICK cc \in -1..(e-1) : 
-               /\ (cc # -1) => /\ SafeAt(cc, w)
-                               /\ \A ax \in Q :
-                                    \A z \in Value :
-                                        VotedFor(ax, cc, z) => (z = w)
-               /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)
-          BY <1>5, <3>3, <2>2, <4>1
-        <5>2. (cc # -1) => (SafeAt(cc, w) => SafeAt(cc, w)')
-            BY <2>2  DEF Ballot
-        <5>3. CASE IncreaseMaxBal(self, b)
-          <6>1. /\ \A x, y, z : VotedFor(x, y, z)' = VotedFor(x,y,z)
-                /\ \A x, y: DidNotVoteIn(x, y)' = DidNotVoteIn(x, y)
-             BY <5>3 DEF IncreaseMaxBal, VotedFor, DidNotVoteIn
-          <6>2 QED
-            BY <5>1, <5>2, <6>1
-        <5>4. CASE \E v \in Value : VoteFor(self, b, v)
-          <6> PICK v \in Value : VoteFor(self, b, v)
-            BY <5>4
-          <6> QED
-            BY <5>4, <4>2, <5>1, <5>2, <2>2, QA 
-            DEF VoteFor, VotedFor, DidNotVoteIn, VInv, TypeOK, Ballot
-        <5>5. QED
-          BY <5>3, <5>4 DEF BallotAction
-      <4>4.  \/ e = 0
-             \/ \E Q_1 \in Quorum :
-                   /\ \A aa \in Q_1 : maxBal'[aa] \geq e
-                   /\ \E c_1 \in -1..e - 1 :
-                         /\ c_1 # -1
-                            => (/\ SafeAt(c_1, w)'
-                                /\ \A aa \in Q_1 :
-                                      \A w_1 \in Value :
-                                         VotedFor(aa, c_1, w_1)' => w_1 = w)
-                         /\ \A d_1 \in c_1 + 1..e - 1, aa \in Q_1 :
-                               DidNotVoteIn(aa, d_1)'
-         BY <4>2, <4>3, <3>1, <3>5
-      <4>5. e \in Ballot
-        BY <3>1, <3>5
-      <4>6. SafeAt(e, w)' = <4>4
-        BY <2>SafeAtPropPrime, <4>5
-      <4>7. QED
-        BY <4>2, <4>3, <4>6 
-    <3>6. QED
-      BY <3>3, <3>4, <3>5
-  <2>3. \A d \in Ballot : P[d]
-    BY <2>1, <2>2, SimpleNatInduction DEF Ballot
-  <2>4. QED
-    BY <2>3, <1>5, SMT DEF Ballot
-
-<1>6. VInv2'
-  BY <1>3, <1>5, SMT DEF VInv, VInv2, VoteFor
-
-<1>7. VInv3'
-  BY <1>3, SMT DEF VoteFor, DidNotVoteIn, VInv, TypeOK, VotedFor, VInv, VInv3
-
-<1>8. VInv4'
-  BY <1>3, <1>4, SMT DEF Ballot, VInv, VInv4, VoteFor, DidNotVoteIn, TypeOK
-
-<1>9. QED
-  BY <1>2, <1>6, <1>7, <1>8 DEF VInv
-
-THEOREM InitImpliesInv == Init => VInv
-<1> SUFFICES ASSUME Init PROVE VInv
-  OBVIOUS
-<1> USE DEF Init
-<1>1. TypeOK
-  BY DEF TypeOK, ProcSet
-<1>2. VInv2
-  BY  DEF VInv2, VotedFor
-<1>3. VInv3
-  BY DEF VInv3, VotedFor
-<1>4. VInv4
-  BY DEF VInv4, DidNotVoteIn, VotedFor
-<1>5. QED
-  BY <1>1, <1>2, <1>3, <1>4 DEF VInv
-
-THEOREM VT2 == Spec => []VInv
-  BY InductiveInvariance, InitImpliesInv, PTL DEF Spec
-
-C == INSTANCE Consensus 
-
-THEOREM VT3 == Spec => C!Spec 
-<1>1. Init => C!Init
-  BY QuorumNonEmpty, QA, SMT DEF Init, C!Init, chosen, ChosenIn, VotedFor
-
-<1>2. ASSUME VInv, VInv', [Next]_vars
-      PROVE  [C!Next]_C!vars
-  <2> USE VInv
-  <2>1. CASE vars' = vars
-    BY <2>1 DEF vars, C!vars, chosen, ChosenIn, VotedFor
-  <2>2. SUFFICES ASSUME NEW self \in Acceptor,
-                        NEW b \in Ballot, 
-                        BallotAction(self, b)
-                 PROVE  [C!Next]_C!vars
-    BY <1>2, <2>1, NextDef DEF VInv 
-  <2>3. ASSUME IncreaseMaxBal(self, b)
-        PROVE  C!vars' = C!vars
-    BY <2>3 DEF IncreaseMaxBal, C!vars, chosen, ChosenIn, VotedFor
-  <2>4. ASSUME NEW v \in Value,
-               VoteFor(self, b, v)
-        PROVE  [C!Next]_C!vars
-    <3>3. ASSUME NEW w \in chosen
-          PROVE  w \in chosen'
-      BY <3>3, <2>4, <1>2, QA, SMT 
-      DEF VoteFor, VInv, TypeOK, chosen, ChosenIn, VotedFor
-    <3>VT1P. VT1'
-      BY VT1, PTL
-    <3>1. ASSUME NEW w \in chosen,
-                     v \in chosen'
-          PROVE  w = v
-      BY <1>2, <3>1, <3>3, <3>VT1P, SMT DEF VInv, VInv1, VInv3 
-    <3>2. ASSUME NEW w, w \notin chosen, w \in chosen'
-          PROVE  w = v
-      BY <3>2, <2>4, <1>2, QA, SMT
-      DEF chosen, ChosenIn, VotedFor, VoteFor, VInv, TypeOK
-    <3>4. CASE chosen' = chosen
-      BY <3>4 DEF C!vars
-    <3>5. CASE chosen' # chosen
-      <4>1. chosen \subseteq chosen'
-        BY <3>3
-      <4>2. PICK w \in chosen' : w \notin chosen
-        BY <3>5, <4>1
-      <4>3. w = v
-        BY <4>2, <3>2
-      <4>4. chosen' = chosen \cup {v}
-        BY <3>2, <4>1, <4>3
-      <4>5. chosen = {}
-        BY <4>4, <3>1, <3>5
-      <4>6. QED
-        BY <4>4, <4>5 DEF C!Next
-    <3>6. QED
-      BY <3>4, <3>5
-  <2>5. QED
-    BY <2>2, <2>3, <2>4 DEF BallotAction
-    
-<1>3. QED
-  BY <1>1, <1>2, VT2, PTL DEF C!Spec, Spec
-
-ASSUME AcceptorFinite == IsFiniteSet(Acceptor)
-
-ASSUME ValueNonempty == Value # {}
-
-AXIOM SubsetOfFiniteSetFinite == 
-        \A S, T : IsFiniteSet(T) /\ (S \subseteq T) => IsFiniteSet(S)
-
-AXIOM FiniteSetHasMax == 
-        \A S \in SUBSET Int :
-          IsFiniteSet(S) /\ (S # {}) => \E max \in S : \A x \in S : max >= x
-
-AXIOM IntervalFinite == \A i, j \in Int : IsFiniteSet(i..j)
-
-THEOREM VT4 == TypeOK /\ VInv2 /\ VInv3  =>
-                \A Q \in Quorum, b \in Ballot :
-                   (\A a \in Q : (maxBal[a] >= b)) => \E v \in Value : SafeAt(b,v)
-
-<1>1. SUFFICES ASSUME TypeOK, VInv2, VInv3,
-                      NEW Q \in Quorum, NEW b \in Ballot,
-                      (\A a \in Q : (maxBal[a] >= b))
-               PROVE  \E v \in Value : SafeAt(b, v)
-  OBVIOUS
-<1>2. CASE b = 0
-  BY ValueNonempty, <1>1, SafeAtProp, <1>2
-<1>3. SUFFICES ASSUME b # 0
-               PROVE  \E v \in Value : SafeAt(b, v)
-  BY <1>2
-<1>4. SUFFICES \E v \in Value : 
-                 \E c \in -1..(b-1) :
-                   /\ (c # -1) => /\ SafeAt(c, v)
-                                  /\ \A a \in Q :
-                                       \A w \in Value :
-                                           VotedFor(a, c, w) => (w = v)
-                   /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
-  <2>1. SUFFICES ASSUME NEW v \in Value,
-                        <1>4!1!(v)
-                 PROVE  SafeAt(b, v)
-    OBVIOUS
-  <2>2. SafeAtProp!(b, v)
-    BY SafeAtProp
-  <2>3. QED
-    BY <2>1, <2>2, <1>1, <1>3
-<1>5. CASE \A a \in Q, c \in 0..(b-1) : DidNotVoteIn(a, c)
-  <2>1. PICK v \in Value : TRUE
-    BY ValueNonempty
-  <2> -1 \in -1..(b-1)
-    BY SMT DEF Ballot
-  <2>2. WITNESS v \in Value
-  <2>3. WITNESS -1 \in -1..(b-1)
-  <2>4. QED
-    BY <1>5
-<1>6. CASE \E a \in Q, c \in 0..(b-1) : ~DidNotVoteIn(a, c)
-  <2>1. PICK c \in 0..(b-1) : 
-               /\ \E a \in Q : ~DidNotVoteIn(a, c)
-               /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
-    <3> DEFINE S == {c \in 0..(b-1) : \E a \in Q : ~DidNotVoteIn(a, c)}
-    <3>1. S # {}
-        BY <1>6
-    <3>2. PICK c \in S : \A d \in S : c >= d
-      <4>1. (0 \in Int) /\ (b-1 \in Int)  /\ (\A x \in 0..(b-1) : x \in Int)
-        BY <1>3, SMT DEF Ballot
-      <4>2. (S \in SUBSET Int) 
-        BY <4>1
-      <4>3. IsFiniteSet(S)
-        BY <4>1, IntervalFinite, SubsetOfFiniteSetFinite
-      <4>4. QED
-        BY <3>1,  <4>2, <4>3, FiniteSetHasMax
-    <3>3. c \in 0..(b-1)
-      OBVIOUS
-    <3>4. \A d \in (c+1)..(b-1) : d \in 0..(b-1) /\ ~(c >= d)
-      BY <3>3, SMT DEF Ballot  (* This works *)
-    <3>5. \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
-      BY <3>2, <3>4
-    <3>6. \E a \in Q : ~DidNotVoteIn(a, c)
-      BY <3>1
-    <3>7. QED
-      BY  <3>3, <3>5, <3>6
-  <2>2. (c \in -1..(b-1)) /\ (c # -1) /\ (c \in Ballot)
-    BY SMT DEF Ballot
-  <2>3. PICK a0 \in Q : ~DidNotVoteIn(a0, c)
-    BY <2>1
-  <2>4. PICK v \in Value : VotedFor(a0, c, v)
-    BY <2>3 DEF DidNotVoteIn
-  <2>5. \A a \in Q : \A w \in Value :
-           VotedFor(a, c, w) => (w = v)
-    BY <2>2, <2>4, QA, <1>1 DEF VInv3
-  <2>6. SafeAt(c, v)
-    BY <1>1, <2>4, QA, <2>2 DEF VInv2
-  <2>7. QED
-    BY <2>1, <2>2, <2>5, <2>6 
-
-<1>7. QED
-  BY <1>5, <1>6
-
-LiveAssumption ==
-  \E Q \in Quorum, b \in Ballot :
-     \A self \in Q :
-       /\ WF_vars(BallotAction(self, b))
-       /\ [] [\A c \in Ballot : (c > b) => ~ BallotAction(self, c)]_vars
-      
-LiveSpec == Spec /\ LiveAssumption  
-===============================================================================
+----------------------------- MODULE VoteProof ------------------------------ 
+
+(*************************************************************************)
+(*                     !!!! REGRESSION TESTS ONLY !!!!                   *)
+(*                                                                       *)
+(* This file is not meant as a reference to TLA+ in general, nor for     *)
+(* VoteProof in particular. Please search the web for an official        *)
+(* version of the VoteProof spec.                                        *)
+(*                                                                       *)
+(*************************************************************************)
+
+EXTENDS Integers , FiniteSets, TLC, TLAPS 
+
+CONSTANT Value,     \* As in module Consensus, the set of choosable values.
+         Acceptor,  \* The set of all acceptors.
+         Quorum     \* The set of all quorums.
+
+ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor
+             /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {}  
+ 
+THEOREM QuorumNonEmpty == \A Q \in Quorum : Q # {}
+PROOF BY QA
+
+Ballot == Nat
+ 
+(***************************
+--algorithm Voting {
+  variables votes = [a \in Acceptor |-> {}],
+            maxBal = [a \in Acceptor |-> -1];
+  define {
+   VotedFor(a, b, v) == <<b, v>> \in votes[a]
+   DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) 
+
+   SafeAt(b, v) ==
+     LET SA[bb \in Ballot] ==
+           \/ bb = 0
+           \/ \E Q \in Quorum :
+                /\ \A a \in Q : maxBal[a] \geq bb
+                /\ \E c \in -1..(bb-1) :
+                     /\ (c # -1) => /\ SA[c]
+                                    /\ \A a \in Q :
+                                         \A w \in Value :
+                                            VotedFor(a, c, w) => (w = v)
+                     /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
+     IN  SA[b]
+    }
+  macro IncreaseMaxBal(b) {
+    when b > maxBal[self] ;
+    maxBal[self] := b
+    }
+    
+  macro VoteFor(b, v) {
+    when /\ maxBal[self] \leq b
+         /\ DidNotVoteIn(self, b)
+         /\ \A p \in Acceptor \ {self} : 
+               \A w \in Value : VotedFor(p, b, w) => (w = v)
+         /\ SafeAt(b, v) ;
+    votes[self]  := votes[self] \cup {<<b, v>>};
+    maxBal[self] := b 
+    }
+    
+  process (acceptor \in Acceptor) {
+    acc : while (TRUE) {
+           with (b \in Ballot) {
+             either IncreaseMaxBal(b)
+             or     with (v \in Value) { VoteFor(b, v) }
+       }
+     }
+    }
+}
+
+The following is the TLA+ specification produced by the translation.
+Blank lines, produced by the translation because of the comments, have
+been deleted.
+****************************)
+\* BEGIN TRANSLATION
+VARIABLES votes, maxBal
+
+(* define statement *)
+VotedFor(a, b, v) == <<b, v>> \in votes[a]
+
+
+
+DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+SafeAt(b, v) ==
+  LET SA[bb \in Ballot] ==
+
+
+
+        \/ bb = 0
+        \/ \E Q \in Quorum :
+             /\ \A a \in Q : maxBal[a] \geq bb
+             /\ \E c \in -1..(bb-1) :
+                  /\ (c # -1) => /\ SA[c]
+                                 /\ \A a \in Q :
+                                      \A w \in Value :
+                                         VotedFor(a, c, w) => (w = v)
+                  /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
+  IN  SA[b]
+
+
+vars == << votes, maxBal >>
+
+ProcSet == (Acceptor)
+
+Init == (* Global variables *)
+        /\ votes = [a \in Acceptor |-> {}]
+        /\ maxBal = [a \in Acceptor |-> -1]
+
+acceptor(self) == \E b \in Ballot:
+                    \/ /\ b > maxBal[self]
+                       /\ maxBal' = [maxBal EXCEPT ![self] = b]
+                       /\ votes' = votes
+                    \/ /\ \E v \in Value:
+                            /\ /\ maxBal[self] \leq b
+                               /\ DidNotVoteIn(self, b)
+                               /\ \A p \in Acceptor \ {self} :
+                                     \A w \in Value : VotedFor(p, b, w) => (w = v)
+                               /\ SafeAt(b, v)
+                            /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}]
+                            /\ maxBal' = [maxBal EXCEPT ![self] = b]
+
+Next == (\E self \in Acceptor: acceptor(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION
+
+THEOREM RecursiveFcnOfNat ==
+          ASSUME NEW Def(_,_), 
+                 \A n \in Nat : 
+                    \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n))
+          PROVE  LET f[n \in Nat] == Def(f, n)
+                 IN  f = [n \in Nat |-> Def(f, n)]
+PROOF OMITTED
+
+THEOREM SafeAtProp ==
+  \A b \in Ballot, v \in Value :
+    SafeAt(b, v) =
+      \/ b = 0
+      \/ \E Q \in Quorum :
+           /\ \A a \in Q : maxBal[a] \geq b
+           /\ \E c \in -1..(b-1) :
+                /\ (c # -1) => /\ SafeAt(c, v)
+                               /\ \A a \in Q :
+                                    \A w \in Value :
+                                        VotedFor(a, c, w) => (w = v)
+                /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
+<1>1. SUFFICES ASSUME NEW v \in Value
+               PROVE  \A b \in Ballot : SafeAtProp!(b, v)
+  OBVIOUS
+<1> USE DEF Ballot
+<1> DEFINE Def(SA, bb) ==
+        \/ bb = 0
+        \/ \E Q \in Quorum :
+             /\ \A a \in Q : maxBal[a] \geq bb
+             /\ \E c \in -1..(bb-1) :
+                  /\ (c # -1) => /\ SA[c]
+                                 /\ \A a \in Q :
+                                      \A w \in Value :
+                                         VotedFor(a, c, w) => (w = v)
+                  /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
+      SA[bb \in Ballot] == Def(SA, bb)
+<1>2. \A b : SafeAt(b, v) = SA[b]
+  BY DEF SafeAt
+<1>3. \A n \in Nat : 
+         \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n))
+  BY SMT
+<1> HIDE DEF Def
+<1>4. SA = [b \in Ballot |-> Def(SA, b)]
+  BY ONLY <1>3, RecursiveFcnOfNat    
+<1>5. \A b \in Ballot : SA[b] = Def(SA, b)
+  BY <1>4
+<1>6. QED
+  BY <1>2, <1>5 DEF SafeAt, Def
+
+TypeOK == /\ votes \in [Acceptor -> SUBSET (Ballot \X Value)]
+          /\ maxBal \in [Acceptor -> Ballot \cup {-1}]
+
+ChosenIn(b, v) == \E Q \in Quorum : \A a \in Q : VotedFor(a, b, v)
+
+chosen == {v \in Value : \E b \in Ballot : ChosenIn(b, v)}
+
+AXIOM SimpleNatInduction == \A f : /\ f[0]
+                                   /\ \A n \in Nat : f[n] => f[n+1]
+                                   => \A n \in Nat : f[n]
+
+THEOREM GeneralNatInduction == 
+         \A f : /\ f[0]
+                /\ \A n \in Nat : (\A j \in 0..n : f[j]) => f[n+1]
+                => \A n \in Nat : f[n]
+<1>1. SUFFICES ASSUME NEW f,
+                      f[0],
+                      \A m \in Nat : (\A j \in 0..m : f[j]) => f[m+1],
+                      NEW n \in Nat
+               PROVE  f[n]
+  OBVIOUS
+<1> DEFINE g == [m \in Nat |-> \A j \in 0..m : f[j]]
+<1>2. g[0]
+  BY <1>1, SMT
+<1>3. ASSUME NEW k \in Nat,  g[k]
+      PROVE  g[k+1]
+  BY <1>1, <1>3, SMT
+<1>4. \A k \in Nat : g[k]
+  BY <1>2, <1>3, SimpleNatInduction
+<1>5. QED  
+  BY <1>4, SMT                         
+
+LEMMA SafeLemma == 
+       TypeOK => 
+         \A b \in Ballot :
+           \A v \in Value :
+              SafeAt(b, v) => 
+                \A c \in 0..(b-1) :
+                  \E Q \in Quorum :
+                    \A a \in Q : /\ maxBal[a] >= c
+                                 /\ \/ DidNotVoteIn(a, c)
+                                    \/ VotedFor(a, c, v)
+<1> SUFFICES ASSUME TypeOK
+             PROVE  SafeLemma!2
+  OBVIOUS
+<1> DEFINE P[b \in Ballot] == \A c \in 0..b : SafeLemma!2!(c)
+<1>1. P[0]
+  BY SMT DEF Ballot  
+<1>2. ASSUME NEW b \in Ballot, P[b]
+      PROVE  P[b+1]
+  <2>1. /\ b+1 \in Ballot 
+        /\ (b+1) - 1 = b
+    BY SMT DEF Ballot
+  <2>2. 0..(b+1) = (0..b) \cup {b+1}
+    BY SMT DEF Ballot
+  <2>3. SUFFICES ASSUME NEW v \in Value,
+                        SafeAt(b+1, v),
+                        NEW c \in 0..b
+                 PROVE  \E Q \in Quorum :
+                           \A a \in Q : /\ maxBal[a] >= c
+                                        /\ \/ DidNotVoteIn(a, c)
+                                           \/ VotedFor(a, c, v)
+    BY <1>2, <2>1, <2>2  
+  <2>4. PICK Q \in Quorum : 
+               /\ \A a \in Q : maxBal[a] \geq (b+1)
+               /\ \E cc \in -1..b :
+                    /\ (cc # -1) => /\ SafeAt(cc, v)
+                                    /\ \A a \in Q :
+                                         \A w \in Value :
+                                            VotedFor(a, cc, w) => (w = v)
+                    /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
+    <3>1. b+1 # 0
+      BY SMT DEF Ballot
+    <3>2. SafeAt(b+1,v) = SafeAtProp!(b+1,v)!2
+       BY SafeAtProp, <2>1
+    <3>3. @ = SafeAtProp!(b+1,v)!2!2
+      BY <3>1
+    <3>4. @ = \E Q \in Quorum : 
+               /\ \A a \in Q : maxBal[a] \geq (b+1)
+               /\ \E cc \in -1..b :
+                    /\ (cc # -1) => /\ SafeAt(cc, v)
+                                    /\ \A a \in Q :
+                                         \A w \in Value :
+                                            VotedFor(a, cc, w) => (w = v)
+                    /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
+      BY <2>1
+    <3>5. QED
+      BY <3>2, <3>3, <3>4, <2>3 
+  <2>5. PICK cc \in -1..b : 
+               /\ (cc # -1) => /\ SafeAt(cc, v)
+                               /\ \A a \in Q :
+                                     \A w \in Value :
+                                        VotedFor(a, cc, w) => (w = v)
+               /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
+    BY <2>4
+  <2>6. CASE c > cc
+    BY <2>6, <2>5, <2>4, QA, SMT DEF TypeOK, Ballot
+  <2>7. CASE c =< cc
+    <3>1. /\ cc \in 0..b
+          /\ cc # -1
+      BY <2>7, SMT DEF Ballot
+    <3>2. SafeLemma!2!(cc)!(v)
+      BY <1>2, <3>1
+    <3>3. SafeAt(cc, v)
+      BY <2>5, <3>1
+    <3>4. CASE c = cc
+      BY <3>4, <3>1, <2>5, <2>4, SMT DEF Ballot, TypeOK, DidNotVoteIn
+    <3>5. CASE c < cc
+      <4>1. c \in 0..(cc-1)
+        BY <3>1, <3>5, SMT
+      <4>2. SafeLemma!2!(cc)
+        BY <3>1, <1>2
+      <4>3. QED
+        BY <4>1,<4>2, <4>2, <3>3
+    <3>6. QED
+      BY <2>7, <3>4, <3>5, SMT DEF Ballot
+  <2>8. QED
+    BY <2>6, <2>7, SMT DEF Ballot
+<1>3. \A b \in Ballot : P[b]
+  BY <1>1, <1>2, SimpleNatInduction DEF Ballot
+<1>4. QED
+  BY <1>3, Z3 DEF Ballot \* SMT fails
+
+VInv1 == \A a \in Acceptor, b \in Ballot, v, w \in Value : 
+           VotedFor(a, b, v) /\ VotedFor(a, b, w) => (v = w)
+
+VInv2 == \A a \in Acceptor, b \in Ballot, v \in Value :
+                  VotedFor(a, b, v) => SafeAt(b, v)
+
+VInv3 ==  \A a1, a2 \in Acceptor, b \in Ballot, v1, v2 \in Value : 
+                VotedFor(a1, b, v1) /\ VotedFor(a2, b, v2) => (v1 = v2)
+
+THEOREM VInv3 => VInv1
+BY DEF VInv1, VInv3
+
+LEMMA VT0 == /\ TypeOK
+             /\ VInv1
+             /\ VInv2
+             => \A v, w \in Value, b, c \in Ballot : 
+                   (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
+<1> SUFFICES ASSUME TypeOK, VInv1, VInv2,
+                    NEW v \in Value, NEW w \in Value 
+             PROVE  \A b, c \in Ballot : 
+                      (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
+  OBVIOUS
+<1> P == [b \in Ballot |-> 
+            \A c \in Ballot : 
+            (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)]
+
+<1>1. P[0]
+  BY SMT DEF Ballot (* This works *)
+<1>2. ASSUME NEW b \in Ballot, \A i \in 0..b : P[i]
+      PROVE  P[b+1]
+  <2>1. b+1 \in Ballot
+    BY SMT DEF Ballot
+  <2>2. SUFFICES ASSUME NEW c \in Ballot, b+1 > c, SafeAt(b+1, v), ChosenIn(c, w)
+                 PROVE  v=w
+    BY <2>1
+  <2>3. PICK Q \in Quorum : \A a \in Q : VotedFor(a, c, w)
+    BY <2>2 DEF ChosenIn
+  <2>4. b+1 # 0 /\ ((b+1)-1 = b) 
+    BY SMT DEF Ballot
+  <2>5. PICK QQ \in Quorum, 
+              d \in -1..((b+1)-1) :
+                /\ (d # -1) => /\ SafeAt(d, v)
+                               /\ \A a \in QQ :
+                                    \A x \in Value :
+                                        VotedFor(a, d, x) => (x = v)
+                /\ \A e \in (d+1)..((b+1)-1), a \in QQ : DidNotVoteIn(a, e)
+   BY <2>1, <2>2, <2>4, SafeAtProp
+  <2> PICK aa \in QQ \cap Q : TRUE
+    BY QA
+  <2>6. c \leq d 
+    BY <2>2, <2>3, <2>5, SMT DEF DidNotVoteIn, Ballot 
+  <2>7. d # -1
+    BY <2>6, SMT DEF Ballot
+  <2>8. CASE c = d
+    BY <2>3, <2>5, <2>7, <2>8
+  <2>9. CASE d > c
+    <3>1. SafeAt(d, v)
+      BY <2>5, <2>7
+    <3>2. d \in Ballot /\ d \in 0..b 
+      BY <2>6, SMT DEF Ballot
+    <3>3. P[d]
+      BY <1>2, <3>2
+    <3>4. QED
+      BY <2>2, <2>9, <3>1,  <3>2, <3>3
+  <2>10. QED
+    BY <2>3, <2>6, <2>8, <2>9, SMT DEF Ballot 
+<1>3. \A b \in Ballot : P[b]
+  BY <1>1, <1>2, GeneralNatInduction DEF Ballot
+
+<1>4. QED
+  BY <1>3
+ 
+THEOREM VT1 == /\ TypeOK 
+               /\ VInv1
+               /\ VInv2
+               => \A v, w : 
+                    (v \in chosen) /\ (w \in chosen) => (v = w)
+<1>1. SUFFICES ASSUME TypeOK, VInv1, VInv2,
+                      NEW v, NEW w, 
+                      v \in chosen, w \in chosen
+               PROVE  v = w
+  OBVIOUS
+<1>2. v \in Value /\ w \in Value
+  BY <1>1 DEF chosen
+<1>3. PICK b \in Ballot, c \in Ballot : ChosenIn(b, v) /\ ChosenIn(c, w)
+  BY <1>1 DEF chosen
+<1>4. PICK Q \in Quorum, R \in Quorum : 
+         /\ \A a \in Q : VotedFor(a, b, v)
+         /\ \A a \in R : VotedFor(a, c, w)
+  BY <1>3 DEF ChosenIn
+<1>5. PICK av \in Q, aw \in R: /\ VotedFor(av, b, v)
+                               /\ VotedFor(aw, c, w)
+  BY <1>4, QuorumNonEmpty
+<1>6. SafeAt(b, v) /\ SafeAt(c, w)
+  BY <1>1, <1>2, <1>5, QA DEF VInv2
+<1>7. CASE b = c
+  <2> PICK a \in Q \cap R : TRUE
+    BY QA
+  <2>1. /\ VotedFor(a, b, v)
+        /\ VotedFor(a, c, w)
+    BY <1>4
+  <2>2. QED
+    BY <1>1, <1>2, <1>7, <2>1, QA DEF VInv1
+<1>8. CASE b > c
+  BY <1>1, <1>6, <1>3, <1>8, VT0, <1>2 \* <2>1
+<1>9. CASE c > b
+    BY <1>1, <1>6, <1>3, <1>9, VT0, <1>2 \* <2>1
+<1>10. QED
+  BY <1>7, <1>8, <1>9 DEF Ballot
+
+VInv4 == \A a \in Acceptor, b \in Ballot : 
+            maxBal[a] < b => DidNotVoteIn(a, b)
+             
+VInv == TypeOK /\ VInv2 /\ VInv3 /\ VInv4
+
+IncreaseMaxBal(self, b) ==  
+  /\ b > maxBal[self]
+  /\ maxBal' = [maxBal EXCEPT ![self] = b]
+  /\ UNCHANGED votes
+
+VoteFor(self, b, v) == 
+  /\ maxBal[self] \leq b
+  /\ DidNotVoteIn(self, b)
+  /\ \A p \in Acceptor \ {self} :
+        \A w \in Value : VotedFor(p, b, w) => (w = v)
+  /\ SafeAt(b, v)
+  /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}]
+  /\ maxBal' = [maxBal EXCEPT ![self] = b]
+
+BallotAction(self, b) ==
+  \/ IncreaseMaxBal(self, b)
+  \/ \E v \in Value : VoteFor(self, b, v)
+
+ASSUME AcceptorNonempty == Acceptor # {}
+
+LEMMA NextDef ==
+  TypeOK => 
+   (Next =  \E self \in Acceptor :
+                 \E b \in Ballot : BallotAction(self, b) )
+<1> HAVE TypeOK
+<1>2. Next = \E self \in Acceptor: acceptor(self)
+ BY AcceptorNonempty DEF Next, ProcSet
+<1>3. @ = NextDef!2!2
+  BY DEF Next, BallotAction, IncreaseMaxBal, VoteFor, ProcSet, acceptor
+<1>4. QED  
+  BY <1>2, <1>3
+
+THEOREM InductiveInvariance == VInv /\ [Next]_vars => VInv'
+<1>1. VInv /\ (vars' = vars) => VInv'
+  \* SMT can't do this because it requires the definition of SafeAt,
+  \* which uses a recursive function definition.
+  <2> SUFFICES ASSUME VInv, vars' = vars
+               PROVE  VInv'
+    OBVIOUS
+  <2> USE DEF vars, VInv
+  <2>1. TypeOK'
+    BY DEF TypeOK
+  <2>2. VInv2'
+    BY DEF VInv2, VotedFor, SafeAt, DidNotVoteIn
+  <2>3. VInv3'
+    BY DEF VInv3, VotedFor
+  <2>4. VInv4'
+    BY DEF VInv4, DidNotVoteIn, VotedFor
+  <2>5. QED
+    BY <2>1, <2>2, <2>3, <2>4 
+
+<1> SUFFICES ASSUME VInv, 
+                    NEW self \in Acceptor,
+                    NEW b \in Ballot, 
+                    BallotAction(self, b)
+             PROVE  VInv'
+  BY <1>1, NextDef DEF VInv
+
+<1>2. TypeOK'
+  <2>1. CASE IncreaseMaxBal(self, b)
+    BY <2>1 DEF IncreaseMaxBal, VInv, TypeOK
+  <2>2. CASE \E v \in Value : VoteFor(self, b, v)
+    BY <2>2 DEF VInv, TypeOK, VoteFor  \* SMT and Zenon failed
+  <2>3. QED
+    BY <2>1, <2>2 DEF BallotAction
+
+<1>3. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value,
+             ~VotedFor(a, c, w), VotedFor(a, c, w)'
+      PROVE  (a = self) /\ (c = b) /\ VoteFor(self, b, w) 
+  BY <1>3, SMT DEF IncreaseMaxBal, VInv, TypeOK, VotedFor, VoteFor, BallotAction
+    
+<1>4. ASSUME NEW a \in Acceptor
+      PROVE  /\ maxBal[a] \in Ballot \cup {-1}
+             /\ maxBal'[a] \in Ballot \cup {-1}
+             /\ maxBal'[a] >= maxBal[a]
+  BY SMT DEF VInv, TypeOK, IncreaseMaxBal, VInv, VoteFor, BallotAction, 
+             DidNotVoteIn, VotedFor, Ballot  
+    
+<1>5. ASSUME NEW c \in Ballot, NEW w \in Value,
+             SafeAt(c, w)
+      PROVE  SafeAt(c, w)'
+  <2>SafeAtPropPrime. SafeAtProp'
+    BY SafeAtProp, PTL
+  <2> DEFINE P[i \in Ballot] == \A j \in 0..i : SafeAt(j, w) => SafeAt(j, w)'
+  <2>1. P[0]
+    <3>1. 0 \in Ballot /\ \A i \in 0..0 : i = 0
+      BY SMT DEF Ballot
+    <3>2. QED
+      BY <2>SafeAtPropPrime, <3>1
+  <2>2. ASSUME NEW d \in Ballot, P[d]
+        PROVE  P[d+1]
+    <3>1. d+1 \in Ballot /\ d+1 # 0
+      BY SMT DEF Ballot
+    <3>2. SUFFICES ASSUME NEW e \in 0..(d+1), SafeAt(e, w)
+                   PROVE  SafeAt(e, w)'
+      BY <3>1
+    <3>3. e \in 0..d \/ e = d+1
+      BY SMT DEF Ballot
+    <3>4. CASE e \in 0..d
+      BY <2>2, <3>2, <3>4
+    <3>5. CASE e = d+1      
+      <4>1. PICK Q \in Quorum : SafeAtProp!(e, w)!2!2!(Q)
+        BY <3>1, <3>2, <3>5, SafeAtProp
+      <4>2. \A aa \in Q : maxBal'[aa] \geq e
+        BY <1>4, <4>1, QA, SMT DEF Ballot
+      <4>3. \E cc \in -1..(e-1) : 
+               /\ (cc # -1) => /\ SafeAt(cc, w)'
+                               /\ \A ax \in Q :
+                                    \A z \in Value :
+                                        VotedFor(ax, cc, z)' => (z = w)
+               /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)'
+        <5>1. PICK cc \in -1..(e-1) : 
+               /\ (cc # -1) => /\ SafeAt(cc, w)
+                               /\ \A ax \in Q :
+                                    \A z \in Value :
+                                        VotedFor(ax, cc, z) => (z = w)
+               /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)
+          BY <1>5, <3>3, <2>2, <4>1
+        <5>2. (cc # -1) => (SafeAt(cc, w) => SafeAt(cc, w)')
+            BY <2>2  DEF Ballot
+        <5>3. CASE IncreaseMaxBal(self, b)
+          <6>1. /\ \A x, y, z : VotedFor(x, y, z)' = VotedFor(x,y,z)
+                /\ \A x, y: DidNotVoteIn(x, y)' = DidNotVoteIn(x, y)
+             BY <5>3 DEF IncreaseMaxBal, VotedFor, DidNotVoteIn
+          <6>2 QED
+            BY <5>1, <5>2, <6>1
+        <5>4. CASE \E v \in Value : VoteFor(self, b, v)
+          <6> PICK v \in Value : VoteFor(self, b, v)
+            BY <5>4
+          <6> QED
+            BY <5>4, <4>2, <5>1, <5>2, <2>2, QA 
+            DEF VoteFor, VotedFor, DidNotVoteIn, VInv, TypeOK, Ballot
+        <5>5. QED
+          BY <5>3, <5>4 DEF BallotAction
+      <4>4.  \/ e = 0
+             \/ \E Q_1 \in Quorum :
+                   /\ \A aa \in Q_1 : maxBal'[aa] \geq e
+                   /\ \E c_1 \in -1..e - 1 :
+                         /\ c_1 # -1
+                            => (/\ SafeAt(c_1, w)'
+                                /\ \A aa \in Q_1 :
+                                      \A w_1 \in Value :
+                                         VotedFor(aa, c_1, w_1)' => w_1 = w)
+                         /\ \A d_1 \in c_1 + 1..e - 1, aa \in Q_1 :
+                               DidNotVoteIn(aa, d_1)'
+         BY <4>2, <4>3, <3>1, <3>5
+      <4>5. e \in Ballot
+        BY <3>1, <3>5
+      <4>6. SafeAt(e, w)' = <4>4
+        BY <2>SafeAtPropPrime, <4>5
+      <4>7. QED
+        BY <4>2, <4>3, <4>6 
+    <3>6. QED
+      BY <3>3, <3>4, <3>5
+  <2>3. \A d \in Ballot : P[d]
+    BY <2>1, <2>2, SimpleNatInduction DEF Ballot
+  <2>4. QED
+    BY <2>3, <1>5, SMT DEF Ballot
+
+<1>6. VInv2'
+  BY <1>3, <1>5, SMT DEF VInv, VInv2, VoteFor
+
+<1>7. VInv3'
+  BY <1>3, SMT DEF VoteFor, DidNotVoteIn, VInv, TypeOK, VotedFor, VInv, VInv3
+
+<1>8. VInv4'
+  BY <1>3, <1>4, SMT DEF Ballot, VInv, VInv4, VoteFor, DidNotVoteIn, TypeOK
+
+<1>9. QED
+  BY <1>2, <1>6, <1>7, <1>8 DEF VInv
+
+THEOREM InitImpliesInv == Init => VInv
+<1> SUFFICES ASSUME Init PROVE VInv
+  OBVIOUS
+<1> USE DEF Init
+<1>1. TypeOK
+  BY DEF TypeOK, ProcSet
+<1>2. VInv2
+  BY  DEF VInv2, VotedFor
+<1>3. VInv3
+  BY DEF VInv3, VotedFor
+<1>4. VInv4
+  BY DEF VInv4, DidNotVoteIn, VotedFor
+<1>5. QED
+  BY <1>1, <1>2, <1>3, <1>4 DEF VInv
+
+THEOREM VT2 == Spec => []VInv
+  BY InductiveInvariance, InitImpliesInv, PTL DEF Spec
+
+C == INSTANCE Consensus 
+
+THEOREM VT3 == Spec => C!Spec 
+<1>1. Init => C!Init
+  BY QuorumNonEmpty, QA, SMT DEF Init, C!Init, chosen, ChosenIn, VotedFor
+
+<1>2. ASSUME VInv, VInv', [Next]_vars
+      PROVE  [C!Next]_C!vars
+  <2> USE VInv
+  <2>1. CASE vars' = vars
+    BY <2>1 DEF vars, C!vars, chosen, ChosenIn, VotedFor
+  <2>2. SUFFICES ASSUME NEW self \in Acceptor,
+                        NEW b \in Ballot, 
+                        BallotAction(self, b)
+                 PROVE  [C!Next]_C!vars
+    BY <1>2, <2>1, NextDef DEF VInv 
+  <2>3. ASSUME IncreaseMaxBal(self, b)
+        PROVE  C!vars' = C!vars
+    BY <2>3 DEF IncreaseMaxBal, C!vars, chosen, ChosenIn, VotedFor
+  <2>4. ASSUME NEW v \in Value,
+               VoteFor(self, b, v)
+        PROVE  [C!Next]_C!vars
+    <3>3. ASSUME NEW w \in chosen
+          PROVE  w \in chosen'
+      BY <3>3, <2>4, <1>2, QA, SMT 
+      DEF VoteFor, VInv, TypeOK, chosen, ChosenIn, VotedFor
+    <3>VT1P. VT1'
+      BY VT1, PTL
+    <3>1. ASSUME NEW w \in chosen,
+                     v \in chosen'
+          PROVE  w = v
+      BY <1>2, <3>1, <3>3, <3>VT1P, SMT DEF VInv, VInv1, VInv3 
+    <3>2. ASSUME NEW w, w \notin chosen, w \in chosen'
+          PROVE  w = v
+      BY <3>2, <2>4, <1>2, QA, SMT
+      DEF chosen, ChosenIn, VotedFor, VoteFor, VInv, TypeOK
+    <3>4. CASE chosen' = chosen
+      BY <3>4 DEF C!vars
+    <3>5. CASE chosen' # chosen
+      <4>1. chosen \subseteq chosen'
+        BY <3>3
+      <4>2. PICK w \in chosen' : w \notin chosen
+        BY <3>5, <4>1
+      <4>3. w = v
+        BY <4>2, <3>2
+      <4>4. chosen' = chosen \cup {v}
+        BY <3>2, <4>1, <4>3
+      <4>5. chosen = {}
+        BY <4>4, <3>1, <3>5
+      <4>6. QED
+        BY <4>4, <4>5 DEF C!Next
+    <3>6. QED
+      BY <3>4, <3>5
+  <2>5. QED
+    BY <2>2, <2>3, <2>4 DEF BallotAction
+    
+<1>3. QED
+  BY <1>1, <1>2, VT2, PTL DEF C!Spec, Spec
+
+ASSUME AcceptorFinite == IsFiniteSet(Acceptor)
+
+ASSUME ValueNonempty == Value # {}
+
+AXIOM SubsetOfFiniteSetFinite == 
+        \A S, T : IsFiniteSet(T) /\ (S \subseteq T) => IsFiniteSet(S)
+
+AXIOM FiniteSetHasMax == 
+        \A S \in SUBSET Int :
+          IsFiniteSet(S) /\ (S # {}) => \E max \in S : \A x \in S : max >= x
+
+AXIOM IntervalFinite == \A i, j \in Int : IsFiniteSet(i..j)
+
+THEOREM VT4 == TypeOK /\ VInv2 /\ VInv3  =>
+                \A Q \in Quorum, b \in Ballot :
+                   (\A a \in Q : (maxBal[a] >= b)) => \E v \in Value : SafeAt(b,v)
+
+<1>1. SUFFICES ASSUME TypeOK, VInv2, VInv3,
+                      NEW Q \in Quorum, NEW b \in Ballot,
+                      (\A a \in Q : (maxBal[a] >= b))
+               PROVE  \E v \in Value : SafeAt(b, v)
+  OBVIOUS
+<1>2. CASE b = 0
+  BY ValueNonempty, <1>1, SafeAtProp, <1>2
+<1>3. SUFFICES ASSUME b # 0
+               PROVE  \E v \in Value : SafeAt(b, v)
+  BY <1>2
+<1>4. SUFFICES \E v \in Value : 
+                 \E c \in -1..(b-1) :
+                   /\ (c # -1) => /\ SafeAt(c, v)
+                                  /\ \A a \in Q :
+                                       \A w \in Value :
+                                           VotedFor(a, c, w) => (w = v)
+                   /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
+  <2>1. SUFFICES ASSUME NEW v \in Value,
+                        <1>4!1!(v)
+                 PROVE  SafeAt(b, v)
+    OBVIOUS
+  <2>2. SafeAtProp!(b, v)
+    BY SafeAtProp
+  <2>3. QED
+    BY <2>1, <2>2, <1>1, <1>3
+<1>5. CASE \A a \in Q, c \in 0..(b-1) : DidNotVoteIn(a, c)
+  <2>1. PICK v \in Value : TRUE
+    BY ValueNonempty
+  <2> -1 \in -1..(b-1)
+    BY SMT DEF Ballot
+  <2>2. WITNESS v \in Value
+  <2>3. WITNESS -1 \in -1..(b-1)
+  <2>4. QED
+    BY <1>5
+<1>6. CASE \E a \in Q, c \in 0..(b-1) : ~DidNotVoteIn(a, c)
+  <2>1. PICK c \in 0..(b-1) : 
+               /\ \E a \in Q : ~DidNotVoteIn(a, c)
+               /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
+    <3> DEFINE S == {c \in 0..(b-1) : \E a \in Q : ~DidNotVoteIn(a, c)}
+    <3>1. S # {}
+        BY <1>6
+    <3>2. PICK c \in S : \A d \in S : c >= d
+      <4>1. (0 \in Int) /\ (b-1 \in Int)  /\ (\A x \in 0..(b-1) : x \in Int)
+        BY <1>3, SMT DEF Ballot
+      <4>2. (S \in SUBSET Int) 
+        BY <4>1
+      <4>3. IsFiniteSet(S)
+        BY <4>1, IntervalFinite, SubsetOfFiniteSetFinite
+      <4>4. QED
+        BY <3>1,  <4>2, <4>3, FiniteSetHasMax
+    <3>3. c \in 0..(b-1)
+      OBVIOUS
+    <3>4. \A d \in (c+1)..(b-1) : d \in 0..(b-1) /\ ~(c >= d)
+      BY <3>3, SMT DEF Ballot  (* This works *)
+    <3>5. \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
+      BY <3>2, <3>4
+    <3>6. \E a \in Q : ~DidNotVoteIn(a, c)
+      BY <3>1
+    <3>7. QED
+      BY  <3>3, <3>5, <3>6
+  <2>2. (c \in -1..(b-1)) /\ (c # -1) /\ (c \in Ballot)
+    BY SMT DEF Ballot
+  <2>3. PICK a0 \in Q : ~DidNotVoteIn(a0, c)
+    BY <2>1
+  <2>4. PICK v \in Value : VotedFor(a0, c, v)
+    BY <2>3 DEF DidNotVoteIn
+  <2>5. \A a \in Q : \A w \in Value :
+           VotedFor(a, c, w) => (w = v)
+    BY <2>2, <2>4, QA, <1>1 DEF VInv3
+  <2>6. SafeAt(c, v)
+    BY <1>1, <2>4, QA, <2>2 DEF VInv2
+  <2>7. QED
+    BY <2>1, <2>2, <2>5, <2>6 
+
+<1>7. QED
+  BY <1>5, <1>6
+
+LiveAssumption ==
+  \E Q \in Quorum, b \in Ballot :
+     \A self \in Q :
+       /\ WF_vars(BallotAction(self, b))
+       /\ [] [\A c \in Ballot : (c > b) => ~ BallotAction(self, c)]_vars
+      
+LiveSpec == Spec /\ LiveAssumption  
+===============================================================================
diff --git a/tlatools/test-model/checkpoint/InfiniteStateSpace.cfg b/tlatools/test-model/checkpoint/InfiniteStateSpace.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/checkpoint/InfiniteStateSpace.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/checkpoint/InfiniteStateSpace.tla b/tlatools/test-model/checkpoint/InfiniteStateSpace.tla
new file mode 100644
index 0000000000000000000000000000000000000000..89ae5a5ca869a16f69470d257748f81ae17643dc
--- /dev/null
+++ b/tlatools/test-model/checkpoint/InfiniteStateSpace.tla
@@ -0,0 +1,7 @@
+------------------------------ MODULE InfiniteStateSpace ------------------------------
+EXTENDS Naturals
+
+VARIABLES x,y
+
+Spec == x = 0 /\ y \in 1..1000 /\ [][x'= x + 1 /\ UNCHANGED y]_<<x,y>>
+=============================================================================
diff --git a/tlatools/test-model/coverage/A.cfg b/tlatools/test-model/coverage/A.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/coverage/A.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/coverage/A.tla b/tlatools/test-model/coverage/A.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4032c98fe581a8eafd84e2188acff006e044da8d
--- /dev/null
+++ b/tlatools/test-model/coverage/A.tla
@@ -0,0 +1,18 @@
+---------------------------- MODULE A ----------------------------
+EXTENDS Naturals, FiniteSets
+VARIABLE x
+
+Op1(S) == \A e \in SUBSET S : Cardinality(e) >= 0
+
+Init == /\ x = 0
+        /\ Op1(1..17)
+
+\* Max(s)
+Op2(n) == CHOOSE i \in 1..n : \A j \in ((1..n) \ {i}) : i > j
+
+A(n) == x' = Op2(n)
+
+B(n) == x' = Op2(n)
+
+Next == A(10) \/ B(3)
+=============================================================================
diff --git a/tlatools/test-model/coverage/B.cfg b/tlatools/test-model/coverage/B.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/coverage/B.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/coverage/B.tla b/tlatools/test-model/coverage/B.tla
new file mode 100644
index 0000000000000000000000000000000000000000..53b166455fbc62956435386512ff9894862cc0cf
--- /dev/null
+++ b/tlatools/test-model/coverage/B.tla
@@ -0,0 +1,13 @@
+---------------------------- MODULE B ----------------------------
+VARIABLE x
+
+Init == x = FALSE
+
+Switch(var) == ~var
+
+A == x' = Switch(x)
+
+B == x' = Switch(x)
+
+Next == A \/ B
+=============================================================================
diff --git a/tlatools/test-model/coverage/C.cfg b/tlatools/test-model/coverage/C.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..123888c29add0dae20544eefc45e779311ecaa30
--- /dev/null
+++ b/tlatools/test-model/coverage/C.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION Spec
+CONSTRAINT Constraint
+INVARIANT Inv
+INVARIANT Inv2
diff --git a/tlatools/test-model/coverage/C.tla b/tlatools/test-model/coverage/C.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0ee2d5da27eb2a5d279283e9e20a21a6ddc796a5
--- /dev/null
+++ b/tlatools/test-model/coverage/C.tla
@@ -0,0 +1,63 @@
+---------------------------- MODULE C ----------------------------
+EXTENDS Naturals, FiniteSets
+VARIABLES x,y
+
+c == x
+d == y
+
+vars == <<x,y>>
+
+a == x'
+
+Op(S) == \A e \in SUBSET S : Cardinality(e) >= 0
+
+Init == /\ x \in 1..3
+        /\ y = 0
+
+A == /\ c \in Nat
+     /\ y \in Nat
+     /\ a = x + 1
+     /\ UNCHANGED y
+
+B == /\ x \in Nat
+     /\ Op(1..5)
+     /\ UNCHANGED <<x,d>>
+
+C == /\ x = 42
+     /\ c' = TRUE
+     /\ y' = FALSE
+
+D == x' \in 0..x /\ UNCHANGED y
+
+U1 == x < 0 /\ UNCHANGED vars
+
+U2 == x < 0 /\ UNCHANGED <<x,y>>
+
+U3 == x < 0 /\ UNCHANGED x /\ UNCHANGED y
+
+Next == A \/ B \/ C \/ D \/ U1 \/ U2 \/ U3
+
+Spec == Init /\ [][Next]_vars
+
+Constraint == x < 20
+
+(* Coverage/Cost statistics for Invariants too. *)
+
+expensive(N) == \A e \in SUBSET (1..N) : \A f \in e : f \in 1..N 
+
+Inv == /\ x \in Nat
+       /\ y \in Nat
+       /\ expensive(8)
+       /\ LET frob == TRUE  
+          IN /\ frob = TRUE
+             /\ \A i \in 1..5: i \in Nat 
+             /\ 42 \in Nat
+
+Inv2 == /\ x \in Nat
+        /\ y \in Nat
+        /\ expensive(9)
+        /\ LET frob == TRUE
+           IN /\ frob = TRUE
+             /\ \A i \in 1..5: i \in Nat 
+              /\ 42 \in Nat
+=============================================================================
diff --git a/tlatools/test-model/coverage/CoverageStatistics.cfg b/tlatools/test-model/coverage/CoverageStatistics.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..46c09d3371bbe6fa743e70c8adcdb79c89ea0723
--- /dev/null
+++ b/tlatools/test-model/coverage/CoverageStatistics.cfg
@@ -0,0 +1,4 @@
+CONSTRAINT
+Constraint
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/coverage/CoverageStatistics.tla b/tlatools/test-model/coverage/CoverageStatistics.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0eb6b975f2838a9e21a6ed2f5fe2e5dbd1f560f0
--- /dev/null
+++ b/tlatools/test-model/coverage/CoverageStatistics.tla
@@ -0,0 +1,50 @@
+---------------------------- MODULE CoverageStatistics ----------------------------
+EXTENDS Naturals, FiniteSets
+VARIABLES x,y
+
+c == x
+d == y
+
+vars == <<x,y>>
+
+a == x'
+
+Init == /\ x \in 1..3
+        /\ y = 0
+
+A == /\ c \in Nat
+     /\ y \in Nat
+     /\ a = x + 1
+     /\ UNCHANGED d \* 18 should be covered (6 should be omitted)
+
+B == /\ x \in Nat
+     /\ UNCHANGED <<x,d>> \* 21 and 6 should be covered (6 should actually be omitted)
+
+B2 == /\ x \in Nat
+     /\ UNCHANGED vars \* 21 and 6 should be covered (6 should actually be omitted)
+     
+C == /\ x = 42
+     /\ c' = TRUE
+     /\ y' = FALSE
+
+U1 == x < 0 /\ UNCHANGED vars \* vars should be reported as uncovered instead of x and y on line 8
+
+U2 == x < 0 /\ UNCHANGED <<x,y>> \* col 28 & 30 
+
+U3 == x < 0 /\ UNCHANGED x /\ UNCHANGED y \* col 26 & 41
+
+U4 == x < 0 /\ UNCHANGED x /\ UNCHANGED d (* d itself instead of y is marked uncovered *)
+
+UC1 == x \in Nat /\ UNCHANGED <<x,y>>
+
+UC2 == x \in Nat /\ UNCHANGED x /\ UNCHANGED y
+
+UC3 == x \in Nat /\ UNCHANGED vars \* Do not mark vars on line 8 as covered
+
+Next == A \/ B \/ C \/ U1 \/ U2 \/ U3 \/ U4 \/ UC1 \/ UC2 \/ UC3
+
+Spec == Init /\ [][Next]_vars
+
+Constraint == x < 20
+
+=============================================================================
diff --git a/tlatools/test-model/coverage/D.cfg b/tlatools/test-model/coverage/D.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d
--- /dev/null
+++ b/tlatools/test-model/coverage/D.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
diff --git a/tlatools/test-model/coverage/D.tla b/tlatools/test-model/coverage/D.tla
new file mode 100644
index 0000000000000000000000000000000000000000..41865d9289b8294117b0c79115a775d686b2354c
--- /dev/null
+++ b/tlatools/test-model/coverage/D.tla
@@ -0,0 +1,18 @@
+---------------------------- MODULE D ----------------------------
+EXTENDS Naturals
+VARIABLE x
+
+Init == x = 0
+
+(* No endless recursion in Coverage/Cost with RECURSIVE. *)
+RECURSIVE fact(_)
+fact(n) == IF n = 1 THEN 1 ELSE n * fact(n - 1)
+
+A == x' = fact(3)
+
+B == x' = fact(9)
+
+Next == A \/ B
+
+Spec == Init /\ [][Next]_<<x>>
+=============================================================================
diff --git a/tlatools/test-model/coverage/E.cfg b/tlatools/test-model/coverage/E.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4ebe91bd18e2e5bbb5152db4a9abbaa2ea951bba
--- /dev/null
+++ b/tlatools/test-model/coverage/E.cfg
@@ -0,0 +1,3 @@
+INIT Init
+NEXT Next
+CONSTANT Op <- Forty2
diff --git a/tlatools/test-model/coverage/E.tla b/tlatools/test-model/coverage/E.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fc774b97d8293f62bfd18ed6fad393fe826402fd
--- /dev/null
+++ b/tlatools/test-model/coverage/E.tla
@@ -0,0 +1,13 @@
+---------------------------- MODULE E ----------------------------
+CONSTANT Op(_)
+
+VARIABLE x
+
+Id(n) == SUBSET {1,2,3}
+
+Forty2(n) == Id(n)
+
+Init == x = 0
+
+Next == x' \in Op(x)
+=============================================================================
diff --git a/tlatools/test-model/coverage/F.cfg b/tlatools/test-model/coverage/F.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/coverage/F.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/coverage/F.tla b/tlatools/test-model/coverage/F.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3d868458608285b71c5bb346e6b96abd43946d03
--- /dev/null
+++ b/tlatools/test-model/coverage/F.tla
@@ -0,0 +1,11 @@
+---------------------------- MODULE F ----------------------------
+EXTENDS Naturals
+
+VARIABLE x
+
+Op(S, P(_), Q(_, _)) == { s \in S : P(s) /\ Q(s, TRUE) }
+
+Init == x \in Op({1,2,3,4,5}, LAMBDA s: s > 1, LAMBDA odd, b: odd % 2 # 0 /\ b) 
+
+Next == UNCHANGED x
+=============================================================================
diff --git a/tlatools/test-model/coverage/G.cfg b/tlatools/test-model/coverage/G.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9b60b87092e262903968bd979325348721290efb
--- /dev/null
+++ b/tlatools/test-model/coverage/G.cfg
@@ -0,0 +1,3 @@
+INIT Init
+NEXT Next
+INVARIANT Prop
\ No newline at end of file
diff --git a/tlatools/test-model/coverage/G.tla b/tlatools/test-model/coverage/G.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d4592b9989c9b5602bc6f5a5fb4885d40880d006
--- /dev/null
+++ b/tlatools/test-model/coverage/G.tla
@@ -0,0 +1,13 @@
+---------------------------- MODULE G ----------------------------
+VARIABLES u1
+
+vars == << u1 >>
+
+Init == /\ u1 = TRUE
+
+Next == /\ UNCHANGED vars
+        /\ UNCHANGED <<u1>>
+        /\ UNCHANGED u1
+
+Prop == ENABLED Next
+=============================================================================
diff --git a/tlatools/test-model/coverage/Github314.cfg b/tlatools/test-model/coverage/Github314.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..dfd087d6b1871aee9e04413d19ed06afc6318409
--- /dev/null
+++ b/tlatools/test-model/coverage/Github314.cfg
@@ -0,0 +1,5 @@
+CONSTANT
+Nat <- [Naturals]MyNat
+\* No bug with Nat <- MyNat
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/coverage/Github314.tla b/tlatools/test-model/coverage/Github314.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0df9fae4db25d8d9034a8c11560e4753dff2fb5c
--- /dev/null
+++ b/tlatools/test-model/coverage/Github314.tla
@@ -0,0 +1,21 @@
+---- MODULE Github314 ----
+EXTENDS Naturals
+
+\* Begin nested (base) module
+   ---- MODULE Base ----
+   EXTENDS Naturals
+
+   VARIABLES x
+
+   Spec == x = 0 /\ [][(x \in Nat /\ x' = x + 1)]_x
+   =====
+\* End nested (base) module
+
+VARIABLES x
+
+Spec == x = 0 /\ [][(x \in Nat /\ x' = x + 1)]_x
+          
+F == INSTANCE Base
+
+MyNat == 0..1
+====
diff --git a/tlatools/test-model/coverage/Github377.cfg b/tlatools/test-model/coverage/Github377.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5ace4490d0a5010629d4458e00586ad1a7fc3011
--- /dev/null
+++ b/tlatools/test-model/coverage/Github377.cfg
@@ -0,0 +1,11 @@
+SPECIFICATION
+Spec
+INVARIANT
+1Inv
+1InvNonRec
+2Inv
+2aInv
+2bInv
+3aInv
+3bInv
+4Inv
diff --git a/tlatools/test-model/coverage/Github377.tla b/tlatools/test-model/coverage/Github377.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0f7b12a0fc32ec4ce7fbcfa1d30019dc485bdf5a
--- /dev/null
+++ b/tlatools/test-model/coverage/Github377.tla
@@ -0,0 +1,75 @@
+---- MODULE Github377 ----
+EXTENDS Integers
+VARIABLES x
+
+Spec == x = TRUE /\ [][UNCHANGED x]_x
+
+--------------
+
+1Inv ==
+ LET recfcn[ i \in {1,2,3} ] == IF i = 1 THEN x = TRUE ELSE recfcn[1]
+ IN recfcn[1]
+
+--------------
+
+1InvNonRec ==
+ LET recfcn[ i \in {1,2,3} ] == x = TRUE
+ IN recfcn[1]
+
+--------------
+
+2Inv ==
+  LET outer ==
+      LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i]
+          innerB[ i \in {1} ] == innerA[i] 
+      IN innerB[1] /\ 42 = 31 + 11 /\ innerA[1]
+  IN  outer
+
+2aInv ==
+  LET outer ==
+      LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i]
+          innerB[ i \in {1} ] == innerA[i]
+      IN /\ innerA[1]
+         /\ innerB[1] 
+  IN  outer
+  
+2bInv ==
+  LET outer ==
+      LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i]
+          innerB[ i \in {1} ] == innerA[i]
+      IN innerA[1] /\ innerB[1]
+  IN  outer
+--------------
+
+3aInv ==
+  LET outer ==
+      LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i]
+          innerB[ i \in {1} ] == innerA[1]
+      IN /\ LET foo(i) == innerA[i]
+            IN foo(1)
+         /\ innerB[1] 
+  IN  outer
+
+--------------
+
+3bInv ==
+  LET outer ==
+      LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i]
+          innerB[ i \in {1} ] == innerA[1]
+      IN LET foo(i) == innerA[i]
+         IN foo(1) /\ innerB[1] 
+  IN  outer
+  
+
+--------------
+
+4Inv ==
+  LET outer ==
+      LET inner[ i \in {1} ] ==
+          LET innerst[ j \in {1} ] == x = TRUE
+          IN  innerst[1]
+      IN  inner[1]
+  IN  outer
+  
+====
+  
\ No newline at end of file
diff --git a/tlatools/test-model/coverage/H.cfg b/tlatools/test-model/coverage/H.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/coverage/H.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/coverage/H.tla b/tlatools/test-model/coverage/H.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cac9a4d2f35392b104ad29504b7b05a6d39382e3
--- /dev/null
+++ b/tlatools/test-model/coverage/H.tla
@@ -0,0 +1,27 @@
+------------------------------ MODULE H ------------------------------
+EXTENDS Naturals
+VARIABLES x
+
+MyNat == 0..30
+
+TypeOK == x \in MyNat
+
+Init == x = 0
+
+A == x' = 23
+
+BandC == \/ /\ x \in MyNat
+            /\ x' \in 23..25
+         \/ /\ x < 25
+            /\ x' = 26
+
+DandE == (x = 23 /\ x' = 42) \/ (x = 123 /\ x' = 4711)
+
+Next == A \/ BandC \/ DandE
+
+Inv == /\ TypeOK
+       /\ x = 0
+
+Spec == Inv /\ [][Next]_x
+
+============
diff --git a/tlatools/test-model/coverage/I.cfg b/tlatools/test-model/coverage/I.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/coverage/I.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/coverage/I.tla b/tlatools/test-model/coverage/I.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b72e8a26860c80abda1862adfb64e61b1d5bc746
--- /dev/null
+++ b/tlatools/test-model/coverage/I.tla
@@ -0,0 +1,15 @@
+------------------------------ MODULE I ------------------------------
+EXTENDS Naturals
+VARIABLES x
+
+F[ i \in {1,2,3,4,5} ] == 
+   IF i = 1 THEN 1 ELSE F[i-1] + 1 \* Recursive Function Definition
+
+N[n \in {1,2,3}] == 
+    /\ UNCHANGED <<x>>
+
+Spec == x \in {1,2,3,4,5} /\ [][\E i \in {1,2,3} : N[i] ]_x
+
+Inv == \E i \in DOMAIN F: F[i] = x
+
+============
diff --git a/tlatools/test-model/coverage/J.cfg b/tlatools/test-model/coverage/J.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/coverage/J.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/coverage/J.tla b/tlatools/test-model/coverage/J.tla
new file mode 100644
index 0000000000000000000000000000000000000000..34bb20b7f7c23456c35811cf5d08af79564299cb
--- /dev/null
+++ b/tlatools/test-model/coverage/J.tla
@@ -0,0 +1,16 @@
+------------------------------ MODULE J ------------------------------
+
+VARIABLES x, y
+
+Init == /\ x \in {1,2,3,4,5}
+        /\ y = [i \in {1,2,3,4,5} |-> 0]
+
+Foo(rcds) ==
+ CASE x = 1 -> [ rcds EXCEPT ![x] = 42 ]
+   [] OTHER -> rcds
+
+Next == /\ y' = Foo(y)
+        /\ UNCHANGED x 
+
+Spec == Init /\ [][Next]_<<x,y>>
+============
diff --git a/tlatools/test-model/coverage/K.cfg b/tlatools/test-model/coverage/K.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/coverage/K.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/coverage/K.tla b/tlatools/test-model/coverage/K.tla
new file mode 100644
index 0000000000000000000000000000000000000000..dfbaa8c4ca1fdb2a3de733475931cd63cd5c6d80
--- /dev/null
+++ b/tlatools/test-model/coverage/K.tla
@@ -0,0 +1,12 @@
+------------------------------ MODULE K ------------------------------
+
+EXTENDS Naturals
+
+Foo(Op(_), S) == \A s \in S: Op(s)
+
+Bar(S) == Foo(LAMBDA e: e \in Nat, S)
+
+VARIABLE x
+
+Spec == x = Bar({1,2,3}) /\ [][UNCHANGED x]_x 
+============
diff --git a/tlatools/test-model/coverage/L.cfg b/tlatools/test-model/coverage/L.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b
--- /dev/null
+++ b/tlatools/test-model/coverage/L.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
diff --git a/tlatools/test-model/coverage/L.tla b/tlatools/test-model/coverage/L.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b0fd28896fcb930b0c72d80c2406e7278528fa5a
--- /dev/null
+++ b/tlatools/test-model/coverage/L.tla
@@ -0,0 +1,10 @@
+------------------------------ MODULE L ------------------------------
+EXTENDS Naturals
+
+VARIABLE v
+
+ChooseOne(S, P(_)) == CHOOSE x \in S : P(x) /\ \A y \in S : P(y)
+
+Spec == v = ChooseOne({1}, LAMBDA x : TRUE) /\ [][UNCHANGED v]_v
+
+============
diff --git a/tlatools/test-model/pcal/.gitignore b/tlatools/test-model/pcal/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..9c80bdb55a33e4c24f2350408a3b543eb16da4e9
--- /dev/null
+++ b/tlatools/test-model/pcal/.gitignore
@@ -0,0 +1,52 @@
+*.old
+/MPNoParams.cfg
+/CallReturn1.cfg
+/Euclid2.cfg
+/EvenOdd.cfg
+/EvenOddBad.cfg
+/Factorial.cfg
+/Factorial2.cfg
+/InnerLabeledIf.cfg
+/MultiAssignment.cfg
+/MultiProc2.cfg
+/MultiprocDefine.cfg
+/NoLoop.cfg
+/NoLoop2.cfg
+/NoParams.cfg
+/NotSoSimpleLoop.cfg
+/Quicksort.cfg
+/SimpleLoop.cfg
+/SimpleLoopWithProcedure.cfg
+/SimpleMultiProc.cfg
+/TestTabs.cfg
+/UniprocDefine.cfg
+/bug_05_10_03.cfg
+/bug_05_12_10a.cfg
+/bug_05_12_16b.cfg
+/bug_05_12_31.cfg
+/bug_06_01_25.cfg
+/bug_07_03_30.cfg
+/bug_07_06_05.cfg
+/Either1.cfg
+/Either2.cfg
+/Either3.cfg
+/Either4.cfg
+/Either5.cfg
+/Euclid3.cfg
+/MPFactorial.cfg
+/MPFactorial2.cfg
+/TestReplace.cfg
+/SubSub.cfg
+/TestPCandStack.cfg
+/Test.cfg
+/ULCallReturn1.cfg
+/ULFactorial2.cfg
+/ReallySimpleMultiProc.cfg
+/NestedMacros.cfg
+/FairSeq2.cfg
+/DiningPhilosophers2.cfg
+/CallReturn2.cfg
+/CCallReturn1.cfg
+/CMultiprocDefine.cfg
+/CEither1.cfg
+/StackTest.cfg
diff --git a/tlatools/test-model/pcal/Bakery.cfg b/tlatools/test-model/pcal/Bakery.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9209a00c6288aab93977bdf9e85b204efd16702f
--- /dev/null
+++ b/tlatools/test-model/pcal/Bakery.cfg
@@ -0,0 +1,7 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANTS NumProcs = 2
+          MaxNum = 3
+INVARIANT Invariant
+CONSTRAINT Constraint
+
diff --git a/tlatools/test-model/pcal/Bakery.tla b/tlatools/test-model/pcal/Bakery.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6eafbb39d21cc9c449463147344c19bac3d1c23b
--- /dev/null
+++ b/tlatools/test-model/pcal/Bakery.tla
@@ -0,0 +1,139 @@
+------------------------------- MODULE Bakery -------------------------------- 
+EXTENDS Naturals, TLC
+
+CONSTANTS NumProcs,     \* The number of processes
+          MaxNum        \* For TLC, the maximum value of num[i].
+
+Proc == 1..NumProcs  \* The set of processes
+
+(*   
+
+  --algorithm Bakery
+  variable num = [i \in Proc |-> 0] ;
+           choosing = [i \in Proc |-> FALSE];
+  process proc \in Proc
+    variables read = { }; max = 0 ; nxt = self ;
+    begin loop : while TRUE
+                   do      choosing[self] := TRUE;
+                           read := { self };
+                           max  := 0 ;
+                      d1 : while read # Proc
+                            do  with p \in Proc \ read
+                                  do if num[p] > max
+                                       then max := num[p];
+                                     end if ;
+                                     read := read \cup {p};
+                                end with ;
+                            end while ;
+                      d2 : num[self] := max + 1 ;
+                      d3 : choosing[self] := FALSE ;
+                           read := { self } ;
+                      w1 : while read # Proc
+                            do  with p \in Proc \ read
+                                  do     when ~ choosing[p] ;
+                                         nxt := p ;
+                                end with;
+                            w2: when \/ num[nxt] = 0 
+                                     \/ num[nxt] > num[self]
+                                     \/ /\ num[nxt] = num[self]
+                                        /\ nxt > self   ;
+                                read := read \cup { nxt } ;
+                           end while;
+                     cs  : when TRUE ;
+                    exit : num[self] := 0;
+                  end while;
+    end process
+  end algorithm
+
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3787a86d71fe978e1e68a7e275a515ae
+VARIABLES num, choosing, pc, read, max, nxt
+
+vars == << num, choosing, pc, read, max, nxt >>
+
+ProcSet == (Proc)
+
+Init == (* Global variables *)
+        /\ num = [i \in Proc |-> 0]
+        /\ choosing = [i \in Proc |-> FALSE]
+        (* Process proc *)
+        /\ read = [self \in Proc |-> { }]
+        /\ max = [self \in Proc |-> 0]
+        /\ nxt = [self \in Proc |-> self]
+        /\ pc = [self \in ProcSet |-> "loop"]
+
+loop(self) == /\ pc[self] = "loop"
+              /\ choosing' = [choosing EXCEPT ![self] = TRUE]
+              /\ read' = [read EXCEPT ![self] = { self }]
+              /\ max' = [max EXCEPT ![self] = 0]
+              /\ pc' = [pc EXCEPT ![self] = "d1"]
+              /\ UNCHANGED << num, nxt >>
+
+d1(self) == /\ pc[self] = "d1"
+            /\ IF read[self] # Proc
+                  THEN /\ \E p \in Proc \ read[self]:
+                            /\ IF num[p] > max[self]
+                                  THEN /\ max' = [max EXCEPT ![self] = num[p]]
+                                  ELSE /\ TRUE
+                                       /\ max' = max
+                            /\ read' = [read EXCEPT ![self] = read[self] \cup {p}]
+                       /\ pc' = [pc EXCEPT ![self] = "d1"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "d2"]
+                       /\ UNCHANGED << read, max >>
+            /\ UNCHANGED << num, choosing, nxt >>
+
+d2(self) == /\ pc[self] = "d2"
+            /\ num' = [num EXCEPT ![self] = max[self] + 1]
+            /\ pc' = [pc EXCEPT ![self] = "d3"]
+            /\ UNCHANGED << choosing, read, max, nxt >>
+
+d3(self) == /\ pc[self] = "d3"
+            /\ choosing' = [choosing EXCEPT ![self] = FALSE]
+            /\ read' = [read EXCEPT ![self] = { self }]
+            /\ pc' = [pc EXCEPT ![self] = "w1"]
+            /\ UNCHANGED << num, max, nxt >>
+
+w1(self) == /\ pc[self] = "w1"
+            /\ IF read[self] # Proc
+                  THEN /\ \E p \in Proc \ read[self]:
+                            /\ ~ choosing[p]
+                            /\ nxt' = [nxt EXCEPT ![self] = p]
+                       /\ pc' = [pc EXCEPT ![self] = "w2"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+                       /\ nxt' = nxt
+            /\ UNCHANGED << num, choosing, read, max >>
+
+w2(self) == /\ pc[self] = "w2"
+            /\ \/ num[nxt[self]] = 0
+               \/ num[nxt[self]] > num[self]
+               \/ /\ num[nxt[self]] = num[self]
+                  /\ nxt[self] > self
+            /\ read' = [read EXCEPT ![self] = read[self] \cup { nxt[self] }]
+            /\ pc' = [pc EXCEPT ![self] = "w1"]
+            /\ UNCHANGED << num, choosing, max, nxt >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "exit"]
+            /\ UNCHANGED << num, choosing, read, max, nxt >>
+
+exit(self) == /\ pc[self] = "exit"
+              /\ num' = [num EXCEPT ![self] = 0]
+              /\ pc' = [pc EXCEPT ![self] = "loop"]
+              /\ UNCHANGED << choosing, read, max, nxt >>
+
+proc(self) == loop(self) \/ d1(self) \/ d2(self) \/ d3(self) \/ w1(self)
+                 \/ w2(self) \/ cs(self) \/ exit(self)
+
+Next == (\E self \in Proc: proc(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-044cd15e6eddcf8edc1cfa67089d17cf
+
+Constraint == \A i \in Proc : num[i] \leq MaxNum
+
+Invariant == \A i, j \in Proc : (pc[i] = "cs") /\ (pc[j] = "cs") => (i = j)
+=============================================================================
diff --git a/tlatools/test-model/pcal/CBakery.cfg b/tlatools/test-model/pcal/CBakery.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..160ae67b63d374a55ca52cb9be22f91b17a00535
--- /dev/null
+++ b/tlatools/test-model/pcal/CBakery.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT NumProcs = 2  MaxNum = 2
+INVARIANT Invariant
+CONSTRAINT Constraint
diff --git a/tlatools/test-model/pcal/CBakery.tla b/tlatools/test-model/pcal/CBakery.tla
new file mode 100644
index 0000000000000000000000000000000000000000..02cbf446cfc7880e483e2a18993961a5700422f3
--- /dev/null
+++ b/tlatools/test-model/pcal/CBakery.tla
@@ -0,0 +1,128 @@
+------------------------------- MODULE CBakery ------------------------------- 
+EXTENDS Naturals, TLC
+
+CONSTANTS NumProcs,     \* The number of processes
+          MaxNum        \* For TLC, the maximum value of num[i].
+
+Proc == 1..NumProcs  \* The set of processes
+
+(********
+  --algorithm Bakery {
+  variable num = [i \in Proc |-> 0] ;
+           choosing = [i \in Proc |-> FALSE];
+  process (proc \in Proc) 
+    variables read = { }; max = 0 ; nxt = self ;
+    { loop : while (TRUE) {
+              choosing[self] := TRUE;
+              read := { self };
+              max  := 0 ;
+         d1 : while (read # Proc)
+                with (p \in Proc \ read) {
+                  if (num[p] > max) max := num[p];
+                  read := read \cup {p} }  ;
+         d2 : num[self] := max + 1 ;
+         d3 : choosing[self] := FALSE ;
+              read := { self } ;
+         w1 : while (read # Proc) {
+                with (p \in Proc \ read) {
+                  when ~ choosing[p] ;
+                  nxt := p } ; 
+            w2: when \/ num[nxt] = 0 
+                     \/ num[nxt] > num[self] 
+                     \/ /\ num[nxt] = num[self] 
+                        /\ nxt > self   ;
+                read := read \cup { nxt } };
+        cs  : when TRUE ;
+              exit : num[self] := 0; } } }
+********)
+
+  
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3787a86d71fe978e1e68a7e275a515ae
+VARIABLES num, choosing, pc, read, max, nxt
+
+vars == << num, choosing, pc, read, max, nxt >>
+
+ProcSet == (Proc)
+
+Init == (* Global variables *)
+        /\ num = [i \in Proc |-> 0]
+        /\ choosing = [i \in Proc |-> FALSE]
+        (* Process proc *)
+        /\ read = [self \in Proc |-> { }]
+        /\ max = [self \in Proc |-> 0]
+        /\ nxt = [self \in Proc |-> self]
+        /\ pc = [self \in ProcSet |-> "loop"]
+
+loop(self) == /\ pc[self] = "loop"
+              /\ choosing' = [choosing EXCEPT ![self] = TRUE]
+              /\ read' = [read EXCEPT ![self] = { self }]
+              /\ max' = [max EXCEPT ![self] = 0]
+              /\ pc' = [pc EXCEPT ![self] = "d1"]
+              /\ UNCHANGED << num, nxt >>
+
+d1(self) == /\ pc[self] = "d1"
+            /\ IF read[self] # Proc
+                  THEN /\ \E p \in Proc \ read[self]:
+                            /\ IF num[p] > max[self]
+                                  THEN /\ max' = [max EXCEPT ![self] = num[p]]
+                                  ELSE /\ TRUE
+                                       /\ max' = max
+                            /\ read' = [read EXCEPT ![self] = read[self] \cup {p}]
+                       /\ pc' = [pc EXCEPT ![self] = "d1"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "d2"]
+                       /\ UNCHANGED << read, max >>
+            /\ UNCHANGED << num, choosing, nxt >>
+
+d2(self) == /\ pc[self] = "d2"
+            /\ num' = [num EXCEPT ![self] = max[self] + 1]
+            /\ pc' = [pc EXCEPT ![self] = "d3"]
+            /\ UNCHANGED << choosing, read, max, nxt >>
+
+d3(self) == /\ pc[self] = "d3"
+            /\ choosing' = [choosing EXCEPT ![self] = FALSE]
+            /\ read' = [read EXCEPT ![self] = { self }]
+            /\ pc' = [pc EXCEPT ![self] = "w1"]
+            /\ UNCHANGED << num, max, nxt >>
+
+w1(self) == /\ pc[self] = "w1"
+            /\ IF read[self] # Proc
+                  THEN /\ \E p \in Proc \ read[self]:
+                            /\ ~ choosing[p]
+                            /\ nxt' = [nxt EXCEPT ![self] = p]
+                       /\ pc' = [pc EXCEPT ![self] = "w2"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+                       /\ nxt' = nxt
+            /\ UNCHANGED << num, choosing, read, max >>
+
+w2(self) == /\ pc[self] = "w2"
+            /\ \/ num[nxt[self]] = 0
+               \/ num[nxt[self]] > num[self]
+               \/ /\ num[nxt[self]] = num[self]
+                  /\ nxt[self] > self
+            /\ read' = [read EXCEPT ![self] = read[self] \cup { nxt[self] }]
+            /\ pc' = [pc EXCEPT ![self] = "w1"]
+            /\ UNCHANGED << num, choosing, max, nxt >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "exit"]
+            /\ UNCHANGED << num, choosing, read, max, nxt >>
+
+exit(self) == /\ pc[self] = "exit"
+              /\ num' = [num EXCEPT ![self] = 0]
+              /\ pc' = [pc EXCEPT ![self] = "loop"]
+              /\ UNCHANGED << choosing, read, max, nxt >>
+
+proc(self) == loop(self) \/ d1(self) \/ d2(self) \/ d3(self) \/ w1(self)
+                 \/ w2(self) \/ cs(self) \/ exit(self)
+
+Next == (\E self \in Proc: proc(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-044cd15e6eddcf8edc1cfa67089d17cf
+Constraint 
+== \A i \in Proc : num[i] \leq MaxNum
+
+Invariant == \A i, j \in Proc : (pc[i] = "cs") /\ (pc[j] = "cs") => (i = j)
+=============================================================================
diff --git a/tlatools/test-model/pcal/CCallReturn1.tla b/tlatools/test-model/pcal/CCallReturn1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ea517740ba64277e804b33a30f45477f3f01c74f
--- /dev/null
+++ b/tlatools/test-model/pcal/CCallReturn1.tla
@@ -0,0 +1,124 @@
+------------------------------ MODULE CCallReturn1 -------------------------- 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+  --algorithm CallReturn1 {
+    procedure Proc1(arg1 = 0) 
+      variable u = 1 ;
+          { p1 : u := 2 ;
+                 call Proc2 ( 2 * u ) ;
+            p2 : assert u = 2;
+                 assert arg1  = 4  ;
+                 call Proc2 ( 2 * u + 1 ) ;
+                 return ; }
+
+    procedure Proc2(arg2 = 0) 
+      variable v = 42 ;
+          { q1 : assert v = 42;
+                 assert arg2 \in {4, 5} ;
+                 call Proc3 ( v + arg2 ) ;
+                 return }
+
+    procedure Proc3(arg3 = 0)
+          { r1 : assert arg3 \in {46, 47} ;
+                 return ; } ;
+
+    {
+      a1 : call Proc1( 4 ) ;
+    } }
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-eb6f25a53750950964fc51a79ea41da3
+VARIABLES pc, stack, arg1, u, arg2, v, arg3
+
+vars == << pc, stack, arg1, u, arg2, v, arg3 >>
+
+Init == (* Procedure Proc1 *)
+        /\ arg1 = 0
+        /\ u = 1
+        (* Procedure Proc2 *)
+        /\ arg2 = 0
+        /\ v = 42
+        (* Procedure Proc3 *)
+        /\ arg3 = 0
+        /\ stack = << >>
+        /\ pc = "a1"
+
+p1 == /\ pc = "p1"
+      /\ u' = 2
+      /\ /\ arg2' = 2 * u'
+         /\ stack' = << [ procedure |->  "Proc2",
+                          pc        |->  "p2",
+                          v         |->  v,
+                          arg2      |->  arg2 ] >>
+                      \o stack
+      /\ v' = 42
+      /\ pc' = "q1"
+      /\ UNCHANGED << arg1, arg3 >>
+
+p2 == /\ pc = "p2"
+      /\ Assert(u = 2, "Failure of assertion at line 10, column 18.")
+      /\ Assert(arg1  = 4, "Failure of assertion at line 11, column 18.")
+      /\ /\ arg2' = 2 * u + 1
+         /\ stack' = << [ procedure |->  "Proc2",
+                          pc        |->  Head(stack).pc,
+                          v         |->  v,
+                          arg2      |->  arg2 ] >>
+                      \o Tail(stack)
+         /\ u' = Head(stack).u
+      /\ v' = 42
+      /\ pc' = "q1"
+      /\ UNCHANGED << arg1, arg3 >>
+
+Proc1 == p1 \/ p2
+
+q1 == /\ pc = "q1"
+      /\ Assert(v = 42, "Failure of assertion at line 17, column 18.")
+      /\ Assert(arg2 \in {4, 5}, 
+                "Failure of assertion at line 18, column 18.")
+      /\ /\ arg3' = v + arg2
+         /\ stack' = << [ procedure |->  "Proc3",
+                          pc        |->  Head(stack).pc,
+                          arg3      |->  arg3 ] >>
+                      \o Tail(stack)
+         /\ v' = Head(stack).v
+      /\ pc' = "r1"
+      /\ UNCHANGED << arg1, u, arg2 >>
+
+Proc2 == q1
+
+r1 == /\ pc = "r1"
+      /\ Assert(arg3 \in {46, 47}, 
+                "Failure of assertion at line 23, column 18.")
+      /\ pc' = Head(stack).pc
+      /\ arg3' = Head(stack).arg3
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << arg1, u, arg2, v >>
+
+Proc3 == r1
+
+a1 == /\ pc = "a1"
+      /\ /\ arg1' = 4
+         /\ stack' = << [ procedure |->  "Proc1",
+                          pc        |->  "Done",
+                          u         |->  u,
+                          arg1      |->  arg1 ] >>
+                      \o stack
+      /\ u' = 1
+      /\ pc' = "p1"
+      /\ UNCHANGED << arg2, v, arg3 >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Proc1 \/ Proc2 \/ Proc3 \/ a1
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-54b1efca8092525f1f0eb4eb06b502c9
+=============================================================================
diff --git a/tlatools/test-model/pcal/CDiningPhilosophers.cfg b/tlatools/test-model/pcal/CDiningPhilosophers.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3085671a0184b6469a675a0dc98a806ed20d917e
--- /dev/null
+++ b/tlatools/test-model/pcal/CDiningPhilosophers.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+INVARIANT Invariant
+CONSTANT N = 4
diff --git a/tlatools/test-model/pcal/CDiningPhilosophers.tla b/tlatools/test-model/pcal/CDiningPhilosophers.tla
new file mode 100644
index 0000000000000000000000000000000000000000..18522fc18cc980a20847f76880fa8d4c13162415
--- /dev/null
+++ b/tlatools/test-model/pcal/CDiningPhilosophers.tla
@@ -0,0 +1,120 @@
+-------------------------- MODULE CDiningPhilosophers ------------------------ 
+
+(***************************************************************************)
+(* A simple solution to the Dining Philosophers problem in which processes *)
+(* 1 through N-1 pick up their lef then their right forks, and process 0   *)
+(* picks them up in the opposite order.                                    *)
+(***************************************************************************)
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat 
+
+(********
+--algorithm DiningPhilosophers {
+  variable sem = [i \in 0..(N-1) |-> 1] ; 
+
+process (Proc \in 1..(N-1)) {
+l1 : while (TRUE) {
+            { when sem[self] = 1 ;      \* Get right fork.
+              sem[self] := 0 } ;
+       l2 : { when sem[(self-1) % N] = 1 ; \* Get left fork.
+              sem[(self-1) % N] := 0  } ;
+       e  : { skip } ;                       \* Eat
+       l3 : { sem[self] := 1 } ;             \* Release right fork.
+       l4 : { sem[(self-1) % N] := 1 }  ;     \* Release left fork.
+      }}
+
+process (Proc0 = 0)
+{
+l01 : while (TRUE)
+          {       when sem[N-1] = 1 ;      \* get left fork
+                  sem[N-1] := 0 ;
+            l02 : when sem[0] = 1 ;    \* get right fork
+                  sem[0] := 0 ;
+            e0  : skip ;                 \* eat
+            l03 : sem[0] := 1 ;          \* release left fork
+            l04 : sem[N-1] := 1 ;        \* release right fork
+          }
+} }
+********)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d34a24305f241c923d0f8daa00682e02
+VARIABLES sem, pc
+
+vars == << sem, pc >>
+
+ProcSet == (1..(N-1)) \cup {0}
+
+Init == (* Global variables *)
+        /\ sem = [i \in 0..(N-1) |-> 1]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..(N-1) -> "l1"
+                                        [] self = 0 -> "l01"]
+
+l1(self) == /\ pc[self] = "l1"
+            /\ sem[self] = 1
+            /\ sem' = [sem EXCEPT ![self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+
+l2(self) == /\ pc[self] = "l2"
+            /\ sem[(self-1) % N] = 1
+            /\ sem' = [sem EXCEPT ![(self-1) % N] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "e"]
+
+e(self) == /\ pc[self] = "e"
+           /\ TRUE
+           /\ pc' = [pc EXCEPT ![self] = "l3"]
+           /\ sem' = sem
+
+l3(self) == /\ pc[self] = "l3"
+            /\ sem' = [sem EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l4"]
+
+l4(self) == /\ pc[self] = "l4"
+            /\ sem' = [sem EXCEPT ![(self-1) % N] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l1"]
+
+Proc(self) == l1(self) \/ l2(self) \/ e(self) \/ l3(self) \/ l4(self)
+
+l01 == /\ pc[0] = "l01"
+       /\ sem[N-1] = 1
+       /\ sem' = [sem EXCEPT ![N-1] = 0]
+       /\ pc' = [pc EXCEPT ![0] = "l02"]
+
+l02 == /\ pc[0] = "l02"
+       /\ sem[0] = 1
+       /\ sem' = [sem EXCEPT ![0] = 0]
+       /\ pc' = [pc EXCEPT ![0] = "e0"]
+
+e0 == /\ pc[0] = "e0"
+      /\ TRUE
+      /\ pc' = [pc EXCEPT ![0] = "l03"]
+      /\ sem' = sem
+
+l03 == /\ pc[0] = "l03"
+       /\ sem' = [sem EXCEPT ![0] = 1]
+       /\ pc' = [pc EXCEPT ![0] = "l04"]
+
+l04 == /\ pc[0] = "l04"
+       /\ sem' = [sem EXCEPT ![N-1] = 1]
+       /\ pc' = [pc EXCEPT ![0] = "l01"]
+
+Proc0 == l01 \/ l02 \/ e0 \/ l03 \/ l04
+
+Next == Proc0
+           \/ (\E self \in 1..(N-1): Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..(N-1) : SF_vars(Proc(self))
+        /\ SF_vars(Proc0)
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ff7acdd47d76382441ae75c392052170
+
+IsEating(i) == IF i = 0 THEN pc[i] = "e0"
+                        ELSE pc[i] = "e"
+
+Invariant == \A i \in 0..(N-1) : ~ (IsEating(i) /\ IsEating((i+1)%N))
+
+StarvationFree == \A i \in 0..(N-1) : []<> IsEating(i) 
+=============================================================================
diff --git a/tlatools/test-model/pcal/CEither1.tla b/tlatools/test-model/pcal/CEither1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4d93a0c8cbf49200ef8b12816dd9acc0271cf1aa
--- /dev/null
+++ b/tlatools/test-model/pcal/CEither1.tla
@@ -0,0 +1,60 @@
+------------------------------- MODULE CEither1 ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(* --algorithm Either {
+      variables x = 0 ; y = 0 ;
+        {   a: either {x := 1 ; b: x := x + 1;} ;
+                   or { y := 1 ; c: y := y + 1;} ; 
+\*               end either ;
+            d: assert x+y = 2 ;
+        } }
+     
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-af9f3b0389ad56ee237e79295b3205ff
+VARIABLES x, y, pc
+
+vars == << x, y, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ \/ /\ x' = 1
+           /\ pc' = "b"
+           /\ y' = y
+        \/ /\ y' = 1
+           /\ pc' = "c"
+           /\ x' = x
+
+b == /\ pc = "b"
+     /\ x' = x + 1
+     /\ pc' = "d"
+     /\ y' = y
+
+c == /\ pc = "c"
+     /\ y' = y + 1
+     /\ pc' = "d"
+     /\ x' = x
+
+d == /\ pc = "d"
+     /\ Assert(x+y = 2, "Failure of assertion at line 9, column 16.")
+     /\ pc' = "Done"
+     /\ UNCHANGED << x, y >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c \/ d
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-91542970fa29f8acc63f0f9ca8f27be3
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/CMultiprocDefine.tla b/tlatools/test-model/pcal/CMultiprocDefine.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9fa61fbdd4ea10881a818eb1184569114e50a685
--- /dev/null
+++ b/tlatools/test-model/pcal/CMultiprocDefine.tla
@@ -0,0 +1,59 @@
+---------------------------- MODULE CMultiprocDefine ------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(*   
+--algorithm MultiprocDefine {
+  variables n = 0 ;
+  define { nplus1 == n + 1 
+           nplus2 == nplus1 + 1 } ;
+  process (Proc \in {1, 2, 3})
+    variables i ;
+    { main : i := nplus2 ;
+             assert i = 2 ;
+    } }
+  
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9c797e1d4e2fc4c259c478fdea1bd5fa
+CONSTANT defaultInitValue
+VARIABLES n, pc
+
+(* define statement *)
+nplus1 == n + 1
+nplus2 == nplus1 + 1
+
+VARIABLE i
+
+vars == << n, pc, i >>
+
+ProcSet == ({1, 2, 3})
+
+Init == (* Global variables *)
+        /\ n = 0
+        (* Process Proc *)
+        /\ i = [self \in {1, 2, 3} |-> defaultInitValue]
+        /\ pc = [self \in ProcSet |-> "main"]
+
+main(self) == /\ pc[self] = "main"
+              /\ i' = [i EXCEPT ![self] = nplus2]
+              /\ Assert(i'[self] = 2, 
+                        "Failure of assertion at line 12, column 14.")
+              /\ pc' = [pc EXCEPT ![self] = "Done"]
+              /\ n' = n
+
+Proc(self) == main(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in {1, 2, 3}: Proc(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in {1, 2, 3} : WF_vars(Proc(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-70c2856f6a2b2707884c9cedc014d866
+=============================================================================
diff --git a/tlatools/test-model/pcal/CallReturn1.tla b/tlatools/test-model/pcal/CallReturn1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ac380f4eb70385c3d809de24998b1b2146c22780
--- /dev/null
+++ b/tlatools/test-model/pcal/CallReturn1.tla
@@ -0,0 +1,126 @@
+------------------------------ MODULE CallReturn1 --------------------------- 
+EXTENDS Sequences, Naturals, TLC
+
+(********
+--algorithm CallReturn1
+    procedure Proc1(arg1)
+      variable u ;
+      begin p1 : u := 2 ;
+                 call Proc2 ( 2 * u ) ;
+            p2 : assert u = 2;
+                 assert arg1  = 4  ;
+                 call Proc2 ( 2 * u + 1 ) ;
+                 return ;
+      end procedure
+    procedure Proc2(arg2 = 0)
+      variable v = 42 ;
+      begin q1 : assert v = 42;
+                 assert arg2 \in {4, 5} ;
+                 call Proc3 ( v + arg2 ) ;
+                 return ;
+      end procedure
+    procedure Proc3(arg3 = 0)
+      begin r1 : assert arg3 \in {46, 47} ;
+                 return ;
+      end procedure
+    begin
+      a1 : call Proc1( 4 ) ;
+    end algorithm
+*****)
+  
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3e53299518ce546a0089212b525c3663
+CONSTANT defaultInitValue
+VARIABLES pc, stack, arg1, u, arg2, v, arg3
+
+vars == << pc, stack, arg1, u, arg2, v, arg3 >>
+
+Init == (* Procedure Proc1 *)
+        /\ arg1 = defaultInitValue
+        /\ u = defaultInitValue
+        (* Procedure Proc2 *)
+        /\ arg2 = 0
+        /\ v = 42
+        (* Procedure Proc3 *)
+        /\ arg3 = 0
+        /\ stack = << >>
+        /\ pc = "a1"
+
+p1 == /\ pc = "p1"
+      /\ u' = 2
+      /\ /\ arg2' = 2 * u'
+         /\ stack' = << [ procedure |->  "Proc2",
+                          pc        |->  "p2",
+                          v         |->  v,
+                          arg2      |->  arg2 ] >>
+                      \o stack
+      /\ v' = 42
+      /\ pc' = "q1"
+      /\ UNCHANGED << arg1, arg3 >>
+
+p2 == /\ pc = "p2"
+      /\ Assert(u = 2, "Failure of assertion at line 10, column 18.")
+      /\ Assert(arg1  = 4, "Failure of assertion at line 11, column 18.")
+      /\ /\ arg2' = 2 * u + 1
+         /\ stack' = << [ procedure |->  "Proc2",
+                          pc        |->  Head(stack).pc,
+                          v         |->  v,
+                          arg2      |->  arg2 ] >>
+                      \o Tail(stack)
+         /\ u' = Head(stack).u
+      /\ v' = 42
+      /\ pc' = "q1"
+      /\ UNCHANGED << arg1, arg3 >>
+
+Proc1 == p1 \/ p2
+
+q1 == /\ pc = "q1"
+      /\ Assert(v = 42, "Failure of assertion at line 17, column 18.")
+      /\ Assert(arg2 \in {4, 5}, 
+                "Failure of assertion at line 18, column 18.")
+      /\ /\ arg3' = v + arg2
+         /\ stack' = << [ procedure |->  "Proc3",
+                          pc        |->  Head(stack).pc,
+                          arg3      |->  arg3 ] >>
+                      \o Tail(stack)
+         /\ v' = Head(stack).v
+      /\ pc' = "r1"
+      /\ UNCHANGED << arg1, u, arg2 >>
+
+Proc2 == q1
+
+r1 == /\ pc = "r1"
+      /\ Assert(arg3 \in {46, 47}, 
+                "Failure of assertion at line 23, column 18.")
+      /\ pc' = Head(stack).pc
+      /\ arg3' = Head(stack).arg3
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << arg1, u, arg2, v >>
+
+Proc3 == r1
+
+a1 == /\ pc = "a1"
+      /\ /\ arg1' = 4
+         /\ stack' = << [ procedure |->  "Proc1",
+                          pc        |->  "Done",
+                          u         |->  u,
+                          arg1      |->  arg1 ] >>
+                      \o stack
+      /\ u' = defaultInitValue
+      /\ pc' = "p1"
+      /\ UNCHANGED << arg2, v, arg3 >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Proc1 \/ Proc2 \/ Proc3 \/ a1
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-21e32d8e366d103d73444c2307bf4aba
+
+                                        
+=============================================================================
diff --git a/tlatools/test-model/pcal/CallReturn2.tla b/tlatools/test-model/pcal/CallReturn2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c0a5ec3e11faf1885928ebdea5a6219c0e27973e
--- /dev/null
+++ b/tlatools/test-model/pcal/CallReturn2.tla
@@ -0,0 +1,199 @@
+The translator is not properly initializing the local procedure variables.
+The assertions indicate how they should be initialized.  (The OldPlusCal
+and Pcal translations have this problem too.)
+
+----------------------------- MODULE CallReturn2 ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+
+(*   
+--algorithm Test
+   variable depth = 3
+   procedure P(a = 7) 
+      variable x = a ; y = x+1 ;
+      begin P1: assert a = 1;
+                assert x = a;
+                assert y = a+1;
+                return;
+      end procedure 
+  procedure Q() 
+     begin Q1: call P(1) ;
+              return ;
+      end procedure 
+  procedure PP(aa = 7)
+      variable xx = aa ; yy = xx+1 ;
+      begin PP1: if depth > 0
+                  then assert aa = 1;
+                     assert xx = aa;
+                     assert yy = aa+1;
+                     depth := depth - 1 ;
+                     call PP(1) ;
+                     return;
+                  else return 
+                end if 
+      end procedure 
+  procedure R(r = 0)
+     variable x
+     begin R1: x := 2 ;
+           R2: call S(x) ;
+               return 
+     end procedure
+  procedure S(s)
+    begin S1: assert s = 2 ;
+               return ;
+    end procedure 
+ begin A: call P(1) ;
+       B: call Q() ;
+       C: call PP(1) ;
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-84a5cdf59d68214ad905732a585890ae
+\* Procedure variable x of procedure P at line 13 col 16 changed to x_
+CONSTANT defaultInitValue
+VARIABLES depth, pc, stack, a, x_, y, aa, xx, yy, r, x, s
+
+vars == << depth, pc, stack, a, x_, y, aa, xx, yy, r, x, s >>
+
+Init == (* Global variables *)
+        /\ depth = 3
+        (* Procedure P *)
+        /\ a = 7
+        /\ x_ = a
+        /\ y = x_+1
+        (* Procedure PP *)
+        /\ aa = 7
+        /\ xx = aa
+        /\ yy = xx+1
+        (* Procedure R *)
+        /\ r = 0
+        /\ x = defaultInitValue
+        (* Procedure S *)
+        /\ s = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "A"
+
+P1 == /\ pc = "P1"
+      /\ Assert(a = 1, "Failure of assertion at line 14, column 17.")
+      /\ Assert(x_ = a, "Failure of assertion at line 15, column 17.")
+      /\ Assert(y = a+1, "Failure of assertion at line 16, column 17.")
+      /\ pc' = Head(stack).pc
+      /\ x_' = Head(stack).x_
+      /\ y' = Head(stack).y
+      /\ a' = Head(stack).a
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << depth, aa, xx, yy, r, x, s >>
+
+P == P1
+
+Q1 == /\ pc = "Q1"
+      /\ /\ a' = 1
+         /\ stack' = << [ procedure |->  "P",
+                          pc        |->  Head(stack).pc,
+                          x_        |->  x_,
+                          y         |->  y,
+                          a         |->  a ] >>
+                      \o Tail(stack)
+      /\ x_' = a'
+      /\ y' = x_'+1
+      /\ pc' = "P1"
+      /\ UNCHANGED << depth, aa, xx, yy, r, x, s >>
+
+Q == Q1
+
+PP1 == /\ pc = "PP1"
+       /\ IF depth > 0
+             THEN /\ Assert(aa = 1, 
+                            "Failure of assertion at line 26, column 24.")
+                  /\ Assert(xx = aa, 
+                            "Failure of assertion at line 27, column 22.")
+                  /\ Assert(yy = aa+1, 
+                            "Failure of assertion at line 28, column 22.")
+                  /\ depth' = depth - 1
+                  /\ aa' = 1
+                  /\ xx' = aa'
+                  /\ yy' = xx'+1
+                  /\ pc' = "PP1"
+                  /\ stack' = stack
+             ELSE /\ pc' = Head(stack).pc
+                  /\ xx' = Head(stack).xx
+                  /\ yy' = Head(stack).yy
+                  /\ aa' = Head(stack).aa
+                  /\ stack' = Tail(stack)
+                  /\ depth' = depth
+       /\ UNCHANGED << a, x_, y, r, x, s >>
+
+PP == PP1
+
+R1 == /\ pc = "R1"
+      /\ x' = 2
+      /\ pc' = "R2"
+      /\ UNCHANGED << depth, stack, a, x_, y, aa, xx, yy, r, s >>
+
+R2 == /\ pc = "R2"
+      /\ /\ s' = x
+         /\ stack' = << [ procedure |->  "S",
+                          pc        |->  Head(stack).pc,
+                          s         |->  s ] >>
+                      \o Tail(stack)
+         /\ x' = Head(stack).x
+      /\ pc' = "S1"
+      /\ UNCHANGED << depth, a, x_, y, aa, xx, yy, r >>
+
+R == R1 \/ R2
+
+S1 == /\ pc = "S1"
+      /\ Assert(s = 2, "Failure of assertion at line 42, column 15.")
+      /\ pc' = Head(stack).pc
+      /\ s' = Head(stack).s
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << depth, a, x_, y, aa, xx, yy, r, x >>
+
+S == S1
+
+A == /\ pc = "A"
+     /\ /\ a' = 1
+        /\ stack' = << [ procedure |->  "P",
+                         pc        |->  "B",
+                         x_        |->  x_,
+                         y         |->  y,
+                         a         |->  a ] >>
+                     \o stack
+     /\ x_' = a'
+     /\ y' = x_'+1
+     /\ pc' = "P1"
+     /\ UNCHANGED << depth, aa, xx, yy, r, x, s >>
+
+B == /\ pc = "B"
+     /\ stack' = << [ procedure |->  "Q",
+                      pc        |->  "C" ] >>
+                  \o stack
+     /\ pc' = "Q1"
+     /\ UNCHANGED << depth, a, x_, y, aa, xx, yy, r, x, s >>
+
+C == /\ pc = "C"
+     /\ /\ aa' = 1
+        /\ stack' = << [ procedure |->  "PP",
+                         pc        |->  "Done",
+                         xx        |->  xx,
+                         yy        |->  yy,
+                         aa        |->  aa ] >>
+                     \o stack
+     /\ xx' = aa'
+     /\ yy' = xx'+1
+     /\ pc' = "PP1"
+     /\ UNCHANGED << depth, a, x_, y, r, x, s >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == P \/ Q \/ PP \/ R \/ S \/ A \/ B \/ C
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c8e76178d4d24aee35fa88a19dd439d8
+=============================================================================
diff --git a/tlatools/test-model/pcal/DetlefSpec.cfg b/tlatools/test-model/pcal/DetlefSpec.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..69620fba44afdea8180a8923da52a55736e4ddf0
--- /dev/null
+++ b/tlatools/test-model/pcal/DetlefSpec.cfg
@@ -0,0 +1,7 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANTS Val = {v1}
+          Procs = {p1}
+          N = 3
+          null = null
+CONSTRAINT Constraint
diff --git a/tlatools/test-model/pcal/DetlefSpec.tla b/tlatools/test-model/pcal/DetlefSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..923bd524312ce432ff4268489dee3515a1d8d111
--- /dev/null
+++ b/tlatools/test-model/pcal/DetlefSpec.tla
@@ -0,0 +1,79 @@
+----------------------------- MODULE DetlefSpec ----------------------------- 
+EXTENDS Sequences, Naturals
+
+CONSTANTS Val, Procs, null
+
+
+(* --algorithm Spec {
+      variable queue = << >> ;
+      process (P \in Procs) 
+      variable rV = null ; {
+L1: while (TRUE) {
+      either 
+        with (v \in Val) {
+           either { queue := queue \o <<v>> ;
+                    rV := "okay" }
+               or { queue := <<v>> \o  queue ;
+                    rV := "okay"} 
+               or rV := "full" ;
+          }
+      or { if (queue # << >>)
+             { either { rV := Head(queue) ;
+                        queue := Tail(queue) }
+                   or { rV := queue[Len(queue)] ;
+                        queue := [ i \in 1 .. (Len(queue) - 1) |-> queue[i]]}
+             }
+           else { rV := "empty" } 
+         } ;
+L2:   rV := null 
+    } } }
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1ffa57620b1dc5120c521caddbcfa0df
+VARIABLES queue, pc, rV
+
+vars == << queue, pc, rV >>
+
+ProcSet == (Procs)
+
+Init == (* Global variables *)
+        /\ queue = << >>
+        (* Process P *)
+        /\ rV = [self \in Procs |-> null]
+        /\ pc = [self \in ProcSet |-> "L1"]
+
+L1(self) == /\ pc[self] = "L1"
+            /\ \/ /\ \E v \in Val:
+                       \/ /\ queue' = queue \o <<v>>
+                          /\ rV' = [rV EXCEPT ![self] = "okay"]
+                       \/ /\ queue' = <<v>> \o  queue
+                          /\ rV' = [rV EXCEPT ![self] = "okay"]
+                       \/ /\ rV' = [rV EXCEPT ![self] = "full"]
+                          /\ queue' = queue
+               \/ /\ IF queue # << >>
+                        THEN /\ \/ /\ rV' = [rV EXCEPT ![self] = Head(queue)]
+                                   /\ queue' = Tail(queue)
+                                \/ /\ rV' = [rV EXCEPT ![self] = queue[Len(queue)]]
+                                   /\ queue' = [ i \in 1 .. (Len(queue) - 1) |-> queue[i]]
+                        ELSE /\ rV' = [rV EXCEPT ![self] = "empty"]
+                             /\ queue' = queue
+            /\ pc' = [pc EXCEPT ![self] = "L2"]
+
+L2(self) == /\ pc[self] = "L2"
+            /\ rV' = [rV EXCEPT ![self] = null]
+            /\ pc' = [pc EXCEPT ![self] = "L1"]
+            /\ queue' = queue
+
+P(self) == L1(self) \/ L2(self)
+
+Next == (\E self \in Procs: P(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in Procs : WF_vars(P(self))
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-a5e9e9156ce3a9bf6c7fb3f657f54020
+
+CONSTANT N
+
+Constraint == Len(queue) \leq N
+=============================================================================
diff --git a/tlatools/test-model/pcal/DetlefTest.cfg b/tlatools/test-model/pcal/DetlefTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..71587f4bf5be09b735bbc537455aef25bb113bbd
--- /dev/null
+++ b/tlatools/test-model/pcal/DetlefTest.cfg
@@ -0,0 +1,10 @@
+SPECIFICATION XSpec
+CONSTANTS a1=a1 a2=a2 a3=a3 a4=a4 v1=v1 v2=v2 p1=p1 p2=p2 p3=p3
+          Val = {v1}
+          Address = {a1, a2, a3, a4}
+          Dummy = a1
+          Procs = {p1, p2} \* , p3}
+          null = null 
+
+PROPERTY XQSpec
+
diff --git a/tlatools/test-model/pcal/DetlefTest.tla b/tlatools/test-model/pcal/DetlefTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9b73cf79009e626ca463dd1818d09f16ad0321ea
--- /dev/null
+++ b/tlatools/test-model/pcal/DetlefTest.tla
@@ -0,0 +1,49 @@
+----------------------------- MODULE DetlefTest ----------------------------- 
+EXTENDS Detlefs2
+
+qRFrom[adr \in Address] == IF adr = LeftHat 
+                THEN  <<Mem[adr].V>>
+                ELSE  qRFrom[Mem[adr].R] \o <<Mem[adr].V>>
+
+qBar == IF Mem[RightHat].R = RightHat 
+          THEN << >> 
+          ELSE qRFrom[RightHat]
+
+rVBar == [p \in Procs |-> 
+           CASE 
+                pc[p] \in {"M4", "M7"} -> Mem[rh[p]].V
+             [] pc[p] = "M8" -> result[p]
+             [] pc[p] \in {"O5", "O8"} -> Mem[rh[p]].V
+             [] pc[p] = "O9" -> result[p]
+             [] OTHER -> rVal[p]]
+
+pcBar == [p \in Procs |-> IF rVBar[p] = null THEN "L1" ELSE "L2"]
+
+SpecBar == INSTANCE DetlefSpec WITH rV <- rVBar, pc <- pcBar,
+                                    N <- 100
+QSpec == SpecBar!Spec
+
+CONSTANTS a1, a2, a3, a4, v1, v2, p1, p2, p3
+XInit == 
+/\ lh = (p1 :> a1 @@ p2 :> a1)
+/\ LeftHat = a2
+/\ nd = (p1 :> a2 @@ p2 :> null)
+/\ RightHat = a2
+/\ rVal = (p1 :> "okay" @@ p2 :> null)
+/\ valBag = (v1 :> 0)
+/\ pc = (p1 :> "Return" @@ p2 :> "M2")
+/\ temp = (p1 :> TRUE @@ p2 :> a1)
+/\ rh = (p1 :> a1 @@ p2 :> a1)
+/\ Mem = ( a1 :> [V |-> null, R |-> a1, L |-> a1] @@
+  a2 :> [V |-> v1, R |-> a1, L |-> a1] @@
+  a3 :> [V |-> null, R |-> null, L |-> null] @@
+  a4 :> [V |-> null, R |-> null, L |-> null] )
+/\ queue = << v1 >>
+/\ ptr = (p1 :> a1 @@ p2 :> a1)
+/\ result = (p1 :> v1 @@ p2 :> v1)
+/\ freeList = {a3, a4}
+
+XSpec == XInit /\ [][Next]_vars
+
+XQSpec == [][SpecBar!Next]_(SpecBar!vars)
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/pcal/Detlefs.cfg b/tlatools/test-model/pcal/Detlefs.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ca8ad7597a33b1c48943fa444f567bf0f34b7f6a
--- /dev/null
+++ b/tlatools/test-model/pcal/Detlefs.cfg
@@ -0,0 +1,11 @@
+SPECIFICATION Spec
+CONSTANT defaultInitValue = defaultInitValue
+\* Add statements after this line.
+CONSTANTS Val = {v1}
+          Address = {a1, a2, a3, a4}
+          Dummy = a1
+          Procs = {p1, p2} \* , p3}
+          null = null
+          GC = GC
+\* PROPERTY Liveness
+SYMMETRY Symmetry
diff --git a/tlatools/test-model/pcal/Detlefs.tla b/tlatools/test-model/pcal/Detlefs.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4bde20a127acb10d05a574ffb56ca5d6fd4c7e69
--- /dev/null
+++ b/tlatools/test-model/pcal/Detlefs.tla
@@ -0,0 +1,1006 @@
+------------------------------- MODULE Detlefs ------------------------------ 
+EXTENDS Naturals, Sequences, TLC, FiniteSets
+
+CONSTANTS Val, Address, Dummy, Procs, GC
+ASSUME Dummy \in Address
+ASSUME GC \notin Procs
+
+
+null == CHOOSE n: n \notin Address
+Node == [R : Address \cup {null}, L : Address \cup {null}, V : Val]
+DummyNode == [R |-> Dummy, L |-> Dummy, V |-> null]
+InitNode ==  [R |-> null,  L |-> null,  V |-> null]
+
+(*
+\* boolean DCAS(val *addr1, val *addr2, 
+\*              val old1, val old2, 
+\*              val new1, val new2) { 
+\*  atomically { 
+\*    if (( *addr1 == old1) && 
+\*        ( *addr2 == old2)) { 
+\*      *addr1 = new1; 
+\*      *addr2 = new2; 
+\*      return true; 
+\*    } else return false; } }
+
+--algorithm Snark {
+variables Mem = [i \in Address |-> IF i = Dummy THEN DummyNode ELSE InitNode],
+          freeList = Address \ {Dummy},
+          LeftHat  = Dummy,
+          RightHat = Dummy,
+          rVal = [i \in Procs |-> "okay"] ,   \* Used for returning values
+          valBag = [i \in Val |-> 0] ;
+            \* For testing: valBag[i] is the number of copies of i 
+            \* that can be in the queue.
+
+macro New(result) {
+  if (freeList # {}) {
+    result := CHOOSE a \in freeList : TRUE ;
+    freeList := freeList \ {result} ;
+  } else result := null
+}
+
+macro DCAS(result, addr1, addr2, old1, old2, new1, new2) {
+  if ( /\ addr1 = old1
+       /\ addr2 = old2) { 
+    addr1 := new1 ||
+    addr2 := new2 ;
+    result := TRUE; 
+  } else result := FALSE; } 
+
+
+\* val pushRight(val v) { 
+\*   nd = new Node(); /* Allocate new Node structure */ 
+\*   if (nd == null) return "full"; 
+\*   nd�>R = Dummy; 
+\*   nd�>V = v; 
+\*   while (true) { 
+\*     rh = RightHat;                           /* Labels A, B,   */ 
+\*     rhR = rh�>R;                             /* etc., are used */ 
+\*     if (rhR == rh) {                         /* in the proof   */ 
+\*       nd�>L = Dummy;                         /* of correctness */ 
+\*       lh = LeftHat; 
+\*       if (DCAS(&RightHat, &LeftHat, rh, lh, nd, nd))      /* A */ 
+\*         return "okay"; 
+\*     } else { 
+\*       nd�>L = rh; 
+\*       if (DCAS(&RightHat, &rh�>R, rh, rhR, nd, nd))      /* B */ 
+\*         return "okay"; 
+\* } } } // Please forgive this brace style 
+
+procedure pushRight(v) 
+ variables nd = null , rh = Dummy, rhR = Dummy, lh = Dummy, 
+           temp = Dummy ; { 
+L1: New(nd) ; 
+    if (nd = null) { rVal[self] := "full"; L1a: return } ;
+L1b: Mem[nd].R := Dummy ||   \* Since no other thread can access nd here,
+     Mem[nd].V := v ; 
+L4: while (TRUE) { 
+      rh  := RightHat;                           
+L5:   rhR := Mem[rh].R;                          
+L6:   if (rhR = rh) {                         
+        Mem[nd].L := Dummy;                   
+        lh := LeftHat; 
+L7:     DCAS(temp, RightHat, LeftHat, rh, lh, nd, nd) ;
+        if (temp) { 
+          rVal[self] := "okay"; L7a: return}
+      } else { 
+L8:       Mem[nd].L := rh; 
+L9:       DCAS(temp, RightHat, Mem[rh].R, rh, rhR, nd, nd) ;
+          if (temp) {
+            rVal[self] := "okay";  L8a: return }
+} } } 
+
+
+\* val popRight() { 
+\*    while (true) { 
+\*      rh = RightHat;                     // Delicate order of operations 
+\*      lh = LeftHat;                      // here (see proof of Theorem 4 
+\*      if (rh�>R == rh) return "empty";   // and the Conclusions section) 
+\*      if (rh == lh) { 
+\*        if (DCAS(&RightHat, &LeftHat, rh, lh, Dummy, Dummy))     /* C */ 
+\*          return rh�>V; 
+\*      } else { 
+\*        rhL = rh�>L; 
+\*        if (DCAS(&RightHat, &rh�>L, rh, rhL, rhL, rh)) {         /* D */ 
+\*          result = rh�>V; 
+\*          rh�>R = Dummy; /* E */ 
+\*          rh�>V = null;                        /* optional (see text) */ 
+\*          return result; 
+\* } } } }                         // Stacking braces this way saves space 
+\* 
+
+procedure popRight()
+ variables rh = Dummy, lh = Dummy, rhL = Dummy, 
+           temp = Dummy , result = null ; { 
+M1: while (TRUE) { 
+      rh := RightHat;    
+M2:   lh := LeftHat;     
+      if (Mem[rh].R = rh) {rVal[self] := "empty"; M2a: return ;} ;
+M3:   if (rh = lh) { 
+        DCAS(temp, RightHat, LeftHat, rh, lh, Dummy, Dummy) ;
+        if (temp) { 
+M4:       rVal[self] := Mem[rh].V ; M4a:  return;} 
+      } else { 
+M5:     rhL := Mem[rh].L ; 
+M6:     DCAS(temp, RightHat, Mem[rh].L, rh, rhL, rhL, rh) ;
+        if (temp) {         
+M7:       result := Mem[rh].V; 
+M8:       Mem[rh].R := Dummy ||
+          Mem[rh].V := null;  
+          rVal[self] := result ; M9a: return ;
+} } } }
+
+\* val pushLeft(val v) { 
+\*   nd = new Node();      /* Allocate new Node structure */ 
+\*   if (nd == null) return "full"; 
+\*   nd�>L = Dummy; 
+\*   nd�>V = v; 
+\*   while (true) { 
+\*     lh = LeftHat; 
+\*     lhL = lh�>L; 
+\*     if (lhL == lh) { 
+\*       nd�>R = Dummy; 
+\*       rh = RightHat; 
+\*       if (DCAS(&LeftHat, &RightHat, lh, rh, nd, nd))          /* A' */ 
+\*         return "okay"; 
+\*     } else { 
+\*       nd�>R = lh; 
+\*       if (DCAS(&LeftHat, &lh�>L, lh, lhL, nd, nd))            /* B' */ 
+\*         return "okay"; 
+\* } } }                        // We were given a firm limit of 15 pages 
+
+procedure pushLeft(v) 
+ variables nd = null , rh = Dummy, lhL = Dummy, lh = Dummy, 
+           temp = Dummy ; { 
+N1: New(nd) ;                          
+    if (nd = null) {rVal[self] := "full"; N1a: return ;} ;
+N1b: Mem[nd].L := Dummy ||   \* Since no other thread can access nd here,
+    Mem[nd].V := v;          \* we can represent this as a single action. 
+N2: while (TRUE) { 
+    lh := LeftHat;          
+N3: lhL := Mem[lh].L; 
+    if (lhL = lh) { 
+N4:   Mem[nd].R := Dummy; 
+N5:   rh := RightHat; 
+N6:   DCAS(temp, LeftHat, RightHat, lh, rh, nd, nd) ;
+      if (temp) {          
+        rVal[self] := "okay";  nd := null ; N6a: return;}
+    } else { 
+N7:   Mem[nd].R := lh; 
+N8:   DCAS(temp, LeftHat, Mem[lh].L, lh, lhL, nd, nd) ;
+      if (temp) {          
+        rVal[self] := "okay"; nd := null ; N8a: return }
+} } }                    
+  
+\* val popLeft() { 
+\*   while (true) { 
+\*     lh = LeftHat;                          // Delicate order of operations 
+\*     rh = RightHat;                         // here (see proof of Theorem 4 
+\*     if (lh�>L == lh) return "empty";       // and the Conclusions section) 
+\*     if (lh == rh) { 
+\*       if (DCAS(&LeftHat, &RightHat, lh, rh, Dummy, Dummy))        /* C' */ 
+\*         return lh�>V; 
+\*     } else { 
+\*       lhR = lh�>R; 
+\*       if (DCAS(&LeftHat, &lh�>R, lh, lhR, lhR, lh)) {             /* D' */ 
+\*         result = lh�>V; 
+\*         lh�>L = Dummy;                                            /* E' */ 
+\*         lh�>V = null;                            /* optional (see text) */ 
+\*         return result; 
+\* } } } }                     // Better to stack braces than to omit a lemma 
+
+procedure popLeft() 
+ variables rh = Dummy, lh = Dummy, lhR = Dummy, 
+           temp = Dummy , result = null ; { 
+O1: while (TRUE) { 
+      lh := LeftHat;                           
+O2:   rh := RightHat;                    
+O3:   if (Mem[lh].L = lh) {rVal[self] := "empty";  O3a: return ;} ;
+O4:   if (lh = rh) { 
+      DCAS(temp, LeftHat, RightHat, lh, rh, Dummy, Dummy) ;
+      if (temp) {          
+O5:     rVal[self] := Mem[lh].V; O5a: return; }
+      } else { 
+O6:     lhR := Mem[lh].R; 
+O7:     DCAS(temp, LeftHat, Mem[lh].R, lh, lhR, lhR, lh) ;
+      if (temp) {               
+O8:     result := Mem[lh].V; 
+O9:     Mem[lh].L := Dummy ||
+        Mem[lh].V := null;                              
+        rVal[self] := result;  O10a: return ;
+} } } }                       
+
+
+\* process (GarbageCollet = GC) {
+\* GC1: while (TRUE) {
+\*      with (adr \in Address \ (freeList \cup {Dummy})) {
+\*        when Mem[adr].canGC ;
+\*        when \A b \in Address \ (freeList \cup {Dummy, adr}) :
+\*               /\ adr # Mem[b].L
+\*               /\ adr # Mem[b].R ;
+\*        freeList := freeList \cup {adr} ;
+\*        Mem[adr] := InitNode;
+\*      } } }
+
+process (test \in Procs)
+variables pushedVal = null ; {
+T1: while(TRUE) {
+      either { \* push
+        with (x \in Val) {pushedVal := x} ;
+        valBag[pushedVal] := valBag[pushedVal] + 1 ;
+        either  call pushLeft(pushedVal) or call pushRight(pushedVal)  ;
+T2:     if (rVal[self] = "full") valBag[pushedVal] := valBag[pushedVal] - 1 
+        }
+      or { \* pop
+        either call popLeft() or call popRight()  ;
+T3:     if (rVal[self] # "empty") {
+          assert valBag[rVal[self]] > 0 ;
+          valBag[rVal[self]] := valBag[rVal[self]] - 1;
+        } 
+} } }}
+*)
+
+\* BEGIN TRANSLATION
+\* Procedure variable nd of procedure pushRight at line 72 col 12 changed to nd_
+\* Procedure variable rh of procedure pushRight at line 72 col 24 changed to rh_
+\* Procedure variable lh of procedure pushRight at line 72 col 49 changed to lh_
+\* Procedure variable temp of procedure pushRight at line 73 col 12 changed to temp_
+\* Procedure variable rh of procedure popRight at line 114 col 12 changed to rh_p
+\* Procedure variable lh of procedure popRight at line 114 col 24 changed to lh_p
+\* Procedure variable temp of procedure popRight at line 115 col 12 changed to temp_p
+\* Procedure variable result of procedure popRight at line 115 col 27 changed to result_
+\* Procedure variable rh of procedure pushLeft at line 154 col 24 changed to rh_pu
+\* Procedure variable lh of procedure pushLeft at line 154 col 49 changed to lh_pu
+\* Procedure variable temp of procedure pushLeft at line 155 col 12 changed to temp_pu
+\* Parameter v of procedure pushRight at line 71 col 21 changed to v_
+CONSTANT defaultInitValue
+VARIABLES Mem, freeList, LeftHat, RightHat, rVal, valBag, pc, stack, v_, nd_, 
+          rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, v, nd, 
+          rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, pushedVal
+
+vars == << Mem, freeList, LeftHat, RightHat, rVal, valBag, pc, stack, v_, nd_, 
+           rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, v, nd, 
+           rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, pushedVal
+        >>
+
+ProcSet == (Procs)
+
+Init == (* Global variables *)
+        /\ Mem = [i \in Address |-> IF i = Dummy THEN DummyNode ELSE InitNode]
+        /\ freeList = Address \ {Dummy}
+        /\ LeftHat = Dummy
+        /\ RightHat = Dummy
+        /\ rVal = [i \in Procs |-> "okay"]
+        /\ valBag = [i \in Val |-> 0]
+        (* Procedure pushRight *)
+        /\ v_ = [ self \in ProcSet |-> defaultInitValue]
+        /\ nd_ = [ self \in ProcSet |-> null]
+        /\ rh_ = [ self \in ProcSet |-> Dummy]
+        /\ rhR = [ self \in ProcSet |-> Dummy]
+        /\ lh_ = [ self \in ProcSet |-> Dummy]
+        /\ temp_ = [ self \in ProcSet |-> Dummy]
+        (* Procedure popRight *)
+        /\ rh_p = [ self \in ProcSet |-> Dummy]
+        /\ lh_p = [ self \in ProcSet |-> Dummy]
+        /\ rhL = [ self \in ProcSet |-> Dummy]
+        /\ temp_p = [ self \in ProcSet |-> Dummy]
+        /\ result_ = [ self \in ProcSet |-> null]
+        (* Procedure pushLeft *)
+        /\ v = [ self \in ProcSet |-> defaultInitValue]
+        /\ nd = [ self \in ProcSet |-> null]
+        /\ rh_pu = [ self \in ProcSet |-> Dummy]
+        /\ lhL = [ self \in ProcSet |-> Dummy]
+        /\ lh_pu = [ self \in ProcSet |-> Dummy]
+        /\ temp_pu = [ self \in ProcSet |-> Dummy]
+        (* Procedure popLeft *)
+        /\ rh = [ self \in ProcSet |-> Dummy]
+        /\ lh = [ self \in ProcSet |-> Dummy]
+        /\ lhR = [ self \in ProcSet |-> Dummy]
+        /\ temp = [ self \in ProcSet |-> Dummy]
+        /\ result = [ self \in ProcSet |-> null]
+        (* Process test *)
+        /\ pushedVal = [self \in Procs |-> null]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> "T1"]
+
+L1(self) == /\ pc[self] = "L1"
+            /\ IF freeList # {}
+                  THEN /\ nd_' = [nd_ EXCEPT ![self] = CHOOSE a \in freeList : TRUE]
+                       /\ freeList' = freeList \ {nd_'[self]}
+                  ELSE /\ nd_' = [nd_ EXCEPT ![self] = null]
+                       /\ UNCHANGED freeList
+            /\ IF nd_'[self] = null
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "full"]
+                       /\ pc' = [pc EXCEPT ![self] = "L1a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "L1b"]
+                       /\ rVal' = rVal
+            /\ UNCHANGED << Mem, LeftHat, RightHat, valBag, stack, v_, rh_, 
+                            rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, 
+                            v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, 
+                            temp, result, pushedVal >>
+
+L1a(self) == /\ pc[self] = "L1a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ nd_' = [nd_ EXCEPT ![self] = Head(stack[self]).nd_]
+             /\ rh_' = [rh_ EXCEPT ![self] = Head(stack[self]).rh_]
+             /\ rhR' = [rhR EXCEPT ![self] = Head(stack[self]).rhR]
+             /\ lh_' = [lh_ EXCEPT ![self] = Head(stack[self]).lh_]
+             /\ temp_' = [temp_ EXCEPT ![self] = Head(stack[self]).temp_]
+             /\ v_' = [v_ EXCEPT ![self] = Head(stack[self]).v_]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             rh_p, lh_p, rhL, temp_p, result_, v, nd, rh_pu, 
+                             lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+L1b(self) == /\ pc[self] = "L1b"
+             /\ Mem' = [Mem EXCEPT ![nd_[self]].R = Dummy,
+                                   ![nd_[self]].V = v_[self]]
+             /\ pc' = [pc EXCEPT ![self] = "L4"]
+             /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                             temp_pu, rh, lh, lhR, temp, result, pushedVal >>
+
+L4(self) == /\ pc[self] = "L4"
+            /\ rh_' = [rh_ EXCEPT ![self] = RightHat]
+            /\ pc' = [pc EXCEPT ![self] = "L5"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+L5(self) == /\ pc[self] = "L5"
+            /\ rhR' = [rhR EXCEPT ![self] = Mem[rh_[self]].R]
+            /\ pc' = [pc EXCEPT ![self] = "L6"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+L6(self) == /\ pc[self] = "L6"
+            /\ IF rhR[self] = rh_[self]
+                  THEN /\ Mem' = [Mem EXCEPT ![nd_[self]].L = Dummy]
+                       /\ lh_' = [lh_ EXCEPT ![self] = LeftHat]
+                       /\ pc' = [pc EXCEPT ![self] = "L7"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "L8"]
+                       /\ UNCHANGED << Mem, lh_ >>
+            /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, 
+                            v_, nd_, rh_, rhR, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+L7(self) == /\ pc[self] = "L7"
+            /\ IF /\ RightHat = rh_[self]
+                  /\ LeftHat = lh_[self]
+                  THEN /\ /\ LeftHat' = nd_[self]
+                          /\ RightHat' = nd_[self]
+                       /\ temp_' = [temp_ EXCEPT ![self] = TRUE]
+                  ELSE /\ temp_' = [temp_ EXCEPT ![self] = FALSE]
+                       /\ UNCHANGED << LeftHat, RightHat >>
+            /\ IF temp_'[self]
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"]
+                       /\ pc' = [pc EXCEPT ![self] = "L7a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "L4"]
+                       /\ rVal' = rVal
+            /\ UNCHANGED << Mem, freeList, valBag, stack, v_, nd_, rh_, rhR, 
+                            lh_, rh_p, lh_p, rhL, temp_p, result_, v, nd, 
+                            rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, 
+                            result, pushedVal >>
+
+L7a(self) == /\ pc[self] = "L7a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ nd_' = [nd_ EXCEPT ![self] = Head(stack[self]).nd_]
+             /\ rh_' = [rh_ EXCEPT ![self] = Head(stack[self]).rh_]
+             /\ rhR' = [rhR EXCEPT ![self] = Head(stack[self]).rhR]
+             /\ lh_' = [lh_ EXCEPT ![self] = Head(stack[self]).lh_]
+             /\ temp_' = [temp_ EXCEPT ![self] = Head(stack[self]).temp_]
+             /\ v_' = [v_ EXCEPT ![self] = Head(stack[self]).v_]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             rh_p, lh_p, rhL, temp_p, result_, v, nd, rh_pu, 
+                             lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+L8(self) == /\ pc[self] = "L8"
+            /\ Mem' = [Mem EXCEPT ![nd_[self]].L = rh_[self]]
+            /\ pc' = [pc EXCEPT ![self] = "L9"]
+            /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+L9(self) == /\ pc[self] = "L9"
+            /\ IF /\ RightHat = rh_[self]
+                  /\ (Mem[rh_[self]].R) = rhR[self]
+                  THEN /\ /\ Mem' = [Mem EXCEPT ![rh_[self]].R = nd_[self]]
+                          /\ RightHat' = nd_[self]
+                       /\ temp_' = [temp_ EXCEPT ![self] = TRUE]
+                  ELSE /\ temp_' = [temp_ EXCEPT ![self] = FALSE]
+                       /\ UNCHANGED << Mem, RightHat >>
+            /\ IF temp_'[self]
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"]
+                       /\ pc' = [pc EXCEPT ![self] = "L8a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "L4"]
+                       /\ rVal' = rVal
+            /\ UNCHANGED << freeList, LeftHat, valBag, stack, v_, nd_, rh_, 
+                            rhR, lh_, rh_p, lh_p, rhL, temp_p, result_, v, nd, 
+                            rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, 
+                            result, pushedVal >>
+
+L8a(self) == /\ pc[self] = "L8a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ nd_' = [nd_ EXCEPT ![self] = Head(stack[self]).nd_]
+             /\ rh_' = [rh_ EXCEPT ![self] = Head(stack[self]).rh_]
+             /\ rhR' = [rhR EXCEPT ![self] = Head(stack[self]).rhR]
+             /\ lh_' = [lh_ EXCEPT ![self] = Head(stack[self]).lh_]
+             /\ temp_' = [temp_ EXCEPT ![self] = Head(stack[self]).temp_]
+             /\ v_' = [v_ EXCEPT ![self] = Head(stack[self]).v_]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             rh_p, lh_p, rhL, temp_p, result_, v, nd, rh_pu, 
+                             lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+pushRight(self) == L1(self) \/ L1a(self) \/ L1b(self) \/ L4(self)
+                      \/ L5(self) \/ L6(self) \/ L7(self) \/ L7a(self)
+                      \/ L8(self) \/ L9(self) \/ L8a(self)
+
+M1(self) == /\ pc[self] = "M1"
+            /\ rh_p' = [rh_p EXCEPT ![self] = RightHat]
+            /\ pc' = [pc EXCEPT ![self] = "M2"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+M2(self) == /\ pc[self] = "M2"
+            /\ lh_p' = [lh_p EXCEPT ![self] = LeftHat]
+            /\ IF Mem[rh_p[self]].R = rh_p[self]
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "empty"]
+                       /\ pc' = [pc EXCEPT ![self] = "M2a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "M3"]
+                       /\ rVal' = rVal
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+M2a(self) == /\ pc[self] = "M2a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ rh_p' = [rh_p EXCEPT ![self] = Head(stack[self]).rh_p]
+             /\ lh_p' = [lh_p EXCEPT ![self] = Head(stack[self]).lh_p]
+             /\ rhL' = [rhL EXCEPT ![self] = Head(stack[self]).rhL]
+             /\ temp_p' = [temp_p EXCEPT ![self] = Head(stack[self]).temp_p]
+             /\ result_' = [result_ EXCEPT ![self] = Head(stack[self]).result_]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, 
+                             lh_pu, temp_pu, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+M3(self) == /\ pc[self] = "M3"
+            /\ IF rh_p[self] = lh_p[self]
+                  THEN /\ IF /\ RightHat = rh_p[self]
+                             /\ LeftHat = lh_p[self]
+                             THEN /\ /\ LeftHat' = Dummy
+                                     /\ RightHat' = Dummy
+                                  /\ temp_p' = [temp_p EXCEPT ![self] = TRUE]
+                             ELSE /\ temp_p' = [temp_p EXCEPT ![self] = FALSE]
+                                  /\ UNCHANGED << LeftHat, RightHat >>
+                       /\ IF temp_p'[self]
+                             THEN /\ pc' = [pc EXCEPT ![self] = "M4"]
+                             ELSE /\ pc' = [pc EXCEPT ![self] = "M1"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "M5"]
+                       /\ UNCHANGED << LeftHat, RightHat, temp_p >>
+            /\ UNCHANGED << Mem, freeList, rVal, valBag, stack, v_, nd_, rh_, 
+                            rhR, lh_, temp_, rh_p, lh_p, rhL, result_, v, nd, 
+                            rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, 
+                            result, pushedVal >>
+
+M5(self) == /\ pc[self] = "M5"
+            /\ rhL' = [rhL EXCEPT ![self] = Mem[rh_p[self]].L]
+            /\ pc' = [pc EXCEPT ![self] = "M6"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+M6(self) == /\ pc[self] = "M6"
+            /\ IF /\ RightHat = rh_p[self]
+                  /\ (Mem[rh_p[self]].L) = rhL[self]
+                  THEN /\ /\ Mem' = [Mem EXCEPT ![rh_p[self]].L = rh_p[self]]
+                          /\ RightHat' = rhL[self]
+                       /\ temp_p' = [temp_p EXCEPT ![self] = TRUE]
+                  ELSE /\ temp_p' = [temp_p EXCEPT ![self] = FALSE]
+                       /\ UNCHANGED << Mem, RightHat >>
+            /\ IF temp_p'[self]
+                  THEN /\ pc' = [pc EXCEPT ![self] = "M7"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "M1"]
+            /\ UNCHANGED << freeList, LeftHat, rVal, valBag, stack, v_, nd_, 
+                            rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, result_, v, 
+                            nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, 
+                            result, pushedVal >>
+
+M7(self) == /\ pc[self] = "M7"
+            /\ result_' = [result_ EXCEPT ![self] = Mem[rh_p[self]].V]
+            /\ pc' = [pc EXCEPT ![self] = "M8"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, 
+                            lh, lhR, temp, result, pushedVal >>
+
+M8(self) == /\ pc[self] = "M8"
+            /\ Mem' = [Mem EXCEPT ![rh_p[self]].R = Dummy,
+                                  ![rh_p[self]].V = null]
+            /\ rVal' = [rVal EXCEPT ![self] = result_[self]]
+            /\ pc' = [pc EXCEPT ![self] = "M9a"]
+            /\ UNCHANGED << freeList, LeftHat, RightHat, valBag, stack, v_, 
+                            nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+M9a(self) == /\ pc[self] = "M9a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ rh_p' = [rh_p EXCEPT ![self] = Head(stack[self]).rh_p]
+             /\ lh_p' = [lh_p EXCEPT ![self] = Head(stack[self]).lh_p]
+             /\ rhL' = [rhL EXCEPT ![self] = Head(stack[self]).rhL]
+             /\ temp_p' = [temp_p EXCEPT ![self] = Head(stack[self]).temp_p]
+             /\ result_' = [result_ EXCEPT ![self] = Head(stack[self]).result_]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, 
+                             lh_pu, temp_pu, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+M4(self) == /\ pc[self] = "M4"
+            /\ rVal' = [rVal EXCEPT ![self] = Mem[rh_p[self]].V]
+            /\ pc' = [pc EXCEPT ![self] = "M4a"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+M4a(self) == /\ pc[self] = "M4a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ rh_p' = [rh_p EXCEPT ![self] = Head(stack[self]).rh_p]
+             /\ lh_p' = [lh_p EXCEPT ![self] = Head(stack[self]).lh_p]
+             /\ rhL' = [rhL EXCEPT ![self] = Head(stack[self]).rhL]
+             /\ temp_p' = [temp_p EXCEPT ![self] = Head(stack[self]).temp_p]
+             /\ result_' = [result_ EXCEPT ![self] = Head(stack[self]).result_]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, 
+                             lh_pu, temp_pu, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+popRight(self) == M1(self) \/ M2(self) \/ M2a(self) \/ M3(self) \/ M5(self)
+                     \/ M6(self) \/ M7(self) \/ M8(self) \/ M9a(self)
+                     \/ M4(self) \/ M4a(self)
+
+N1(self) == /\ pc[self] = "N1"
+            /\ IF freeList # {}
+                  THEN /\ nd' = [nd EXCEPT ![self] = CHOOSE a \in freeList : TRUE]
+                       /\ freeList' = freeList \ {nd'[self]}
+                  ELSE /\ nd' = [nd EXCEPT ![self] = null]
+                       /\ UNCHANGED freeList
+            /\ IF nd'[self] = null
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "full"]
+                       /\ pc' = [pc EXCEPT ![self] = "N1a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "N1b"]
+                       /\ rVal' = rVal
+            /\ UNCHANGED << Mem, LeftHat, RightHat, valBag, stack, v_, nd_, 
+                            rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+N1a(self) == /\ pc[self] = "N1a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ nd' = [nd EXCEPT ![self] = Head(stack[self]).nd]
+             /\ rh_pu' = [rh_pu EXCEPT ![self] = Head(stack[self]).rh_pu]
+             /\ lhL' = [lhL EXCEPT ![self] = Head(stack[self]).lhL]
+             /\ lh_pu' = [lh_pu EXCEPT ![self] = Head(stack[self]).lh_pu]
+             /\ temp_pu' = [temp_pu EXCEPT ![self] = Head(stack[self]).temp_pu]
+             /\ v' = [v EXCEPT ![self] = Head(stack[self]).v]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+N1b(self) == /\ pc[self] = "N1b"
+             /\ Mem' = [Mem EXCEPT ![nd[self]].L = Dummy,
+                                   ![nd[self]].V = v[self]]
+             /\ pc' = [pc EXCEPT ![self] = "N2"]
+             /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                             temp_pu, rh, lh, lhR, temp, result, pushedVal >>
+
+N2(self) == /\ pc[self] = "N2"
+            /\ lh_pu' = [lh_pu EXCEPT ![self] = LeftHat]
+            /\ pc' = [pc EXCEPT ![self] = "N3"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, rh_pu, lhL, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+N3(self) == /\ pc[self] = "N3"
+            /\ lhL' = [lhL EXCEPT ![self] = Mem[lh_pu[self]].L]
+            /\ IF lhL'[self] = lh_pu[self]
+                  THEN /\ pc' = [pc EXCEPT ![self] = "N4"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "N7"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, rh_pu, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+N4(self) == /\ pc[self] = "N4"
+            /\ Mem' = [Mem EXCEPT ![nd[self]].R = Dummy]
+            /\ pc' = [pc EXCEPT ![self] = "N5"]
+            /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+N5(self) == /\ pc[self] = "N5"
+            /\ rh_pu' = [rh_pu EXCEPT ![self] = RightHat]
+            /\ pc' = [pc EXCEPT ![self] = "N6"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+N6(self) == /\ pc[self] = "N6"
+            /\ IF /\ LeftHat = lh_pu[self]
+                  /\ RightHat = rh_pu[self]
+                  THEN /\ /\ LeftHat' = nd[self]
+                          /\ RightHat' = nd[self]
+                       /\ temp_pu' = [temp_pu EXCEPT ![self] = TRUE]
+                  ELSE /\ temp_pu' = [temp_pu EXCEPT ![self] = FALSE]
+                       /\ UNCHANGED << LeftHat, RightHat >>
+            /\ IF temp_pu'[self]
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"]
+                       /\ nd' = [nd EXCEPT ![self] = null]
+                       /\ pc' = [pc EXCEPT ![self] = "N6a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "N2"]
+                       /\ UNCHANGED << rVal, nd >>
+            /\ UNCHANGED << Mem, freeList, valBag, stack, v_, nd_, rh_, rhR, 
+                            lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, v, 
+                            rh_pu, lhL, lh_pu, rh, lh, lhR, temp, result, 
+                            pushedVal >>
+
+N6a(self) == /\ pc[self] = "N6a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ nd' = [nd EXCEPT ![self] = Head(stack[self]).nd]
+             /\ rh_pu' = [rh_pu EXCEPT ![self] = Head(stack[self]).rh_pu]
+             /\ lhL' = [lhL EXCEPT ![self] = Head(stack[self]).lhL]
+             /\ lh_pu' = [lh_pu EXCEPT ![self] = Head(stack[self]).lh_pu]
+             /\ temp_pu' = [temp_pu EXCEPT ![self] = Head(stack[self]).temp_pu]
+             /\ v' = [v EXCEPT ![self] = Head(stack[self]).v]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+N7(self) == /\ pc[self] = "N7"
+            /\ Mem' = [Mem EXCEPT ![nd[self]].R = lh_pu[self]]
+            /\ pc' = [pc EXCEPT ![self] = "N8"]
+            /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+N8(self) == /\ pc[self] = "N8"
+            /\ IF /\ LeftHat = lh_pu[self]
+                  /\ (Mem[lh_pu[self]].L) = lhL[self]
+                  THEN /\ /\ LeftHat' = nd[self]
+                          /\ Mem' = [Mem EXCEPT ![lh_pu[self]].L = nd[self]]
+                       /\ temp_pu' = [temp_pu EXCEPT ![self] = TRUE]
+                  ELSE /\ temp_pu' = [temp_pu EXCEPT ![self] = FALSE]
+                       /\ UNCHANGED << Mem, LeftHat >>
+            /\ IF temp_pu'[self]
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"]
+                       /\ nd' = [nd EXCEPT ![self] = null]
+                       /\ pc' = [pc EXCEPT ![self] = "N8a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "N2"]
+                       /\ UNCHANGED << rVal, nd >>
+            /\ UNCHANGED << freeList, RightHat, valBag, stack, v_, nd_, rh_, 
+                            rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, 
+                            v, rh_pu, lhL, lh_pu, rh, lh, lhR, temp, result, 
+                            pushedVal >>
+
+N8a(self) == /\ pc[self] = "N8a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ nd' = [nd EXCEPT ![self] = Head(stack[self]).nd]
+             /\ rh_pu' = [rh_pu EXCEPT ![self] = Head(stack[self]).rh_pu]
+             /\ lhL' = [lhL EXCEPT ![self] = Head(stack[self]).lhL]
+             /\ lh_pu' = [lh_pu EXCEPT ![self] = Head(stack[self]).lh_pu]
+             /\ temp_pu' = [temp_pu EXCEPT ![self] = Head(stack[self]).temp_pu]
+             /\ v' = [v EXCEPT ![self] = Head(stack[self]).v]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, rh, lh, lhR, temp, result, 
+                             pushedVal >>
+
+pushLeft(self) == N1(self) \/ N1a(self) \/ N1b(self) \/ N2(self)
+                     \/ N3(self) \/ N4(self) \/ N5(self) \/ N6(self)
+                     \/ N6a(self) \/ N7(self) \/ N8(self) \/ N8a(self)
+
+O1(self) == /\ pc[self] = "O1"
+            /\ lh' = [lh EXCEPT ![self] = LeftHat]
+            /\ pc' = [pc EXCEPT ![self] = "O2"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                            temp_pu, rh, lhR, temp, result, pushedVal >>
+
+O2(self) == /\ pc[self] = "O2"
+            /\ rh' = [rh EXCEPT ![self] = RightHat]
+            /\ pc' = [pc EXCEPT ![self] = "O3"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                            temp_pu, lh, lhR, temp, result, pushedVal >>
+
+O3(self) == /\ pc[self] = "O3"
+            /\ IF Mem[lh[self]].L = lh[self]
+                  THEN /\ rVal' = [rVal EXCEPT ![self] = "empty"]
+                       /\ pc' = [pc EXCEPT ![self] = "O3a"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "O4"]
+                       /\ rVal' = rVal
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+O3a(self) == /\ pc[self] = "O3a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ rh' = [rh EXCEPT ![self] = Head(stack[self]).rh]
+             /\ lh' = [lh EXCEPT ![self] = Head(stack[self]).lh]
+             /\ lhR' = [lhR EXCEPT ![self] = Head(stack[self]).lhR]
+             /\ temp' = [temp EXCEPT ![self] = Head(stack[self]).temp]
+             /\ result' = [result EXCEPT ![self] = Head(stack[self]).result]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                             temp_pu, pushedVal >>
+
+O4(self) == /\ pc[self] = "O4"
+            /\ IF lh[self] = rh[self]
+                  THEN /\ IF /\ LeftHat = lh[self]
+                             /\ RightHat = rh[self]
+                             THEN /\ /\ LeftHat' = Dummy
+                                     /\ RightHat' = Dummy
+                                  /\ temp' = [temp EXCEPT ![self] = TRUE]
+                             ELSE /\ temp' = [temp EXCEPT ![self] = FALSE]
+                                  /\ UNCHANGED << LeftHat, RightHat >>
+                       /\ IF temp'[self]
+                             THEN /\ pc' = [pc EXCEPT ![self] = "O5"]
+                             ELSE /\ pc' = [pc EXCEPT ![self] = "O1"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "O6"]
+                       /\ UNCHANGED << LeftHat, RightHat, temp >>
+            /\ UNCHANGED << Mem, freeList, rVal, valBag, stack, v_, nd_, rh_, 
+                            rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, 
+                            v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, 
+                            result, pushedVal >>
+
+O6(self) == /\ pc[self] = "O6"
+            /\ lhR' = [lhR EXCEPT ![self] = Mem[lh[self]].R]
+            /\ pc' = [pc EXCEPT ![self] = "O7"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                            temp_pu, rh, lh, temp, result, pushedVal >>
+
+O7(self) == /\ pc[self] = "O7"
+            /\ IF /\ LeftHat = lh[self]
+                  /\ (Mem[lh[self]].R) = lhR[self]
+                  THEN /\ /\ LeftHat' = lhR[self]
+                          /\ Mem' = [Mem EXCEPT ![lh[self]].R = lh[self]]
+                       /\ temp' = [temp EXCEPT ![self] = TRUE]
+                  ELSE /\ temp' = [temp EXCEPT ![self] = FALSE]
+                       /\ UNCHANGED << Mem, LeftHat >>
+            /\ IF temp'[self]
+                  THEN /\ pc' = [pc EXCEPT ![self] = "O8"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "O1"]
+            /\ UNCHANGED << freeList, RightHat, rVal, valBag, stack, v_, nd_, 
+                            rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, result, pushedVal >>
+
+O8(self) == /\ pc[self] = "O8"
+            /\ result' = [result EXCEPT ![self] = Mem[lh[self]].V]
+            /\ pc' = [pc EXCEPT ![self] = "O9"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                            stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, 
+                            rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                            temp_pu, rh, lh, lhR, temp, pushedVal >>
+
+O9(self) == /\ pc[self] = "O9"
+            /\ Mem' = [Mem EXCEPT ![lh[self]].L = Dummy,
+                                  ![lh[self]].V = null]
+            /\ rVal' = [rVal EXCEPT ![self] = result[self]]
+            /\ pc' = [pc EXCEPT ![self] = "O10a"]
+            /\ UNCHANGED << freeList, LeftHat, RightHat, valBag, stack, v_, 
+                            nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+O10a(self) == /\ pc[self] = "O10a"
+              /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+              /\ rh' = [rh EXCEPT ![self] = Head(stack[self]).rh]
+              /\ lh' = [lh EXCEPT ![self] = Head(stack[self]).lh]
+              /\ lhR' = [lhR EXCEPT ![self] = Head(stack[self]).lhR]
+              /\ temp' = [temp EXCEPT ![self] = Head(stack[self]).temp]
+              /\ result' = [result EXCEPT ![self] = Head(stack[self]).result]
+              /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+              /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                              v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                              temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                              temp_pu, pushedVal >>
+
+O5(self) == /\ pc[self] = "O5"
+            /\ rVal' = [rVal EXCEPT ![self] = Mem[lh[self]].V]
+            /\ pc' = [pc EXCEPT ![self] = "O5a"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, 
+                            v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                            temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, 
+                            rh, lh, lhR, temp, result, pushedVal >>
+
+O5a(self) == /\ pc[self] = "O5a"
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ rh' = [rh EXCEPT ![self] = Head(stack[self]).rh]
+             /\ lh' = [lh EXCEPT ![self] = Head(stack[self]).lh]
+             /\ lhR' = [lhR EXCEPT ![self] = Head(stack[self]).lhR]
+             /\ temp' = [temp EXCEPT ![self] = Head(stack[self]).temp]
+             /\ result' = [result EXCEPT ![self] = Head(stack[self]).result]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, 
+                             v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, 
+                             temp_p, result_, v, nd, rh_pu, lhL, lh_pu, 
+                             temp_pu, pushedVal >>
+
+popLeft(self) == O1(self) \/ O2(self) \/ O3(self) \/ O3a(self) \/ O4(self)
+                    \/ O6(self) \/ O7(self) \/ O8(self) \/ O9(self)
+                    \/ O10a(self) \/ O5(self) \/ O5a(self)
+
+T1(self) == /\ pc[self] = "T1"
+            /\ \/ /\ \E x \in Val:
+                       pushedVal' = [pushedVal EXCEPT ![self] = x]
+                  /\ valBag' = [valBag EXCEPT ![pushedVal'[self]] = valBag[pushedVal'[self]] + 1]
+                  /\ \/ /\ /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "pushLeft",
+                                                                    pc        |->  "T2",
+                                                                    nd        |->  nd[self],
+                                                                    rh_pu     |->  rh_pu[self],
+                                                                    lhL       |->  lhL[self],
+                                                                    lh_pu     |->  lh_pu[self],
+                                                                    temp_pu   |->  temp_pu[self],
+                                                                    v         |->  v[self] ] >>
+                                                                \o stack[self]]
+                           /\ v' = [v EXCEPT ![self] = pushedVal'[self]]
+                        /\ nd' = [nd EXCEPT ![self] = null]
+                        /\ rh_pu' = [rh_pu EXCEPT ![self] = Dummy]
+                        /\ lhL' = [lhL EXCEPT ![self] = Dummy]
+                        /\ lh_pu' = [lh_pu EXCEPT ![self] = Dummy]
+                        /\ temp_pu' = [temp_pu EXCEPT ![self] = Dummy]
+                        /\ pc' = [pc EXCEPT ![self] = "N1"]
+                        /\ UNCHANGED <<v_, nd_, rh_, rhR, lh_, temp_>>
+                     \/ /\ /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "pushRight",
+                                                                    pc        |->  "T2",
+                                                                    nd_       |->  nd_[self],
+                                                                    rh_       |->  rh_[self],
+                                                                    rhR       |->  rhR[self],
+                                                                    lh_       |->  lh_[self],
+                                                                    temp_     |->  temp_[self],
+                                                                    v_        |->  v_[self] ] >>
+                                                                \o stack[self]]
+                           /\ v_' = [v_ EXCEPT ![self] = pushedVal'[self]]
+                        /\ nd_' = [nd_ EXCEPT ![self] = null]
+                        /\ rh_' = [rh_ EXCEPT ![self] = Dummy]
+                        /\ rhR' = [rhR EXCEPT ![self] = Dummy]
+                        /\ lh_' = [lh_ EXCEPT ![self] = Dummy]
+                        /\ temp_' = [temp_ EXCEPT ![self] = Dummy]
+                        /\ pc' = [pc EXCEPT ![self] = "L1"]
+                        /\ UNCHANGED <<v, nd, rh_pu, lhL, lh_pu, temp_pu>>
+                  /\ UNCHANGED <<rh_p, lh_p, rhL, temp_p, result_, rh, lh, lhR, temp, result>>
+               \/ /\ \/ /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "popLeft",
+                                                                 pc        |->  "T3",
+                                                                 rh        |->  rh[self],
+                                                                 lh        |->  lh[self],
+                                                                 lhR       |->  lhR[self],
+                                                                 temp      |->  temp[self],
+                                                                 result    |->  result[self] ] >>
+                                                             \o stack[self]]
+                        /\ rh' = [rh EXCEPT ![self] = Dummy]
+                        /\ lh' = [lh EXCEPT ![self] = Dummy]
+                        /\ lhR' = [lhR EXCEPT ![self] = Dummy]
+                        /\ temp' = [temp EXCEPT ![self] = Dummy]
+                        /\ result' = [result EXCEPT ![self] = null]
+                        /\ pc' = [pc EXCEPT ![self] = "O1"]
+                        /\ UNCHANGED <<rh_p, lh_p, rhL, temp_p, result_>>
+                     \/ /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "popRight",
+                                                                 pc        |->  "T3",
+                                                                 rh_p      |->  rh_p[self],
+                                                                 lh_p      |->  lh_p[self],
+                                                                 rhL       |->  rhL[self],
+                                                                 temp_p    |->  temp_p[self],
+                                                                 result_   |->  result_[self] ] >>
+                                                             \o stack[self]]
+                        /\ rh_p' = [rh_p EXCEPT ![self] = Dummy]
+                        /\ lh_p' = [lh_p EXCEPT ![self] = Dummy]
+                        /\ rhL' = [rhL EXCEPT ![self] = Dummy]
+                        /\ temp_p' = [temp_p EXCEPT ![self] = Dummy]
+                        /\ result_' = [result_ EXCEPT ![self] = null]
+                        /\ pc' = [pc EXCEPT ![self] = "M1"]
+                        /\ UNCHANGED <<rh, lh, lhR, temp, result>>
+                  /\ UNCHANGED <<valBag, v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, lh_pu, temp_pu, pushedVal>>
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal >>
+
+T2(self) == /\ pc[self] = "T2"
+            /\ IF rVal[self] = "full"
+                  THEN /\ valBag' = [valBag EXCEPT ![pushedVal[self]] = valBag[pushedVal[self]] - 1]
+                  ELSE /\ TRUE
+                       /\ UNCHANGED valBag
+            /\ pc' = [pc EXCEPT ![self] = "T1"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, stack, v_, 
+                            nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+T3(self) == /\ pc[self] = "T3"
+            /\ IF rVal[self] # "empty"
+                  THEN /\ Assert(valBag[rVal[self]] > 0, 
+                                 "Failure of assertion at line 238, column 11.")
+                       /\ valBag' = [valBag EXCEPT ![rVal[self]] = valBag[rVal[self]] - 1]
+                  ELSE /\ TRUE
+                       /\ UNCHANGED valBag
+            /\ pc' = [pc EXCEPT ![self] = "T1"]
+            /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, stack, v_, 
+                            nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, 
+                            result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, 
+                            lhR, temp, result, pushedVal >>
+
+test(self) == T1(self) \/ T2(self) \/ T3(self)
+
+Next == (\E self \in ProcSet:  \/ pushRight(self) \/ popRight(self)
+                               \/ pushLeft(self) \/ popLeft(self))
+           \/ (\E self \in Procs: test(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION
+
+Symmetry == Permutations(Address \ {Dummy}) \cup Permutations(Procs)
+
+Liveness == \A p \in Procs : []<> (pc[p] = "T1")
+=============================================================================
+
+Checked on SVC-LAMPORT-3 with
+   CONSTANTS Val = {v1, v2}
+             Address = {a1, a2, a3}
+             Dummy = a1
+             Procs = {p1, p2}
+             null = null
+             GC = GC
+
+found 2886585 states, depth 67, in 17 min 38.3 sec
+
+==============================
+
+Run on SVC-LAMPORT-2 with
+
+   CONSTANTS Val = {v1}
+             Address = {a1, a2, a3, a4}
+             Dummy = a1
+             Procs = {p1, p2, p3}
+             null = null
+             GC = GC
+
+stopped after 1482 min 40 sec, with
+109078490 states generated, 37886049 distinct states, 5107502 states
+left on queue.
diff --git a/tlatools/test-model/pcal/Dijkstra1.cfg b/tlatools/test-model/pcal/Dijkstra1.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..2b93631d3e6bbf904652490bceaf2e39627dec59
--- /dev/null
+++ b/tlatools/test-model/pcal/Dijkstra1.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANTS N = 4
+          K = 5
+INVARIANT SomeoneHoldsToken
+PROPERTY EventuallyJustOneHoldsToken
diff --git a/tlatools/test-model/pcal/Dijkstra1.tla b/tlatools/test-model/pcal/Dijkstra1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c10721c98ca9a6e9177245f0188c09d41f654fd9
--- /dev/null
+++ b/tlatools/test-model/pcal/Dijkstra1.tla
@@ -0,0 +1,84 @@
+--algorithm Dijkstra1
+variable M \in [ProcSet -> 0..(K-1)];
+
+  process P0 = 0
+    begin
+p0: while TRUE do
+      when M[0] = M[N-1];
+p1:   M[0] := (M[0] + 1) % K;
+      end while
+    end process;
+
+  process Pi \in 1..(N-1)
+    begin
+pi: while (TRUE) do
+      when M[self] # M[self - 1];
+pj:   M[self] := M[self - 1];
+      end while
+    end process;
+
+  end algorithm
+
+----------- MODULE Dijkstra1 -----------
+EXTENDS FiniteSets, Naturals
+
+CONSTANT K, N
+
+ASSUME (K > N) /\ (N > 0) 
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-64143a14f748e286ec27eafe96303703
+VARIABLES M, pc
+
+vars == << M, pc >>
+
+ProcSet == {0} \cup (1..(N-1))
+
+Init == (* Global variables *)
+        /\ M \in [ProcSet -> 0..(K-1)]
+        /\ pc = [self \in ProcSet |-> CASE self = 0 -> "p0"
+                                        [] self \in 1..(N-1) -> "pi"]
+
+p0 == /\ pc[0] = "p0"
+      /\ M[0] = M[N-1]
+      /\ pc' = [pc EXCEPT ![0] = "p1"]
+      /\ M' = M
+
+p1 == /\ pc[0] = "p1"
+      /\ M' = [M EXCEPT ![0] = (M[0] + 1) % K]
+      /\ pc' = [pc EXCEPT ![0] = "p0"]
+
+P0 == p0 \/ p1
+
+pi(self) == /\ pc[self] = "pi"
+            /\ M[self] # M[self - 1]
+            /\ pc' = [pc EXCEPT ![self] = "pj"]
+            /\ M' = M
+
+pj(self) == /\ pc[self] = "pj"
+            /\ M' = [M EXCEPT ![self] = M[self - 1]]
+            /\ pc' = [pc EXCEPT ![self] = "pi"]
+
+Pi(self) == pi(self) \/ pj(self)
+
+Next == P0
+           \/ (\E self \in 1..(N-1): Pi(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(P0)
+        /\ \A self \in 1..(N-1) : WF_vars(Pi(self))
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-80735c28b54285a89ec67fce31b85c9d
+
+HasToken(self) == \/ (self = 0) /\ (M[0] = M[N - 1])
+                  \/ (self > 0) /\ (M[self] # M[self - 1])
+
+TokenHolders == {self \in ProcSet: HasToken(self)}
+
+SomeoneHoldsToken  == Cardinality(TokenHolders) > 0
+
+EventuallyJustOneHoldsToken == <>[] (Cardinality(TokenHolders) = 1)
+
+THEOREM Spec => [] SomeoneHoldsToken
+
+THEOREM Spec => EventuallyJustOneHoldsToken
+========================================
diff --git a/tlatools/test-model/pcal/DiningPhilosophers.cfg b/tlatools/test-model/pcal/DiningPhilosophers.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a174cf298c441c5cb0d1f0028577442a7726703b
--- /dev/null
+++ b/tlatools/test-model/pcal/DiningPhilosophers.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT N = 4
+INVARIANT Invariant
+PROPERTY StarvationFree
diff --git a/tlatools/test-model/pcal/DiningPhilosophers.tla b/tlatools/test-model/pcal/DiningPhilosophers.tla
new file mode 100644
index 0000000000000000000000000000000000000000..17c4c87ed23087175780541c2f824bedb2d8b10d
--- /dev/null
+++ b/tlatools/test-model/pcal/DiningPhilosophers.tla
@@ -0,0 +1,127 @@
+-------------------------- MODULE DiningPhilosophers ------------------------ 
+
+(***************************************************************************)
+(* A simple solution to the Dining Philosophers problem in which processes *)
+(* 1 through N-1 pick up their lef then their right forks, and process 0   *)
+(* picks them up in the opposite order.                                    *)
+(***************************************************************************)
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+(**********************
+
+--algorithm DiningPhilosophers
+  variable sem = [i \in 0..(N-1) |-> 1] ; 
+
+process Proc \in 1..(N-1)
+begin 
+l1 : while TRUE
+       do      when sem[self] = 1 ;      \* Get right fork.
+               sem[self] := 0 ;
+       l2 : when sem[(self-1) % N] = 1 ; \* Get left fork.
+            sem[(self-1) % N] := 0 ;
+       e  : skip ;                       \* Eat
+       l3 : sem[self] := 1 ;             \* Release right fork.
+       l4 : sem[(self-1) % N] := 1 ;     \* Release left fork.
+      end while ;
+end process
+
+process Proc0 = 0
+begin
+l01 : while TRUE
+         do       when sem[N-1] = 1 ;      \* get left fork
+                  sem[N-1] := 0 ;
+            l02 : when sem[0] = 1 ;    \* get right fork
+                  sem[0] := 0 ;
+            e0  : skip ;                 \* eat
+            l03 : sem[0] := 1 ;          \* release left fork
+            l04 : sem[N-1] := 1 ;        \* release right fork
+      end while ;
+end process
+
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d34a24305f241c923d0f8daa00682e02
+VARIABLES sem, pc
+
+vars == << sem, pc >>
+
+ProcSet == (1..(N-1)) \cup {0}
+
+Init == (* Global variables *)
+        /\ sem = [i \in 0..(N-1) |-> 1]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..(N-1) -> "l1"
+                                        [] self = 0 -> "l01"]
+
+l1(self) == /\ pc[self] = "l1"
+            /\ sem[self] = 1
+            /\ sem' = [sem EXCEPT ![self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+
+l2(self) == /\ pc[self] = "l2"
+            /\ sem[(self-1) % N] = 1
+            /\ sem' = [sem EXCEPT ![(self-1) % N] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "e"]
+
+e(self) == /\ pc[self] = "e"
+           /\ TRUE
+           /\ pc' = [pc EXCEPT ![self] = "l3"]
+           /\ sem' = sem
+
+l3(self) == /\ pc[self] = "l3"
+            /\ sem' = [sem EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l4"]
+
+l4(self) == /\ pc[self] = "l4"
+            /\ sem' = [sem EXCEPT ![(self-1) % N] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l1"]
+
+Proc(self) == l1(self) \/ l2(self) \/ e(self) \/ l3(self) \/ l4(self)
+
+l01 == /\ pc[0] = "l01"
+       /\ sem[N-1] = 1
+       /\ sem' = [sem EXCEPT ![N-1] = 0]
+       /\ pc' = [pc EXCEPT ![0] = "l02"]
+
+l02 == /\ pc[0] = "l02"
+       /\ sem[0] = 1
+       /\ sem' = [sem EXCEPT ![0] = 0]
+       /\ pc' = [pc EXCEPT ![0] = "e0"]
+
+e0 == /\ pc[0] = "e0"
+      /\ TRUE
+      /\ pc' = [pc EXCEPT ![0] = "l03"]
+      /\ sem' = sem
+
+l03 == /\ pc[0] = "l03"
+       /\ sem' = [sem EXCEPT ![0] = 1]
+       /\ pc' = [pc EXCEPT ![0] = "l04"]
+
+l04 == /\ pc[0] = "l04"
+       /\ sem' = [sem EXCEPT ![N-1] = 1]
+       /\ pc' = [pc EXCEPT ![0] = "l01"]
+
+Proc0 == l01 \/ l02 \/ e0 \/ l03 \/ l04
+
+Next == Proc0
+           \/ (\E self \in 1..(N-1): Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..(N-1) : SF_vars(Proc(self))
+        /\ SF_vars(Proc0)
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ff7acdd47d76382441ae75c392052170
+
+IsEating(i) == IF i = 0 THEN pc[i] = "e0"
+                        ELSE pc[i] = "e"
+
+Invariant == \A i \in 0..(N-1) : ~ (IsEating(i) /\ IsEating((i+1)%N))
+
+StarvationFree == \A i \in 0..(N-1) : []<> IsEating(i) 
+=============================================================================
diff --git a/tlatools/test-model/pcal/DiningPhilosophers2.tla b/tlatools/test-model/pcal/DiningPhilosophers2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d6dac6d75951822b2036e79b90d08d1cb7c5a91c
--- /dev/null
+++ b/tlatools/test-model/pcal/DiningPhilosophers2.tla
@@ -0,0 +1,220 @@
+
+-------------------------- MODULE DiningPhilosophers2  ------------------------ 
+
+(***************************************************************************)
+(* A simple solution to the Dining Philosophers problem in which processes *)
+(* 1 through N-1 pick up their lef then their right forks, and process 0   *)
+(* picks them up in the opposite order.                                    *)
+(***************************************************************************)
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+(**********************
+
+--algorithm DiningPhilosophers
+
+variable sem = [i \in 0..(N-1) |-> 1] ; 
+  procedure foo () 
+    begin l2 :+ skip;
+          e :+ skip;
+          l3 :+ skip;
+          p4 : skip ;
+          return ;
+    end procedure
+
+  procedure foo2 ()
+     begin foo2begin : skip;
+           return;
+     end procedure ;
+
+  fair process DummyProcessSet \in -3..0
+   begin dp1: skip ;
+   end process
+
+  fair process DummySingleProc = -42
+    begin dp1: skip ;
+    end process;
+
+  fair process Proc \in 1..(N-1)
+begin 
+l1 : while TRUE
+       do      when sem[self] = 1 ;      \* Get right fork.
+               sem[self] := 0 ;
+       fox :+ when sem[(self-1) % N] = 1 ; \* Get left fork.
+            sem[(self-1) % N] := 0 ;
+       e  :+ skip ;                       \* Eat
+       l3 :+ sem[self] := 1 ;             \* Release right fork.
+       l4 :+ sem[(self-1) % N] := 1 ;     \* Release left fork.
+      end while ;
+end process
+
+process Leader = 0 \* Proc0 = 0
+begin
+l01 : while TRUE
+         do       when sem[N-1] = 1 ;      \* get left fork
+                  sem[N-1] := 0 ;
+            l2xx : when sem[0] = 1 ;    \* get right fork
+                  sem[0] := 0 ;
+            exx  : call foo() ; \* eat
+            l03 :+ sem[0] := 1 ;         \* release left fork
+                   call foo() ; 
+            l04 :+ sem[N-1] := 1 ;        \* release right fork
+      end while ;
+end process
+
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION
+\* Label e of procedure foo at line 23 col 16 changed to e_
+\* Label l3 of procedure foo at line 24 col 17 changed to l3_
+\* Label dp1 of process DummyProcessSet at line 35 col 15 changed to dp1_
+VARIABLES sem, pc, stack
+
+vars == << sem, pc, stack >>
+
+ProcSet == (-3..0) \cup {-42} \cup (1..(N-1)) \cup {0}
+
+Init == (* Global variables *)
+        /\ sem = [i \in 0..(N-1) |-> 1]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self \in -3..0 -> "dp1_"
+                                        [] self = -42 -> "dp1"
+                                        [] self \in 1..(N-1) -> "l1"
+                                        [] self = 0 -> "l01"]
+
+l2(self) == /\ pc[self] = "l2"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "e_"]
+            /\ UNCHANGED << sem, stack >>
+
+e_(self) == /\ pc[self] = "e_"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "l3_"]
+            /\ UNCHANGED << sem, stack >>
+
+l3_(self) == /\ pc[self] = "l3_"
+             /\ TRUE
+             /\ pc' = [pc EXCEPT ![self] = "p4"]
+             /\ UNCHANGED << sem, stack >>
+
+p4(self) == /\ pc[self] = "p4"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+            /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+            /\ sem' = sem
+
+foo(self) == l2(self) \/ e_(self) \/ l3_(self) \/ p4(self)
+
+foo2begin(self) == /\ pc[self] = "foo2begin"
+                   /\ TRUE
+                   /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+                   /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+                   /\ sem' = sem
+
+foo2(self) == foo2begin(self)
+
+dp1_(self) == /\ pc[self] = "dp1_"
+              /\ TRUE
+              /\ pc' = [pc EXCEPT ![self] = "Done"]
+              /\ UNCHANGED << sem, stack >>
+
+DummyProcessSet(self) == dp1_(self)
+
+dp1 == /\ pc[-42] = "dp1"
+       /\ TRUE
+       /\ pc' = [pc EXCEPT ![-42] = "Done"]
+       /\ UNCHANGED << sem, stack >>
+
+DummySingleProc == dp1
+
+l1(self) == /\ pc[self] = "l1"
+            /\ sem[self] = 1
+            /\ sem' = [sem EXCEPT ![self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "fox"]
+            /\ stack' = stack
+
+fox(self) == /\ pc[self] = "fox"
+             /\ sem[(self-1) % N] = 1
+             /\ sem' = [sem EXCEPT ![(self-1) % N] = 0]
+             /\ pc' = [pc EXCEPT ![self] = "e"]
+             /\ stack' = stack
+
+e(self) == /\ pc[self] = "e"
+           /\ TRUE
+           /\ pc' = [pc EXCEPT ![self] = "l3"]
+           /\ UNCHANGED << sem, stack >>
+
+l3(self) == /\ pc[self] = "l3"
+            /\ sem' = [sem EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l4"]
+            /\ stack' = stack
+
+l4(self) == /\ pc[self] = "l4"
+            /\ sem' = [sem EXCEPT ![(self-1) % N] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l1"]
+            /\ stack' = stack
+
+Proc(self) == l1(self) \/ fox(self) \/ e(self) \/ l3(self) \/ l4(self)
+
+l01 == /\ pc[0] = "l01"
+       /\ sem[N-1] = 1
+       /\ sem' = [sem EXCEPT ![N-1] = 0]
+       /\ pc' = [pc EXCEPT ![0] = "l2xx"]
+       /\ stack' = stack
+
+l2xx == /\ pc[0] = "l2xx"
+        /\ sem[0] = 1
+        /\ sem' = [sem EXCEPT ![0] = 0]
+        /\ pc' = [pc EXCEPT ![0] = "exx"]
+        /\ stack' = stack
+
+exx == /\ pc[0] = "exx"
+       /\ stack' = [stack EXCEPT ![0] = << [ procedure |->  "foo",
+                                             pc        |->  "l03" ] >>
+                                         \o stack[0]]
+       /\ pc' = [pc EXCEPT ![0] = "l2"]
+       /\ sem' = sem
+
+l03 == /\ pc[0] = "l03"
+       /\ sem' = [sem EXCEPT ![0] = 1]
+       /\ stack' = [stack EXCEPT ![0] = << [ procedure |->  "foo",
+                                             pc        |->  "l04" ] >>
+                                         \o stack[0]]
+       /\ pc' = [pc EXCEPT ![0] = "l2"]
+
+l04 == /\ pc[0] = "l04"
+       /\ sem' = [sem EXCEPT ![N-1] = 1]
+       /\ pc' = [pc EXCEPT ![0] = "l01"]
+       /\ stack' = stack
+
+Leader == l01 \/ l2xx \/ exx \/ l03 \/ l04
+
+Next == DummySingleProc \/ Leader
+           \/ (\E self \in ProcSet: foo(self) \/ foo2(self))
+           \/ (\E self \in -3..0: DummyProcessSet(self))
+           \/ (\E self \in 1..(N-1): Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in -3..0 : WF_vars(DummyProcessSet(self))
+        /\ WF_vars(DummySingleProc)
+        /\ \A self \in 1..(N-1) : /\ WF_vars(Proc(self))
+                                  /\ SF_vars(fox(self)) /\ SF_vars(e(self)) /\ SF_vars(l3(self)) /\ SF_vars(l4(self))
+        /\ /\ WF_vars(Leader) /\ SF_vars(l03) /\ SF_vars(l04)
+           /\ WF_vars(foo(0))
+           /\ SF_vars(l2(0)) /\ SF_vars(e_(0)) /\ SF_vars(l3_(0))
+
+\* END TRANSLATION
+
+IsEating(i) == IF i = 0 THEN pc[i] = "e0"
+                        ELSE pc[i] = "e"
+
+Invariant == \A i \in 0..(N-1) : ~ (IsEating(i) /\ IsEating((i+1)%N))
+
+StarvationFree == \A i \in 0..(N-1) : []<> IsEating(i) 
+=============================================================================
diff --git a/tlatools/test-model/pcal/Either1.tla b/tlatools/test-model/pcal/Either1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..93aa90dd84337c5142b56a980d79e41760eaa25c
--- /dev/null
+++ b/tlatools/test-model/pcal/Either1.tla
@@ -0,0 +1,59 @@
+------------------------------- MODULE Either1 ------------------------------ 
+EXTENDS Naturals, Sequences, TLC
+
+(* --algorithm Either
+      variables x = 0 ; y = 0 ;
+      begin a: either x := 1 ; b: x := x + 1;
+                   or y := 1 ; c: y := y + 1;
+               end either ;
+            d: assert x+y = 2 ;
+     end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-af9f3b0389ad56ee237e79295b3205ff
+VARIABLES x, y, pc
+
+vars == << x, y, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ \/ /\ x' = 1
+           /\ pc' = "b"
+           /\ y' = y
+        \/ /\ y' = 1
+           /\ pc' = "c"
+           /\ x' = x
+
+b == /\ pc = "b"
+     /\ x' = x + 1
+     /\ pc' = "d"
+     /\ y' = y
+
+c == /\ pc = "c"
+     /\ y' = y + 1
+     /\ pc' = "d"
+     /\ x' = x
+
+d == /\ pc = "d"
+     /\ Assert(x+y = 2, "Failure of assertion at line 9, column 16.")
+     /\ pc' = "Done"
+     /\ UNCHANGED << x, y >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c \/ d
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-91542970fa29f8acc63f0f9ca8f27be3
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Either2.tla b/tlatools/test-model/pcal/Either2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..81d26103ae3dba9a3d4aebefa52c5d4960163212
--- /dev/null
+++ b/tlatools/test-model/pcal/Either2.tla
@@ -0,0 +1,71 @@
+------------------------------- MODULE Either2 ------------------------------ 
+EXTENDS Naturals, Sequences, TLC
+
+(* --algorithm Either
+      variables x = 0 ; y = 0 ; z = 0 ;
+      begin a: either    x := 1 ; 
+                      b: x := x + 1; 
+                   or y := 1 ; c: y := y + 1;
+               end either ;
+            d: either when x = 0 ; z := z + 1 ;
+                   or when x = 2 ; z := z + 3 ;
+               end either ;     
+               assert x+y = 2 ;
+               assert z = x + 1 ;
+     end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-92c2a3ead1499974b8b837c3d25d0ccd
+VARIABLES x, y, z, pc
+
+vars == << x, y, z, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ z = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ \/ /\ x' = 1
+           /\ pc' = "b"
+           /\ y' = y
+        \/ /\ y' = 1
+           /\ pc' = "c"
+           /\ x' = x
+     /\ z' = z
+
+b == /\ pc = "b"
+     /\ x' = x + 1
+     /\ pc' = "d"
+     /\ UNCHANGED << y, z >>
+
+c == /\ pc = "c"
+     /\ y' = y + 1
+     /\ pc' = "d"
+     /\ UNCHANGED << x, z >>
+
+d == /\ pc = "d"
+     /\ \/ /\ x = 0
+           /\ z' = z + 1
+        \/ /\ x = 2
+           /\ z' = z + 3
+     /\ Assert(x+y = 2, "Failure of assertion at line 13, column 16.")
+     /\ Assert(z' = x + 1, "Failure of assertion at line 14, column 16.")
+     /\ pc' = "Done"
+     /\ UNCHANGED << x, y >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c \/ d
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-dc9b13cd9fc5d96b6144b722cc2ffb3d
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Either3.tla b/tlatools/test-model/pcal/Either3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a1af934e141e141bacc0cce75e091a1ccc94927a
--- /dev/null
+++ b/tlatools/test-model/pcal/Either3.tla
@@ -0,0 +1,76 @@
+------------------------------- MODULE Either3 ------------------------------ 
+EXTENDS Naturals, Sequences, TLC
+
+(* --algorithm Either
+      variables x = 0 ; y = 0 ; z = 0 ;
+      begin a: either    x := 1 ; 
+                      b: x := x + 1; 
+                   or y := 1 ; c: y := y + 1;
+                   or z := 100
+               end either ;
+            d: either when x = 0 ; z := z + 1 ;
+                   or when x = 2 ; z := z + 3 ;
+               end either ;     
+               assert (x+y = 2) \/ ((z = 101) /\ (x+y=0));
+               assert (z < 100) => (z = x + 1) ;
+     end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d275a21163e8b7088902b968d404026a
+VARIABLES x, y, z, pc
+
+vars == << x, y, z, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ z = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ \/ /\ x' = 1
+           /\ pc' = "b"
+           /\ UNCHANGED <<y, z>>
+        \/ /\ y' = 1
+           /\ pc' = "c"
+           /\ UNCHANGED <<x, z>>
+        \/ /\ z' = 100
+           /\ pc' = "d"
+           /\ UNCHANGED <<x, y>>
+
+b == /\ pc = "b"
+     /\ x' = x + 1
+     /\ pc' = "d"
+     /\ UNCHANGED << y, z >>
+
+c == /\ pc = "c"
+     /\ y' = y + 1
+     /\ pc' = "d"
+     /\ UNCHANGED << x, z >>
+
+d == /\ pc = "d"
+     /\ \/ /\ x = 0
+           /\ z' = z + 1
+        \/ /\ x = 2
+           /\ z' = z + 3
+     /\ Assert((x+y = 2) \/ ((z' = 101) /\ (x+y=0)), 
+               "Failure of assertion at line 14, column 16.")
+     /\ Assert((z' < 100) => (z' = x + 1), 
+               "Failure of assertion at line 15, column 16.")
+     /\ pc' = "Done"
+     /\ UNCHANGED << x, y >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c \/ d
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-db7d9133d865d6309280098cc66b6a85
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Either4.tla b/tlatools/test-model/pcal/Either4.tla
new file mode 100644
index 0000000000000000000000000000000000000000..926340197d1129ecf0f3250c1e5693cc0fce6103
--- /dev/null
+++ b/tlatools/test-model/pcal/Either4.tla
@@ -0,0 +1,83 @@
+------------------------------- MODULE Either4 ------------------------------ 
+EXTENDS Naturals, Sequences, TLC
+
+(* --algorithm Either
+      process Foo \in {1, 2}
+      variables x = 0 ; y = 0 ; z = 0 ;
+      begin a: either    x := 1 ; 
+                      b: x := x + 1; 
+                   or y := 1 ; c: y := y + 1;
+                   or z := 100
+               end either ;
+            d: either when x = 0 ; z := z + 1 ;
+                   or when x = 2 ; z := z + 3 ;
+               end either ;     
+               assert (x+y = 2) \/ ((z = 101) /\ (x+y=0));
+               assert (z < 100) => (z = x + 1) ;
+     end process
+     end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9a6afbc4f2a88a20f8db8f24ec88b676
+VARIABLES pc, x, y, z
+
+vars == << pc, x, y, z >>
+
+ProcSet == ({1, 2})
+
+Init == (* Process Foo *)
+        /\ x = [self \in {1, 2} |-> 0]
+        /\ y = [self \in {1, 2} |-> 0]
+        /\ z = [self \in {1, 2} |-> 0]
+        /\ pc = [self \in ProcSet |-> "a"]
+
+a(self) == /\ pc[self] = "a"
+           /\ \/ /\ x' = [x EXCEPT ![self] = 1]
+                 /\ pc' = [pc EXCEPT ![self] = "b"]
+                 /\ UNCHANGED <<y, z>>
+              \/ /\ y' = [y EXCEPT ![self] = 1]
+                 /\ pc' = [pc EXCEPT ![self] = "c"]
+                 /\ UNCHANGED <<x, z>>
+              \/ /\ z' = [z EXCEPT ![self] = 100]
+                 /\ pc' = [pc EXCEPT ![self] = "d"]
+                 /\ UNCHANGED <<x, y>>
+
+b(self) == /\ pc[self] = "b"
+           /\ x' = [x EXCEPT ![self] = x[self] + 1]
+           /\ pc' = [pc EXCEPT ![self] = "d"]
+           /\ UNCHANGED << y, z >>
+
+c(self) == /\ pc[self] = "c"
+           /\ y' = [y EXCEPT ![self] = y[self] + 1]
+           /\ pc' = [pc EXCEPT ![self] = "d"]
+           /\ UNCHANGED << x, z >>
+
+d(self) == /\ pc[self] = "d"
+           /\ \/ /\ x[self] = 0
+                 /\ z' = [z EXCEPT ![self] = z[self] + 1]
+              \/ /\ x[self] = 2
+                 /\ z' = [z EXCEPT ![self] = z[self] + 3]
+           /\ Assert((x[self]+y[self] = 2) \/ ((z'[self] = 101) /\ (x[self]+y[self]=0)), 
+                     "Failure of assertion at line 15, column 16.")
+           /\ Assert((z'[self] < 100) => (z'[self] = x[self] + 1), 
+                     "Failure of assertion at line 16, column 16.")
+           /\ pc' = [pc EXCEPT ![self] = "Done"]
+           /\ UNCHANGED << x, y >>
+
+Foo(self) == a(self) \/ b(self) \/ c(self) \/ d(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in {1, 2}: Foo(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in {1, 2} : WF_vars(Foo(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8050fab129ad8ea32f6a8eefb5f96b57
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Either5.tla b/tlatools/test-model/pcal/Either5.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ef5123c6ef384edc676cebd2853aa8ff12b2f023
--- /dev/null
+++ b/tlatools/test-model/pcal/Either5.tla
@@ -0,0 +1,84 @@
+------------------------------- MODULE Either5 ------------------------------ 
+EXTENDS Naturals, Sequences, TLC
+
+(* --algorithm Either
+      variables x = 0 ; y = 0 ; z = 0 ;
+      procedure Foo(a) 
+       begin c: x := x + a ;
+                return 
+       end procedure;           
+      begin a: either x := 1 ; y := 0 ;
+                   or y := 1 ; 
+                   or call Foo(1) ; 
+                     b: assert x = 1 ;
+               end either ;
+             d:  assert x+y = 1 ;
+     end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-be6b6d1cc0a0bacc6a419e686126c4c9
+\* Label a at line 10 col 16 changed to a_
+CONSTANT defaultInitValue
+VARIABLES x, y, z, pc, stack, a
+
+vars == << x, y, z, pc, stack, a >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ z = 0
+        (* Procedure Foo *)
+        /\ a = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "a_"
+
+c == /\ pc = "c"
+     /\ x' = x + a
+     /\ pc' = Head(stack).pc
+     /\ a' = Head(stack).a
+     /\ stack' = Tail(stack)
+     /\ UNCHANGED << y, z >>
+
+Foo == c
+
+a_ == /\ pc = "a_"
+      /\ \/ /\ x' = 1
+            /\ y' = 0
+            /\ pc' = "d"
+            /\ UNCHANGED <<stack, a>>
+         \/ /\ y' = 1
+            /\ pc' = "d"
+            /\ UNCHANGED <<x, stack, a>>
+         \/ /\ /\ a' = 1
+               /\ stack' = << [ procedure |->  "Foo",
+                                pc        |->  "b",
+                                a         |->  a ] >>
+                            \o stack
+            /\ pc' = "c"
+            /\ UNCHANGED <<x, y>>
+      /\ z' = z
+
+b == /\ pc = "b"
+     /\ Assert(x = 1, "Failure of assertion at line 13, column 25.")
+     /\ pc' = "d"
+     /\ UNCHANGED << x, y, z, stack, a >>
+
+d == /\ pc = "d"
+     /\ Assert(x+y = 1, "Failure of assertion at line 15, column 18.")
+     /\ pc' = "Done"
+     /\ UNCHANGED << x, y, z, stack, a >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Foo \/ a_ \/ b \/ d
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-5c5a7c7b427cee2468d220b4a9fada35
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Euclid.cfg b/tlatools/test-model/pcal/Euclid.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..886249f10a49afefccd3318c4856c04098d202a5
--- /dev/null
+++ b/tlatools/test-model/pcal/Euclid.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+INVARIANT Invariant
diff --git a/tlatools/test-model/pcal/Euclid.tla b/tlatools/test-model/pcal/Euclid.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f20dd1adbdbf04ec9421faf9b94da0ede4dff356
--- /dev/null
+++ b/tlatools/test-model/pcal/Euclid.tla
@@ -0,0 +1,93 @@
+-------------------------------- MODULE Euclid ------------------------------- 
+(***************************************************************************)
+(* Euclid's algorithm.                                                     *)
+(***************************************************************************)
+
+EXTENDS Naturals, TLC
+
+\*CONSTANT MaxNum
+MaxNum == 20
+
+ASSUME MaxNum > 0
+
+ASSUME /\ Print(<<"Testing Euclid's algorithm on all numbers between 1 and ",
+                   MaxNum>>, TRUE)
+       /\ Print("Most time spent evaluating naive definition of GCD for test",
+                TRUE)
+
+(******
+Adapted from page 8 of the 2nd edition of Robert Sedgewick's "Algorithms".
+
+--algorithm Euclid
+  variables u_ini \in 1 .. MaxNum ; 
+            v_ini \in 1 .. MaxNum ;
+            u = u_ini ; v = v_ini ;
+  begin a : while u # 0
+              do     if u < v then u := v || v := u ; end if ;
+                 b:  u := u - v;
+            end while ;
+            assert v = GCD(u_ini, v_ini) ;
+            \* print <<"gcd of ", u_ini, v_ini, " equals ", v >> ;
+  end algorithm 
+
+*)
+					
+GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) :
+                /\ x % i = 0 
+                /\ y % i = 0
+                /\ \A j \in (1..x) \cap (1..y) :
+                        /\ x % j = 0 
+                        /\ y % j = 0
+                        => i \geq j
+                      
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a069024aba51f10ce87a41824584b8ee
+VARIABLES u_ini, v_ini, u, v, pc
+
+vars == << u_ini, v_ini, u, v, pc >>
+
+Init == (* Global variables *)
+        /\ u_ini \in 1 .. MaxNum
+        /\ v_ini \in 1 .. MaxNum
+        /\ u = u_ini
+        /\ v = v_ini
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ IF u # 0
+           THEN /\ IF u < v
+                      THEN /\ /\ u' = v
+                              /\ v' = u
+                      ELSE /\ TRUE
+                           /\ UNCHANGED << u, v >>
+                /\ pc' = "b"
+           ELSE /\ Assert(v = GCD(u_ini, v_ini), 
+                          "Failure of assertion at line 29, column 13.")
+                /\ pc' = "Done"
+                /\ UNCHANGED << u, v >>
+     /\ UNCHANGED << u_ini, v_ini >>
+
+b == /\ pc = "b"
+     /\ u' = u - v
+     /\ pc' = "a"
+     /\ UNCHANGED << u_ini, v_ini, v >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-969126b68f5e9c79e9cbf33f45804301
+
+
+Invariant == 
+   (pc = "Done") => (v = GCD(u_ini, v_ini))
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Euclid2.tla b/tlatools/test-model/pcal/Euclid2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..33b6272a3a0cf518bdd56abe65e2f5e14bae78f5
--- /dev/null
+++ b/tlatools/test-model/pcal/Euclid2.tla
@@ -0,0 +1,68 @@
+--algorithm EuclidAlg
+variables u = 24 ; v \in 1 .. N ; v_ini = v ;
+begin
+lp: while u # 0 do   
+        if u < v then u := v || v := u ;   
+        end if ; 
+     a: u := u - v; 
+    end while ; 
+    assert v = GCD(24, v_ini) ;
+end algorithm
+
+-------------------------------- MODULE Euclid2 ------------------------------ 
+EXTENDS Naturals, TLC
+\*CONSTANT N
+N == 500
+
+GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) :
+                /\ x % i = 0 
+                /\ y % i = 0
+                /\ \A j \in (1..x) \cap (1..y) :
+                        /\ x % j = 0 
+                        /\ y % j = 0
+                        => i \geq j
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-e89e843c1bd319cda8806c448153d025
+VARIABLES u, v, v_ini, pc
+
+vars == << u, v, v_ini, pc >>
+
+Init == (* Global variables *)
+        /\ u = 24
+        /\ v \in 1 .. N
+        /\ v_ini = v
+        /\ pc = "lp"
+
+lp == /\ pc = "lp"
+      /\ IF u # 0
+            THEN /\ IF u < v
+                       THEN /\ /\ u' = v
+                               /\ v' = u
+                       ELSE /\ TRUE
+                            /\ UNCHANGED << u, v >>
+                 /\ pc' = "a"
+            ELSE /\ Assert(v = GCD(24, v_ini), 
+                           "Failure of assertion at line 9, column 5.")
+                 /\ pc' = "Done"
+                 /\ UNCHANGED << u, v >>
+      /\ v_ini' = v_ini
+
+a == /\ pc = "a"
+     /\ u' = u - v
+     /\ pc' = "lp"
+     /\ UNCHANGED << v, v_ini >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == lp \/ a
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-13d9c528c7c380488278c40608c11ba4
+ 
+=============================================================================
diff --git a/tlatools/test-model/pcal/Euclid3.tla b/tlatools/test-model/pcal/Euclid3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b6484fc4a23a5d42e8b46ce7b2391a35d1ad91ba
--- /dev/null
+++ b/tlatools/test-model/pcal/Euclid3.tla
@@ -0,0 +1,66 @@
+--algorithm EuclidAlg
+variables u = 24 ; v \in 1 .. N ; v_ini = v ;
+begin
+lp: while u # 0 do   
+        if u < v then u := v || v := u ;   
+        end if ; 
+     a: u := u - v; 
+    end while ; 
+    print <<v, "= GCD of 24 and ", v_ini>> ;
+end algorithm
+
+-------------------------------- MODULE Euclid3 ------------------------------ 
+EXTENDS Naturals, TLC
+CONSTANT N
+
+GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) :
+                /\ x % i = 0 
+                /\ y % i = 0
+                /\ \A j \in (1..x) \cap (1..y) :
+                        /\ x % j = 0 
+                        /\ y % j = 0
+                        => i \geq j
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-309bc67718dab989f45f39e55b144a3e
+VARIABLES u, v, v_ini, pc
+
+vars == << u, v, v_ini, pc >>
+
+Init == (* Global variables *)
+        /\ u = 24
+        /\ v \in 1 .. N
+        /\ v_ini = v
+        /\ pc = "lp"
+
+lp == /\ pc = "lp"
+      /\ IF u # 0
+            THEN /\ IF u < v
+                       THEN /\ /\ u' = v
+                               /\ v' = u
+                       ELSE /\ TRUE
+                            /\ UNCHANGED << u, v >>
+                 /\ pc' = "a"
+            ELSE /\ PrintT(<<v, "= GCD of 24 and ", v_ini>>)
+                 /\ pc' = "Done"
+                 /\ UNCHANGED << u, v >>
+      /\ v_ini' = v_ini
+
+a == /\ pc = "a"
+     /\ u' = u - v
+     /\ pc' = "lp"
+     /\ UNCHANGED << v, v_ini >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == lp \/ a
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-696f44302fe4977c6881615aedc0e885
+ 
+=============================================================================
diff --git a/tlatools/test-model/pcal/EvenOdd.tla b/tlatools/test-model/pcal/EvenOdd.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a3bc90d7e25d25838d93245d3bae94b21dbff66a
--- /dev/null
+++ b/tlatools/test-model/pcal/EvenOdd.tla
@@ -0,0 +1,116 @@
+--algorithm EvenOdd
+variable result = FALSE;
+procedure Even (xEven = 0)
+begin
+  Even1: if xEven = 0 then
+           result := TRUE;
+           return;
+         else
+           call Odd(xEven - 1);
+           return;
+         end if;
+  end procedure
+procedure Odd (xOdd = 0)
+begin
+  Odd1: if xOdd = 0 then result := FALSE;
+        else call Even(xOdd - 1);
+        end if;
+  Odd2: return;
+  end procedure
+begin
+  a1: call Even(N);
+  a2: print result;
+end algorithm
+
+--------------- MODULE EvenOdd ---------------
+
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT N
+
+----------------------------------------------
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a4673a20b45b60ed7b169b671068cf77
+VARIABLES result, pc, stack, xEven, xOdd
+
+vars == << result, pc, stack, xEven, xOdd >>
+
+Init == (* Global variables *)
+        /\ result = FALSE
+        (* Procedure Even *)
+        /\ xEven = 0
+        (* Procedure Odd *)
+        /\ xOdd = 0
+        /\ stack = << >>
+        /\ pc = "a1"
+
+Even1 == /\ pc = "Even1"
+         /\ IF xEven = 0
+               THEN /\ result' = TRUE
+                    /\ pc' = Head(stack).pc
+                    /\ xEven' = Head(stack).xEven
+                    /\ stack' = Tail(stack)
+                    /\ xOdd' = xOdd
+               ELSE /\ /\ stack' = << [ procedure |->  "Odd",
+                                        pc        |->  Head(stack).pc,
+                                        xOdd      |->  xOdd ] >>
+                                    \o Tail(stack)
+                       /\ xOdd' = xEven - 1
+                    /\ pc' = "Odd1"
+                    /\ UNCHANGED << result, xEven >>
+
+Even == Even1
+
+Odd1 == /\ pc = "Odd1"
+        /\ IF xOdd = 0
+              THEN /\ result' = FALSE
+                   /\ pc' = "Odd2"
+                   /\ UNCHANGED << stack, xEven >>
+              ELSE /\ /\ stack' = << [ procedure |->  "Even",
+                                       pc        |->  "Odd2",
+                                       xEven     |->  xEven ] >>
+                                   \o stack
+                      /\ xEven' = xOdd - 1
+                   /\ pc' = "Even1"
+                   /\ UNCHANGED result
+        /\ xOdd' = xOdd
+
+Odd2 == /\ pc = "Odd2"
+        /\ pc' = Head(stack).pc
+        /\ xOdd' = Head(stack).xOdd
+        /\ stack' = Tail(stack)
+        /\ UNCHANGED << result, xEven >>
+
+Odd == Odd1 \/ Odd2
+
+a1 == /\ pc = "a1"
+      /\ /\ stack' = << [ procedure |->  "Even",
+                          pc        |->  "a2",
+                          xEven     |->  xEven ] >>
+                      \o stack
+         /\ xEven' = N
+      /\ pc' = "Even1"
+      /\ UNCHANGED << result, xOdd >>
+
+a2 == /\ pc = "a2"
+      /\ PrintT(result)
+      /\ pc' = "Done"
+      /\ UNCHANGED << result, stack, xEven, xOdd >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Even \/ Odd \/ a1 \/ a2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c666315b11da5eecc32bda536975b934
+
+==============================================
+
+
diff --git a/tlatools/test-model/pcal/EvenOddBad.tla b/tlatools/test-model/pcal/EvenOddBad.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8adb3ecbf36e0264cfda3857947db2060a1088fb
--- /dev/null
+++ b/tlatools/test-model/pcal/EvenOddBad.tla
@@ -0,0 +1,113 @@
+---------------------------- MODULE EvenOddBad -----------------------------
+
+EXTENDS Naturals, Sequences, TLC
+
+(*
+--algorithm EvenOddBad
+variable result \in { TRUE, FALSE };
+procedure Even (xEven = 0)
+begin
+  Even1: if xEven = 0 then result := TRUE;
+         else call Odd(xEven - 1);
+         end if;
+     e1  :  return;
+  end procedure;
+procedure Odd (xOdd = 0)
+begin
+  Odd1: if xOdd = 0 then result := FALSE;
+        else call Even(xOdd - 1);
+        end if;
+      o1 :  return;
+  end procedure
+begin
+  a1: call Even(2);
+  a2: print result;
+end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-4f4604590279cc4b2920a972bf28fdd0
+VARIABLES result, pc, stack, xEven, xOdd
+
+vars == << result, pc, stack, xEven, xOdd >>
+
+Init == (* Global variables *)
+        /\ result \in { TRUE, FALSE }
+        (* Procedure Even *)
+        /\ xEven = 0
+        (* Procedure Odd *)
+        /\ xOdd = 0
+        /\ stack = << >>
+        /\ pc = "a1"
+
+Even1 == /\ pc = "Even1"
+         /\ IF xEven = 0
+               THEN /\ result' = TRUE
+                    /\ pc' = "e1"
+                    /\ UNCHANGED << stack, xOdd >>
+               ELSE /\ /\ stack' = << [ procedure |->  "Odd",
+                                        pc        |->  "e1",
+                                        xOdd      |->  xOdd ] >>
+                                    \o stack
+                       /\ xOdd' = xEven - 1
+                    /\ pc' = "Odd1"
+                    /\ UNCHANGED result
+         /\ xEven' = xEven
+
+e1 == /\ pc = "e1"
+      /\ pc' = Head(stack).pc
+      /\ xEven' = Head(stack).xEven
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << result, xOdd >>
+
+Even == Even1 \/ e1
+
+Odd1 == /\ pc = "Odd1"
+        /\ IF xOdd = 0
+              THEN /\ result' = FALSE
+                   /\ pc' = "o1"
+                   /\ UNCHANGED << stack, xEven >>
+              ELSE /\ /\ stack' = << [ procedure |->  "Even",
+                                       pc        |->  "o1",
+                                       xEven     |->  xEven ] >>
+                                   \o stack
+                      /\ xEven' = xOdd - 1
+                   /\ pc' = "Even1"
+                   /\ UNCHANGED result
+        /\ xOdd' = xOdd
+
+o1 == /\ pc = "o1"
+      /\ pc' = Head(stack).pc
+      /\ xOdd' = Head(stack).xOdd
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << result, xEven >>
+
+Odd == Odd1 \/ o1
+
+a1 == /\ pc = "a1"
+      /\ /\ stack' = << [ procedure |->  "Even",
+                          pc        |->  "a2",
+                          xEven     |->  xEven ] >>
+                      \o stack
+         /\ xEven' = 2
+      /\ pc' = "Even1"
+      /\ UNCHANGED << result, xOdd >>
+
+a2 == /\ pc = "a2"
+      /\ PrintT(result)
+      /\ pc' = "Done"
+      /\ UNCHANGED << result, stack, xEven, xOdd >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Even \/ Odd \/ a1 \/ a2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-7dc8ff54d82fc57126829898458b6c6f
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/Factorial.tla b/tlatools/test-model/pcal/Factorial.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8d70425b20036104248a7f0a268084cf79488449
--- /dev/null
+++ b/tlatools/test-model/pcal/Factorial.tla
@@ -0,0 +1,83 @@
+------------------------------ MODULE Factorial ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(***************************************************************************
+--algorithm Factorial
+  variable result = 1;        \* are comments ok?
+  procedure FactProc(arg1 = 0)    (* are comments ok? *)
+   (* what about (* nested multi-line *)
+       comments? *)
+    variable u = 1 ;
+    begin p1 : if arg1 = 0
+                 then return;     \* HERE IS A 
+                 else result := result * arg1;
+                      call FactProc ( arg1 - 1 ) ;
+                      return;
+               end if;
+    end procedure
+  begin
+    a1 : call FactProc( 5 ) ;
+    a2 : assert result = 120 ;
+  end algorithm
+***************************************************************************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-8354903ee5c9ddae0b8bb4f55d782009
+VARIABLES result, pc, stack, arg1, u
+
+vars == << result, pc, stack, arg1, u >>
+
+Init == (* Global variables *)
+        /\ result = 1
+        (* Procedure FactProc *)
+        /\ arg1 = 0
+        /\ u = 1
+        /\ stack = << >>
+        /\ pc = "a1"
+
+p1 == /\ pc = "p1"
+      /\ IF arg1 = 0
+            THEN /\ pc' = Head(stack).pc
+                 /\ u' = Head(stack).u
+                 /\ arg1' = Head(stack).arg1
+                 /\ stack' = Tail(stack)
+                 /\ UNCHANGED result
+            ELSE /\ result' = result * arg1
+                 /\ arg1' = arg1 - 1
+                 /\ u' = 1
+                 /\ pc' = "p1"
+                 /\ stack' = stack
+
+FactProc == p1
+
+a1 == /\ pc = "a1"
+      /\ /\ arg1' = 5
+         /\ stack' = << [ procedure |->  "FactProc",
+                          pc        |->  "a2",
+                          u         |->  u,
+                          arg1      |->  arg1 ] >>
+                      \o stack
+      /\ u' = 1
+      /\ pc' = "p1"
+      /\ UNCHANGED result
+
+a2 == /\ pc = "a2"
+      /\ Assert(result = 120, "Failure of assertion at line 20, column 10.")
+      /\ pc' = "Done"
+      /\ UNCHANGED << result, stack, arg1, u >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == FactProc \/ a1 \/ a2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-53ca468546a563a45762642894b2b2a3
+
+
+Invariant == result \in Nat
+=============================================================================
diff --git a/tlatools/test-model/pcal/Factorial2.tla b/tlatools/test-model/pcal/Factorial2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ea34ede1d2d1f2c688e49baf8cbcffb850e1d447
--- /dev/null
+++ b/tlatools/test-model/pcal/Factorial2.tla
@@ -0,0 +1,132 @@
+---------------------------- MODULE Factorial2 --------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(***************************************************************************
+Factorial Algorithm with 2 procedures
+
+--algorithm Factorial
+  variable result = 1;        
+  procedure FactProc(arg1 = 0 )    
+    variable u = 1 ;
+    begin p1 : if arg1 = 0
+                 then return;     
+                 else result := result * arg1;
+                      call FactProc2 ( arg1 - 1 ) ;
+                      b: return;
+               end if;
+    end procedure
+  procedure FactProc2(arg2 = 0)    
+    variable u2 = 1 ;
+    begin p12 : if arg2 = 0
+                 then return;     
+                 else result := result * arg2;
+                      call FactProc ( arg2 - 1 ) ;
+                      return;
+               end if;
+    end procedure
+  begin
+    a1 : call FactProc( 5 ) ;
+    a2 : if result = 120  then print <<"Correct =", 120>>;
+                          else print <<"Error = ", result>> ;
+         end if;
+  end algorithm
+***************************************************************************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-7a6949afa2a72d41ea8a9ba97e423c98
+VARIABLES result, pc, stack, arg1, u, arg2, u2
+
+vars == << result, pc, stack, arg1, u, arg2, u2 >>
+
+Init == (* Global variables *)
+        /\ result = 1
+        (* Procedure FactProc *)
+        /\ arg1 = 0
+        /\ u = 1
+        (* Procedure FactProc2 *)
+        /\ arg2 = 0
+        /\ u2 = 1
+        /\ stack = << >>
+        /\ pc = "a1"
+
+p1 == /\ pc = "p1"
+      /\ IF arg1 = 0
+            THEN /\ pc' = Head(stack).pc
+                 /\ u' = Head(stack).u
+                 /\ arg1' = Head(stack).arg1
+                 /\ stack' = Tail(stack)
+                 /\ UNCHANGED << result, arg2, u2 >>
+            ELSE /\ result' = result * arg1
+                 /\ /\ arg2' = arg1 - 1
+                    /\ stack' = << [ procedure |->  "FactProc2",
+                                     pc        |->  "b",
+                                     u2        |->  u2,
+                                     arg2      |->  arg2 ] >>
+                                 \o stack
+                 /\ u2' = 1
+                 /\ pc' = "p12"
+                 /\ UNCHANGED << arg1, u >>
+
+b == /\ pc = "b"
+     /\ pc' = Head(stack).pc
+     /\ u' = Head(stack).u
+     /\ arg1' = Head(stack).arg1
+     /\ stack' = Tail(stack)
+     /\ UNCHANGED << result, arg2, u2 >>
+
+FactProc == p1 \/ b
+
+p12 == /\ pc = "p12"
+       /\ IF arg2 = 0
+             THEN /\ pc' = Head(stack).pc
+                  /\ u2' = Head(stack).u2
+                  /\ arg2' = Head(stack).arg2
+                  /\ stack' = Tail(stack)
+                  /\ UNCHANGED << result, arg1, u >>
+             ELSE /\ result' = result * arg2
+                  /\ /\ arg1' = arg2 - 1
+                     /\ stack' = << [ procedure |->  "FactProc",
+                                      pc        |->  Head(stack).pc,
+                                      u         |->  u,
+                                      arg1      |->  arg1 ] >>
+                                  \o Tail(stack)
+                     /\ u2' = Head(stack).u2
+                  /\ u' = 1
+                  /\ pc' = "p1"
+                  /\ arg2' = arg2
+
+FactProc2 == p12
+
+a1 == /\ pc = "a1"
+      /\ /\ arg1' = 5
+         /\ stack' = << [ procedure |->  "FactProc",
+                          pc        |->  "a2",
+                          u         |->  u,
+                          arg1      |->  arg1 ] >>
+                      \o stack
+      /\ u' = 1
+      /\ pc' = "p1"
+      /\ UNCHANGED << result, arg2, u2 >>
+
+a2 == /\ pc = "a2"
+      /\ IF result = 120
+            THEN /\ PrintT(<<"Correct =", 120>>)
+            ELSE /\ PrintT(<<"Error = ", result>>)
+      /\ pc' = "Done"
+      /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == FactProc \/ FactProc2 \/ a1 \/ a2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-6c949f27864a3ffc1d874b0a64419ab7
+
+
+Invariant == result \in Nat
+=============================================================================
diff --git a/tlatools/test-model/pcal/FairSeq.cfg b/tlatools/test-model/pcal/FairSeq.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..eef01cc22c6ccc68e3ccebb6338999b0382ecbbc
--- /dev/null
+++ b/tlatools/test-model/pcal/FairSeq.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+PROPERTY Termination
diff --git a/tlatools/test-model/pcal/FairSeq.tla b/tlatools/test-model/pcal/FairSeq.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4eb048733aecb388e413825e685d891bbfb5d9dd
--- /dev/null
+++ b/tlatools/test-model/pcal/FairSeq.tla
@@ -0,0 +1,44 @@
+------------------------------ MODULE FairSeq ------------------------------
+EXTENDS Integers
+(***************************************************************************
+PlusCal options (version 1.5)
+--algorithm FairSeq {
+    variable x = 0 ;
+    fair { while (x < 10) {
+            x := x+1;
+         }
+    }
+}
+ ***************************************************************************)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-07e0e68497291a78b07d8fb9d5597180
+VARIABLES x, pc
+
+vars == << x, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ pc = "Lbl_1"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ IF x < 10
+               THEN /\ x' = x+1
+                    /\ pc' = "Lbl_1"
+               ELSE /\ pc' = "Done"
+                    /\ x' = x
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Lbl_1
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8d9de25f8162fd0489585bc374dff964
+=============================================================================
+\* Modification History
+\* Last modified Sun Mar 20 10:13:11 PDT 2011 by lamport
+\* Created Sun Mar 20 10:10:54 PDT 2011 by lamport
diff --git a/tlatools/test-model/pcal/FairSeq2.tla b/tlatools/test-model/pcal/FairSeq2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e17d76d811ef7e0d76bbe19f8a1111515187f8c0
--- /dev/null
+++ b/tlatools/test-model/pcal/FairSeq2.tla
@@ -0,0 +1,44 @@
+------------------------------ MODULE FairSeq2 ------------------------------
+EXTENDS Integers
+(***************************************************************************
+PlusCal options (version 1.5)
+--fair algorithm FairSeq {
+    variable x = 0 ;
+    fair { while (x < 10) {
+            x := x+1;
+         }
+    }
+}
+ ***************************************************************************)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-07e0e68497291a78b07d8fb9d5597180
+VARIABLES x, pc
+
+vars == << x, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ pc = "Lbl_1"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ IF x < 10
+               THEN /\ x' = x+1
+                    /\ pc' = "Lbl_1"
+               ELSE /\ pc' = "Done"
+                    /\ x' = x
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Lbl_1
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8d9de25f8162fd0489585bc374dff964
+=============================================================================
+\* Modification History
+\* Last modified Sun Mar 20 10:13:11 PDT 2011 by lamport
+\* Created Sun Mar 20 10:10:54 PDT 2011 by lamport
diff --git a/tlatools/test-model/pcal/FastMutex.cfg b/tlatools/test-model/pcal/FastMutex.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0e9b97c14b32cf64a2bec7b2ab69133ff609a772
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutex.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+CONSTANT defaultInitValue = defaultInitValue
+\* Add statements after this line.
+CONSTANT N = 2
+INVARIANT Invariant
+PROPERTY Liveness
diff --git a/tlatools/test-model/pcal/FastMutex.tla b/tlatools/test-model/pcal/FastMutex.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2c67388a8736b09879c3ca9fe0da4562206fbea4
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutex.tla
@@ -0,0 +1,167 @@
+------------------------------ MODULE FastMutex ----------------------------- 
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+(**********************
+--algorithm FastMutex
+  variables x ; y = 0 ; b = [i \in 1..N |-> FALSE] ; 
+process Proc \in 1..N
+variables j = 0 ; failed = FALSE ;
+begin
+start : while TRUE
+         do l1 : b[self] := TRUE ;
+            l2 : x := self ;
+            l3 : if y # 0
+                   then l4 : b[self] := FALSE ;
+                        l5 : when y = 0 ; skip ;
+                   else l6 : y := self ;
+                        l7 : if x # self 
+                               then l8 : b[self] := FALSE ;
+                                         j := 1 ;
+                                    l9 : while (j \leq N)
+                                           do when ~b[j] ;
+                                              j := j+1 ;
+                                         end while ;
+                                    l10 : if y # self
+                                            then when y = 0 ;
+                                                 failed := TRUE ;
+                                          end if;
+                             end if ;
+                        cs : if ~ failed
+                               then       skip ; \* the critical section
+                                    l11 : y := 0 ;
+                                    l12 : b[self] := FALSE ;
+                               else failed := FALSE ;
+                             end if ;
+                  end if ;
+        end while ;
+end process
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-0bb63b56a09bbe2e9360c99d30162534
+CONSTANT defaultInitValue
+VARIABLES x, y, b, pc, j, failed
+
+vars == << x, y, b, pc, j, failed >>
+
+ProcSet == (1..N)
+
+Init == (* Global variables *)
+        /\ x = defaultInitValue
+        /\ y = 0
+        /\ b = [i \in 1..N |-> FALSE]
+        (* Process Proc *)
+        /\ j = [self \in 1..N |-> 0]
+        /\ failed = [self \in 1..N |-> FALSE]
+        /\ pc = [self \in ProcSet |-> "start"]
+
+start(self) == /\ pc[self] = "start"
+               /\ pc' = [pc EXCEPT ![self] = "l1"]
+               /\ UNCHANGED << x, y, b, j, failed >>
+
+l1(self) == /\ pc[self] = "l1"
+            /\ b' = [b EXCEPT ![self] = TRUE]
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+            /\ UNCHANGED << x, y, j, failed >>
+
+l2(self) == /\ pc[self] = "l2"
+            /\ x' = self
+            /\ pc' = [pc EXCEPT ![self] = "l3"]
+            /\ UNCHANGED << y, b, j, failed >>
+
+l3(self) == /\ pc[self] = "l3"
+            /\ IF y # 0
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l4"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l6"]
+            /\ UNCHANGED << x, y, b, j, failed >>
+
+l4(self) == /\ pc[self] = "l4"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ pc' = [pc EXCEPT ![self] = "l5"]
+            /\ UNCHANGED << x, y, j, failed >>
+
+l5(self) == /\ pc[self] = "l5"
+            /\ y = 0
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "start"]
+            /\ UNCHANGED << x, y, b, j, failed >>
+
+l6(self) == /\ pc[self] = "l6"
+            /\ y' = self
+            /\ pc' = [pc EXCEPT ![self] = "l7"]
+            /\ UNCHANGED << x, b, j, failed >>
+
+l7(self) == /\ pc[self] = "l7"
+            /\ IF x # self
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l8"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+            /\ UNCHANGED << x, y, b, j, failed >>
+
+l8(self) == /\ pc[self] = "l8"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ j' = [j EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l9"]
+            /\ UNCHANGED << x, y, failed >>
+
+l9(self) == /\ pc[self] = "l9"
+            /\ IF (j[self] \leq N)
+                  THEN /\ ~b[j[self]]
+                       /\ j' = [j EXCEPT ![self] = j[self]+1]
+                       /\ pc' = [pc EXCEPT ![self] = "l9"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l10"]
+                       /\ j' = j
+            /\ UNCHANGED << x, y, b, failed >>
+
+l10(self) == /\ pc[self] = "l10"
+             /\ IF y # self
+                   THEN /\ y = 0
+                        /\ failed' = [failed EXCEPT ![self] = TRUE]
+                   ELSE /\ TRUE
+                        /\ UNCHANGED failed
+             /\ pc' = [pc EXCEPT ![self] = "cs"]
+             /\ UNCHANGED << x, y, b, j >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ IF ~ failed[self]
+                  THEN /\ TRUE
+                       /\ pc' = [pc EXCEPT ![self] = "l11"]
+                       /\ UNCHANGED failed
+                  ELSE /\ failed' = [failed EXCEPT ![self] = FALSE]
+                       /\ pc' = [pc EXCEPT ![self] = "start"]
+            /\ UNCHANGED << x, y, b, j >>
+
+l11(self) == /\ pc[self] = "l11"
+             /\ y' = 0
+             /\ pc' = [pc EXCEPT ![self] = "l12"]
+             /\ UNCHANGED << x, b, j, failed >>
+
+l12(self) == /\ pc[self] = "l12"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "start"]
+             /\ UNCHANGED << x, y, j, failed >>
+
+Proc(self) == start(self) \/ l1(self) \/ l2(self) \/ l3(self) \/ l4(self)
+                 \/ l5(self) \/ l6(self) \/ l7(self) \/ l8(self)
+                 \/ l9(self) \/ l10(self) \/ cs(self) \/ l11(self)
+                 \/ l12(self)
+
+Next == (\E self \in 1..N: Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..N : WF_vars(Proc(self))
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-7588f44b8dba7eba3195a1299d84da82
+
+inCS(i) ==  (pc[i] = "cs") /\ (~failed[i])
+
+Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k))
+
+
+Liveness == []<> \E i \in 1..N : inCS(i)
+=============================================================================
diff --git a/tlatools/test-model/pcal/FastMutex2.cfg b/tlatools/test-model/pcal/FastMutex2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d8d7506fe9585e7c018adb246dc9ae7229148ea6
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutex2.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT N = 2 M = 1
+INVARIANT Invariant
+PROPERTY Liveness
diff --git a/tlatools/test-model/pcal/FastMutex2.tla b/tlatools/test-model/pcal/FastMutex2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..1bae411bdf5778c6cf5f5ebe8fc7f0bc9d0eb521
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutex2.tla
@@ -0,0 +1,297 @@
+----------------------------- MODULE FastMutex2 ----------------------------- 
+
+\* This tests the translation when there are two different processes
+
+EXTENDS Naturals, TLC
+
+CONSTANT N, M
+
+ASSUME (N \in Nat) /\ (M \in Nat)
+
+(**********************
+--algorithm FastMutex
+  variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; 
+process Proc1 \in 1..M
+variables j = 0 ; failed = FALSE ; 
+begin
+start : while TRUE
+         do l1 : b[self] := TRUE ;
+            l2 : x := self ;
+            l3 : if y # 0
+                   then l4 : b[self] := FALSE ;
+                        l5 : when y = 0 ; skip ;
+                   else l6 : y := self ;
+                        l7 : if x # self 
+                               then l8 : b[self] := FALSE ;
+                                         j := 1 ;
+                                    l9 : while (j \leq N)
+                                           do when ~b[j] ;
+                                              j := j+1 ;
+                                         end while ;
+                                    l10 : if y # self
+                                            then when y = 0 ;
+                                                 failed := TRUE ;
+                                          end if;
+                             end if ;
+                        cs : if ~ failed
+                               then       skip ; \* the critical section
+                                    l11 : y := 0 ;
+                                    l12 : b[self] := FALSE ;
+                               else failed := FALSE ;
+                             end if ;
+                  end if ;
+        end while ;
+end process
+process Proc2 \in (M+1)..N
+variables j2 = 0 ; failed2 = FALSE ; 
+begin
+2start : while TRUE
+         do 2l1 : b[self] := TRUE ;
+            2l2 : x := self ;
+            2l3 : if y # 0
+                   then 2l4 : b[self] := FALSE ;
+                        2l5 : when y = 0 ; skip ;
+                   else 2l6 : y := self ;
+                        2l7 : if x # self 
+                               then 2l8 : b[self] := FALSE ;
+                                         j2 := 1 ;
+                                    2l9 : while (j2 \leq N)
+                                           do when ~b[j2] ;
+                                              j2 := j2+1 ;
+                                         end while ;
+                                    2l10 : if y # self
+                                            then when y = 0 ;
+                                                 failed2 := TRUE ;
+                                          end if;
+                             end if ;
+                        2cs : if ~ failed2
+                               then       skip ; \* the critical section
+                                    2l11 : y := 0 ;
+                                    2l12 : b[self] := FALSE ;
+                               else failed2 := FALSE ;
+                             end if ;
+                  end if ;
+        end while ;
+end process
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-cc9279d759058abea504462b4ff084d7
+VARIABLES x, y, b, pc, j, failed, j2, failed2
+
+vars == << x, y, b, pc, j, failed, j2, failed2 >>
+
+ProcSet == (1..M) \cup ((M+1)..N)
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ b = [i \in 1..N |-> FALSE]
+        (* Process Proc1 *)
+        /\ j = [self \in 1..M |-> 0]
+        /\ failed = [self \in 1..M |-> FALSE]
+        (* Process Proc2 *)
+        /\ j2 = [self \in (M+1)..N |-> 0]
+        /\ failed2 = [self \in (M+1)..N |-> FALSE]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..M -> "start"
+                                        [] self \in (M+1)..N -> "2start"]
+
+start(self) == /\ pc[self] = "start"
+               /\ pc' = [pc EXCEPT ![self] = "l1"]
+               /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l1(self) == /\ pc[self] = "l1"
+            /\ b' = [b EXCEPT ![self] = TRUE]
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+            /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+l2(self) == /\ pc[self] = "l2"
+            /\ x' = self
+            /\ pc' = [pc EXCEPT ![self] = "l3"]
+            /\ UNCHANGED << y, b, j, failed, j2, failed2 >>
+
+l3(self) == /\ pc[self] = "l3"
+            /\ IF y # 0
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l4"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l6"]
+            /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l4(self) == /\ pc[self] = "l4"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ pc' = [pc EXCEPT ![self] = "l5"]
+            /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+l5(self) == /\ pc[self] = "l5"
+            /\ y = 0
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "start"]
+            /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l6(self) == /\ pc[self] = "l6"
+            /\ y' = self
+            /\ pc' = [pc EXCEPT ![self] = "l7"]
+            /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+l7(self) == /\ pc[self] = "l7"
+            /\ IF x # self
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l8"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+            /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l8(self) == /\ pc[self] = "l8"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ j' = [j EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l9"]
+            /\ UNCHANGED << x, y, failed, j2, failed2 >>
+
+l9(self) == /\ pc[self] = "l9"
+            /\ IF (j[self] \leq N)
+                  THEN /\ ~b[j[self]]
+                       /\ j' = [j EXCEPT ![self] = j[self]+1]
+                       /\ pc' = [pc EXCEPT ![self] = "l9"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l10"]
+                       /\ j' = j
+            /\ UNCHANGED << x, y, b, failed, j2, failed2 >>
+
+l10(self) == /\ pc[self] = "l10"
+             /\ IF y # self
+                   THEN /\ y = 0
+                        /\ failed' = [failed EXCEPT ![self] = TRUE]
+                   ELSE /\ TRUE
+                        /\ UNCHANGED failed
+             /\ pc' = [pc EXCEPT ![self] = "cs"]
+             /\ UNCHANGED << x, y, b, j, j2, failed2 >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ IF ~ failed[self]
+                  THEN /\ TRUE
+                       /\ pc' = [pc EXCEPT ![self] = "l11"]
+                       /\ UNCHANGED failed
+                  ELSE /\ failed' = [failed EXCEPT ![self] = FALSE]
+                       /\ pc' = [pc EXCEPT ![self] = "start"]
+            /\ UNCHANGED << x, y, b, j, j2, failed2 >>
+
+l11(self) == /\ pc[self] = "l11"
+             /\ y' = 0
+             /\ pc' = [pc EXCEPT ![self] = "l12"]
+             /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+l12(self) == /\ pc[self] = "l12"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "start"]
+             /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+Proc1(self) == start(self) \/ l1(self) \/ l2(self) \/ l3(self) \/ l4(self)
+                  \/ l5(self) \/ l6(self) \/ l7(self) \/ l8(self)
+                  \/ l9(self) \/ l10(self) \/ cs(self) \/ l11(self)
+                  \/ l12(self)
+
+2start(self) == /\ pc[self] = "2start"
+                /\ pc' = [pc EXCEPT ![self] = "2l1"]
+                /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l1(self) == /\ pc[self] = "2l1"
+             /\ b' = [b EXCEPT ![self] = TRUE]
+             /\ pc' = [pc EXCEPT ![self] = "2l2"]
+             /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+2l2(self) == /\ pc[self] = "2l2"
+             /\ x' = self
+             /\ pc' = [pc EXCEPT ![self] = "2l3"]
+             /\ UNCHANGED << y, b, j, failed, j2, failed2 >>
+
+2l3(self) == /\ pc[self] = "2l3"
+             /\ IF y # 0
+                   THEN /\ pc' = [pc EXCEPT ![self] = "2l4"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "2l6"]
+             /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l4(self) == /\ pc[self] = "2l4"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "2l5"]
+             /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+2l5(self) == /\ pc[self] = "2l5"
+             /\ y = 0
+             /\ TRUE
+             /\ pc' = [pc EXCEPT ![self] = "2start"]
+             /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l6(self) == /\ pc[self] = "2l6"
+             /\ y' = self
+             /\ pc' = [pc EXCEPT ![self] = "2l7"]
+             /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+2l7(self) == /\ pc[self] = "2l7"
+             /\ IF x # self
+                   THEN /\ pc' = [pc EXCEPT ![self] = "2l8"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "2cs"]
+             /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l8(self) == /\ pc[self] = "2l8"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ j2' = [j2 EXCEPT ![self] = 1]
+             /\ pc' = [pc EXCEPT ![self] = "2l9"]
+             /\ UNCHANGED << x, y, j, failed, failed2 >>
+
+2l9(self) == /\ pc[self] = "2l9"
+             /\ IF (j2[self] \leq N)
+                   THEN /\ ~b[j2[self]]
+                        /\ j2' = [j2 EXCEPT ![self] = j2[self]+1]
+                        /\ pc' = [pc EXCEPT ![self] = "2l9"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "2l10"]
+                        /\ j2' = j2
+             /\ UNCHANGED << x, y, b, j, failed, failed2 >>
+
+2l10(self) == /\ pc[self] = "2l10"
+              /\ IF y # self
+                    THEN /\ y = 0
+                         /\ failed2' = [failed2 EXCEPT ![self] = TRUE]
+                    ELSE /\ TRUE
+                         /\ UNCHANGED failed2
+              /\ pc' = [pc EXCEPT ![self] = "2cs"]
+              /\ UNCHANGED << x, y, b, j, failed, j2 >>
+
+2cs(self) == /\ pc[self] = "2cs"
+             /\ IF ~ failed2[self]
+                   THEN /\ TRUE
+                        /\ pc' = [pc EXCEPT ![self] = "2l11"]
+                        /\ UNCHANGED failed2
+                   ELSE /\ failed2' = [failed2 EXCEPT ![self] = FALSE]
+                        /\ pc' = [pc EXCEPT ![self] = "2start"]
+             /\ UNCHANGED << x, y, b, j, failed, j2 >>
+
+2l11(self) == /\ pc[self] = "2l11"
+              /\ y' = 0
+              /\ pc' = [pc EXCEPT ![self] = "2l12"]
+              /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+2l12(self) == /\ pc[self] = "2l12"
+              /\ b' = [b EXCEPT ![self] = FALSE]
+              /\ pc' = [pc EXCEPT ![self] = "2start"]
+              /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+Proc2(self) == 2start(self) \/ 2l1(self) \/ 2l2(self) \/ 2l3(self)
+                  \/ 2l4(self) \/ 2l5(self) \/ 2l6(self) \/ 2l7(self)
+                  \/ 2l8(self) \/ 2l9(self) \/ 2l10(self) \/ 2cs(self)
+                  \/ 2l11(self) \/ 2l12(self)
+
+Next == (\E self \in 1..M: Proc1(self))
+           \/ (\E self \in (M+1)..N: Proc2(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..M : WF_vars(Proc1(self))
+        /\ \A self \in (M+1)..N : WF_vars(Proc2(self))
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3862da72da966d861f548da308a59a9e
+
+ASSUME Print(<<"ProcSet =" , ProcSet>>, TRUE)
+inCS(i) ==  IF i \in 1..M 
+              THEN (pc[i] = "cs") /\ (~failed[i])
+              ELSE (pc[i] = "2cs") /\ (~failed2[i])
+
+Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k))
+
+Liveness == []<> \E i \in 1..N : inCS(i)
+=============================================================================
diff --git a/tlatools/test-model/pcal/FastMutex3.cfg b/tlatools/test-model/pcal/FastMutex3.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..06b6866abd1c5a3c865f733fc2dc62af47317824
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutex3.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT N = 2
+INVARIANT Invariant
+PROPERTY Liveness
diff --git a/tlatools/test-model/pcal/FastMutex3.tla b/tlatools/test-model/pcal/FastMutex3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b18f8c509d586d505a943539fe877b33bd5d4ce9
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutex3.tla
@@ -0,0 +1,293 @@
+----------------------------- MODULE FastMutex3 ----------------------------- 
+
+\* This tests the translation when there are two different processes
+
+EXTENDS Naturals, TLC
+
+CONSTANT N
+
+ASSUME (N \in Nat) 
+
+(**********************
+--algorithm FastMutex
+  variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; 
+process Proc1 = 1
+variables j = 0 ; failed = FALSE ; 
+begin
+start : while TRUE
+         do l1 : b[1] := TRUE ;
+            l2 : x := 1 ;
+            l3 : if y # 0
+                   then l4 : b[1] := FALSE ;
+                        l5 : when y = 0 ; skip ;
+                   else l6 : y := 1 ;
+                        l7 : if x # 1 
+                               then l8 : b[1] := FALSE ;
+                                         j := 1 ;
+                                    l9 : while (j \leq N)
+                                           do when ~b[j] ;
+                                              j := j+1 ;
+                                         end while ;
+                                    l10 : if y # 1
+                                            then when y = 0 ;
+                                                 failed := TRUE ;
+                                          end if;
+                             end if ;
+                        cs : if ~ failed
+                               then       skip ; \* the critical section
+                                    l11 : y := 0 ;
+                                    l12 : b[1] := FALSE ;
+                               else failed := FALSE ;
+                             end if ;
+                  end if ;
+        end while ;
+end process
+process Proc2 \in 2..N
+variables j2 = 0 ; failed2 = FALSE ; 
+begin
+2start : while TRUE
+         do 2l1 : b[self] := TRUE ;
+            2l2 : x := self ;
+            2l3 : if y # 0
+                   then 2l4 : b[self] := FALSE ;
+                        2l5 : when y = 0 ; skip ;
+                   else 2l6 : y := self ;
+                        2l7 : if x # self 
+                               then 2l8 : b[self] := FALSE ;
+                                         j2 := 1 ;
+                                    2l9 : while (j2 \leq N)
+                                           do when ~b[j2] ;
+                                              j2 := j2+1 ;
+                                         end while ;
+                                    2l10 : if y # self
+                                            then when y = 0 ;
+                                                 failed2 := TRUE ;
+                                          end if;
+                             end if ;
+                        2cs : if ~ failed2
+                               then       skip ; \* the critical section
+                                    2l11 : y := 0 ;
+                                    2l12 : b[self] := FALSE ;
+                               else failed2 := FALSE ;
+                             end if ;
+                  end if ;
+        end while ;
+end process
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1901ed6bf685f13ebc84143ab4ef5b13
+VARIABLES x, y, b, pc, j, failed, j2, failed2
+
+vars == << x, y, b, pc, j, failed, j2, failed2 >>
+
+ProcSet == {1} \cup (2..N)
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ b = [i \in 1..N |-> FALSE]
+        (* Process Proc1 *)
+        /\ j = 0
+        /\ failed = FALSE
+        (* Process Proc2 *)
+        /\ j2 = [self \in 2..N |-> 0]
+        /\ failed2 = [self \in 2..N |-> FALSE]
+        /\ pc = [self \in ProcSet |-> CASE self = 1 -> "start"
+                                        [] self \in 2..N -> "2start"]
+
+start == /\ pc[1] = "start"
+         /\ pc' = [pc EXCEPT ![1] = "l1"]
+         /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l1 == /\ pc[1] = "l1"
+      /\ b' = [b EXCEPT ![1] = TRUE]
+      /\ pc' = [pc EXCEPT ![1] = "l2"]
+      /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+l2 == /\ pc[1] = "l2"
+      /\ x' = 1
+      /\ pc' = [pc EXCEPT ![1] = "l3"]
+      /\ UNCHANGED << y, b, j, failed, j2, failed2 >>
+
+l3 == /\ pc[1] = "l3"
+      /\ IF y # 0
+            THEN /\ pc' = [pc EXCEPT ![1] = "l4"]
+            ELSE /\ pc' = [pc EXCEPT ![1] = "l6"]
+      /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l4 == /\ pc[1] = "l4"
+      /\ b' = [b EXCEPT ![1] = FALSE]
+      /\ pc' = [pc EXCEPT ![1] = "l5"]
+      /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+l5 == /\ pc[1] = "l5"
+      /\ y = 0
+      /\ TRUE
+      /\ pc' = [pc EXCEPT ![1] = "start"]
+      /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l6 == /\ pc[1] = "l6"
+      /\ y' = 1
+      /\ pc' = [pc EXCEPT ![1] = "l7"]
+      /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+l7 == /\ pc[1] = "l7"
+      /\ IF x # 1
+            THEN /\ pc' = [pc EXCEPT ![1] = "l8"]
+            ELSE /\ pc' = [pc EXCEPT ![1] = "cs"]
+      /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+l8 == /\ pc[1] = "l8"
+      /\ b' = [b EXCEPT ![1] = FALSE]
+      /\ j' = 1
+      /\ pc' = [pc EXCEPT ![1] = "l9"]
+      /\ UNCHANGED << x, y, failed, j2, failed2 >>
+
+l9 == /\ pc[1] = "l9"
+      /\ IF (j \leq N)
+            THEN /\ ~b[j]
+                 /\ j' = j+1
+                 /\ pc' = [pc EXCEPT ![1] = "l9"]
+            ELSE /\ pc' = [pc EXCEPT ![1] = "l10"]
+                 /\ j' = j
+      /\ UNCHANGED << x, y, b, failed, j2, failed2 >>
+
+l10 == /\ pc[1] = "l10"
+       /\ IF y # 1
+             THEN /\ y = 0
+                  /\ failed' = TRUE
+             ELSE /\ TRUE
+                  /\ UNCHANGED failed
+       /\ pc' = [pc EXCEPT ![1] = "cs"]
+       /\ UNCHANGED << x, y, b, j, j2, failed2 >>
+
+cs == /\ pc[1] = "cs"
+      /\ IF ~ failed
+            THEN /\ TRUE
+                 /\ pc' = [pc EXCEPT ![1] = "l11"]
+                 /\ UNCHANGED failed
+            ELSE /\ failed' = FALSE
+                 /\ pc' = [pc EXCEPT ![1] = "start"]
+      /\ UNCHANGED << x, y, b, j, j2, failed2 >>
+
+l11 == /\ pc[1] = "l11"
+       /\ y' = 0
+       /\ pc' = [pc EXCEPT ![1] = "l12"]
+       /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+l12 == /\ pc[1] = "l12"
+       /\ b' = [b EXCEPT ![1] = FALSE]
+       /\ pc' = [pc EXCEPT ![1] = "start"]
+       /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+Proc1 == start \/ l1 \/ l2 \/ l3 \/ l4 \/ l5 \/ l6 \/ l7 \/ l8 \/ l9 \/ l10
+            \/ cs \/ l11 \/ l12
+
+2start(self) == /\ pc[self] = "2start"
+                /\ pc' = [pc EXCEPT ![self] = "2l1"]
+                /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l1(self) == /\ pc[self] = "2l1"
+             /\ b' = [b EXCEPT ![self] = TRUE]
+             /\ pc' = [pc EXCEPT ![self] = "2l2"]
+             /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+2l2(self) == /\ pc[self] = "2l2"
+             /\ x' = self
+             /\ pc' = [pc EXCEPT ![self] = "2l3"]
+             /\ UNCHANGED << y, b, j, failed, j2, failed2 >>
+
+2l3(self) == /\ pc[self] = "2l3"
+             /\ IF y # 0
+                   THEN /\ pc' = [pc EXCEPT ![self] = "2l4"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "2l6"]
+             /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l4(self) == /\ pc[self] = "2l4"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "2l5"]
+             /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+2l5(self) == /\ pc[self] = "2l5"
+             /\ y = 0
+             /\ TRUE
+             /\ pc' = [pc EXCEPT ![self] = "2start"]
+             /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l6(self) == /\ pc[self] = "2l6"
+             /\ y' = self
+             /\ pc' = [pc EXCEPT ![self] = "2l7"]
+             /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+2l7(self) == /\ pc[self] = "2l7"
+             /\ IF x # self
+                   THEN /\ pc' = [pc EXCEPT ![self] = "2l8"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "2cs"]
+             /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >>
+
+2l8(self) == /\ pc[self] = "2l8"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ j2' = [j2 EXCEPT ![self] = 1]
+             /\ pc' = [pc EXCEPT ![self] = "2l9"]
+             /\ UNCHANGED << x, y, j, failed, failed2 >>
+
+2l9(self) == /\ pc[self] = "2l9"
+             /\ IF (j2[self] \leq N)
+                   THEN /\ ~b[j2[self]]
+                        /\ j2' = [j2 EXCEPT ![self] = j2[self]+1]
+                        /\ pc' = [pc EXCEPT ![self] = "2l9"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "2l10"]
+                        /\ j2' = j2
+             /\ UNCHANGED << x, y, b, j, failed, failed2 >>
+
+2l10(self) == /\ pc[self] = "2l10"
+              /\ IF y # self
+                    THEN /\ y = 0
+                         /\ failed2' = [failed2 EXCEPT ![self] = TRUE]
+                    ELSE /\ TRUE
+                         /\ UNCHANGED failed2
+              /\ pc' = [pc EXCEPT ![self] = "2cs"]
+              /\ UNCHANGED << x, y, b, j, failed, j2 >>
+
+2cs(self) == /\ pc[self] = "2cs"
+             /\ IF ~ failed2[self]
+                   THEN /\ TRUE
+                        /\ pc' = [pc EXCEPT ![self] = "2l11"]
+                        /\ UNCHANGED failed2
+                   ELSE /\ failed2' = [failed2 EXCEPT ![self] = FALSE]
+                        /\ pc' = [pc EXCEPT ![self] = "2start"]
+             /\ UNCHANGED << x, y, b, j, failed, j2 >>
+
+2l11(self) == /\ pc[self] = "2l11"
+              /\ y' = 0
+              /\ pc' = [pc EXCEPT ![self] = "2l12"]
+              /\ UNCHANGED << x, b, j, failed, j2, failed2 >>
+
+2l12(self) == /\ pc[self] = "2l12"
+              /\ b' = [b EXCEPT ![self] = FALSE]
+              /\ pc' = [pc EXCEPT ![self] = "2start"]
+              /\ UNCHANGED << x, y, j, failed, j2, failed2 >>
+
+Proc2(self) == 2start(self) \/ 2l1(self) \/ 2l2(self) \/ 2l3(self)
+                  \/ 2l4(self) \/ 2l5(self) \/ 2l6(self) \/ 2l7(self)
+                  \/ 2l8(self) \/ 2l9(self) \/ 2l10(self) \/ 2cs(self)
+                  \/ 2l11(self) \/ 2l12(self)
+
+Next == Proc1
+           \/ (\E self \in 2..N: Proc2(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Proc1)
+        /\ \A self \in 2..N : WF_vars(Proc2(self))
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-35555ac3cdd01367ca89de83ceeebd64
+
+ASSUME Print(<<"ProcSet =" , ProcSet>>, TRUE)
+inCS(i) ==  IF i = 1 THEN (pc[i] = "cs") /\ (~failed)
+                     ELSE (pc[i] = "2cs") /\ (~failed2[i])
+Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k))
+
+Liveness == []<> \E i \in 1..N : inCS(i)
+=============================================================================
diff --git a/tlatools/test-model/pcal/FastMutexWithGoto.cfg b/tlatools/test-model/pcal/FastMutexWithGoto.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..cd016e674cbb13c109259f05e6e587fb82d6a4ae
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutexWithGoto.cfg
@@ -0,0 +1,5 @@
+\* Add statements after this line.
+CONSTANT N = 2
+INVARIANT Invariant
+PROPERTY CondLiveness
+SPECIFICATION FairSpec
diff --git a/tlatools/test-model/pcal/FastMutexWithGoto.tla b/tlatools/test-model/pcal/FastMutexWithGoto.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a39b53e28de250b045bb9971a2f725a584465b2e
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutexWithGoto.tla
@@ -0,0 +1,179 @@
+------------------------- MODULE FastMutexWithGoto -------------------------- 
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+(**********************
+--algorithm FastMutex
+variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; 
+
+process Proc \in 1..N
+  variables j = 0 ; 
+  begin
+    ncs: while TRUE do 
+              skip ;  \* Noncritical section.
+       start: b[self] := TRUE ;
+          l1: x := self ;
+          l2: if y # 0
+                then l3: b[self] := FALSE ;
+                     l4: when y = 0 ; 
+                          goto start ;
+              end if ;
+          l5: y := self ;
+          l6: if x # self 
+                then l7: b[self] := FALSE ;
+                         j := 1 ;
+                     l8: while j \leq N do 
+                           when ~b[j] ;
+                           j := j+1 ;
+                         end while ;
+                     l9: if y # self then l10: when y = 0 ;
+                         goto start ;
+                           end if ;
+               end if;
+          cs: skip ; \* the critical section
+         l11: y := 0 ;
+         l12: b[self] := FALSE ;
+         end while ;
+end process
+
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-765a4c9fba722b8f4115732919fba3a0
+VARIABLES x, y, b, pc, j
+
+vars == << x, y, b, pc, j >>
+
+ProcSet == (1..N)
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ b = [i \in 1..N |-> FALSE]
+        (* Process Proc *)
+        /\ j = [self \in 1..N |-> 0]
+        /\ pc = [self \in ProcSet |-> "ncs"]
+
+ncs(self) == /\ pc[self] = "ncs"
+             /\ TRUE
+             /\ pc' = [pc EXCEPT ![self] = "start"]
+             /\ UNCHANGED << x, y, b, j >>
+
+start(self) == /\ pc[self] = "start"
+               /\ b' = [b EXCEPT ![self] = TRUE]
+               /\ pc' = [pc EXCEPT ![self] = "l1"]
+               /\ UNCHANGED << x, y, j >>
+
+l1(self) == /\ pc[self] = "l1"
+            /\ x' = self
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+            /\ UNCHANGED << y, b, j >>
+
+l2(self) == /\ pc[self] = "l2"
+            /\ IF y # 0
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l3"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l5"]
+            /\ UNCHANGED << x, y, b, j >>
+
+l3(self) == /\ pc[self] = "l3"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ pc' = [pc EXCEPT ![self] = "l4"]
+            /\ UNCHANGED << x, y, j >>
+
+l4(self) == /\ pc[self] = "l4"
+            /\ y = 0
+            /\ pc' = [pc EXCEPT ![self] = "start"]
+            /\ UNCHANGED << x, y, b, j >>
+
+l5(self) == /\ pc[self] = "l5"
+            /\ y' = self
+            /\ pc' = [pc EXCEPT ![self] = "l6"]
+            /\ UNCHANGED << x, b, j >>
+
+l6(self) == /\ pc[self] = "l6"
+            /\ IF x # self
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l7"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+            /\ UNCHANGED << x, y, b, j >>
+
+l7(self) == /\ pc[self] = "l7"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ j' = [j EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "l8"]
+            /\ UNCHANGED << x, y >>
+
+l8(self) == /\ pc[self] = "l8"
+            /\ IF j[self] \leq N
+                  THEN /\ ~b[j[self]]
+                       /\ j' = [j EXCEPT ![self] = j[self]+1]
+                       /\ pc' = [pc EXCEPT ![self] = "l8"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l9"]
+                       /\ j' = j
+            /\ UNCHANGED << x, y, b >>
+
+l9(self) == /\ pc[self] = "l9"
+            /\ IF y # self
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l10"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+            /\ UNCHANGED << x, y, b, j >>
+
+l10(self) == /\ pc[self] = "l10"
+             /\ y = 0
+             /\ pc' = [pc EXCEPT ![self] = "start"]
+             /\ UNCHANGED << x, y, b, j >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "l11"]
+            /\ UNCHANGED << x, y, b, j >>
+
+l11(self) == /\ pc[self] = "l11"
+             /\ y' = 0
+             /\ pc' = [pc EXCEPT ![self] = "l12"]
+             /\ UNCHANGED << x, b, j >>
+
+l12(self) == /\ pc[self] = "l12"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "ncs"]
+             /\ UNCHANGED << x, y, j >>
+
+Proc(self) == ncs(self) \/ start(self) \/ l1(self) \/ l2(self) \/ l3(self)
+                 \/ l4(self) \/ l5(self) \/ l6(self) \/ l7(self)
+                 \/ l8(self) \/ l9(self) \/ l10(self) \/ cs(self)
+                 \/ l11(self) \/ l12(self)
+
+Next == (\E self \in 1..N: Proc(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-556f4fffd8fac96ddb09d8c9c984f878
+
+inCS(i) ==  (pc[i] = "cs") 
+
+Invariant == \A i, k \in 1..N: (i # k) => ~ (inCS(i) /\ inCS(k))
+
+Liveness == []<> \E i \in 1..N: inCS(i)
+
+CondLiveness == ([]<> \E i \in 1..N : pc[i] # "ncs") => Liveness
+
+Fairness == \A i \in 1..N : 
+               /\ WF_vars(start(i))
+               /\ WF_vars(l1(i))
+               /\ WF_vars(l2(i))
+               /\ WF_vars(l3(i))
+               /\ WF_vars(l4(i))
+               /\ WF_vars(l5(i))
+               /\ WF_vars(l6(i))
+               /\ WF_vars(l7(i))
+               /\ WF_vars(l8(i))
+               /\ WF_vars(l9(i))
+               /\ WF_vars(l10(i))
+               /\ WF_vars(l11(i))
+               /\ WF_vars(l12(i))
+FairSpec == Spec /\ Fairness
+=============================================================================
diff --git a/tlatools/test-model/pcal/FastMutexWithGoto2.cfg b/tlatools/test-model/pcal/FastMutexWithGoto2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..de1000468b848d4daa608674a58a8b347b759671
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutexWithGoto2.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT N = 3
+INVARIANT Invariant
+PROPERTY Liveness
diff --git a/tlatools/test-model/pcal/FastMutexWithGoto2.tla b/tlatools/test-model/pcal/FastMutexWithGoto2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cd98a524bd71a7899f4c6e3765b283c38e76b8b6
--- /dev/null
+++ b/tlatools/test-model/pcal/FastMutexWithGoto2.tla
@@ -0,0 +1,161 @@
+------------------------ MODULE FastMutexWithGoto2 -------------------------- 
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+(**********************
+--algorithm FastMutex
+  variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; 
+process Proc \in 1..N
+variables S = {} ; 
+begin
+start : while TRUE
+         do l1 : b[self] := TRUE ;
+            l2 : x := self ;
+            l3 : if y # 0
+                   then l4 : b[self] := FALSE ;
+                        l5 : when y = 0 ; 
+                             goto start ;
+                 end if ;
+            l6 : y := self ;
+            l7 : if x # self 
+                   then l8 : b[self] := FALSE ;
+                             S := 1..N \ {self} ;
+                        l9 : while S # {} do
+                              with j \in S do when ~b[j] ;
+                                     S := S \ {j}
+                              end with ;
+                             end while ;
+                       l10 : if y # self then l11 : when y = 0 ;
+                                                    goto start ;
+                             end if ;
+                 end if;
+             cs : skip ; \* the critical section
+            l12 : y := 0 ;
+            l13 : b[self] := FALSE ;
+        end while ;
+end process
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d10cf42a8598db9135c9a3e8255fdc3c
+VARIABLES x, y, b, pc, S
+
+vars == << x, y, b, pc, S >>
+
+ProcSet == (1..N)
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ b = [i \in 1..N |-> FALSE]
+        (* Process Proc *)
+        /\ S = [self \in 1..N |-> {}]
+        /\ pc = [self \in ProcSet |-> "start"]
+
+start(self) == /\ pc[self] = "start"
+               /\ pc' = [pc EXCEPT ![self] = "l1"]
+               /\ UNCHANGED << x, y, b, S >>
+
+l1(self) == /\ pc[self] = "l1"
+            /\ b' = [b EXCEPT ![self] = TRUE]
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+            /\ UNCHANGED << x, y, S >>
+
+l2(self) == /\ pc[self] = "l2"
+            /\ x' = self
+            /\ pc' = [pc EXCEPT ![self] = "l3"]
+            /\ UNCHANGED << y, b, S >>
+
+l3(self) == /\ pc[self] = "l3"
+            /\ IF y # 0
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l4"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l6"]
+            /\ UNCHANGED << x, y, b, S >>
+
+l4(self) == /\ pc[self] = "l4"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ pc' = [pc EXCEPT ![self] = "l5"]
+            /\ UNCHANGED << x, y, S >>
+
+l5(self) == /\ pc[self] = "l5"
+            /\ y = 0
+            /\ pc' = [pc EXCEPT ![self] = "start"]
+            /\ UNCHANGED << x, y, b, S >>
+
+l6(self) == /\ pc[self] = "l6"
+            /\ y' = self
+            /\ pc' = [pc EXCEPT ![self] = "l7"]
+            /\ UNCHANGED << x, b, S >>
+
+l7(self) == /\ pc[self] = "l7"
+            /\ IF x # self
+                  THEN /\ pc' = [pc EXCEPT ![self] = "l8"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+            /\ UNCHANGED << x, y, b, S >>
+
+l8(self) == /\ pc[self] = "l8"
+            /\ b' = [b EXCEPT ![self] = FALSE]
+            /\ S' = [S EXCEPT ![self] = 1..N \ {self}]
+            /\ pc' = [pc EXCEPT ![self] = "l9"]
+            /\ UNCHANGED << x, y >>
+
+l9(self) == /\ pc[self] = "l9"
+            /\ IF S[self] # {}
+                  THEN /\ \E j \in S[self]:
+                            /\ ~b[j]
+                            /\ S' = [S EXCEPT ![self] = S[self] \ {j}]
+                       /\ pc' = [pc EXCEPT ![self] = "l9"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "l10"]
+                       /\ S' = S
+            /\ UNCHANGED << x, y, b >>
+
+l10(self) == /\ pc[self] = "l10"
+             /\ IF y # self
+                   THEN /\ pc' = [pc EXCEPT ![self] = "l11"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+             /\ UNCHANGED << x, y, b, S >>
+
+l11(self) == /\ pc[self] = "l11"
+             /\ y = 0
+             /\ pc' = [pc EXCEPT ![self] = "start"]
+             /\ UNCHANGED << x, y, b, S >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "l12"]
+            /\ UNCHANGED << x, y, b, S >>
+
+l12(self) == /\ pc[self] = "l12"
+             /\ y' = 0
+             /\ pc' = [pc EXCEPT ![self] = "l13"]
+             /\ UNCHANGED << x, b, S >>
+
+l13(self) == /\ pc[self] = "l13"
+             /\ b' = [b EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "start"]
+             /\ UNCHANGED << x, y, S >>
+
+Proc(self) == start(self) \/ l1(self) \/ l2(self) \/ l3(self) \/ l4(self)
+                 \/ l5(self) \/ l6(self) \/ l7(self) \/ l8(self)
+                 \/ l9(self) \/ l10(self) \/ l11(self) \/ cs(self)
+                 \/ l12(self) \/ l13(self)
+
+Next == (\E self \in 1..N: Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-71043efb021aa260bc5d04181cce7e16
+
+inCS(i) ==  (pc[i] = "cs") 
+
+Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k))
+
+
+Liveness == []<> \E i \in 1..N : inCS(i)
+=============================================================================
diff --git a/tlatools/test-model/pcal/Fischer.cfg b/tlatools/test-model/pcal/Fischer.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..699094a9583bbdf24c5155731fce87e5d9a56b92
--- /dev/null
+++ b/tlatools/test-model/pcal/Fischer.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT N = 3  Epsilon = 3 Delta = 2
+INVARIANT Invariant
+PROPERTY Liveness
diff --git a/tlatools/test-model/pcal/Fischer.tla b/tlatools/test-model/pcal/Fischer.tla
new file mode 100644
index 0000000000000000000000000000000000000000..94f8697638adb3bf2d04c2434d69b960306e0e12
--- /dev/null
+++ b/tlatools/test-model/pcal/Fischer.tla
@@ -0,0 +1,137 @@
+Plus Cal options (-wf -spec PlusCal2)
+----------------------------- MODULE Fischer    ----------------------------- 
+EXTENDS Naturals, TLC
+\* what's up 
+CONSTANT Delta, Epsilon  \* The timing delays  
+CONSTANT N   \* The number of synchronizing processes
+\* CONSTANT defaultInitValue
+
+ASSUME /\ Print("Testing Fischer's Mutual Exclusion Algorithm", TRUE)
+       /\ Print(<<" Number of processes = ", N>>, TRUE)
+       /\ Print(<<" Delta   = ", Delta>>, TRUE)
+       /\ Print(<<" Epsilon = ", Epsilon>>, TRUE)
+       /\ Print("Should find a bug if N > 1 and Delta >= Epsilon", TRUE)
+
+
+Infinity == 9999999
+
+(**********************
+--algorithm Fischer
+  variables x = 0 ; timer = [i \in 1..N |-> Infinity] ;
+  process Proc \in 1..N
+   variable firstTime = TRUE ;
+   begin a : while TRUE             
+     (**********************************************************************)
+     (* Note that the +cal syntax requires that both while statements be   *)
+     (* labeled, adding a useless atomic action.  The only ways I see to   *)
+     (* eliminate that would be by adding a "goto" statement that could be *)
+     (* used to encode the inner "while" loop.                             *)
+     (**********************************************************************)
+              do b : while x # self  
+                        (***************************************************)
+                        (* x can't equal i the first time through the loop *)
+                        (***************************************************)
+                       do c : when x = 0 ;
+                              timer[self] := Delta ;
+                          d : x := self ; 
+                              timer[self] := Epsilon ;
+                          e : when timer[self] = 0 ;
+                              timer[self] := Infinity ;
+                     end while ; 
+                cs : skip ;  \* critical section
+                 f : x := 0 ;
+             end while ;
+   end process  
+  process Tick = 0
+    begin t1 : while TRUE
+                 do when \A i \in 1..N : timer[i] > 0 ;
+                    timer := [i \in 1..N |-> IF timer[i] < Infinity
+                                           THEN timer[i] - 1 
+                                           ELSE timer[i] ] ;
+               end while ;
+   end process
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-fcd66548d87762ce6cd61f8b60ad89ec
+VARIABLES x, timer, pc, firstTime
+
+vars == << x, timer, pc, firstTime >>
+
+ProcSet == (1..N) \cup {0}
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ timer = [i \in 1..N |-> Infinity]
+        (* Process Proc *)
+        /\ firstTime = [self \in 1..N |-> TRUE]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..N -> "a"
+                                        [] self = 0 -> "t1"]
+
+a(self) == /\ pc[self] = "a"
+           /\ pc' = [pc EXCEPT ![self] = "b"]
+           /\ UNCHANGED << x, timer, firstTime >>
+
+b(self) == /\ pc[self] = "b"
+           /\ IF x # self
+                 THEN /\ pc' = [pc EXCEPT ![self] = "c"]
+                 ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+           /\ UNCHANGED << x, timer, firstTime >>
+
+c(self) == /\ pc[self] = "c"
+           /\ x = 0
+           /\ timer' = [timer EXCEPT ![self] = Delta]
+           /\ pc' = [pc EXCEPT ![self] = "d"]
+           /\ UNCHANGED << x, firstTime >>
+
+d(self) == /\ pc[self] = "d"
+           /\ x' = self
+           /\ timer' = [timer EXCEPT ![self] = Epsilon]
+           /\ pc' = [pc EXCEPT ![self] = "e"]
+           /\ UNCHANGED firstTime
+
+e(self) == /\ pc[self] = "e"
+           /\ timer[self] = 0
+           /\ timer' = [timer EXCEPT ![self] = Infinity]
+           /\ pc' = [pc EXCEPT ![self] = "b"]
+           /\ UNCHANGED << x, firstTime >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "f"]
+            /\ UNCHANGED << x, timer, firstTime >>
+
+f(self) == /\ pc[self] = "f"
+           /\ x' = 0
+           /\ pc' = [pc EXCEPT ![self] = "a"]
+           /\ UNCHANGED << timer, firstTime >>
+
+Proc(self) == a(self) \/ b(self) \/ c(self) \/ d(self) \/ e(self)
+                 \/ cs(self) \/ f(self)
+
+t1 == /\ pc[0] = "t1"
+      /\ \A i \in 1..N : timer[i] > 0
+      /\ timer' = [i \in 1..N |-> IF timer[i] < Infinity
+                                THEN timer[i] - 1
+                                ELSE timer[i] ]
+      /\ pc' = [pc EXCEPT ![0] = "t1"]
+      /\ UNCHANGED << x, firstTime >>
+
+Tick == t1
+
+Next == Tick
+           \/ (\E self \in 1..N: Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..N : WF_vars(Proc(self))
+        /\ WF_vars(Tick)
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-d8d38bac35efc64e68e3d99ac06a0837
+
+inCS(i) ==  pc[i] = "cs"
+
+Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k))
+
+Liveness == []<> \E i \in 1..N : inCS(i)
+=============================================================================
diff --git a/tlatools/test-model/pcal/InnerLabeledIf.tla b/tlatools/test-model/pcal/InnerLabeledIf.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a54b23102e1b97d993e92876aaac9531aa1d8d51
--- /dev/null
+++ b/tlatools/test-model/pcal/InnerLabeledIf.tla
@@ -0,0 +1,83 @@
+--------------------------- MODULE InnerLabeledIf ---------------------------- 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+  --algorithm InnerLabeledIf
+    variable x \in 1..4 ;
+    begin a : if (x < 3)
+                then if (x = 1)
+                       then skip ; 
+                            b : assert x = 1 
+                       else c : assert x = 2
+                     end if ;
+                else if (x = 3)
+                       then skip ; 
+                            d : assert x = 3 
+                       else e : assert x = 4 ;
+                     end if ;
+              end if ;
+          f : print("made it to end") ;
+    end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-8d605a76553c67cf0740a3d27b57470b
+VARIABLES x, pc
+
+vars == << x, pc >>
+
+Init == (* Global variables *)
+        /\ x \in 1..4
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ IF (x < 3)
+           THEN /\ IF (x = 1)
+                      THEN /\ TRUE
+                           /\ pc' = "b"
+                      ELSE /\ pc' = "c"
+           ELSE /\ IF (x = 3)
+                      THEN /\ TRUE
+                           /\ pc' = "d"
+                      ELSE /\ pc' = "e"
+     /\ x' = x
+
+b == /\ pc = "b"
+     /\ Assert(x = 1, "Failure of assertion at line 10, column 33.")
+     /\ pc' = "f"
+     /\ x' = x
+
+c == /\ pc = "c"
+     /\ Assert(x = 2, "Failure of assertion at line 11, column 33.")
+     /\ pc' = "f"
+     /\ x' = x
+
+d == /\ pc = "d"
+     /\ Assert(x = 3, "Failure of assertion at line 15, column 33.")
+     /\ pc' = "f"
+     /\ x' = x
+
+e == /\ pc = "e"
+     /\ Assert(x = 4, "Failure of assertion at line 16, column 33.")
+     /\ pc' = "f"
+     /\ x' = x
+
+f == /\ pc = "f"
+     /\ PrintT(("made it to end"))
+     /\ pc' = "Done"
+     /\ x' = x
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c \/ d \/ e \/ f
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c43944356bde2029519b5137bfd80466
+
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/MPFactorial.tla b/tlatools/test-model/pcal/MPFactorial.tla
new file mode 100644
index 0000000000000000000000000000000000000000..99001e9dbad33069862700cf92da2fd71b97193c
--- /dev/null
+++ b/tlatools/test-model/pcal/MPFactorial.tla
@@ -0,0 +1,120 @@
+----------------------------- MODULE MPFactorial ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(***************************************************************************
+--algorithm Factorial
+  variable result = [i \in 1..3 |-> 1];        \* are comments ok?
+  procedure FactProc(arg1 (* = 0 *) )    (* are comments ok? *)
+   (* what about (* nested multi-line *)
+       comments? *)
+    variable u = 1 ;
+    begin p1 : if arg1 = 0
+                 then return;     \* HERE IS A 
+                 else result[self] := result[self] * arg1;
+                      call FactProc ( arg1 - 1) ;
+                      return;
+               end if;
+    end procedure
+  process Main \in 1..2 
+    begin
+    a1 : call FactProc( 5 ) ;
+    a2 : assert result[self] = 120 ;
+  end process
+  process Minor = 3
+    begin
+    b1 : call FactProc( 5 ) ;
+    b2 : assert result[3] = 120 ;
+  end process
+  end algorithm
+***************************************************************************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1dbfa1687c2aacb174fffdcfcb1e6b08
+CONSTANT defaultInitValue
+VARIABLES result, pc, stack, arg1, u
+
+vars == << result, pc, stack, arg1, u >>
+
+ProcSet == (1..2) \cup {3}
+
+Init == (* Global variables *)
+        /\ result = [i \in 1..3 |-> 1]
+        (* Procedure FactProc *)
+        /\ arg1 = [ self \in ProcSet |-> defaultInitValue]
+        /\ u = [ self \in ProcSet |-> 1]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..2 -> "a1"
+                                        [] self = 3 -> "b1"]
+
+p1(self) == /\ pc[self] = "p1"
+            /\ IF arg1[self] = 0
+                  THEN /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+                       /\ u' = [u EXCEPT ![self] = Head(stack[self]).u]
+                       /\ arg1' = [arg1 EXCEPT ![self] = Head(stack[self]).arg1]
+                       /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+                       /\ UNCHANGED result
+                  ELSE /\ result' = [result EXCEPT ![self] = result[self] * arg1[self]]
+                       /\ arg1' = [arg1 EXCEPT ![self] = arg1[self] - 1]
+                       /\ u' = [u EXCEPT ![self] = 1]
+                       /\ pc' = [pc EXCEPT ![self] = "p1"]
+                       /\ stack' = stack
+
+FactProc(self) == p1(self)
+
+a1(self) == /\ pc[self] = "a1"
+            /\ /\ arg1' = [arg1 EXCEPT ![self] = 5]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "FactProc",
+                                                        pc        |->  "a2",
+                                                        u         |->  u[self],
+                                                        arg1      |->  arg1[self] ] >>
+                                                    \o stack[self]]
+            /\ u' = [u EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "p1"]
+            /\ UNCHANGED result
+
+a2(self) == /\ pc[self] = "a2"
+            /\ Assert(result[self] = 120, 
+                      "Failure of assertion at line 21, column 10.")
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+            /\ UNCHANGED << result, stack, arg1, u >>
+
+Main(self) == a1(self) \/ a2(self)
+
+b1 == /\ pc[3] = "b1"
+      /\ /\ arg1' = [arg1 EXCEPT ![3] = 5]
+         /\ stack' = [stack EXCEPT ![3] = << [ procedure |->  "FactProc",
+                                               pc        |->  "b2",
+                                               u         |->  u[3],
+                                               arg1      |->  arg1[3] ] >>
+                                           \o stack[3]]
+      /\ u' = [u EXCEPT ![3] = 1]
+      /\ pc' = [pc EXCEPT ![3] = "p1"]
+      /\ UNCHANGED result
+
+b2 == /\ pc[3] = "b2"
+      /\ Assert(result[3] = 120, 
+                "Failure of assertion at line 26, column 10.")
+      /\ pc' = [pc EXCEPT ![3] = "Done"]
+      /\ UNCHANGED << result, stack, arg1, u >>
+
+Minor == b1 \/ b2
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == Minor
+           \/ (\E self \in ProcSet: FactProc(self))
+           \/ (\E self \in 1..2: Main(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..2 : WF_vars(Main(self)) /\ WF_vars(FactProc(self))
+        /\ WF_vars(Minor) /\ WF_vars(FactProc(3))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c530d8d4ac433df112955db00ba9f4a7
+
+
+Invariant == result \in Nat
+=============================================================================
diff --git a/tlatools/test-model/pcal/MPFactorial2.tla b/tlatools/test-model/pcal/MPFactorial2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e0dd136dbf500ca5742603a140db4df97cc9bf51
--- /dev/null
+++ b/tlatools/test-model/pcal/MPFactorial2.tla
@@ -0,0 +1,164 @@
+----------------------------- MODULE MPFactorial2 ---------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(***************************************************************************
+Factorial Algorithm with 2 procedures
+
+--algorithm Factorial
+  variable result = [i \in 1..3 |-> 1];        
+  procedure FactProc(arg1 = 0 )    
+    variable u = 1 ;
+    begin p1 : if arg1 = 0
+                 then return;     
+                 else result[self] := result[self] * arg1;
+                      call FactProc2 ( arg1 - 1 ) ;
+                      b: return;
+               end if;
+    end procedure
+  procedure FactProc2(arg2 = 0)    
+    variable u2 = 1 ;
+    begin p12 : if arg2 = 0
+                 then return;     
+                 else result[self] := result[self] * arg2;
+                      call FactProc ( arg2 - 1 ) ;
+                      return;
+               end if;
+    end procedure
+  process Main \in 1..2
+  begin
+    a1 : call FactProc( 5 ) ;
+    a2 : assert result[self] = 120  
+  end process
+  process Minor = 3
+  begin
+    b1 : call FactProc( 5 ) ;
+    b2 : assert result[3] = 120 
+  end process
+  end algorithm
+***************************************************************************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-b4a39db84a865a6dcb14d3b14f4d88b3
+VARIABLES result, pc, stack, arg1, u, arg2, u2
+
+vars == << result, pc, stack, arg1, u, arg2, u2 >>
+
+ProcSet == (1..2) \cup {3}
+
+Init == (* Global variables *)
+        /\ result = [i \in 1..3 |-> 1]
+        (* Procedure FactProc *)
+        /\ arg1 = [ self \in ProcSet |-> 0]
+        /\ u = [ self \in ProcSet |-> 1]
+        (* Procedure FactProc2 *)
+        /\ arg2 = [ self \in ProcSet |-> 0]
+        /\ u2 = [ self \in ProcSet |-> 1]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..2 -> "a1"
+                                        [] self = 3 -> "b1"]
+
+p1(self) == /\ pc[self] = "p1"
+            /\ IF arg1[self] = 0
+                  THEN /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+                       /\ u' = [u EXCEPT ![self] = Head(stack[self]).u]
+                       /\ arg1' = [arg1 EXCEPT ![self] = Head(stack[self]).arg1]
+                       /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+                       /\ UNCHANGED << result, arg2, u2 >>
+                  ELSE /\ result' = [result EXCEPT ![self] = result[self] * arg1[self]]
+                       /\ /\ arg2' = [arg2 EXCEPT ![self] = arg1[self] - 1]
+                          /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "FactProc2",
+                                                                   pc        |->  "b",
+                                                                   u2        |->  u2[self],
+                                                                   arg2      |->  arg2[self] ] >>
+                                                               \o stack[self]]
+                       /\ u2' = [u2 EXCEPT ![self] = 1]
+                       /\ pc' = [pc EXCEPT ![self] = "p12"]
+                       /\ UNCHANGED << arg1, u >>
+
+b(self) == /\ pc[self] = "b"
+           /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+           /\ u' = [u EXCEPT ![self] = Head(stack[self]).u]
+           /\ arg1' = [arg1 EXCEPT ![self] = Head(stack[self]).arg1]
+           /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+           /\ UNCHANGED << result, arg2, u2 >>
+
+FactProc(self) == p1(self) \/ b(self)
+
+p12(self) == /\ pc[self] = "p12"
+             /\ IF arg2[self] = 0
+                   THEN /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+                        /\ u2' = [u2 EXCEPT ![self] = Head(stack[self]).u2]
+                        /\ arg2' = [arg2 EXCEPT ![self] = Head(stack[self]).arg2]
+                        /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+                        /\ UNCHANGED << result, arg1, u >>
+                   ELSE /\ result' = [result EXCEPT ![self] = result[self] * arg2[self]]
+                        /\ /\ arg1' = [arg1 EXCEPT ![self] = arg2[self] - 1]
+                           /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "FactProc",
+                                                                    pc        |->  Head(stack[self]).pc,
+                                                                    u         |->  u[self],
+                                                                    arg1      |->  arg1[self] ] >>
+                                                                \o Tail(stack[self])]
+                           /\ u2' = [u2 EXCEPT ![self] = Head(stack[self]).u2]
+                        /\ u' = [u EXCEPT ![self] = 1]
+                        /\ pc' = [pc EXCEPT ![self] = "p1"]
+                        /\ arg2' = arg2
+
+FactProc2(self) == p12(self)
+
+a1(self) == /\ pc[self] = "a1"
+            /\ /\ arg1' = [arg1 EXCEPT ![self] = 5]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "FactProc",
+                                                        pc        |->  "a2",
+                                                        u         |->  u[self],
+                                                        arg1      |->  arg1[self] ] >>
+                                                    \o stack[self]]
+            /\ u' = [u EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "p1"]
+            /\ UNCHANGED << result, arg2, u2 >>
+
+a2(self) == /\ pc[self] = "a2"
+            /\ Assert(result[self] = 120, 
+                      "Failure of assertion at line 30, column 10.")
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+            /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >>
+
+Main(self) == a1(self) \/ a2(self)
+
+b1 == /\ pc[3] = "b1"
+      /\ /\ arg1' = [arg1 EXCEPT ![3] = 5]
+         /\ stack' = [stack EXCEPT ![3] = << [ procedure |->  "FactProc",
+                                               pc        |->  "b2",
+                                               u         |->  u[3],
+                                               arg1      |->  arg1[3] ] >>
+                                           \o stack[3]]
+      /\ u' = [u EXCEPT ![3] = 1]
+      /\ pc' = [pc EXCEPT ![3] = "p1"]
+      /\ UNCHANGED << result, arg2, u2 >>
+
+b2 == /\ pc[3] = "b2"
+      /\ Assert(result[3] = 120, 
+                "Failure of assertion at line 35, column 10.")
+      /\ pc' = [pc EXCEPT ![3] = "Done"]
+      /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >>
+
+Minor == b1 \/ b2
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == Minor
+           \/ (\E self \in ProcSet: FactProc(self) \/ FactProc2(self))
+           \/ (\E self \in 1..2: Main(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..2 : WF_vars(Main(self)) /\ WF_vars(FactProc(self)) /\ WF_vars(FactProc2(self))
+        /\ WF_vars(Minor) /\ WF_vars(FactProc(3)) /\ WF_vars(FactProc2(3))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-81af4b8b8e1c44a573ab6b21fa7843b0
+
+
+Invariant == result \in Nat
+=============================================================================
diff --git a/tlatools/test-model/pcal/MPNoParams.tla b/tlatools/test-model/pcal/MPNoParams.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8f71899be5288284eee52236b4c609caa119ce60
--- /dev/null
+++ b/tlatools/test-model/pcal/MPNoParams.tla
@@ -0,0 +1,92 @@
+----------------------------- MODULE MPNoParams ------------------------------ 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+--algorithm MPNoParams
+    variables sum = 0; 
+
+    procedure Sum ()
+      begin s1: sum := sum + 1;
+                return;
+      end procedure;
+    process P1 = 1 
+    begin p1 : call Sum();
+          p2 : when sum = 4 ;
+    end process 
+    process P2 \in 2..4 
+     begin
+          q1 : call Sum();
+          q2 : when sum = 4 ;
+   end process 
+   end algorithm
+
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-deb6c76a28ce9358817b0dff2194ebab
+VARIABLES sum, pc, stack
+
+vars == << sum, pc, stack >>
+
+ProcSet == {1} \cup (2..4)
+
+Init == (* Global variables *)
+        /\ sum = 0
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self = 1 -> "p1"
+                                        [] self \in 2..4 -> "q1"]
+
+s1(self) == /\ pc[self] = "s1"
+            /\ sum' = sum + 1
+            /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+            /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+
+Sum(self) == s1(self)
+
+p1 == /\ pc[1] = "p1"
+      /\ stack' = [stack EXCEPT ![1] = << [ procedure |->  "Sum",
+                                            pc        |->  "p2" ] >>
+                                        \o stack[1]]
+      /\ pc' = [pc EXCEPT ![1] = "s1"]
+      /\ sum' = sum
+
+p2 == /\ pc[1] = "p2"
+      /\ sum = 4
+      /\ pc' = [pc EXCEPT ![1] = "Done"]
+      /\ UNCHANGED << sum, stack >>
+
+P1 == p1 \/ p2
+
+q1(self) == /\ pc[self] = "q1"
+            /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "Sum",
+                                                     pc        |->  "q2" ] >>
+                                                 \o stack[self]]
+            /\ pc' = [pc EXCEPT ![self] = "s1"]
+            /\ sum' = sum
+
+q2(self) == /\ pc[self] = "q2"
+            /\ sum = 4
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+            /\ UNCHANGED << sum, stack >>
+
+P2(self) == q1(self) \/ q2(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == P1
+           \/ (\E self \in ProcSet: Sum(self))
+           \/ (\E self \in 2..4: P2(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(P1) /\ WF_vars(Sum(1))
+        /\ \A self \in 2..4 : WF_vars(P2(self)) /\ WF_vars(Sum(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-714bbad3547f7f99ec47cfca79fad75e
+
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/MacroQuicksort.cfg b/tlatools/test-model/pcal/MacroQuicksort.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a662e03b0eb6080164b084439f2edc97a0050a0d
--- /dev/null
+++ b/tlatools/test-model/pcal/MacroQuicksort.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+CONSTANT ArrayLen = 4
diff --git a/tlatools/test-model/pcal/MacroQuicksort.tla b/tlatools/test-model/pcal/MacroQuicksort.tla
new file mode 100644
index 0000000000000000000000000000000000000000..42a91b4aa35418a765a8204c0c9057c507711575
--- /dev/null
+++ b/tlatools/test-model/pcal/MacroQuicksort.tla
@@ -0,0 +1,146 @@
+---------------------------- MODULE MacroQuicksort --------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT ArrayLen
+
+ASSUME ArrayLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   
+--algorithm Quicksort
+  variables Ainit \in [1..ArrayLen -> 1..ArrayLen]; A = Ainit;
+  macro Partition(pivot, lo, hi)
+    begin   with piv \in lo..(hi-1)
+              do pivot := piv ;
+                 with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..(lo-1) : AA[i] = A[i])
+                          /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                          /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                  AA[i] \leq AA[j])}
+                   do A := Ap;
+                   end with ;
+              end with;
+    end macro
+  procedure  QS(qlo = 1, qhi = 1)
+    variable pivot = 1 ;
+    begin qs1 : if qlo < qhi
+                  then       Partition(pivot, qlo, qhi) ;
+                       qs2 : call QS(qlo, pivot) ;
+                       qs3 : call QS(pivot +1,qhi) ;
+                end if;
+          qs4 : return ;
+    end procedure
+  begin  main : call QS(1, Len(A)) ;
+         test : assert     A \in PermsOf(Ainit)
+                       /\ \A i, j \in 1..ArrayLen : 
+                            (i < j) =>  A[i] \leq A[j]
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION
+VARIABLES Ainit, A, pc, stack, qlo, qhi, pivot
+
+vars == << Ainit, A, pc, stack, qlo, qhi, pivot >>
+
+Init == (* Global variables *)
+        /\ Ainit \in [1..ArrayLen -> 1..ArrayLen]
+        /\ A = Ainit
+        (* Procedure QS *)
+        /\ qlo = 1
+        /\ qhi = 1
+        /\ pivot = 1
+        /\ stack = << >>
+        /\ pc = "main"
+
+qs1 == /\ pc = "qs1"
+       /\ IF qlo < qhi
+             THEN /\ \E piv \in qlo..(qhi-1):
+                       /\ pivot' = piv
+                       /\ \E Ap \in {AA \in PermsOf(A) :
+                                           (\A i \in 1..(qlo-1) : AA[i] = A[i])
+                                        /\ (\A i \in (qhi+1)..Len(A) : AA[i] = A[i])
+                                        /\ (\A i \in qlo..piv, j \in (piv+1)..qhi :
+                                                AA[i] \leq AA[j])}:
+                            A' = Ap
+                  /\ pc' = "qs2"
+             ELSE /\ pc' = "qs4"
+                  /\ UNCHANGED << A, pivot >>
+       /\ UNCHANGED << Ainit, stack, qlo, qhi >>
+
+qs2 == /\ pc = "qs2"
+       /\ /\ qhi' = pivot
+          /\ qlo' = qlo
+          /\ stack' = << [ procedure |->  "QS",
+                           pc        |->  "qs3",
+                           pivot     |->  pivot,
+                           qlo       |->  qlo,
+                           qhi       |->  qhi ] >>
+                       \o stack
+       /\ pivot' = 1
+       /\ pc' = "qs1"
+       /\ UNCHANGED << Ainit, A >>
+
+qs3 == /\ pc = "qs3"
+       /\ /\ qhi' = qhi
+          /\ qlo' = pivot +1
+          /\ stack' = << [ procedure |->  "QS",
+                           pc        |->  "qs4",
+                           pivot     |->  pivot,
+                           qlo       |->  qlo,
+                           qhi       |->  qhi ] >>
+                       \o stack
+       /\ pivot' = 1
+       /\ pc' = "qs1"
+       /\ UNCHANGED << Ainit, A >>
+
+qs4 == /\ pc = "qs4"
+       /\ pc' = Head(stack).pc
+       /\ pivot' = Head(stack).pivot
+       /\ qlo' = Head(stack).qlo
+       /\ qhi' = Head(stack).qhi
+       /\ stack' = Tail(stack)
+       /\ UNCHANGED << Ainit, A >>
+
+QS == qs1 \/ qs2 \/ qs3 \/ qs4
+
+main == /\ pc = "main"
+        /\ /\ qhi' = Len(A)
+           /\ qlo' = 1
+           /\ stack' = << [ procedure |->  "QS",
+                            pc        |->  "test",
+                            pivot     |->  pivot,
+                            qlo       |->  qlo,
+                            qhi       |->  qhi ] >>
+                        \o stack
+        /\ pivot' = 1
+        /\ pc' = "qs1"
+        /\ UNCHANGED << Ainit, A >>
+
+test == /\ pc = "test"
+        /\ Assert(    A \in PermsOf(Ainit)
+                  /\ \A i, j \in 1..ArrayLen :
+                       (i < j) =>  A[i] \leq A[j], 
+                  "Failure of assertion at line 40, column 17.")
+        /\ pc' = "Done"
+        /\ UNCHANGED << Ainit, A, stack, qlo, qhi, pivot >>
+
+Next == QS \/ main \/ test
+           \/ (* Disjunct to prevent deadlock on termination *)
+              (pc = "Done" /\ UNCHANGED vars)
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION
+=============================================================================
+Checked without termination on svc-lamport-2 with 2 workers:
+  arrayLen = 4 in 15 sec, 32280 states, depth 22
+  arrayLen = 5 in 138min 17 sec 1529005 states, depth 28
diff --git a/tlatools/test-model/pcal/MacroRealQuicksort.cfg b/tlatools/test-model/pcal/MacroRealQuicksort.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..672c5ec21152f6c0135cca7850d5415e163e2259
--- /dev/null
+++ b/tlatools/test-model/pcal/MacroRealQuicksort.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+CONSTANT N = 4
diff --git a/tlatools/test-model/pcal/MacroRealQuicksort.tla b/tlatools/test-model/pcal/MacroRealQuicksort.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4c52cbc72a1d3a284942993e188479da9251f130
--- /dev/null
+++ b/tlatools/test-model/pcal/MacroRealQuicksort.tla
@@ -0,0 +1,90 @@
+-------------------------- MODULE MacroRealQuicksort ------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   
+--algorithm Quicksort
+  variables Ainit \in [1..N -> 1..N]; A = Ainit;
+            S = {<<1,N>>} ; pivot = 1 ;
+  macro Partition(pivot, lo, hi)
+    begin   with piv \in lo..(hi-1)
+              do pivot := piv ;
+                 with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..(lo-1) : AA[i] = A[i])
+                          /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                          /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                  AA[i] \leq AA[j])}
+                   do A := Ap;
+                   end with ;
+              end with;
+    end macro
+   begin qs1 : while S # {}
+                do with I \in S
+                   do if I[1] < I[2]
+                        then Partition(pivot, I[1], I[2]) ;
+                             S := (S \ {I}) 
+                                    \cup {<<I[1], pivot>>, <<pivot+1, I[2]>>}
+                        else S := (S \ {I}) 
+                    end if
+                   end with
+                end while
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION
+VARIABLES Ainit, A, S, pivot, pc
+
+vars == << Ainit, A, S, pivot, pc >>
+
+Init == (* Global variables *)
+        /\ Ainit \in [1..N -> 1..N]
+        /\ A = Ainit
+        /\ S = {<<1,N>>}
+        /\ pivot = 1
+        /\ pc = "qs1"
+
+qs1 == /\ pc = "qs1"
+       /\ IF S # {}
+             THEN /\ \E I \in S:
+                       IF I[1] < I[2]
+                          THEN /\ \E piv \in (I[1])..((I[2])-1):
+                                    /\ pivot' = piv
+                                    /\ \E Ap \in {AA \in PermsOf(A) :
+                                                        (\A i \in 1..((I[1])-1) : AA[i] = A[i])
+                                                     /\ (\A i \in ((I[2])+1)..Len(A) : AA[i] = A[i])
+                                                     /\ (\A i \in (I[1])..piv, j \in (piv+1)..(I[2]) :
+                                                             AA[i] \leq AA[j])}:
+                                         A' = Ap
+                               /\ S' = (S \ {I})
+                                         \cup {<<I[1], pivot'>>, <<pivot'+1, I[2]>>}
+                          ELSE /\ S' = (S \ {I})
+                               /\ UNCHANGED << A, pivot >>
+                  /\ pc' = "qs1"
+             ELSE /\ pc' = "Done"
+                  /\ UNCHANGED << A, S, pivot >>
+       /\ Ainit' = Ainit
+
+Next == qs1
+           \/ (* Disjunct to prevent deadlock on termination *)
+              (pc = "Done" /\ UNCHANGED vars)
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION
+=============================================================================
+Checked without termination on svc-lamport-2 with 2 workers:
+  arrayLen = 4 in 15 sec, 32280 states, depth 22
+  arrayLen = 5 in 138min 17 sec 1529005 states, depth 28
diff --git a/tlatools/test-model/pcal/MergeSort.cfg b/tlatools/test-model/pcal/MergeSort.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fef7332dcd82181639a8ebe35c5a84e7458712e0
--- /dev/null
+++ b/tlatools/test-model/pcal/MergeSort.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+PROPERTY Termination
+CONSTANT defaultInitValue = defaultInitValue
+\* Add statements after this line.
+CONSTANTS ArrayLen = 2
+INVARIANT Invariant
diff --git a/tlatools/test-model/pcal/MergeSort.tla b/tlatools/test-model/pcal/MergeSort.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a9df3645e7660041a3759abc9ab418acb1cab780
--- /dev/null
+++ b/tlatools/test-model/pcal/MergeSort.tla
@@ -0,0 +1,248 @@
+------------------------------- MODULE MergeSort ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT ArrayLen
+
+ASSUME ArrayLen \in Nat
+
+ASSUME Print(<<"Testing Mergesort on all arrays of length <= ", ArrayLen>>,
+             TRUE)
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   
+
+Copied from page 166 of the 2nd edition of Robert Sedgewick's "Algorithms".
+
+--algorithm Mergesort
+  variables a \in UNION {[1..N -> 1..N] : N \in 0..ArrayLen} ;
+            b = [x \in DOMAIN a |-> 99]  \* ;
+  procedure mergesort(l, r)
+    variables i ; j  ; k ; m \* ; 
+    begin l1: if r - l > 0
+                then      m := (r + l) \div 2 ;
+                     l2 : call mergesort(l, m) ;
+                     l3 : call mergesort(m+1, r) ;
+                     l4 : i := m ;
+                     l5 : while i \geq l
+                            do b[i] := a[i];
+                               i := i - 1 \* ;
+                          end while ;
+                          (*************************************************)
+                          (* I don't know what the Pascal statement        *)
+                          (*                                               *)
+                          (*       for i := m downto l ...                 *)
+                          (*                                               *)
+                          (* is supposed to set i to if m < l, so the      *)
+                          (* algorithm reports an error and stops in this    *)
+                          (*************************************************)
+                          if m \geq l
+                            then i := l ; 
+                            else print "not sure of semantics of Pascal" ;
+                                 i := CHOOSE x \in {} : FALSE ;
+                          end if ;
+                          j := m + 1 ;
+                     l6 : while j \leq r
+                            do b[r + m + 1 - j] := a[j] ;
+                               j := j + 1 ;
+                          end while ;
+                          (*************************************************)
+                          (* I don't know what the Pascal statement        *)
+                          (*                                               *)
+                          (*       for j := m+1 to r ...                   *)
+                          (*                                               *)
+                          (* is supposed to set j to if m+1 < r, so the    *)
+                          (* algorithm reports an error and stops in this    *)
+                          (*************************************************)
+                          if m+1 \leq r
+                            then j := r ;  
+                            else print "not sure of semantics of Pascal" ;
+                                 i := CHOOSE x \in {} : FALSE ;
+                          end if ;
+                          k := l ;
+                     l7 : while k \leq r
+                            do if b[i] < b[j]
+                                 then a[k] := b[i] ;
+                                      i := i + 1 ;
+                                 else a[k] := b[j] ;
+                                      j := j - 1 ;
+                               end if ;
+                            k := k + 1 ;
+                          end while ;
+              end if ;
+          l8: return ;
+    end procedure
+  begin  main : call mergesort (1, Len(a)) ;
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3f24d15c1de7a8bf341d55378b5a76c0
+CONSTANT defaultInitValue
+VARIABLES a, b, pc, stack, l, r, i, j, k, m
+
+vars == << a, b, pc, stack, l, r, i, j, k, m >>
+
+Init == (* Global variables *)
+        /\ a \in UNION {[1..N -> 1..N] : N \in 0..ArrayLen}
+        /\ b = [x \in DOMAIN a |-> 99]
+        (* Procedure mergesort *)
+        /\ l = defaultInitValue
+        /\ r = defaultInitValue
+        /\ i = defaultInitValue
+        /\ j = defaultInitValue
+        /\ k = defaultInitValue
+        /\ m = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "main"
+
+l1 == /\ pc = "l1"
+      /\ IF r - l > 0
+            THEN /\ m' = ((r + l) \div 2)
+                 /\ pc' = "l2"
+            ELSE /\ pc' = "l8"
+                 /\ m' = m
+      /\ UNCHANGED << a, b, stack, l, r, i, j, k >>
+
+l2 == /\ pc = "l2"
+      /\ /\ l' = l
+         /\ r' = m
+         /\ stack' = << [ procedure |->  "mergesort",
+                          pc        |->  "l3",
+                          i         |->  i,
+                          j         |->  j,
+                          k         |->  k,
+                          m         |->  m,
+                          l         |->  l,
+                          r         |->  r ] >>
+                      \o stack
+      /\ i' = defaultInitValue
+      /\ j' = defaultInitValue
+      /\ k' = defaultInitValue
+      /\ m' = defaultInitValue
+      /\ pc' = "l1"
+      /\ UNCHANGED << a, b >>
+
+l3 == /\ pc = "l3"
+      /\ /\ l' = m+1
+         /\ r' = r
+         /\ stack' = << [ procedure |->  "mergesort",
+                          pc        |->  "l4",
+                          i         |->  i,
+                          j         |->  j,
+                          k         |->  k,
+                          m         |->  m,
+                          l         |->  l,
+                          r         |->  r ] >>
+                      \o stack
+      /\ i' = defaultInitValue
+      /\ j' = defaultInitValue
+      /\ k' = defaultInitValue
+      /\ m' = defaultInitValue
+      /\ pc' = "l1"
+      /\ UNCHANGED << a, b >>
+
+l4 == /\ pc = "l4"
+      /\ i' = m
+      /\ pc' = "l5"
+      /\ UNCHANGED << a, b, stack, l, r, j, k, m >>
+
+l5 == /\ pc = "l5"
+      /\ IF i \geq l
+            THEN /\ b' = [b EXCEPT ![i] = a[i]]
+                 /\ i' = i - 1
+                 /\ pc' = "l5"
+                 /\ j' = j
+            ELSE /\ IF m \geq l
+                       THEN /\ i' = l
+                       ELSE /\ PrintT("not sure of semantics of Pascal")
+                            /\ i' = (CHOOSE x \in {} : FALSE)
+                 /\ j' = m + 1
+                 /\ pc' = "l6"
+                 /\ b' = b
+      /\ UNCHANGED << a, stack, l, r, k, m >>
+
+l6 == /\ pc = "l6"
+      /\ IF j \leq r
+            THEN /\ b' = [b EXCEPT ![r + m + 1 - j] = a[j]]
+                 /\ j' = j + 1
+                 /\ pc' = "l6"
+                 /\ UNCHANGED << i, k >>
+            ELSE /\ IF m+1 \leq r
+                       THEN /\ j' = r
+                            /\ i' = i
+                       ELSE /\ PrintT("not sure of semantics of Pascal")
+                            /\ i' = (CHOOSE x \in {} : FALSE)
+                            /\ j' = j
+                 /\ k' = l
+                 /\ pc' = "l7"
+                 /\ b' = b
+      /\ UNCHANGED << a, stack, l, r, m >>
+
+l7 == /\ pc = "l7"
+      /\ IF k \leq r
+            THEN /\ IF b[i] < b[j]
+                       THEN /\ a' = [a EXCEPT ![k] = b[i]]
+                            /\ i' = i + 1
+                            /\ j' = j
+                       ELSE /\ a' = [a EXCEPT ![k] = b[j]]
+                            /\ j' = j - 1
+                            /\ i' = i
+                 /\ k' = k + 1
+                 /\ pc' = "l7"
+            ELSE /\ pc' = "l8"
+                 /\ UNCHANGED << a, i, j, k >>
+      /\ UNCHANGED << b, stack, l, r, m >>
+
+l8 == /\ pc = "l8"
+      /\ pc' = Head(stack).pc
+      /\ i' = Head(stack).i
+      /\ j' = Head(stack).j
+      /\ k' = Head(stack).k
+      /\ m' = Head(stack).m
+      /\ l' = Head(stack).l
+      /\ r' = Head(stack).r
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << a, b >>
+
+mergesort == l1 \/ l2 \/ l3 \/ l4 \/ l5 \/ l6 \/ l7 \/ l8
+
+main == /\ pc = "main"
+        /\ /\ l' = 1
+           /\ r' = Len(a)
+           /\ stack' = << [ procedure |->  "mergesort",
+                            pc        |->  "Done",
+                            i         |->  i,
+                            j         |->  j,
+                            k         |->  k,
+                            m         |->  m,
+                            l         |->  l,
+                            r         |->  r ] >>
+                        \o stack
+        /\ i' = defaultInitValue
+        /\ j' = defaultInitValue
+        /\ k' = defaultInitValue
+        /\ m' = defaultInitValue
+        /\ pc' = "l1"
+        /\ UNCHANGED << a, b >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == mergesort \/ main
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ff6c7e89b9c9baac60a504f5cbacb5a2
+
+Invariant == 
+   (pc = "Done") => \A x, y \in DOMAIN a :
+                       (x < y) => a[x] \leq a[y]
+=============================================================================
diff --git a/tlatools/test-model/pcal/MultiAssignment.tla b/tlatools/test-model/pcal/MultiAssignment.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2f2732c089d1af1913d7cd66c05b7cd5b4355224
--- /dev/null
+++ b/tlatools/test-model/pcal/MultiAssignment.tla
@@ -0,0 +1,65 @@
+--------------------------- MODULE MultiAssignment -------------------------- 
+(***************************************************************************)
+(* A test of multi-assignment statements with arrays.                      *)
+(***************************************************************************)
+
+EXTENDS Naturals, TLC
+
+
+
+(*****
+
+
+--algorithm MultiAssignment
+  process Proc \in 1..3
+  variables A = [i \in 1..5 |-> i] ; x = 0 ;
+  begin a : A[1] := A[3] ||  x := 7 || A[3] := A[1] ;
+            assert <<3 , 1>> = <<A[1], A[3]>> ;
+        b : assert <<3 , 1>> = <<A[1], A[3]>> ;
+  end process
+  end algorithm 
+
+****)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-922bd40bbb66854d6ea0cf6dbef935cb
+VARIABLES pc, A, x
+
+vars == << pc, A, x >>
+
+ProcSet == (1..3)
+
+Init == (* Process Proc *)
+        /\ A = [self \in 1..3 |-> [i \in 1..5 |-> i]]
+        /\ x = [self \in 1..3 |-> 0]
+        /\ pc = [self \in ProcSet |-> "a"]
+
+a(self) == /\ pc[self] = "a"
+           /\ /\ A' = [A EXCEPT ![self][1] = A[self][3],
+                                ![self][3] = A[self][1]]
+              /\ x' = [x EXCEPT ![self] = 7]
+           /\ Assert(<<3 , 1>> = <<A'[self][1], A'[self][3]>>, 
+                     "Failure of assertion at line 17, column 13.")
+           /\ pc' = [pc EXCEPT ![self] = "b"]
+
+b(self) == /\ pc[self] = "b"
+           /\ Assert(<<3 , 1>> = <<A[self][1], A[self][3]>>, 
+                     "Failure of assertion at line 18, column 13.")
+           /\ pc' = [pc EXCEPT ![self] = "Done"]
+           /\ UNCHANGED << A, x >>
+
+Proc(self) == a(self) \/ b(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in 1..3: Proc(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..3 : WF_vars(Proc(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-6c5ff91175ea2e80dc22b1dde5479e58
+=============================================================================
diff --git a/tlatools/test-model/pcal/MultiProc2.tla b/tlatools/test-model/pcal/MultiProc2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..136c0c3110b13afedcb0b59737fbedcbd1a43f56
--- /dev/null
+++ b/tlatools/test-model/pcal/MultiProc2.tla
@@ -0,0 +1,131 @@
+----------------------------- MODULE MultiProc2 ---------------------------- 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+
+  --algorithm MultiProc2
+    variables
+      x = [i \in ProcSet |-> CASE i = 41 -> 1 []
+                                 i = 42 -> 2 []
+                                 i = 43 -> 3];
+      sum = 0 ;
+      done = {};
+    procedure Sum(arg = 0)
+      variable u = 1 ;
+      begin p1 : u := u + arg ;
+            p2 : sum := sum + u;
+                 return;
+      end procedure
+    process ProcA = 41
+      variable y = 0  ;
+      begin a1 : call Sum( x [ 41 ] + y ) ;
+            a2 : done := done \cup { 41 } ;
+            a3 : when done = { 41, 42, 43 } ;
+                 when Print ( sum , TRUE ) ;
+      end process
+    process ProcB \in {42, 43}
+      variable z \in {2, 3} ;
+      begin b1 : call Sum ( x [ self ] + z ) ;
+            b2 : done := done \cup { self } ;
+      end process
+    end algorithm 
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-6a3f53faa6a08b14086cf6736a14399e
+VARIABLES x, sum, done, pc, stack, arg, u, y, z
+
+vars == << x, sum, done, pc, stack, arg, u, y, z >>
+
+ProcSet == {41} \cup ({42, 43})
+
+Init == (* Global variables *)
+        /\ x = [i \in ProcSet |-> CASE i = 41 -> 1 []
+                                      i = 42 -> 2 []
+                                      i = 43 -> 3]
+        /\ sum = 0
+        /\ done = {}
+        (* Procedure Sum *)
+        /\ arg = [ self \in ProcSet |-> 0]
+        /\ u = [ self \in ProcSet |-> 1]
+        (* Process ProcA *)
+        /\ y = 0
+        (* Process ProcB *)
+        /\ z \in [{42, 43} -> {2, 3}]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self = 41 -> "a1"
+                                        [] self \in {42, 43} -> "b1"]
+
+p1(self) == /\ pc[self] = "p1"
+            /\ u' = [u EXCEPT ![self] = u[self] + arg[self]]
+            /\ pc' = [pc EXCEPT ![self] = "p2"]
+            /\ UNCHANGED << x, sum, done, stack, arg, y, z >>
+
+p2(self) == /\ pc[self] = "p2"
+            /\ sum' = sum + u[self]
+            /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+            /\ u' = [u EXCEPT ![self] = Head(stack[self]).u]
+            /\ arg' = [arg EXCEPT ![self] = Head(stack[self]).arg]
+            /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+            /\ UNCHANGED << x, done, y, z >>
+
+Sum(self) == p1(self) \/ p2(self)
+
+a1 == /\ pc[41] = "a1"
+      /\ /\ arg' = [arg EXCEPT ![41] = x [ 41 ] + y]
+         /\ stack' = [stack EXCEPT ![41] = << [ procedure |->  "Sum",
+                                                pc        |->  "a2",
+                                                u         |->  u[41],
+                                                arg       |->  arg[41] ] >>
+                                            \o stack[41]]
+      /\ u' = [u EXCEPT ![41] = 1]
+      /\ pc' = [pc EXCEPT ![41] = "p1"]
+      /\ UNCHANGED << x, sum, done, y, z >>
+
+a2 == /\ pc[41] = "a2"
+      /\ done' = (done \cup { 41 })
+      /\ pc' = [pc EXCEPT ![41] = "a3"]
+      /\ UNCHANGED << x, sum, stack, arg, u, y, z >>
+
+a3 == /\ pc[41] = "a3"
+      /\ done = { 41, 42, 43 }
+      /\ Print ( sum , TRUE )
+      /\ pc' = [pc EXCEPT ![41] = "Done"]
+      /\ UNCHANGED << x, sum, done, stack, arg, u, y, z >>
+
+ProcA == a1 \/ a2 \/ a3
+
+b1(self) == /\ pc[self] = "b1"
+            /\ /\ arg' = [arg EXCEPT ![self] = x [ self ] + z[self]]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "Sum",
+                                                        pc        |->  "b2",
+                                                        u         |->  u[self],
+                                                        arg       |->  arg[self] ] >>
+                                                    \o stack[self]]
+            /\ u' = [u EXCEPT ![self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "p1"]
+            /\ UNCHANGED << x, sum, done, y, z >>
+
+b2(self) == /\ pc[self] = "b2"
+            /\ done' = (done \cup { self })
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+            /\ UNCHANGED << x, sum, stack, arg, u, y, z >>
+
+ProcB(self) == b1(self) \/ b2(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == ProcA
+           \/ (\E self \in ProcSet: Sum(self))
+           \/ (\E self \in {42, 43}: ProcB(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(ProcA) /\ WF_vars(Sum(41))
+        /\ \A self \in {42, 43} : WF_vars(ProcB(self)) /\ WF_vars(Sum(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3f17bd1aee44cca22b14ef7de16f1390
+=============================================================================
diff --git a/tlatools/test-model/pcal/MultiprocDefine.tla b/tlatools/test-model/pcal/MultiprocDefine.tla
new file mode 100644
index 0000000000000000000000000000000000000000..535bbde59c7a17b18e82800b5dbfe3a863ffab55
--- /dev/null
+++ b/tlatools/test-model/pcal/MultiprocDefine.tla
@@ -0,0 +1,59 @@
+---------------------------- MODULE MultiprocDefine -------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(*   
+--algorithm MultiprocDefine
+  variables n = 0 ;
+  define nplus1 == n + 1 
+         nplus2 == nplus1 + 1
+  end define ;
+  process Proc \in {1, 2, 3}
+  variables i ;
+  begin  main : i := nplus2 ;
+                assert i = 2 ;
+  end process
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-255a10565612be8265861699149ea328
+CONSTANT defaultInitValue
+VARIABLES n, pc
+
+(* define statement *)
+nplus1 == n + 1
+nplus2 == nplus1 + 1
+
+VARIABLE i
+
+vars == << n, pc, i >>
+
+ProcSet == ({1, 2, 3})
+
+Init == (* Global variables *)
+        /\ n = 0
+        (* Process Proc *)
+        /\ i = [self \in {1, 2, 3} |-> defaultInitValue]
+        /\ pc = [self \in ProcSet |-> "main"]
+
+main(self) == /\ pc[self] = "main"
+              /\ i' = [i EXCEPT ![self] = nplus2]
+              /\ Assert(i'[self] = 2, 
+                        "Failure of assertion at line 13, column 17.")
+              /\ pc' = [pc EXCEPT ![self] = "Done"]
+              /\ n' = n
+
+Proc(self) == main(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in {1, 2, 3}: Proc(self))
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-0c8f6e25783592e29e1972ef47f12501
+=============================================================================
diff --git a/tlatools/test-model/pcal/NestedMacros.tla b/tlatools/test-model/pcal/NestedMacros.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b03dee89a8f9867321b4ff5679c1dba9b0709330
--- /dev/null
+++ b/tlatools/test-model/pcal/NestedMacros.tla
@@ -0,0 +1,72 @@
+----------------------------- MODULE NestedMacros ---------------------------
+EXTENDS Naturals, TLC
+
+\* PlusCal options(label)
+(***************************************************************************
+--algorithm Test {
+  variables x, y ;
+
+  macro ff(a, b) {
+    a := b
+  }
+  macro foo(a) {
+   ff(z,a);
+   y := a
+  }
+
+  macro bar(b) {
+   x := b;
+   foo(22)
+  }
+  process (foob  \in {1,2}) 
+   variable z ;
+  { l1 : z := 0 ; 
+    l2 : bar(42);
+          assert z = 22 /\ x = 42
+  }
+}
+ ***************************************************************************)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-45eef21f51ec0ad28fe46e4b9a7b10ce
+CONSTANT defaultInitValue
+VARIABLES x, y, pc, z
+
+vars == << x, y, pc, z >>
+
+ProcSet == ({1,2})
+
+Init == (* Global variables *)
+        /\ x = defaultInitValue
+        /\ y = defaultInitValue
+        (* Process foob *)
+        /\ z = [self \in {1,2} |-> defaultInitValue]
+        /\ pc = [self \in ProcSet |-> "l1"]
+
+l1(self) == /\ pc[self] = "l1"
+            /\ z' = [z EXCEPT ![self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "l2"]
+            /\ UNCHANGED << x, y >>
+
+l2(self) == /\ pc[self] = "l2"
+            /\ x' = 42
+            /\ z' = [z EXCEPT ![self] = 22]
+            /\ y' = 22
+            /\ Assert(z'[self] = 22 /\ x' = 42, 
+                      "Failure of assertion at line 25, column 11.")
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+
+foob(self) == l1(self) \/ l2(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in {1,2}: foob(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in {1,2} : WF_vars(foob(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-5f6bcf611a462be83724e9522b1f4062
+=============================================================================
diff --git a/tlatools/test-model/pcal/NoLoop.tla b/tlatools/test-model/pcal/NoLoop.tla
new file mode 100644
index 0000000000000000000000000000000000000000..89678c0db0c904a3002c3f3d670827a0e517e5b5
--- /dev/null
+++ b/tlatools/test-model/pcal/NoLoop.tla
@@ -0,0 +1,56 @@
+------------------------------- MODULE NoLoop ------------------------------- 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+  --algorithm NoLoop
+    variable x = 0; y = 0 ;
+    begin a : with i = 3 do x := i ; end with;
+          b : with j \in { 1 , 2 } do y := j ; x := x + y ; end with ;
+          c : if y = 1 then x := x + 1 ; else x := x + y; end if;
+              when Print ( x , TRUE );
+    end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-688f592294a3ad1b9996ea0f884f9746
+VARIABLES x, y, pc
+
+vars == << x, y, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ LET i == 3 IN
+          x' = i
+     /\ pc' = "b"
+     /\ y' = y
+
+b == /\ pc = "b"
+     /\ \E j \in { 1 , 2 }:
+          /\ y' = j
+          /\ x' = x + y'
+     /\ pc' = "c"
+
+c == /\ pc = "c"
+     /\ IF y = 1
+           THEN /\ x' = x + 1
+           ELSE /\ x' = x + y
+     /\ Print ( x' , TRUE )
+     /\ pc' = "Done"
+     /\ y' = y
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-e78115b3384d72d09de2ad789d3620a6
+=============================================================================
diff --git a/tlatools/test-model/pcal/NoLoop2.tla b/tlatools/test-model/pcal/NoLoop2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b155e5aa7a428169bf86ae56279b73477caaa5f5
--- /dev/null
+++ b/tlatools/test-model/pcal/NoLoop2.tla
@@ -0,0 +1,70 @@
+------------------------------- MODULE NoLoop2 ------------------------------- 
+EXTENDS Naturals, TLC
+
+(*   
+
+  --algorithm NoLoop2
+    variable x = 0; y = 0 ;
+    begin a : with i = 3 do x := i ; end with;
+          b : with j \in { 1 , 2 } do y := j ; x := x + y ; end with;
+          c : if y = 1 then x := x + 1 ; d : x := 2 * x ;
+                       else x := x + y ; end if;
+          e : when Print ( x , TRUE );
+    end algorithm
+   \* should print out 10 or 7
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a111e49229d06e75e660ee8777442285
+VARIABLES x, y, pc
+
+vars == << x, y, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ LET i == 3 IN
+          x' = i
+     /\ pc' = "b"
+     /\ y' = y
+
+b == /\ pc = "b"
+     /\ \E j \in { 1 , 2 }:
+          /\ y' = j
+          /\ x' = x + y'
+     /\ pc' = "c"
+
+c == /\ pc = "c"
+     /\ IF y = 1
+           THEN /\ x' = x + 1
+                /\ pc' = "d"
+           ELSE /\ x' = x + y
+                /\ pc' = "e"
+     /\ y' = y
+
+d == /\ pc = "d"
+     /\ x' = 2 * x
+     /\ pc' = "e"
+     /\ y' = y
+
+e == /\ pc = "e"
+     /\ Print ( x , TRUE )
+     /\ pc' = "Done"
+     /\ UNCHANGED << x, y >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b \/ c \/ d \/ e
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-7bfbfdcfed4c1aea23c410420c1f4f9d
+=============================================================================
diff --git a/tlatools/test-model/pcal/NoParams.tla b/tlatools/test-model/pcal/NoParams.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ccb98c820452e034f16a870154adf2381414fe26
--- /dev/null
+++ b/tlatools/test-model/pcal/NoParams.tla
@@ -0,0 +1,69 @@
+----------------------------- MODULE NoParams ------------------------------ 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+
+--algorithm NoParams
+    variables sum = 0 ;
+    procedure Sum() 
+      begin s1: sum := sum + 1;
+                return;
+      end procedure;
+    begin m1 : call Sum();
+          m2 : call Sum();
+          m3 : when Print(sum, TRUE) ;
+   end algorithm 
+
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-053c2fa748febe4e17ba5f50b599466c
+VARIABLES sum, pc, stack
+
+vars == << sum, pc, stack >>
+
+Init == (* Global variables *)
+        /\ sum = 0
+        /\ stack = << >>
+        /\ pc = "m1"
+
+s1 == /\ pc = "s1"
+      /\ sum' = sum + 1
+      /\ pc' = Head(stack).pc
+      /\ stack' = Tail(stack)
+
+Sum == s1
+
+m1 == /\ pc = "m1"
+      /\ stack' = << [ procedure |->  "Sum",
+                       pc        |->  "m2" ] >>
+                   \o stack
+      /\ pc' = "s1"
+      /\ sum' = sum
+
+m2 == /\ pc = "m2"
+      /\ stack' = << [ procedure |->  "Sum",
+                       pc        |->  "m3" ] >>
+                   \o stack
+      /\ pc' = "s1"
+      /\ sum' = sum
+
+m3 == /\ pc = "m3"
+      /\ Print(sum, TRUE)
+      /\ pc' = "Done"
+      /\ UNCHANGED << sum, stack >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Sum \/ m1 \/ m2 \/ m3
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-621925aa8b4c5ddf90178ffc7da092a5
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/NotSoSimpleLoop.tla b/tlatools/test-model/pcal/NotSoSimpleLoop.tla
new file mode 100644
index 0000000000000000000000000000000000000000..335e5a9bb18ea23539defbcb553d8a92cd9fbd8e
--- /dev/null
+++ b/tlatools/test-model/pcal/NotSoSimpleLoop.tla
@@ -0,0 +1,55 @@
+--------------------------- MODULE NotSoSimpleLoop -------------------------- 
+EXTENDS Naturals, TLC
+
+(*   --algorithm NotSoSimpleLoop                                             
+     variable x = 0;                                                     
+     begin a : while x < 10                                              
+                 do x := x+1 ;                                           
+                    skip ;                                               
+                    assert x \in 1..10
+               end while ;                                               
+               x := 4*x ;                                                
+               assert x = 40 ;                                                 
+           b : assert 2 * x = 80;                                             
+     end algorithm                  *)
+
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-50f6f9a537b91874ce70a2eef129eb7f
+VARIABLES x, pc
+
+vars == << x, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ IF x < 10
+           THEN /\ x' = x+1
+                /\ TRUE
+                /\ Assert(x' \in 1..10, 
+                          "Failure of assertion at line 9, column 21.")
+                /\ pc' = "a"
+           ELSE /\ x' = 4*x
+                /\ Assert(x' = 40, 
+                          "Failure of assertion at line 12, column 16.")
+                /\ pc' = "b"
+
+b == /\ pc = "b"
+     /\ Assert(2 * x = 80, "Failure of assertion at line 13, column 16.")
+     /\ pc' = "Done"
+     /\ x' = x
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a \/ b
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-0694bcde67f647179b7e22184415182a
+=============================================================================
diff --git a/tlatools/test-model/pcal/PcalPaxos.cfg b/tlatools/test-model/pcal/PcalPaxos.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8537dc757c18205aadb1e072561098ec298d10b1
--- /dev/null
+++ b/tlatools/test-model/pcal/PcalPaxos.cfg
@@ -0,0 +1,19 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+
+CONSTANTS
+  MaxBallot = 2
+  Acceptor <- MCAcceptor 
+  Leader   <- MCLeader   
+  Learner  <- MCLearner  
+  Ballot   <- MCBallot   
+  LeaderOf <- MCLeaderOf 
+  Command  <- MCCommand  
+  Majority <- MCMajority 
+  a1 = a1  a2 = a2  a3 = a3  
+  ldr1 = ldr1  ldr2 = ldr2  
+  lrn1 = lrn1  lrn2 = lrn2
+  cmd1 = cmd1  cmd2 = cmd2
+  NotACmd = NotACmd
+
+INVARIANTS TypeOK Safety
diff --git a/tlatools/test-model/pcal/PcalPaxos.tla b/tlatools/test-model/pcal/PcalPaxos.tla
new file mode 100644
index 0000000000000000000000000000000000000000..186785d1610d36369582ae765e04cfdf9c3dfccf
--- /dev/null
+++ b/tlatools/test-model/pcal/PcalPaxos.tla
@@ -0,0 +1,220 @@
+------------------------------ MODULE PcalPaxos ----------------------------- 
+EXTENDS Integers, TLC, FiniteSets
+
+CONSTANTS Acceptor, Leader, Learner, Ballot, LeaderOf, Command, Majority
+
+ASSUME /\ LeaderOf \in [Ballot -> Leader]
+       /\ Ballot \subseteq Nat
+       /\ 0 \in Ballot
+       /\ Majority \subseteq SUBSET Acceptor
+       /\ \A M1, M2 \in Majority : M1 \cap M2 # {}
+
+NotACmd == CHOOSE c : c \notin Command
+
+BallotsOf(ldr) == {b \in Ballot : LeaderOf[b] = ldr}
+
+Message == [type : {"1a"}, bal : Ballot] \cup
+           [type : {"1b"}, bal : Ballot, acc : Acceptor,
+            mbal : Ballot \cup {-1}, mcmd : Command \cup {NotACmd}] 
+              \cup
+           [type : {"2a"}, bal : Ballot, cmd : Command]  \cup
+           [type : {"2b"}, bal : Ballot, cmd : Command, acc : Acceptor] 
+
+(*
+--algorithm Paxos
+  variables msgs = {}; 
+  macro Send(m) 
+   begin msgs := msgs \cup {m}
+   end macro
+
+  process Ldr \in Leader
+    variables ldrBal = -1 ;
+              ldrPhs = 2 
+    begin L: 
+      while TRUE do
+        with b \in {bb \in Ballot : LeaderOf[bb] = self}
+          do either when ldrBal < b;
+                    ldrBal := b ;
+                    ldrPhs := 1 ;
+                    Send([type |-> "1a", bal |-> b])
+
+             or     when (ldrBal = b) /\ (ldrPhs = 1) ;
+                    with M = {m \in msgs : (m.type = "1b") /\ (m.bal = b)};
+                         A = {m.acc : m \in M} ;
+                         mmsg \in {m \in M : 
+                                    \A m2 \in M : m.mbal \geq m2.mbal}
+                       do when A \in Majority ;
+                          if mmsg.mbal > -1
+                            then Send([type |-> "2a", bal |-> b, 
+                                       cmd |-> mmsg.mcmd])
+                            else with c \in Command
+                                   do Send([type |-> "2a", bal |-> b, 
+                                            cmd |-> c])
+                                 end with
+                          end if ;
+                     end with ;
+                     ldrPhs := 2
+                end either
+             end with
+      end while
+    end process
+
+  process Acc \in Acceptor
+    variables bal = -1 ; mbal = -1 ; mcmd = NotACmd
+    begin A: 
+      while TRUE do 
+        with m \in msgs
+          do either when (m.type = "1a") /\ (m.bal > bal) ;
+                    bal := m.bal ;
+                    Send([type |-> "1b", bal |-> m.bal, acc |-> self,
+                          mbal |-> mbal, mcmd |-> mcmd])
+             or     when    (m.type = "2a")
+                         /\ (m.bal \geq bal) 
+                         /\ (m.bal > mbal);
+                    bal  := m.bal ;
+                    mbal := m.bal ;
+                    mcmd := m.cmd ;
+                    Send([type |-> "2b", bal |-> m.bal, acc |-> self,
+                           cmd |-> m.cmd])
+             end either
+        end with
+      end while
+    end process
+
+
+  process Lrn \in Learner
+    variable learned = NotACmd
+    begin N: with b \in Ballot ;
+                  2bMsgs = {m \in msgs : (m.type = "2b") /\ (m.bal = b)}
+               do when {m.acc : m \in 2bMsgs} \in Majority ;
+                  with m \in 2bMsgs
+                    do learned := m.cmd
+                  end with
+             end with ;
+    end process
+end algorithm
+*)
+
+\* BEGIN TRANSLATION
+VARIABLES msgs, pc, ldrBal, ldrPhs, bal, mbal, mcmd, learned
+
+vars == << msgs, pc, ldrBal, ldrPhs, bal, mbal, mcmd, learned >>
+
+ProcSet == (Leader) \cup (Acceptor) \cup (Learner)
+
+Init == (* Global variables *)
+        /\ msgs = {}
+        (* Process Ldr *)
+        /\ ldrBal = [self \in Leader |-> -1]
+        /\ ldrPhs = [self \in Leader |-> 2]
+        (* Process Acc *)
+        /\ bal = [self \in Acceptor |-> -1]
+        /\ mbal = [self \in Acceptor |-> -1]
+        /\ mcmd = [self \in Acceptor |-> NotACmd]
+        (* Process Lrn *)
+        /\ learned = [self \in Learner |-> NotACmd]
+        /\ pc = [self \in ProcSet |-> CASE self \in Leader -> "L"
+                                        [] self \in Acceptor -> "A"
+                                        [] self \in Learner -> "N"]
+
+L(self) == /\ pc[self] = "L"
+           /\ \E b \in {bb \in Ballot : LeaderOf[bb] = self}:
+                \/ /\ ldrBal[self] < b
+                   /\ ldrBal' = [ldrBal EXCEPT ![self] = b]
+                   /\ ldrPhs' = [ldrPhs EXCEPT ![self] = 1]
+                   /\ msgs' = (msgs \cup {([type |-> "1a", bal |-> b])})
+                \/ /\ (ldrBal[self] = b) /\ (ldrPhs[self] = 1)
+                   /\ LET M == {m \in msgs : (m.type = "1b") /\ (m.bal = b)} IN
+                        LET A == {m.acc : m \in M} IN
+                          \E mmsg \in {m \in M :
+                                        \A m2 \in M : m.mbal \geq m2.mbal}:
+                            /\ A \in Majority
+                            /\ IF mmsg.mbal > -1
+                                  THEN /\ msgs' = (msgs \cup {([type |-> "2a", bal |-> b,
+                                                                cmd |-> mmsg.mcmd])})
+                                  ELSE /\ \E c \in Command:
+                                            msgs' = (msgs \cup {([type |-> "2a", bal |-> b,
+                                                                  cmd |-> c])})
+                   /\ ldrPhs' = [ldrPhs EXCEPT ![self] = 2]
+                   /\ UNCHANGED ldrBal
+           /\ pc' = [pc EXCEPT ![self] = "L"]
+           /\ UNCHANGED << bal, mbal, mcmd, learned >>
+
+Ldr(self) == L(self)
+
+A(self) == /\ pc[self] = "A"
+           /\ \E m \in msgs:
+                \/ /\ (m.type = "1a") /\ (m.bal > bal[self])
+                   /\ bal' = [bal EXCEPT ![self] = m.bal]
+                   /\ msgs' = (msgs \cup {([type |-> "1b", bal |-> m.bal, acc |-> self,
+                                            mbal |-> mbal[self], mcmd |-> mcmd[self]])})
+                   /\ UNCHANGED <<mbal, mcmd>>
+                \/ /\    (m.type = "2a")
+                      /\ (m.bal \geq bal[self])
+                      /\ (m.bal > mbal[self])
+                   /\ bal' = [bal EXCEPT ![self] = m.bal]
+                   /\ mbal' = [mbal EXCEPT ![self] = m.bal]
+                   /\ mcmd' = [mcmd EXCEPT ![self] = m.cmd]
+                   /\ msgs' = (msgs \cup {([type |-> "2b", bal |-> m.bal, acc |-> self,
+                                             cmd |-> m.cmd])})
+           /\ pc' = [pc EXCEPT ![self] = "A"]
+           /\ UNCHANGED << ldrBal, ldrPhs, learned >>
+
+Acc(self) == A(self)
+
+N(self) == /\ pc[self] = "N"
+           /\ \E b \in Ballot:
+                LET 2bMsgs == {m \in msgs : (m.type = "2b") /\ (m.bal = b)} IN
+                  /\ {m.acc : m \in 2bMsgs} \in Majority
+                  /\ \E m \in 2bMsgs:
+                       learned' = [learned EXCEPT ![self] = m.cmd]
+           /\ pc' = [pc EXCEPT ![self] = "Done"]
+           /\ UNCHANGED << msgs, ldrBal, ldrPhs, bal, mbal, mcmd >>
+
+Lrn(self) == N(self)
+
+Next == (\E self \in Leader: Ldr(self))
+           \/ (\E self \in Acceptor: Acc(self))
+           \/ (\E self \in Learner: Lrn(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION
+
+TypeOK == /\ msgs \subseteq Message
+          /\ learned \in [Learner -> Command \cup {NotACmd}]
+          /\ ldrBal  \in [Leader -> Ballot \cup {-1}]
+          /\ ldrPhs  \in [Leader -> {1, 2}]
+          /\ bal     \in [Acceptor -> Ballot \cup {-1}]
+          /\ mbal    \in [Acceptor -> Ballot \cup {-1}]
+          /\ mcmd    \in [Acceptor -> Command \cup {NotACmd}]
+\*          /\ \A b \in Ballot :
+\*               Cardinality({m \in msgs : (m.type = "1b") /\ (m.bal = b)})
+\*                  < 2
+
+Safety == \A l1, l2 \in Learner : 
+             \/ learned[l2] = NotACmd
+             \/ learned[l1] \in {learned[l2], NotACmd}
+
+CONSTANTS MaxBallot, a1, a2, a3, ldr1, ldr2, lrn1, lrn2, cmd1, cmd2
+
+MCAcceptor == {a1, a2, a3} \*  {a1} 
+MCLeader   == {ldr1}
+MCLearner  == {lrn1, lrn2} \*  {lrn1} 
+MCLeaderOf == [i \in Ballot |-> ldr1]
+MCCommand  == {cmd1, cmd2}
+MCMajority == {{a1, a3}, {a1, a2}, {a2, a3}} \*  {{a1}} 
+
+MCBallot   == 0..MaxBallot
+=============================================================================
+With
+   MaxBallot = 2
+   MCAcceptor == {a1, a2, a3}
+   MCLeader   == {ldr1}
+   MCLearner  == {lrn1, lrn2}
+   MCLeaderOf == [i \in Ballot |-> ldr1]
+   MCCommand  == {cmd1, cmd2}
+   MCMajority == {{a1, a3}, {a1, a2}, {a2, a3}}
+
+592889 distinct states found in Paxos and PcalPaxos
+
diff --git a/tlatools/test-model/pcal/Peterson.cfg b/tlatools/test-model/pcal/Peterson.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a99527de5a0215e987814884692dda8c78004f27
--- /dev/null
+++ b/tlatools/test-model/pcal/Peterson.cfg
@@ -0,0 +1,9 @@
+\* Add statements after this line.
+
+SPECIFICATION Spec
+INVARIANT Inv
+\** INVARIANT MutualExclusion
+\** PROPERTY InfinitelyOftenInCS
+
+\* SPECIFICATION ISpec
+\* INVARIANT MutualExclusion Inv
diff --git a/tlatools/test-model/pcal/Peterson.tla b/tlatools/test-model/pcal/Peterson.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6c4bbb5aeb975899d291fb592b45c4e83cb4bb26
--- /dev/null
+++ b/tlatools/test-model/pcal/Peterson.tla
@@ -0,0 +1,191 @@
+------------------------------ MODULE Peterson ------------------------------ 
+
+(***************************************************************************)
+(* This specification describes Peterson's algorithm.  It is a modification *)
+(* modification of one found on Wikipedia, written in terms of a single    *)
+(* parameterized process, instantiated with parameter self = 0 and 1.      *)
+(***************************************************************************)
+
+(***************************************************************************)
+(* We define Not(i) to be the process number other than i, so Not(0) = 1   *)
+(* and Not(1) = 0.                                                         *)
+(***************************************************************************)
+Not(i) == IF i = 0 THEN 1 ELSE 0
+
+(*************************************************************************
+Here is the algorithm in +Cal (using the C syntax):
+
+--algorithm Peterson {
+   variables flag = [i \in {0, 1} |-> FALSE], turn = 0;
+   process (proc \in {0,1}) {
+     a0: while (TRUE) { 
+     a1:   flag[self] := TRUE; 
+     a2:   turn := Not(self);       
+     a3:   while (flag[Not(self)] /\ turn = Not(self)) {
+             skip };
+     cs:   skip;  \* critical section   
+     a4:   flag[self] := FALSE;            
+     } \* end while
+    } \* end process
+  }
+************************************************************************)
+
+(***************************************************************************)
+(* Here is the TLA+ translation of the +Cal code, obtained by running pcal *)
+(* with the -wf option, which defines Spec to be a specification that      *)
+(* assumes weak fairness of the next-state actions of both processes.      *)
+(* This fairness assumption is discussed below.                            *)
+(***************************************************************************)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9255d446219a56b97b6d3847e8a2c94c
+VARIABLES flag, turn, pc
+
+vars == << flag, turn, pc >>
+
+ProcSet == ({0,1})
+
+Init == (* Global variables *)
+        /\ flag = [i \in {0, 1} |-> FALSE]
+        /\ turn = 0
+        /\ pc = [self \in ProcSet |-> "a0"]
+
+a0(self) == /\ pc[self] = "a0"
+            /\ pc' = [pc EXCEPT ![self] = "a1"]
+            /\ UNCHANGED << flag, turn >>
+
+a1(self) == /\ pc[self] = "a1"
+            /\ flag' = [flag EXCEPT ![self] = TRUE]
+            /\ pc' = [pc EXCEPT ![self] = "a2"]
+            /\ turn' = turn
+
+a2(self) == /\ pc[self] = "a2"
+            /\ turn' = Not(self)
+            /\ pc' = [pc EXCEPT ![self] = "a3"]
+            /\ flag' = flag
+
+a3(self) == /\ pc[self] = "a3"
+            /\ IF flag[Not(self)] /\ turn = Not(self)
+                  THEN /\ TRUE
+                       /\ pc' = [pc EXCEPT ![self] = "a3"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+            /\ UNCHANGED << flag, turn >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "a4"]
+            /\ UNCHANGED << flag, turn >>
+
+a4(self) == /\ pc[self] = "a4"
+            /\ flag' = [flag EXCEPT ![self] = FALSE]
+            /\ pc' = [pc EXCEPT ![self] = "a0"]
+            /\ turn' = turn
+
+proc(self) == a0(self) \/ a1(self) \/ a2(self) \/ a3(self) \/ cs(self)
+                 \/ a4(self)
+
+Next == (\E self \in {0,1}: proc(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3c75243f0ddf7b933ebf70f115d692f3
+
+(***************************************************************************)
+(* Here is the invariant property that the algorithm should satisfy.  It   *)
+(* asserts that at least one process is not in its critical section.,      *)
+(***************************************************************************)
+MutualExclusion == \E i \in {0, 1} : pc[i] # "cs"
+
+(***************************************************************************)
+(* It's a good idea to check an algorithm for liveness, to make sure that  *)
+(* safety isn't ensured by an error that prevents anything interesting     *)
+(* from happening.  Peterson's algorithm is supposed to be starvation      *)
+(* free.  The easiest and best way to check that no process is ever        *)
+(* starved is to assume that both processes keep trying to enter their     *)
+(* critical sections and check that both of them do enter it infinitely    *)
+(* often.  This is done by assuming weak fairness of both processes next   *)
+(* state actions and checking that the following formula is satisfied.     *)
+(***************************************************************************)
+InfinitelyOftenInCS == \A i \in {0, 1} : []<>(pc[i] = "cs")
+
+(***************************************************************************)
+(* The spec of a starvation free mutual exclusion algorithm asserts that,  *)
+(* if any process wants to enter its critical section, then it eventually  *)
+(* does.  A process indicates that it wants to enter by leaving its        *)
+(* noncritical section.  In this representation of the algorithm, process  *)
+(* i is in its noncritical section iff pc[i] = "a0".  It leaves the        *)
+(* noncritical section by executing an a0(i) step.  that sets pc[i] to     *)
+(* "a1".  So we assume weak fairness of the action proc(i) /\ pc[i] #      *)
+(* "a0", the next-state action of process i when it is not in its          *)
+(* noncritical section, for each i.  This gives us the spec.               *)
+(***************************************************************************)
+FairSpec == /\ Init 
+            /\ [][Next]_vars 
+            /\ \A self \in {0,1}: WF_vars(proc(self) /\ (pc[self] # "a0"))
+
+(***************************************************************************)
+(* We check that this spec satisfies the following condition, which        *)
+(* asserts that whenever a process i leaves the noncritical section (by    *)
+(* setting pc[i] to "a1"), it eventually enters its critical section.      *)
+(***************************************************************************)
+DeadlockFree == \A i \in {0,1} : (pc[i] = "a1") ~> (pc[i] = "cs") 
+
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(* To prove mutual exclusion, we find an inductive invariant Inv that is   *)
+(* true initially and implies mutual exclusion.  In other words, we must   *)
+(* find an Inv that satisfies                                              *)
+(*                                                                         *)
+(*    THEOREM /\ Init => Inv                                               *)
+(*            /\ Inv /\ [Next]_vars => Inv'                                *)
+(*            /\ Inv => MutualExclusion                                    *)
+(*                                                                         *)
+(* The inductive invariant Inv will have the form TypeOK /\ I              *)
+(* where TypeOK just asserts type-correctness and I is the interesting     *)
+(* part.  For Peterson's algorithm, TypeOK is:                             *)
+(***************************************************************************)
+TypeOK == /\ pc \in [{0,1} -> {"a0", "a1", "a2", "a3", "cs", "a4"}]
+          /\ turn \in {0, 1}
+          /\ flag \in [{0,1} -> BOOLEAN]
+
+(***************************************************************************)
+(* It turns out that we can use the following I.  (I explain below how I   *)
+(* found I.)                                                               *)
+(***************************************************************************)
+I == \A i \in {0, 1} :
+       /\ (pc[i] \in {"a2", "a3", "cs", "a4"} ) => flag[i]
+       /\ (pc[i] \in {"cs", "a4"}) => /\ pc[Not(i)] \notin {"cs", "a4"}
+                                      /\ (pc[Not(i)] = "a3") => (turn = i)
+
+(***************************************************************************)
+(* so we have:                                                             *)
+(***************************************************************************)
+Inv == TypeOK /\ I
+
+(***************************************************************************)
+(* We can easily use TLC to check Init => Inv by checking that Inv is an   *)
+(* invariant of the specification.  We can check that Inv satisfies        *)
+(*                                                                         *)
+(*    Inv /\ [Next]_vars => Inv'                                           *)
+(*                                                                         *)
+(* by checking that Inv is an invariant of the following specification.    *)
+(***************************************************************************)
+ISpec == Inv /\ [][Next]_vars
+
+(***************************************************************************)
+(* This works only because Inv is written as the conjunction TypeOK and    *)
+(* something else, where TypeOK has the form it does.  (Otherwise, TLC     *)
+(* would not be able to handle Inv as an initial predicate.)  Moreover, to *)
+(* compute the initial states, TLC must calculate all states that satisfy  *)
+(* the type-correctness invariant.  For most specs, this is an enormous    *)
+(* number of states except perhaps for the tiniest of models, so this      *)
+(* seldom works.  However, Peterson's algorithm is so simple that it works *)
+(* fine.  In fact, I discovered the invariant I by trial and error, using  *)
+(* TLC to discover the problems with I that did not make Inv an invariant  *)
+(* of ISpec.                                                               *)
+(*                                                                         *)
+(* Finally, we can check                                                   *)
+(*                                                                         *)
+(*    Inv => MutualExclusion                                               *)
+(*                                                                         *)
+(* by having TLC check that MutualExclusion is an invariant of ISpec.      *)
+(***************************************************************************)
+=============================================================================
diff --git a/tlatools/test-model/pcal/Quicksort.tla b/tlatools/test-model/pcal/Quicksort.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4b8b7b46ad6561b9c4b98049fc455ef8c719e3ac
--- /dev/null
+++ b/tlatools/test-model/pcal/Quicksort.tla
@@ -0,0 +1,169 @@
+------------------------------- MODULE Quicksort ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT ArrayLen
+
+ASSUME ArrayLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   
+--algorithm Quicksort
+  variables Ainit \in [1..ArrayLen -> 1..ArrayLen]; A = Ainit;
+            returnVal = 1
+  procedure Partition(lo, hi )
+    begin pt1 : with piv \in lo..(hi-1)
+                  do returnVal := piv ;
+                     with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..(lo-1) : AA[i] = A[i])
+                          /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                          /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                  AA[i] \leq AA[j])}
+                        do A := Ap;
+                        return ;
+                     end with ;
+                end with;
+    end procedure
+  procedure  QS(qlo, qhi )
+    variable pivot ;
+    begin qs1 : if qlo < qhi
+                  then       call Partition(qlo, qhi) ;
+                       qs2 : pivot := returnVal ;
+                       qs3 : call QS(qlo, pivot) ;
+                       qs4 : call QS(pivot +1,qhi) ;
+                             return ;
+                  else    return;
+                end if;
+    end procedure
+  begin  main : call QS(1, Len(A)) ;
+         test : assert     A \in PermsOf(Ainit)
+                       /\ \A i, j \in 1..ArrayLen : 
+                            (i < j) =>  A[i] \leq A[j]
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9360dabbf03d04fb938a53e6f43a0dc3
+CONSTANT defaultInitValue
+VARIABLES Ainit, A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot
+
+vars == << Ainit, A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot >>
+
+Init == (* Global variables *)
+        /\ Ainit \in [1..ArrayLen -> 1..ArrayLen]
+        /\ A = Ainit
+        /\ returnVal = 1
+        (* Procedure Partition *)
+        /\ lo = defaultInitValue
+        /\ hi = defaultInitValue
+        (* Procedure QS *)
+        /\ qlo = defaultInitValue
+        /\ qhi = defaultInitValue
+        /\ pivot = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "main"
+
+pt1 == /\ pc = "pt1"
+       /\ \E piv \in lo..(hi-1):
+            /\ returnVal' = piv
+            /\ \E Ap \in {AA \in PermsOf(A) :
+                                (\A i \in 1..(lo-1) : AA[i] = A[i])
+                             /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                             /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                     AA[i] \leq AA[j])}:
+                 /\ A' = Ap
+                 /\ pc' = Head(stack).pc
+                 /\ lo' = Head(stack).lo
+                 /\ hi' = Head(stack).hi
+                 /\ stack' = Tail(stack)
+       /\ UNCHANGED << Ainit, qlo, qhi, pivot >>
+
+Partition == pt1
+
+qs1 == /\ pc = "qs1"
+       /\ IF qlo < qhi
+             THEN /\ /\ hi' = qhi
+                     /\ lo' = qlo
+                     /\ stack' = << [ procedure |->  "Partition",
+                                      pc        |->  "qs2",
+                                      lo        |->  lo,
+                                      hi        |->  hi ] >>
+                                  \o stack
+                  /\ pc' = "pt1"
+                  /\ UNCHANGED << qlo, qhi, pivot >>
+             ELSE /\ pc' = Head(stack).pc
+                  /\ pivot' = Head(stack).pivot
+                  /\ qlo' = Head(stack).qlo
+                  /\ qhi' = Head(stack).qhi
+                  /\ stack' = Tail(stack)
+                  /\ UNCHANGED << lo, hi >>
+       /\ UNCHANGED << Ainit, A, returnVal >>
+
+qs2 == /\ pc = "qs2"
+       /\ pivot' = returnVal
+       /\ pc' = "qs3"
+       /\ UNCHANGED << Ainit, A, returnVal, stack, lo, hi, qlo, qhi >>
+
+qs3 == /\ pc = "qs3"
+       /\ /\ qhi' = pivot
+          /\ qlo' = qlo
+          /\ stack' = << [ procedure |->  "QS",
+                           pc        |->  "qs4",
+                           pivot     |->  pivot,
+                           qlo       |->  qlo,
+                           qhi       |->  qhi ] >>
+                       \o stack
+       /\ pivot' = defaultInitValue
+       /\ pc' = "qs1"
+       /\ UNCHANGED << Ainit, A, returnVal, lo, hi >>
+
+qs4 == /\ pc = "qs4"
+       /\ /\ qhi' = qhi
+          /\ qlo' = pivot +1
+       /\ pivot' = defaultInitValue
+       /\ pc' = "qs1"
+       /\ UNCHANGED << Ainit, A, returnVal, stack, lo, hi >>
+
+QS == qs1 \/ qs2 \/ qs3 \/ qs4
+
+main == /\ pc = "main"
+        /\ /\ qhi' = Len(A)
+           /\ qlo' = 1
+           /\ stack' = << [ procedure |->  "QS",
+                            pc        |->  "test",
+                            pivot     |->  pivot,
+                            qlo       |->  qlo,
+                            qhi       |->  qhi ] >>
+                        \o stack
+        /\ pivot' = defaultInitValue
+        /\ pc' = "qs1"
+        /\ UNCHANGED << Ainit, A, returnVal, lo, hi >>
+
+test == /\ pc = "test"
+        /\ Assert(    A \in PermsOf(Ainit)
+                  /\ \A i, j \in 1..ArrayLen :
+                       (i < j) =>  A[i] \leq A[j], 
+                  "Failure of assertion at line 44, column 17.")
+        /\ pc' = "Done"
+        /\ UNCHANGED << Ainit, A, returnVal, stack, lo, hi, qlo, qhi, pivot >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Partition \/ QS \/ main \/ test
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c890d98935ff53e5f234680508ac16aa
+=============================================================================
+Checked without termination on svc-lamport-2 with 2 workers:
+  arrayLen = 4 in 15 sec, 32280 states, depth 22
+  arrayLen = 5 in 138min 17 sec 1529005 states, depth 28
diff --git a/tlatools/test-model/pcal/Quicksort2Procs.cfg b/tlatools/test-model/pcal/Quicksort2Procs.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8e5945333cd1398dc09ad5bbbab24c46e8121b21
--- /dev/null
+++ b/tlatools/test-model/pcal/Quicksort2Procs.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+CONSTANTS ArrayLen = 3
+INVARIANT Invariant
+
diff --git a/tlatools/test-model/pcal/Quicksort2Procs.tla b/tlatools/test-model/pcal/Quicksort2Procs.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b0e2d4ff7bf1ae9bec80323fc77bcf39a4b5bed7
--- /dev/null
+++ b/tlatools/test-model/pcal/Quicksort2Procs.tla
@@ -0,0 +1,243 @@
+--------------------------- MODULE Quicksort2Procs --------------------------- 
+EXTENDS Naturals, Sequences
+
+CONSTANT ArrayLen
+
+ASSUME ArrayLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   This algorithm uses two different copies of the QS procedure
+     to test whether tail recursion works when calling a different
+     procedure.
+
+--algorithm Quicksort
+  variables A \in [1..ArrayLen -> 1..ArrayLen];
+            returnVal = 99;
+  procedure Partition(lo = 99, hi = 99)
+    begin pt1 : with piv \in lo..(hi-1)
+                  do returnVal := piv ;
+                     with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..(lo-1) : AA[i] = A[i])
+                          /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                          /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                  AA[i] \leq AA[j])}
+                        do A := Ap;
+                        return ;
+                     end with ;
+                end with;
+    end procedure
+  procedure  QS(qlo = 99, qhi = 99)
+    variable pivot = 99 ;
+    begin qs1 : if qlo < qhi
+                  then       call Partition(qlo, qhi) ;
+                       qs2 : pivot := returnVal ;
+                       qs3 : call QS2(qlo, pivot) ;
+                       qs4 : call QS2(pivot +1,qhi) ;
+                             return ;
+                  else    return;
+                end if;
+    end procedure
+  procedure  QS2(qlo2 = 99, qhi2 = 99)
+    variable pivot2 = 99 ;
+    begin 2qs1 : if qlo2 < qhi2
+                  then       call Partition(qlo2, qhi2) ;
+                       2qs2 : pivot2 := returnVal ;
+                       2qs3 : call QS(qlo2, pivot2) ;
+                       2qs4 : call QS(pivot2 +1,qhi2) ;
+                             return ;
+                  else    return;
+                end if;
+    end procedure
+  begin  main : call QS(1, Len(A)) ;
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-041626606cd9c50bd2700d7f84bf1f9c
+VARIABLES A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot, qlo2, qhi2, 
+          pivot2
+
+vars == << A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot, qlo2, qhi2, 
+           pivot2 >>
+
+Init == (* Global variables *)
+        /\ A \in [1..ArrayLen -> 1..ArrayLen]
+        /\ returnVal = 99
+        (* Procedure Partition *)
+        /\ lo = 99
+        /\ hi = 99
+        (* Procedure QS *)
+        /\ qlo = 99
+        /\ qhi = 99
+        /\ pivot = 99
+        (* Procedure QS2 *)
+        /\ qlo2 = 99
+        /\ qhi2 = 99
+        /\ pivot2 = 99
+        /\ stack = << >>
+        /\ pc = "main"
+
+pt1 == /\ pc = "pt1"
+       /\ \E piv \in lo..(hi-1):
+            /\ returnVal' = piv
+            /\ \E Ap \in {AA \in PermsOf(A) :
+                                (\A i \in 1..(lo-1) : AA[i] = A[i])
+                             /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                             /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                     AA[i] \leq AA[j])}:
+                 /\ A' = Ap
+                 /\ pc' = Head(stack).pc
+                 /\ lo' = Head(stack).lo
+                 /\ hi' = Head(stack).hi
+                 /\ stack' = Tail(stack)
+       /\ UNCHANGED << qlo, qhi, pivot, qlo2, qhi2, pivot2 >>
+
+Partition == pt1
+
+qs1 == /\ pc = "qs1"
+       /\ IF qlo < qhi
+             THEN /\ /\ hi' = qhi
+                     /\ lo' = qlo
+                     /\ stack' = << [ procedure |->  "Partition",
+                                      pc        |->  "qs2",
+                                      lo        |->  lo,
+                                      hi        |->  hi ] >>
+                                  \o stack
+                  /\ pc' = "pt1"
+                  /\ UNCHANGED << qlo, qhi, pivot >>
+             ELSE /\ pc' = Head(stack).pc
+                  /\ pivot' = Head(stack).pivot
+                  /\ qlo' = Head(stack).qlo
+                  /\ qhi' = Head(stack).qhi
+                  /\ stack' = Tail(stack)
+                  /\ UNCHANGED << lo, hi >>
+       /\ UNCHANGED << A, returnVal, qlo2, qhi2, pivot2 >>
+
+qs2 == /\ pc = "qs2"
+       /\ pivot' = returnVal
+       /\ pc' = "qs3"
+       /\ UNCHANGED << A, returnVal, stack, lo, hi, qlo, qhi, qlo2, qhi2, 
+                       pivot2 >>
+
+qs3 == /\ pc = "qs3"
+       /\ /\ qhi2' = pivot
+          /\ qlo2' = qlo
+          /\ stack' = << [ procedure |->  "QS2",
+                           pc        |->  "qs4",
+                           pivot2    |->  pivot2,
+                           qlo2      |->  qlo2,
+                           qhi2      |->  qhi2 ] >>
+                       \o stack
+       /\ pivot2' = 99
+       /\ pc' = "2qs1"
+       /\ UNCHANGED << A, returnVal, lo, hi, qlo, qhi, pivot >>
+
+qs4 == /\ pc = "qs4"
+       /\ /\ pivot' = Head(stack).pivot
+          /\ qhi2' = qhi
+          /\ qlo2' = pivot +1
+          /\ stack' = << [ procedure |->  "QS2",
+                           pc        |->  Head(stack).pc,
+                           pivot2    |->  pivot2,
+                           qlo2      |->  qlo2,
+                           qhi2      |->  qhi2 ] >>
+                       \o Tail(stack)
+       /\ pivot2' = 99
+       /\ pc' = "2qs1"
+       /\ UNCHANGED << A, returnVal, lo, hi, qlo, qhi >>
+
+QS == qs1 \/ qs2 \/ qs3 \/ qs4
+
+2qs1 == /\ pc = "2qs1"
+        /\ IF qlo2 < qhi2
+              THEN /\ /\ hi' = qhi2
+                      /\ lo' = qlo2
+                      /\ stack' = << [ procedure |->  "Partition",
+                                       pc        |->  "2qs2",
+                                       lo        |->  lo,
+                                       hi        |->  hi ] >>
+                                   \o stack
+                   /\ pc' = "pt1"
+                   /\ UNCHANGED << qlo2, qhi2, pivot2 >>
+              ELSE /\ pc' = Head(stack).pc
+                   /\ pivot2' = Head(stack).pivot2
+                   /\ qlo2' = Head(stack).qlo2
+                   /\ qhi2' = Head(stack).qhi2
+                   /\ stack' = Tail(stack)
+                   /\ UNCHANGED << lo, hi >>
+        /\ UNCHANGED << A, returnVal, qlo, qhi, pivot >>
+
+2qs2 == /\ pc = "2qs2"
+        /\ pivot2' = returnVal
+        /\ pc' = "2qs3"
+        /\ UNCHANGED << A, returnVal, stack, lo, hi, qlo, qhi, pivot, qlo2, 
+                        qhi2 >>
+
+2qs3 == /\ pc = "2qs3"
+        /\ /\ qhi' = pivot2
+           /\ qlo' = qlo2
+           /\ stack' = << [ procedure |->  "QS",
+                            pc        |->  "2qs4",
+                            pivot     |->  pivot,
+                            qlo       |->  qlo,
+                            qhi       |->  qhi ] >>
+                        \o stack
+        /\ pivot' = 99
+        /\ pc' = "qs1"
+        /\ UNCHANGED << A, returnVal, lo, hi, qlo2, qhi2, pivot2 >>
+
+2qs4 == /\ pc = "2qs4"
+        /\ /\ pivot2' = Head(stack).pivot2
+           /\ qhi' = qhi2
+           /\ qlo' = pivot2 +1
+           /\ stack' = << [ procedure |->  "QS",
+                            pc        |->  Head(stack).pc,
+                            pivot     |->  pivot,
+                            qlo       |->  qlo,
+                            qhi       |->  qhi ] >>
+                        \o Tail(stack)
+        /\ pivot' = 99
+        /\ pc' = "qs1"
+        /\ UNCHANGED << A, returnVal, lo, hi, qlo2, qhi2 >>
+
+QS2 == 2qs1 \/ 2qs2 \/ 2qs3 \/ 2qs4
+
+main == /\ pc = "main"
+        /\ /\ qhi' = Len(A)
+           /\ qlo' = 1
+           /\ stack' = << [ procedure |->  "QS",
+                            pc        |->  "Done",
+                            pivot     |->  pivot,
+                            qlo       |->  qlo,
+                            qhi       |->  qhi ] >>
+                        \o stack
+        /\ pivot' = 99
+        /\ pc' = "qs1"
+        /\ UNCHANGED << A, returnVal, lo, hi, qlo2, qhi2, pivot2 >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Partition \/ QS \/ QS2 \/ main
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-da193a603aa89ac5bf05f91d5ffaf922
+
+Invariant == 
+   (pc = "Done") => \A i, j \in 1..ArrayLen :
+                       (i < j) => A[i] \leq A[j]
+
+\* Tested on 28 June 2005 with arrayLen = 5 in
+\*  2 hours 9 min 6.5 seconds on SVC-Lamport-3
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/QuicksortMacro.cfg b/tlatools/test-model/pcal/QuicksortMacro.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0ebf8fe12d66c62cbd54cf80207efc9dda94587b
--- /dev/null
+++ b/tlatools/test-model/pcal/QuicksortMacro.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+CONSTANTS ArrayLen = 3
+INVARIANT Invariant
diff --git a/tlatools/test-model/pcal/QuicksortMacro.tla b/tlatools/test-model/pcal/QuicksortMacro.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ce284e9145de3934ad94bdee20943bbfe9b60476
--- /dev/null
+++ b/tlatools/test-model/pcal/QuicksortMacro.tla
@@ -0,0 +1,138 @@
+---------------------------- MODULE QuicksortMacro --------------------------- 
+EXTENDS Naturals, Sequences
+
+CONSTANT ArrayLen
+
+ASSUME ArrayLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   
+--algorithm QuicksortMacro
+  variables A \in [1..ArrayLen -> 1..ArrayLen];
+            returnVal = 1;
+  macro Partition(lo, hi)
+    begin      with piv \in lo..(hi-1)
+                  do returnVal := piv ;
+                     with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..(lo-1) : AA[i] = A[i])
+                          /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                          /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                  AA[i] \leq AA[j])}
+                        do A := Ap;
+                     end with ;
+                end with;
+    end macro
+  procedure  QS(qlo = 1, qhi = 1)
+    variable pivot = 1 ;
+    begin qs1 : if qlo < qhi
+                  then       Partition(qlo, qhi) ;
+                       qs2 : pivot := returnVal ;
+                       qs3 : call QS(qlo, pivot) ;
+                       qs4 : call QS(pivot +1,qhi) ;
+                             return ;
+                  else    return;
+                end if;
+    end procedure
+  begin  main : call QS(1, Len(A)) ;
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-f3c86b18c12d46dfb100d83f44bd15cc
+VARIABLES A, returnVal, pc, stack, qlo, qhi, pivot
+
+vars == << A, returnVal, pc, stack, qlo, qhi, pivot >>
+
+Init == (* Global variables *)
+        /\ A \in [1..ArrayLen -> 1..ArrayLen]
+        /\ returnVal = 1
+        (* Procedure QS *)
+        /\ qlo = 1
+        /\ qhi = 1
+        /\ pivot = 1
+        /\ stack = << >>
+        /\ pc = "main"
+
+qs1 == /\ pc = "qs1"
+       /\ IF qlo < qhi
+             THEN /\ \E piv \in qlo..(qhi-1):
+                       /\ returnVal' = piv
+                       /\ \E Ap \in {AA \in PermsOf(A) :
+                                           (\A i \in 1..(qlo-1) : AA[i] = A[i])
+                                        /\ (\A i \in (qhi+1)..Len(A) : AA[i] = A[i])
+                                        /\ (\A i \in qlo..piv, j \in (piv+1)..qhi :
+                                                AA[i] \leq AA[j])}:
+                            A' = Ap
+                  /\ pc' = "qs2"
+                  /\ UNCHANGED << stack, qlo, qhi, pivot >>
+             ELSE /\ pc' = Head(stack).pc
+                  /\ pivot' = Head(stack).pivot
+                  /\ qlo' = Head(stack).qlo
+                  /\ qhi' = Head(stack).qhi
+                  /\ stack' = Tail(stack)
+                  /\ UNCHANGED << A, returnVal >>
+
+qs2 == /\ pc = "qs2"
+       /\ pivot' = returnVal
+       /\ pc' = "qs3"
+       /\ UNCHANGED << A, returnVal, stack, qlo, qhi >>
+
+qs3 == /\ pc = "qs3"
+       /\ /\ qhi' = pivot
+          /\ qlo' = qlo
+          /\ stack' = << [ procedure |->  "QS",
+                           pc        |->  "qs4",
+                           pivot     |->  pivot,
+                           qlo       |->  qlo,
+                           qhi       |->  qhi ] >>
+                       \o stack
+       /\ pivot' = 1
+       /\ pc' = "qs1"
+       /\ UNCHANGED << A, returnVal >>
+
+qs4 == /\ pc = "qs4"
+       /\ /\ qhi' = qhi
+          /\ qlo' = pivot +1
+       /\ pivot' = 1
+       /\ pc' = "qs1"
+       /\ UNCHANGED << A, returnVal, stack >>
+
+QS == qs1 \/ qs2 \/ qs3 \/ qs4
+
+main == /\ pc = "main"
+        /\ /\ qhi' = Len(A)
+           /\ qlo' = 1
+           /\ stack' = << [ procedure |->  "QS",
+                            pc        |->  "Done",
+                            pivot     |->  pivot,
+                            qlo       |->  qlo,
+                            qhi       |->  qhi ] >>
+                        \o stack
+        /\ pivot' = 1
+        /\ pc' = "qs1"
+        /\ UNCHANGED << A, returnVal >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == QS \/ main
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-f36591cc8efd54110514a4b1f13d3500
+
+Invariant == 
+   (pc = "Done") => \A i, j \in 1..ArrayLen :
+                       (i < j) => A[i] \leq A[j]
+
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/RAB.cfg b/tlatools/test-model/pcal/RAB.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..31078b2505852cc4c10f20c6b1643aef28d53a24
--- /dev/null
+++ b/tlatools/test-model/pcal/RAB.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT Pid = { p0, p1 }
+INVARIANT Consistency
+
diff --git a/tlatools/test-model/pcal/RAB.tla b/tlatools/test-model/pcal/RAB.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4d920d96784b29b8da65505ca364931390bba4f3
--- /dev/null
+++ b/tlatools/test-model/pcal/RAB.tla
@@ -0,0 +1,208 @@
+------------------------------- MODULE RAB -------------------------------
+
+EXTENDS Naturals, Sequences, TLC
+
+
+(**************************************************************************)
+(* The Remoting Attributes bug.                                           *)
+(*                                                                        *)
+(*                                                                        *)
+(* Simplified version of the c program (comment in the original):         *)
+(*                                                                        *)
+(*                                                                        *)
+(*    class Attributes {                                                  *)
+(*        int flags = 0;                                                  *)
+(*                                                                        *)
+(*        boolean CalcA () { ... }                                        *)
+(*        boolean CalcB () { ... }                                        *)
+(*                                                                        *)
+(*        // We are not protecting against a race.                        *)
+(*        // If there is a race while setting flags we                    *)
+(*        // will have to compute the result again, but                   *)
+(*        // we will always return the correct result.                    *)
+(*                                                                        *)
+(*        boolean isA () {                                                *)
+(*            if ((flags & 0x02) == 0) {                                  *)
+(*                int temp = (CalcA() ? 0x03 : 0x02);                     *)
+(*                flags |= temp;                                          *)
+(*            }                                                           *)
+(*            return (flags & 0x01) != 0;                                 *)
+(*        }                                                               *)
+(*                                                                        *)
+(*        boolean isB () {                                                *)
+(*            if ((flags & 0x20) == 0) {                                  *)
+(*                int temp = (CalcB() ? 0x30 : 0x20);                     *)
+(*                flags |= temp;                                          *)
+(*            }                                                           *)
+(*            return (flags & 0x10) != 0;                                 *)
+(*        }                                                               *)
+(*    }                                                                   *)
+(*                                                                        *)
+(*                                                                        *)
+(* CalcA and CalcB are assumed to be functions that each always returns   *)
+(* the same value; we just do not know what value that is.  We model this *)
+(* using an "oracle" global variable named calc.                          *)
+(*                                                                        *)
+(* We model the environment as a set of processes Pid.  Each process runs *)
+(* in a loop, selecting a random attribute ("A" or "B") to access on each *)
+(* iteration.                                                             *)
+(*                                                                        *)
+(**************************************************************************)
+
+CONSTANT Pid        \* set of process ids
+
+Attr == { "A", "B" }         \* what are the attributes
+Boolean == { FALSE, TRUE }
+
+Flags == [ Attr -> [ valid: Boolean, value: Boolean ]]
+
+
+
+(**************************************************************************)
+(* How to compute the "bitwise or" of two Flags.                          *)
+(**************************************************************************)
+f | g == [ a \in DOMAIN f |-> [ n \in DOMAIN f[a] |-> f[a][n] \/ g[a][n] ]]
+
+
+
+
+
+
+
+(*--algorithm rab
+
+    variables
+        (****************************************************************)
+        (* Global variable containing flags for all attributes.   The   *)
+        (* initial state has all valid and value bits as FALSE.         *)
+        (****************************************************************)
+        flags = [ a \in Attr |-> [ valid |-> FALSE, value |-> FALSE ]];
+
+
+        (****************************************************************)
+        (* Oracle that says what the value is for each attribute.       *)
+        (* Technically this is a variable, but we never change it.      *)
+        (****************************************************************)
+        calc \in [ Attr -> { FALSE, TRUE } ];
+
+
+
+
+    process work \in Pid
+        variables
+            (************************************************************)
+            (* Arbitrary initial values of the correct type.            *)
+            (************************************************************)
+            temp = CHOOSE f \in Flags : TRUE;
+            myattr = CHOOSE a \in Attr : TRUE;
+    begin
+      Loop:
+        while TRUE do
+            (************************************************************)
+            (* Choose an attribute to access.                           *)
+            (************************************************************)
+            with attr \in Attr do myattr := attr; end with;
+
+            if \lnot flags[myattr].valid then
+                (********************************************************)
+                (* My component of the global flags variable is not     *)
+                (* valid.   Compute the temporary.                      *)
+                (********************************************************)
+                temp :=
+                [ a \in Attr |->
+                    IF a = myattr
+                    THEN [ valid |-> TRUE, value |-> calc[myattr] ]
+                    ELSE [ valid |-> FALSE, value |-> FALSE ]
+                ];
+
+              FetchFlags:
+                (********************************************************)
+                (* Fetch the global flags variable and "bitwise or" it  *)
+                (* into the temporary.                                  *)
+                (********************************************************)
+                temp := flags | temp;
+
+              StoreFlags:
+                (********************************************************)
+                (* Store the temporary back into the global flags       *)
+                (* variable.                                            *)
+                (********************************************************)
+	
+                flags := temp;
+            end if;
+
+          ReadFlags:
+            (************************************************************)
+            (* Read my component of the global flags variable.  It is   *)
+            (* supposed to be consistent with the oracle.               *)
+            (************************************************************)
+            \* assert flags[myattr].value = calc[myattr];
+            skip;
+        end while;
+    end process;
+end algorithm
+
+*)    
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-36470f24ec099c641e8894d14dc1be7a
+VARIABLES flags, calc, pc, temp, myattr
+
+vars == << flags, calc, pc, temp, myattr >>
+
+ProcSet == (Pid)
+
+Init == (* Global variables *)
+        /\ flags = [ a \in Attr |-> [ valid |-> FALSE, value |-> FALSE ]]
+        /\ calc \in [ Attr -> { FALSE, TRUE } ]
+        (* Process work *)
+        /\ temp = [self \in Pid |-> CHOOSE f \in Flags : TRUE]
+        /\ myattr = [self \in Pid |-> CHOOSE a \in Attr : TRUE]
+        /\ pc = [self \in ProcSet |-> "Loop"]
+
+Loop(self) == /\ pc[self] = "Loop"
+              /\ \E attr \in Attr:
+                   myattr' = [myattr EXCEPT ![self] = attr]
+              /\ IF \lnot flags[myattr'[self]].valid
+                    THEN /\ temp' = [temp EXCEPT ![self] = [ a \in Attr |->
+                                                               IF a = myattr'[self]
+                                                               THEN [ valid |-> TRUE, value |-> calc[myattr'[self]] ]
+                                                               ELSE [ valid |-> FALSE, value |-> FALSE ]
+                                                           ]]
+                         /\ pc' = [pc EXCEPT ![self] = "FetchFlags"]
+                    ELSE /\ pc' = [pc EXCEPT ![self] = "ReadFlags"]
+                         /\ temp' = temp
+              /\ UNCHANGED << flags, calc >>
+
+ReadFlags(self) == /\ pc[self] = "ReadFlags"
+                   /\ TRUE
+                   /\ pc' = [pc EXCEPT ![self] = "Loop"]
+                   /\ UNCHANGED << flags, calc, temp, myattr >>
+
+FetchFlags(self) == /\ pc[self] = "FetchFlags"
+                    /\ temp' = [temp EXCEPT ![self] = flags | temp[self]]
+                    /\ pc' = [pc EXCEPT ![self] = "StoreFlags"]
+                    /\ UNCHANGED << flags, calc, myattr >>
+
+StoreFlags(self) == /\ pc[self] = "StoreFlags"
+                    /\ flags' = temp[self]
+                    /\ pc' = [pc EXCEPT ![self] = "ReadFlags"]
+                    /\ UNCHANGED << calc, temp, myattr >>
+
+work(self) == Loop(self) \/ ReadFlags(self) \/ FetchFlags(self)
+                 \/ StoreFlags(self)
+
+Next == (\E self \in Pid: work(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-e0d61e0fb8a75ee2eb592e9a5b0f5f46
+
+
+Consistency ==
+    \A self \in ProcSet :
+    pc[self] = "ReadFlags" =>
+        flags[myattr[self]].value = calc[myattr[self]]
+
+
+============================================================================
diff --git a/tlatools/test-model/pcal/RealQuicksort.cfg b/tlatools/test-model/pcal/RealQuicksort.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..105b80b88f83103a719c8d296f999f4d9a8c580a
--- /dev/null
+++ b/tlatools/test-model/pcal/RealQuicksort.cfg
@@ -0,0 +1,7 @@
+SPECIFICATION Spec
+PROPERTY Termination
+CONSTANT defaultInitValue = defaultInitValue
+\* Add statements after this line.
+CONSTANTS MaxLen = 3
+INVARIANT Invariant
+
diff --git a/tlatools/test-model/pcal/RealQuicksort.tla b/tlatools/test-model/pcal/RealQuicksort.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3392236c96d5d5d470f179d399623a8be2a63dd8
--- /dev/null
+++ b/tlatools/test-model/pcal/RealQuicksort.tla
@@ -0,0 +1,124 @@
+----------------------------- MODULE RealQuicksort --------------------------- 
+EXTENDS Naturals, Sequences, FiniteSets
+
+CONSTANT MaxLen
+
+ASSUME MaxLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+Min(S) == CHOOSE i \in S : \A j \in S : i \leq j
+Max(S) == CHOOSE i \in S : \A j \in S : i \geq j
+
+(*   
+
+--algorithm RealQuicksort
+  variables A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen};
+            Uns = {1..Len(A)} ;
+            new = {} ;
+  procedure Part(parg)
+    begin pt1 : with new1 \in
+                      {Min(parg)..piv : piv \in parg \ {Max(parg)}}  do
+                with new2 = parg \ new1      do
+                new := {new1, new2} ;
+                with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..Len(A) \ parg : AA[i] = A[i])
+                          /\ (\A i \in new1, j \in new2 :
+                                  AA[i] \leq AA[j])}  do
+                A := Ap;
+                return ;
+                end with ;
+                end with ;
+                end with;
+    end procedure
+  begin rqs : while Uns # {}
+               do with next \in Uns
+                   do Uns := Uns \ {next};
+                      if Cardinality(next) > 1
+                        then call Part(next) ;
+                        else new := {} ;
+                      end if ;
+                  end with ;
+                  rqs2 : Uns := Uns \cup new ;
+              end while;
+  end algorithm
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1f05a46c32a9d2bb1934b39aad724eed
+CONSTANT defaultInitValue
+VARIABLES A, Uns, new, pc, stack, parg
+
+vars == << A, Uns, new, pc, stack, parg >>
+
+Init == (* Global variables *)
+        /\ A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen}
+        /\ Uns = {1..Len(A)}
+        /\ new = {}
+        (* Procedure Part *)
+        /\ parg = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "rqs"
+
+pt1 == /\ pc = "pt1"
+       /\ \E new1 \in {Min(parg)..piv : piv \in parg \ {Max(parg)}}:
+            LET new2 == parg \ new1 IN
+              /\ new' = {new1, new2}
+              /\ \E Ap \in {AA \in PermsOf(A) :
+                                  (\A i \in 1..Len(A) \ parg : AA[i] = A[i])
+                               /\ (\A i \in new1, j \in new2 :
+                                       AA[i] \leq AA[j])}:
+                   /\ A' = Ap
+                   /\ pc' = Head(stack).pc
+                   /\ parg' = Head(stack).parg
+                   /\ stack' = Tail(stack)
+       /\ Uns' = Uns
+
+Part == pt1
+
+rqs == /\ pc = "rqs"
+       /\ IF Uns # {}
+             THEN /\ \E next \in Uns:
+                       /\ Uns' = Uns \ {next}
+                       /\ IF Cardinality(next) > 1
+                             THEN /\ /\ parg' = next
+                                     /\ stack' = << [ procedure |->  "Part",
+                                                      pc        |->  "rqs2",
+                                                      parg      |->  parg ] >>
+                                                  \o stack
+                                  /\ pc' = "pt1"
+                                  /\ new' = new
+                             ELSE /\ new' = {}
+                                  /\ pc' = "rqs2"
+                                  /\ UNCHANGED << stack, parg >>
+             ELSE /\ pc' = "Done"
+                  /\ UNCHANGED << Uns, new, stack, parg >>
+       /\ A' = A
+
+rqs2 == /\ pc = "rqs2"
+        /\ Uns' = (Uns \cup new)
+        /\ pc' = "rqs"
+        /\ UNCHANGED << A, new, stack, parg >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Part \/ rqs \/ rqs2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-4de5033005d037379ed702fb72c5f705
+
+Invariant == 
+   (pc = "Done") => \A i, j \in 1..Len(A) :
+                       (i < j) => A[i] \leq A[j]
+=============================================================================
diff --git a/tlatools/test-model/pcal/RealQuicksort2.cfg b/tlatools/test-model/pcal/RealQuicksort2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..11946ee160e9c567089c7a04b4e059fdf484a7a1
--- /dev/null
+++ b/tlatools/test-model/pcal/RealQuicksort2.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+PROPERTY Termination
+CONSTANT defaultInitValue = defaultInitValue
+\* Add statements after this line.
+CONSTANTS MaxLen = 3
+INVARIANT Invariant
diff --git a/tlatools/test-model/pcal/RealQuicksort2.tla b/tlatools/test-model/pcal/RealQuicksort2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c30fb7d371fe3cdf540e8fcef096b352600abd20
--- /dev/null
+++ b/tlatools/test-model/pcal/RealQuicksort2.tla
@@ -0,0 +1,124 @@
+---------------------------- MODULE RealQuicksort2 -------------------------- 
+EXTENDS Naturals, Sequences, FiniteSets
+
+CONSTANT MaxLen
+
+ASSUME MaxLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+Min(S) == CHOOSE i \in S : \A j \in S : i \leq j
+Max(S) == CHOOSE i \in S : \A j \in S : i \geq j
+
+(*   
+
+--algorithm RealQuicksort2
+  variables A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen};
+            Uns = {1..Len(A)} ;
+            new = {} ;
+            next = {} ;
+  procedure Part(parg)
+    begin pt1 : with new1 \in
+                      {Min(parg)..piv : piv \in parg \ {Max(parg)}}  do
+                with new2 = parg \ new1      do
+                new := {new1, new2} ;
+                with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..Len(A) \ parg : AA[i] = A[i])
+                          /\ (\A i \in new1, j \in new2 :
+                                  AA[i] \leq AA[j])}  do
+                A := Ap;
+                return ;
+                end with ;
+                end with ;
+                end with;
+    end procedure;
+  begin rqs : while Uns # {}
+               do with nxt \in Uns do next := nxt ;
+                  end with ;
+                  Uns := Uns \ {next};
+                  if Cardinality(next) > 1
+                    then        call Part(next) ;
+                         rqs2 : Uns := Uns \cup new ;
+                  end if ;
+              end while;
+  end algorithm
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-23d3f8a2c70b06b61c3d312c6981bc33
+CONSTANT defaultInitValue
+VARIABLES A, Uns, new, next, pc, stack, parg
+
+vars == << A, Uns, new, next, pc, stack, parg >>
+
+Init == (* Global variables *)
+        /\ A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen}
+        /\ Uns = {1..Len(A)}
+        /\ new = {}
+        /\ next = {}
+        (* Procedure Part *)
+        /\ parg = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "rqs"
+
+pt1 == /\ pc = "pt1"
+       /\ \E new1 \in {Min(parg)..piv : piv \in parg \ {Max(parg)}}:
+            LET new2 == parg \ new1 IN
+              /\ new' = {new1, new2}
+              /\ \E Ap \in {AA \in PermsOf(A) :
+                                  (\A i \in 1..Len(A) \ parg : AA[i] = A[i])
+                               /\ (\A i \in new1, j \in new2 :
+                                       AA[i] \leq AA[j])}:
+                   /\ A' = Ap
+                   /\ pc' = Head(stack).pc
+                   /\ parg' = Head(stack).parg
+                   /\ stack' = Tail(stack)
+       /\ UNCHANGED << Uns, next >>
+
+Part == pt1
+
+rqs == /\ pc = "rqs"
+       /\ IF Uns # {}
+             THEN /\ \E nxt \in Uns:
+                       next' = nxt
+                  /\ Uns' = Uns \ {next'}
+                  /\ IF Cardinality(next') > 1
+                        THEN /\ /\ parg' = next'
+                                /\ stack' = << [ procedure |->  "Part",
+                                                 pc        |->  "rqs2",
+                                                 parg      |->  parg ] >>
+                                             \o stack
+                             /\ pc' = "pt1"
+                        ELSE /\ pc' = "rqs"
+                             /\ UNCHANGED << stack, parg >>
+             ELSE /\ pc' = "Done"
+                  /\ UNCHANGED << Uns, next, stack, parg >>
+       /\ UNCHANGED << A, new >>
+
+rqs2 == /\ pc = "rqs2"
+        /\ Uns' = (Uns \cup new)
+        /\ pc' = "rqs"
+        /\ UNCHANGED << A, new, next, stack, parg >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Part \/ rqs \/ rqs2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-86e50a32c6061aae8bbd3a4da8513951
+
+Invariant == 
+   (pc = "Done") => \A i, j \in 1..Len(A) :
+                       (i < j) => A[i] \leq A[j]
+=============================================================================
diff --git a/tlatools/test-model/pcal/ReallySimpleMultiProc.tla b/tlatools/test-model/pcal/ReallySimpleMultiProc.tla
new file mode 100644
index 0000000000000000000000000000000000000000..29501d33d7111b3f6840a6d11a65815e21105241
--- /dev/null
+++ b/tlatools/test-model/pcal/ReallySimpleMultiProc.tla
@@ -0,0 +1,89 @@
+--------------------------- MODULE ReallySimpleMultiProc -------------------------- 
+EXTENDS Naturals, TLC
+
+(*   
+--algorithm SimpleMultiProc                                             
+     variables                                                           
+       x = [i \in ProcSet |-> CASE i = 41 -> 1 []                         
+                                  i = 42 -> 2 []                         
+                                  i = 43 -> 3];                          
+       sum = 0 ;                                                         
+       done = {};                                                        
+     process ProcA = 41                                                  
+       variable y = 0;                                                   
+       begin a1 : sum := sum + y + x [ 41 ] ||                           
+                  done := done \cup { 41 } ;                             
+             a2 : when done = { 41, 42, 43 } ;                           
+                  when Print ( sum , TRUE ) ;                            
+       end process                                                       
+     process ProcB \in {42, 43}                                          
+       variable z \in {2, 3} ;                                           
+       begin b1 : sum := sum + z + x [ self ] ;                          
+             b2 : done := done \cup { self } ;                           
+       end process                                                       
+     end algorithm         
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-489e6572dfcfeaa9e2ac64d8c5013ef0
+VARIABLES x, sum, done, pc, y, z
+
+vars == << x, sum, done, pc, y, z >>
+
+ProcSet == {41} \cup ({42, 43})
+
+Init == (* Global variables *)
+        /\ x = [i \in ProcSet |-> CASE i = 41 -> 1 []
+                                      i = 42 -> 2 []
+                                      i = 43 -> 3]
+        /\ sum = 0
+        /\ done = {}
+        (* Process ProcA *)
+        /\ y = 0
+        (* Process ProcB *)
+        /\ z \in [{42, 43} -> {2, 3}]
+        /\ pc = [self \in ProcSet |-> CASE self = 41 -> "a1"
+                                        [] self \in {42, 43} -> "b1"]
+
+a1 == /\ pc[41] = "a1"
+      /\ /\ done' = (done \cup { 41 })
+         /\ sum' = sum + y + x [ 41 ]
+      /\ pc' = [pc EXCEPT ![41] = "a2"]
+      /\ UNCHANGED << x, y, z >>
+
+a2 == /\ pc[41] = "a2"
+      /\ done = { 41, 42, 43 }
+      /\ Print ( sum , TRUE )
+      /\ pc' = [pc EXCEPT ![41] = "Done"]
+      /\ UNCHANGED << x, sum, done, y, z >>
+
+ProcA == a1 \/ a2
+
+b1(self) == /\ pc[self] = "b1"
+            /\ sum' = sum + z[self] + x [ self ]
+            /\ pc' = [pc EXCEPT ![self] = "b2"]
+            /\ UNCHANGED << x, done, y, z >>
+
+b2(self) == /\ pc[self] = "b2"
+            /\ done' = (done \cup { self })
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+            /\ UNCHANGED << x, sum, y, z >>
+
+ProcB(self) == b1(self) \/ b2(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == ProcA
+           \/ (\E self \in {42, 43}: ProcB(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(ProcA)
+        /\ \A self \in {42, 43} : WF_vars(ProcB(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-9c6ea370932e887119161ccedba84a47
+=============================================================================
diff --git a/tlatools/test-model/pcal/SBB.cfg b/tlatools/test-model/pcal/SBB.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9acb2839f67c4a2f76f25b5185c0309d20896a7a
--- /dev/null
+++ b/tlatools/test-model/pcal/SBB.cfg
@@ -0,0 +1,11 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT Pid = { p0, p1 }
+CONSTANT NoPid = NoPid
+
+CONSTANT Buf = { b0, b1, b2, b3 }
+CONSTANT NoBuf = NoBuf
+
+INVARIANT Immutability
+
+CONSTRAINT Constraint
diff --git a/tlatools/test-model/pcal/SBB.tla b/tlatools/test-model/pcal/SBB.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9bb98be275aa2ee48342de98a3c649a054c5ab1f
--- /dev/null
+++ b/tlatools/test-model/pcal/SBB.tla
@@ -0,0 +1,154 @@
+------------------------------- MODULE SBB -------------------------------
+
+EXTENDS Naturals, Sequences, TLC
+
+
+(**************************************************************************)
+(* The StringBuilder bug.                                                 *)
+(**************************************************************************)
+
+CONSTANT Pid        \* set of process ids
+NoPid == CHOOSE p : p \notin Pid
+
+CONSTANT Buf        \* set of buffers
+NoBuf == CHOOSE b : b \notin Buf
+
+
+
+(*--algorithm sbb
+
+    variables
+        sb = [ owner |-> NoPid, buf |-> CHOOSE b \in Buf : TRUE ];
+        availablebuffers = Buf \ {sb.buf};
+        publishedbuffers = {};
+
+
+    process work \in Pid
+        variable
+            buf = NoBuf;
+	    op = {};
+    begin
+      Loop:
+        while TRUE do
+            with lop \in { "Publish", "Modify" } do
+                op := lop;
+            end with;
+
+	    if (op = "Publish") then
+                buf := sb.buf;		    
+              Publish1:
+                if sb.owner # self /\ sb.owner # NoPid then
+                    buf := CHOOSE b \in availablebuffers : TRUE;
+                    availablebuffers := availablebuffers \ {buf};
+                else
+                  Publish2:
+                    sb.owner := NoPid;
+                end if;
+              Publish3:
+                publishedbuffers := publishedbuffers \cup {buf};
+
+
+            else
+                buf := sb.buf;
+              Modify1:
+                if sb.owner # self then
+                    buf := CHOOSE b \in availablebuffers : TRUE;
+                    availablebuffers := availablebuffers \ {buf};
+                end if;
+              Modify2:
+              \* assert buf \notin publishedbuffers;
+                sb.owner := self;
+              Modify3:
+                sb.buf := buf;
+            end if
+        end while;
+    end process;
+end algorithm
+
+*)    
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-28938b6571490e0f82ac59c63d6f47e4
+VARIABLES sb, availablebuffers, publishedbuffers, pc, buf, op
+
+vars == << sb, availablebuffers, publishedbuffers, pc, buf, op >>
+
+ProcSet == (Pid)
+
+Init == (* Global variables *)
+        /\ sb = [ owner |-> NoPid, buf |-> CHOOSE b \in Buf : TRUE ]
+        /\ availablebuffers = Buf \ {sb.buf}
+        /\ publishedbuffers = {}
+        (* Process work *)
+        /\ buf = [self \in Pid |-> NoBuf]
+        /\ op = [self \in Pid |-> {}]
+        /\ pc = [self \in ProcSet |-> "Loop"]
+
+Loop(self) == /\ pc[self] = "Loop"
+              /\ \E lop \in { "Publish", "Modify" }:
+                   op' = [op EXCEPT ![self] = lop]
+              /\ IF (op'[self] = "Publish")
+                    THEN /\ buf' = [buf EXCEPT ![self] = sb.buf]
+                         /\ pc' = [pc EXCEPT ![self] = "Publish1"]
+                    ELSE /\ buf' = [buf EXCEPT ![self] = sb.buf]
+                         /\ pc' = [pc EXCEPT ![self] = "Modify1"]
+              /\ UNCHANGED << sb, availablebuffers, publishedbuffers >>
+
+Publish1(self) == /\ pc[self] = "Publish1"
+                  /\ IF sb.owner # self /\ sb.owner # NoPid
+                        THEN /\ buf' = [buf EXCEPT ![self] = CHOOSE b \in availablebuffers : TRUE]
+                             /\ availablebuffers' = availablebuffers \ {buf'[self]}
+                             /\ pc' = [pc EXCEPT ![self] = "Publish3"]
+                        ELSE /\ pc' = [pc EXCEPT ![self] = "Publish2"]
+                             /\ UNCHANGED << availablebuffers, buf >>
+                  /\ UNCHANGED << sb, publishedbuffers, op >>
+
+Publish2(self) == /\ pc[self] = "Publish2"
+                  /\ sb' = [sb EXCEPT !.owner = NoPid]
+                  /\ pc' = [pc EXCEPT ![self] = "Publish3"]
+                  /\ UNCHANGED << availablebuffers, publishedbuffers, buf, op >>
+
+Publish3(self) == /\ pc[self] = "Publish3"
+                  /\ publishedbuffers' = (publishedbuffers \cup {buf[self]})
+                  /\ pc' = [pc EXCEPT ![self] = "Loop"]
+                  /\ UNCHANGED << sb, availablebuffers, buf, op >>
+
+Modify1(self) == /\ pc[self] = "Modify1"
+                 /\ IF sb.owner # self
+                       THEN /\ buf' = [buf EXCEPT ![self] = CHOOSE b \in availablebuffers : TRUE]
+                            /\ availablebuffers' = availablebuffers \ {buf'[self]}
+                       ELSE /\ TRUE
+                            /\ UNCHANGED << availablebuffers, buf >>
+                 /\ pc' = [pc EXCEPT ![self] = "Modify2"]
+                 /\ UNCHANGED << sb, publishedbuffers, op >>
+
+Modify2(self) == /\ pc[self] = "Modify2"
+                 /\ sb' = [sb EXCEPT !.owner = self]
+                 /\ pc' = [pc EXCEPT ![self] = "Modify3"]
+                 /\ UNCHANGED << availablebuffers, publishedbuffers, buf, op >>
+
+Modify3(self) == /\ pc[self] = "Modify3"
+                 /\ sb' = [sb EXCEPT !.buf = buf[self]]
+                 /\ pc' = [pc EXCEPT ![self] = "Loop"]
+                 /\ UNCHANGED << availablebuffers, publishedbuffers, buf, op >>
+
+work(self) == Loop(self) \/ Publish1(self) \/ Publish2(self)
+                 \/ Publish3(self) \/ Modify1(self) \/ Modify2(self)
+                 \/ Modify3(self)
+
+Next == (\E self \in Pid: work(self))
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-0476e94d7836683a2a4a6e344c5a0a7b
+
+
+Immutability ==
+    \A self \in ProcSet :
+    pc[self] = "Modify2" => buf[self] \notin publishedbuffers
+
+Constraint ==
+    availablebuffers # {}
+
+
+============================================================================
diff --git a/tlatools/test-model/pcal/SemaphoreMutex.cfg b/tlatools/test-model/pcal/SemaphoreMutex.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..de1000468b848d4daa608674a58a8b347b759671
--- /dev/null
+++ b/tlatools/test-model/pcal/SemaphoreMutex.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT N = 3
+INVARIANT Invariant
+PROPERTY Liveness
diff --git a/tlatools/test-model/pcal/SemaphoreMutex.tla b/tlatools/test-model/pcal/SemaphoreMutex.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d7332cbc3dbd083329985041c4b9ac6930f91ff3
--- /dev/null
+++ b/tlatools/test-model/pcal/SemaphoreMutex.tla
@@ -0,0 +1,75 @@
+---------------------------- MODULE SemaphoreMutex --------------------------
+
+EXTENDS Naturals
+
+CONSTANT N
+
+ASSUME N \in Nat
+
+(**********************
+--algorithm SemaphoreMutex
+variables sem = 1 ; 
+macro P(s) begin when s > 0 ;
+                 s := s - 1 ;
+end macro
+
+macro V(s) begin s := s + 1 ;
+end macro
+
+process Proc \in 1..N
+begin
+start : while TRUE
+         do enter : P(sem) ;
+            cs    : skip ;
+            exit  : V(sem) ;
+        end while ;
+end process
+end algorithm
+
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-44c6b54854f894e836bedeb86826fe34
+VARIABLES sem, pc
+
+vars == << sem, pc >>
+
+ProcSet == (1..N)
+
+Init == (* Global variables *)
+        /\ sem = 1
+        /\ pc = [self \in ProcSet |-> "start"]
+
+start(self) == /\ pc[self] = "start"
+               /\ pc' = [pc EXCEPT ![self] = "enter"]
+               /\ sem' = sem
+
+enter(self) == /\ pc[self] = "enter"
+               /\ sem > 0
+               /\ sem' = sem - 1
+               /\ pc' = [pc EXCEPT ![self] = "cs"]
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "exit"]
+            /\ sem' = sem
+
+exit(self) == /\ pc[self] = "exit"
+              /\ sem' = sem + 1
+              /\ pc' = [pc EXCEPT ![self] = "start"]
+
+Proc(self) == start(self) \/ enter(self) \/ cs(self) \/ exit(self)
+
+Next == (\E self \in 1..N: Proc(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in 1..N : SF_vars(Proc(self))
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-30232ad9c07579ad742b84411a6a279a
+
+inCS(i) ==  (pc[i] = "cs") 
+
+Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k))
+
+Liveness == \A i \in 1..N : []<> inCS(i)
+=============================================================================
diff --git a/tlatools/test-model/pcal/SimpleLoop.tla b/tlatools/test-model/pcal/SimpleLoop.tla
new file mode 100644
index 0000000000000000000000000000000000000000..53e5993f1429a48993c45f292576e69c15f78048
--- /dev/null
+++ b/tlatools/test-model/pcal/SimpleLoop.tla
@@ -0,0 +1,46 @@
+----------------------------- MODULE SimpleLoop ----------------------------- 
+EXTENDS Naturals, TLC
+(*
+
+   --algorithm SimpleLoop                                                  
+     variable x = 0;                                                     
+     begin a : while x < 10                                              
+                 do x := x+1 ;                                           
+                    skip ;                                               
+                    assert x \in 1..10;                                             
+               end while ;                                               
+     end algorithm                                                         
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-33bbd786fd9a21815a80c1c8d918b5c0
+VARIABLES x, pc
+
+vars == << x, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ pc = "a"
+
+a == /\ pc = "a"
+     /\ IF x < 10
+           THEN /\ x' = x+1
+                /\ TRUE
+                /\ Assert(x' \in 1..10, 
+                          "Failure of assertion at line 10, column 21.")
+                /\ pc' = "a"
+           ELSE /\ pc' = "Done"
+                /\ x' = x
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == a
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-d8f87f4745585bbecc6137f522211233
+=============================================================================
diff --git a/tlatools/test-model/pcal/SimpleLoopWithProcedure.tla b/tlatools/test-model/pcal/SimpleLoopWithProcedure.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b69c62963c3830ea211473710cb900d28b19cd86
--- /dev/null
+++ b/tlatools/test-model/pcal/SimpleLoopWithProcedure.tla
@@ -0,0 +1,81 @@
+---------------------- MODULE SimpleLoopWithProcedure ----------------------
+EXTENDS Naturals, Sequences, TLC
+
+(*   
+
+--algorithm SimpleLoopWithProcedure                                     
+     variable x = 0; y \in {1, 2}; n = 0; i = 0;                         
+     procedure Incr(incr = 0)                                                
+      variable z = 2;                                                    
+      begin i1 : x := incr + z + x;                                      
+            i2 : return;                                                 
+     end procedure                                                       
+     begin a : while i < 10                                              
+                 do   when Print(x, TRUE);                               
+                      i := i + 1 ;                                       
+                      call Incr(y) ;                                     
+               end while ;                                               
+     end algorithm  
+
+*)
+
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-925d38ef233a8a8c33b34bb25ba0c30d
+VARIABLES x, y, n, i, pc, stack, incr, z
+
+vars == << x, y, n, i, pc, stack, incr, z >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y \in {1, 2}
+        /\ n = 0
+        /\ i = 0
+        (* Procedure Incr *)
+        /\ incr = 0
+        /\ z = 2
+        /\ stack = << >>
+        /\ pc = "a"
+
+i1 == /\ pc = "i1"
+      /\ x' = incr + z + x
+      /\ pc' = "i2"
+      /\ UNCHANGED << y, n, i, stack, incr, z >>
+
+i2 == /\ pc = "i2"
+      /\ pc' = Head(stack).pc
+      /\ z' = Head(stack).z
+      /\ incr' = Head(stack).incr
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << x, y, n, i >>
+
+Incr == i1 \/ i2
+
+a == /\ pc = "a"
+     /\ IF i < 10
+           THEN /\ Print(x, TRUE)
+                /\ i' = i + 1
+                /\ /\ incr' = y
+                   /\ stack' = << [ procedure |->  "Incr",
+                                    pc        |->  "a",
+                                    z         |->  z,
+                                    incr      |->  incr ] >>
+                                \o stack
+                /\ z' = 2
+                /\ pc' = "i1"
+           ELSE /\ pc' = "Done"
+                /\ UNCHANGED << i, stack, incr, z >>
+     /\ UNCHANGED << x, y, n >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Incr \/ a
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-1145b37773754e0c960b38596f8082a0
+=============================================================================
diff --git a/tlatools/test-model/pcal/SimpleMultiProc.tla b/tlatools/test-model/pcal/SimpleMultiProc.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5c2a3165b83729971a9a55114e1eb8b026fb52e0
--- /dev/null
+++ b/tlatools/test-model/pcal/SimpleMultiProc.tla
@@ -0,0 +1,162 @@
+--------------------------- MODULE SimpleMultiProc -------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(*   
+--algorithm SimpleMultiProc                                             
+     variables                                                           
+       x = [i \in ProcSet |-> CASE i = 41 -> 1 []                         
+                                   i = 42 -> 2 []                         
+                                   i = 43 -> 3 []                         
+                                   i = 44 -> 4 []                         
+                                   i = 45 -> 5];                          
+       sum = 0 ;                                                         
+       done = {};
+     procedure AddMe(me = 0)
+       variable y = 0;
+       begin am: done := done \cup { me } ;
+                 return ;
+       end procedure                                                         
+     process ProcA = 41                                                  
+       variable y = 0;                                                   
+       begin a1 : sum := sum + y + x [ 41 ] ||
+                  y := sum ;                           
+             a2 : call AddMe(41) ;                             
+             a3 : when done = { 41, 42, 43, 44, 45 } ; 
+       end process                                                       
+     process ProcB \in 42 .. 43                                          
+       variable z \in {2, 3} ;                                           
+       begin b1 : sum := sum + z + x [ self ] ;                          
+             b2 : call AddMe(self);                           
+       end process                                                       
+     process ProcC \in { 44,
+                         45 }                                          
+       variable z \in {4, 5} ;                                           
+       begin c1 : sum := sum + z + x [ self ] ;                          
+             c2 : call AddMe(self) ;                           
+       end process                                                       
+     end algorithm         
+
+*)
+					
+\* BEGIN TRANSLATION
+\* Process variable y of process ProcA at line 20 col 17 changed to y_
+\* Process variable z of process ProcB at line 27 col 17 changed to z_
+VARIABLES x, sum, done, pc, stack, me, y, y_, z_, z
+
+vars == << x, sum, done, pc, stack, me, y, y_, z_, z >>
+
+ProcSet == {41} \cup (42 .. 43) \cup ({ 44,
+                                        45 })
+
+Init == (* Global variables *)
+        /\ x = [i \in ProcSet |-> CASE i = 41 -> 1 []
+                                       i = 42 -> 2 []
+                                       i = 43 -> 3 []
+                                       i = 44 -> 4 []
+                                       i = 45 -> 5]
+        /\ sum = 0
+        /\ done = {}
+        (* Procedure AddMe *)
+        /\ me = [ self \in ProcSet |-> 0]
+        /\ y = [ self \in ProcSet |-> 0]
+        (* Process ProcA *)
+        /\ y_ = 0
+        (* Process ProcB *)
+        /\ z_ \in [42 .. 43 -> {2, 3}]
+        (* Process ProcC *)
+        /\ z \in [{ 44,
+                    45 } -> {4, 5}]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> CASE self = 41 -> "a1"
+                                        [] self \in 42 .. 43 -> "b1"
+                                        [] self \in { 44,
+                                                      45 } -> "c1"]
+
+am(self) == /\ pc[self] = "am"
+            /\ done' = (done \cup { me[self] })
+            /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+            /\ y' = [y EXCEPT ![self] = Head(stack[self]).y]
+            /\ me' = [me EXCEPT ![self] = Head(stack[self]).me]
+            /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+            /\ UNCHANGED << x, sum, y_, z_, z >>
+
+AddMe(self) == am(self)
+
+a1 == /\ pc[41] = "a1"
+      /\ /\ sum' = sum + y_ + x [ 41 ]
+         /\ y_' = sum
+      /\ pc' = [pc EXCEPT ![41] = "a2"]
+      /\ UNCHANGED << x, done, stack, me, y, z_, z >>
+
+a2 == /\ pc[41] = "a2"
+      /\ /\ me' = [me EXCEPT ![41] = 41]
+         /\ stack' = [stack EXCEPT ![41] = << [ procedure |->  "AddMe",
+                                                pc        |->  "a3",
+                                                y         |->  y[41],
+                                                me        |->  me[41] ] >>
+                                            \o stack[41]]
+      /\ y' = [y EXCEPT ![41] = 0]
+      /\ pc' = [pc EXCEPT ![41] = "am"]
+      /\ UNCHANGED << x, sum, done, y_, z_, z >>
+
+a3 == /\ pc[41] = "a3"
+      /\ done = { 41, 42, 43, 44, 45 }
+      /\ pc' = [pc EXCEPT ![41] = "Done"]
+      /\ UNCHANGED << x, sum, done, stack, me, y, y_, z_, z >>
+
+ProcA == a1 \/ a2 \/ a3
+
+b1(self) == /\ pc[self] = "b1"
+            /\ sum' = sum + z_[self] + x [ self ]
+            /\ pc' = [pc EXCEPT ![self] = "b2"]
+            /\ UNCHANGED << x, done, stack, me, y, y_, z_, z >>
+
+b2(self) == /\ pc[self] = "b2"
+            /\ /\ me' = [me EXCEPT ![self] = self]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "AddMe",
+                                                        pc        |->  "Done",
+                                                        y         |->  y[self],
+                                                        me        |->  me[self] ] >>
+                                                    \o stack[self]]
+            /\ y' = [y EXCEPT ![self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "am"]
+            /\ UNCHANGED << x, sum, done, y_, z_, z >>
+
+ProcB(self) == b1(self) \/ b2(self)
+
+c1(self) == /\ pc[self] = "c1"
+            /\ sum' = sum + z[self] + x [ self ]
+            /\ pc' = [pc EXCEPT ![self] = "c2"]
+            /\ UNCHANGED << x, done, stack, me, y, y_, z_, z >>
+
+c2(self) == /\ pc[self] = "c2"
+            /\ /\ me' = [me EXCEPT ![self] = self]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "AddMe",
+                                                        pc        |->  "Done",
+                                                        y         |->  y[self],
+                                                        me        |->  me[self] ] >>
+                                                    \o stack[self]]
+            /\ y' = [y EXCEPT ![self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "am"]
+            /\ UNCHANGED << x, sum, done, y_, z_, z >>
+
+ProcC(self) == c1(self) \/ c2(self)
+
+Next == ProcA
+           \/ (\E self \in ProcSet: AddMe(self))
+           \/ (\E self \in 42 .. 43: ProcB(self))
+           \/ (\E self \in { 44,
+                             45 }: ProcC(self))
+           \/ (* Disjunct to prevent deadlock on termination *)
+              ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(ProcA) /\ WF_vars(AddMe(41))
+        /\ \A self \in 42 .. 43 : WF_vars(ProcB(self)) /\ WF_vars(AddMe(self))
+        /\ \A self \in { 44,
+                         45 } : WF_vars(ProcC(self)) /\ WF_vars(AddMe(self))
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION
+=============================================================================
diff --git a/tlatools/test-model/pcal/StackTest.tla b/tlatools/test-model/pcal/StackTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..85250e43848213aa4222f403709e0cea9370df27
--- /dev/null
+++ b/tlatools/test-model/pcal/StackTest.tla
@@ -0,0 +1,73 @@
+Bug: Currently putting an extra `self' subscript in assignment to
+     `stack'.
+
+
+
+----------- MODULE StackTest -----------
+EXTENDS Sequences, Naturals, TLC
+
+(***************************************************************************
+--algorithm StackAndPCTest
+   procedure P(a=42)
+      begin P1: stack[self][1].a := stack[self][1].a + 1;
+                assert Head(stack[self]).a = 43 ;
+                assert pc[self] = "P1" ;
+            P2: return
+      end procedure
+   process Q \in 0..2
+     begin Q1: assert \A i \in ProcSet : pc[i] \in {"P1", "P2", "Q1", "Done"};
+               call P(22) ;
+     end process;
+   end algorithm
+ ***************************************************************************)
+\* BEGIN TRANSLATION
+VARIABLES pc, stack, a
+
+vars == << pc, stack, a >>
+
+ProcSet == (0..2)
+
+Init == (* Procedure P *)
+        /\ a = [ self \in ProcSet |-> 42]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> "Q1"]
+
+P1(self) == /\ pc[self] = "P1"
+            /\ stack' = [stack EXCEPT ![self][self][1].a = stack[self][1].a + 1]
+            /\ Assert(Head(stack'[self]).a = 43, 
+                      "Failure of assertion at line 13, column 17.")
+            /\ Assert(pc[self] = "P1", 
+                      "Failure of assertion at line 14, column 17.")
+            /\ pc' = [pc EXCEPT ![self] = "P2"]
+            /\ a' = a
+
+P2(self) == /\ pc[self] = "P2"
+            /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+            /\ a' = [a EXCEPT ![self] = Head(stack[self]).a]
+            /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+
+P(self) == P1(self) \/ P2(self)
+
+Q1(self) == /\ pc[self] = "Q1"
+            /\ Assert(\A i \in ProcSet : pc[i] \in {"P1", "P2", "Q1", "Done"}, 
+                      "Failure of assertion at line 18, column 16.")
+            /\ /\ a' = [a EXCEPT ![self] = 22]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "P",
+                                                        pc        |->  "Done",
+                                                        a         |->  a[self] ] >>
+                                                    \o stack[self]]
+            /\ pc' = [pc EXCEPT ![self] = "P1"]
+
+Q(self) == Q1(self)
+
+Next == (\E self \in ProcSet: P(self))
+           \/ (\E self \in 0..2: Q(self))
+           \/ (* Disjunct to prevent deadlock on termination *)
+              ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION
+========================================
diff --git a/tlatools/test-model/pcal/StarkMutex.cfg b/tlatools/test-model/pcal/StarkMutex.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8459e52f22b00841a0b8d2bdaff8bd8581e93e04
--- /dev/null
+++ b/tlatools/test-model/pcal/StarkMutex.cfg
@@ -0,0 +1,5 @@
+\* Add statements after this line.
+CONSTANT      N = 3
+SPECIFICATION StarkSpec \* A comment
+INVARIANT     Mutex
+PROPERTY      StarvationFree
diff --git a/tlatools/test-model/pcal/StarkMutex.tla b/tlatools/test-model/pcal/StarkMutex.tla
new file mode 100644
index 0000000000000000000000000000000000000000..09087d4ff76786afa174bc584157f49325f587be
--- /dev/null
+++ b/tlatools/test-model/pcal/StarkMutex.tla
@@ -0,0 +1,208 @@
+--algorithm StarkMutex
+  variables flag = [i \in 1..N |-> FALSE]; 
+            next = 1;
+            empty = TRUE;
+            mutex = 1;
+            weakSem = [i \in 1..N |-> 0];
+  process i \in 1..N
+    variables first = FALSE;
+              j = 1;
+    begin
+    in1: while (TRUE) do
+         flag[self] := TRUE;
+         first := FALSE;
+    in2: when mutex = 1; mutex := 0;
+    in3: if empty then
+    in4:    empty := FALSE;
+            first := TRUE;
+            end if;
+    in5:    mutex := 1;
+    in6:    if ~ first then
+               when weakSem[self] = 1; weakSem[self] := 0;
+             end if;
+    cs:     skip;
+    ex1:    flag[self] := FALSE;
+    ex2:    when mutex = 1; mutex := 0;
+    ex3:    j := 1;
+            empty := TRUE;
+    ex4:    while j \leq N do
+               if flag[IF next + j > N THEN next + j - N ELSE next + j] then
+    ex5:          with n = IF next + j > N THEN next + j - N ELSE next + j do
+                     next := n;
+                     weakSem[n] := 1;
+                     j := N + 1;
+                     end with;
+    ex6:          empty := FALSE;
+               else
+    ex7:          j := j + 1;
+               end if;
+            end while;
+    ex8:    mutex := 1;
+    nc:     skip;
+            end while;
+    end process
+  end algorithm
+
+
+
+------------------ MODULE StarkMutex ------------------
+
+EXTENDS Naturals, Sequences
+CONSTANT N
+
+-------------------------------------------------------
+
+
+
+\* BEGIN TRANSLATION
+VARIABLES flag, next, empty, mutex, weakSem, pc, first, j
+
+vars == << flag, next, empty, mutex, weakSem, pc, first, j >>
+
+ProcSet == (1..N)
+
+Init == (* Global variables *)
+        /\ flag = [i \in 1..N |-> FALSE]
+        /\ next = 1
+        /\ empty = TRUE
+        /\ mutex = 1
+        /\ weakSem = [i \in 1..N |-> 0]
+        (* Process i *)
+        /\ first = [self \in 1..N |-> FALSE]
+        /\ j = [self \in 1..N |-> 1]
+        /\ pc = [self \in ProcSet |-> "in1"]
+
+in1(self) == /\ pc[self] = "in1"
+             /\ flag' = [flag EXCEPT ![self] = TRUE]
+             /\ first' = [first EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "in2"]
+             /\ UNCHANGED << next, empty, mutex, weakSem, j >>
+
+in2(self) == /\ pc[self] = "in2"
+             /\ mutex = 1
+             /\ mutex' = 0
+             /\ pc' = [pc EXCEPT ![self] = "in3"]
+             /\ UNCHANGED << flag, next, empty, weakSem, first, j >>
+
+in3(self) == /\ pc[self] = "in3"
+             /\ IF empty
+                   THEN /\ pc' = [pc EXCEPT ![self] = "in4"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "in5"]
+             /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >>
+
+in4(self) == /\ pc[self] = "in4"
+             /\ empty' = FALSE
+             /\ first' = [first EXCEPT ![self] = TRUE]
+             /\ pc' = [pc EXCEPT ![self] = "in5"]
+             /\ UNCHANGED << flag, next, mutex, weakSem, j >>
+
+in5(self) == /\ pc[self] = "in5"
+             /\ mutex' = 1
+             /\ pc' = [pc EXCEPT ![self] = "in6"]
+             /\ UNCHANGED << flag, next, empty, weakSem, first, j >>
+
+in6(self) == /\ pc[self] = "in6"
+             /\ IF ~ first[self]
+                   THEN /\ weakSem[self] = 1
+                        /\ weakSem' = [weakSem EXCEPT ![self] = 0]
+                   ELSE /\ TRUE
+                        /\ UNCHANGED weakSem
+             /\ pc' = [pc EXCEPT ![self] = "cs"]
+             /\ UNCHANGED << flag, next, empty, mutex, first, j >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "ex1"]
+            /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >>
+
+ex1(self) == /\ pc[self] = "ex1"
+             /\ flag' = [flag EXCEPT ![self] = FALSE]
+             /\ pc' = [pc EXCEPT ![self] = "ex2"]
+             /\ UNCHANGED << next, empty, mutex, weakSem, first, j >>
+
+ex2(self) == /\ pc[self] = "ex2"
+             /\ mutex = 1
+             /\ mutex' = 0
+             /\ pc' = [pc EXCEPT ![self] = "ex3"]
+             /\ UNCHANGED << flag, next, empty, weakSem, first, j >>
+
+ex3(self) == /\ pc[self] = "ex3"
+             /\ j' = [j EXCEPT ![self] = 1]
+             /\ empty' = TRUE
+             /\ pc' = [pc EXCEPT ![self] = "ex4"]
+             /\ UNCHANGED << flag, next, mutex, weakSem, first >>
+
+ex4(self) == /\ pc[self] = "ex4"
+             /\ IF j[self] \leq N
+                   THEN /\ IF flag[IF next + j[self] > N THEN next + j[self] - N ELSE next + j[self]]
+                              THEN /\ pc' = [pc EXCEPT ![self] = "ex5"]
+                              ELSE /\ pc' = [pc EXCEPT ![self] = "ex7"]
+                   ELSE /\ pc' = [pc EXCEPT ![self] = "ex8"]
+             /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >>
+
+ex5(self) == /\ pc[self] = "ex5"
+             /\ LET n == IF next + j[self] > N THEN next + j[self] - N ELSE next + j[self] IN
+                  /\ next' = n
+                  /\ weakSem' = [weakSem EXCEPT ![n] = 1]
+                  /\ j' = [j EXCEPT ![self] = N + 1]
+             /\ pc' = [pc EXCEPT ![self] = "ex6"]
+             /\ UNCHANGED << flag, empty, mutex, first >>
+
+ex6(self) == /\ pc[self] = "ex6"
+             /\ empty' = FALSE
+             /\ pc' = [pc EXCEPT ![self] = "ex4"]
+             /\ UNCHANGED << flag, next, mutex, weakSem, first, j >>
+
+ex7(self) == /\ pc[self] = "ex7"
+             /\ j' = [j EXCEPT ![self] = j[self] + 1]
+             /\ pc' = [pc EXCEPT ![self] = "ex4"]
+             /\ UNCHANGED << flag, next, empty, mutex, weakSem, first >>
+
+ex8(self) == /\ pc[self] = "ex8"
+             /\ mutex' = 1
+             /\ pc' = [pc EXCEPT ![self] = "nc"]
+             /\ UNCHANGED << flag, next, empty, weakSem, first, j >>
+
+nc(self) == /\ pc[self] = "nc"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "in1"]
+            /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >>
+
+i(self) == in1(self) \/ in2(self) \/ in3(self) \/ in4(self) \/ in5(self)
+              \/ in6(self) \/ cs(self) \/ ex1(self) \/ ex2(self)
+              \/ ex3(self) \/ ex4(self) \/ ex5(self) \/ ex6(self)
+              \/ ex7(self) \/ ex8(self) \/ nc(self)
+
+Next == (\E self \in 1..N: i(self))
+           \/ (* Disjunct to prevent deadlock on termination *)
+              ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION
+
+StarkSpec == /\ Spec
+             /\ \A self \in ProcSet:
+                /\ WF_vars(in1(self))
+                /\ WF_vars(in2(self))
+                /\ WF_vars(in3(self))
+                /\ WF_vars(in4(self))
+                /\ WF_vars(in5(self))
+                /\ WF_vars(in6(self))
+                /\ WF_vars(ex1(self))
+                /\ WF_vars(ex2(self))
+                /\ WF_vars(ex3(self))
+                /\ WF_vars(ex4(self))
+                /\ WF_vars(ex5(self))
+                /\ WF_vars(ex6(self))
+                /\ WF_vars(ex7(self))
+                /\ WF_vars(ex8(self))
+                /\ WF_vars(cs(self))
+
+Mutex == \A p1, p2 \in ProcSet: (pc[p1] = "cs" /\ pc[p2] = "pc") => (p1 = p2)
+
+StarvationFree == \A p \in ProcSet: pc[p] = "in1" ~> pc[p] = "cs"
+=======================================================
+
diff --git a/tlatools/test-model/pcal/SubSub.tla b/tlatools/test-model/pcal/SubSub.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fff5f6a594b5b94f0dbb7e5a664cce38951d4d17
--- /dev/null
+++ b/tlatools/test-model/pcal/SubSub.tla
@@ -0,0 +1,70 @@
+Test that "[self]" subscripts are added to expressions within subscripts.
+
+------------------------- MODULE SubSub ----------------------------
+EXTENDS Naturals, TLC
+
+ServerID == {1}
+ObjectID == {1}
+
+--------------------------------------------------------------------------
+(*********
+--algorithm SubSub
+  process proc \in 1..3
+   variables x = [i \in {"a", "b"} |-> 0] ,
+             y = [i \in 5..6 |-> "a"] ,
+             z
+   begin
+     lab : z := 5 ;
+           y[z] := "b" ;
+           x[y[z]] := 1 ;
+           assert x[y[z]] = 1 ;
+           assert y[z] = "b"
+   end process
+
+end algorithm
+*****)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-e4495880aa3211ed2f4c6f44d3918dc7
+CONSTANT defaultInitValue
+VARIABLES pc, x, y, z
+
+vars == << pc, x, y, z >>
+
+ProcSet == (1..3)
+
+Init == (* Process proc *)
+        /\ x = [self \in 1..3 |-> [i \in {"a", "b"} |-> 0]]
+        /\ y = [self \in 1..3 |-> [i \in 5..6 |-> "a"]]
+        /\ z = [self \in 1..3 |-> defaultInitValue]
+        /\ pc = [self \in ProcSet |-> "lab"]
+
+lab(self) == /\ pc[self] = "lab"
+             /\ z' = [z EXCEPT ![self] = 5]
+             /\ y' = [y EXCEPT ![self][z'[self]] = "b"]
+             /\ x' = [x EXCEPT ![self][y'[self][z'[self]]] = 1]
+             /\ Assert(x'[self][y'[self][z'[self]]] = 1, 
+                       "Failure of assertion at line 20, column 12.")
+             /\ Assert(y'[self][z'[self]] = "b", 
+                       "Failure of assertion at line 21, column 12.")
+             /\ pc' = [pc EXCEPT ![self] = "Done"]
+
+proc(self) == lab(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in 1..3: proc(self))
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-4eda2626bbd263ed274c0990ecc17263
+
+--------------------------------------------------------------------------
+
+
+
+==========================================================================
diff --git a/tlatools/test-model/pcal/SyncCons.cfg b/tlatools/test-model/pcal/SyncCons.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a0e5c59a498af864a06c1a9b2ebc8143516de156
--- /dev/null
+++ b/tlatools/test-model/pcal/SyncCons.cfg
@@ -0,0 +1,8 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANTS N = 2
+          t = 1
+          Data = {0, 1}
+	  bot = bot
+INVARIANT C1
+INVARIANT C2
diff --git a/tlatools/test-model/pcal/SyncCons.tla b/tlatools/test-model/pcal/SyncCons.tla
new file mode 100644
index 0000000000000000000000000000000000000000..63f32e354cbef760d5162bb0ae07dbde9b7d1d76
--- /dev/null
+++ b/tlatools/test-model/pcal/SyncCons.tla
@@ -0,0 +1,264 @@
+--algorithm SyncCons
+        variables clock = 0;
+                  input \in Data;
+		  round = [i \in 1..N |-> 0];
+		  buffer= { };
+		  crashed = { };
+
+\***** Macros for sending and receiving messages
+       macro Send(i, j, msg)
+       begin 
+       buffer := buffer \cup
+         {[from |->  i,
+           to   |->  j,
+           msg  |-> IF i \in crashed
+                     THEN bot
+                      ELSE msg]};
+       end macro
+
+       macro Receive(i, j, msg)
+       begin 
+       when [from |->  i,
+             to   |->  j,
+             msg  |-> msg] \in buffer;
+       buffer := buffer 
+                   \ {[from |->  i,
+                       to   |->  j,
+                       msg  |-> msg]};
+       end macro
+
+\***** Synchronous consensus protocol for crash failures
+\***** A crashed process sends "bot" messages, which model timeouts
+       process Participant \in 1..N
+        variables output = bot;
+                  procs = { };
+                  value = IF self = 1
+                            THEN input
+                            ELSE bot;
+		  recd = { };
+          begin
+s1:         while round[self] < t + 1 do
+              when round[self] = clock;
+              procs := 1..N;
+s2:           while procs # { } do
+                with dest \in procs do
+                  Send(self, dest, value);
+                  procs := procs \ {dest};
+                  end with
+                end while;
+s3:           if self \notin crashed then
+                procs := 1..N;
+                recd := { };
+s4:             while procs # { } do
+                  with source \in Proc do
+                    with data \in Data \cup {bot} do
+                      Receive(source, self, data);
+                      recd := recd \cup {data};
+                      procs := procs \ {source};
+                      end with
+                    end with
+                  end while;
+s5:             if recd \cap Data = { }
+                   then value := bot;
+                   else value := CHOOSE i \in Data:
+                                   i \in recd \cap Data;
+                   end if;
+                end if;
+s6:           round[self] := round[self] + 1;
+              end while;
+            output := IF value = bot
+                         THEN 0
+                         ELSE value;  
+            end process;
+              
+\***** Model of clock: ticks when all processes finish the current round
+       process Clock = N + 1
+         begin
+clock:     while clock < t + 1 do
+             when \A i \in 1..N: round[i] = clock + 1;
+             clock := clock + 1;
+             end while;
+           end process;
+
+\***** Crashing processes
+       process Crash = N + 2
+         begin
+crash:     while Cardinality(crashed) < t do
+             with x  \in (1..N) \ crashed do
+               crashed  := crashed \cup {x}
+               end with
+             end while
+           end process;
+
+end algorithm
+
+-------------------------------- MODULE SyncCons --------------------------------
+EXTENDS Naturals, FiniteSets, TLC
+
+CONSTANTS N, t, Data
+
+ASSUME N > t
+
+bot == CHOOSE v: v \notin Data
+
+Proc == 1..N
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-6c62906a7a71f4a60f9b9f0eecf7e59e
+\* Label clock of process Clock at line 77 col 12 changed to clock_
+VARIABLES clock, input, round, buffer, crashed, pc, output, procs, value, 
+          recd
+
+vars == << clock, input, round, buffer, crashed, pc, output, procs, value, 
+           recd >>
+
+ProcSet == (1..N) \cup {N + 1} \cup {N + 2}
+
+Init == (* Global variables *)
+        /\ clock = 0
+        /\ input \in Data
+        /\ round = [i \in 1..N |-> 0]
+        /\ buffer = { }
+        /\ crashed = { }
+        (* Process Participant *)
+        /\ output = [self \in 1..N |-> bot]
+        /\ procs = [self \in 1..N |-> { }]
+        /\ value = [self \in 1..N |-> IF self = 1
+                                        THEN input
+                                        ELSE bot]
+        /\ recd = [self \in 1..N |-> { }]
+        /\ pc = [self \in ProcSet |-> CASE self \in 1..N -> "s1"
+                                        [] self = N + 1 -> "clock_"
+                                        [] self = N + 2 -> "crash"]
+
+s1(self) == /\ pc[self] = "s1"
+            /\ IF round[self] < t + 1
+                  THEN /\ round[self] = clock
+                       /\ procs' = [procs EXCEPT ![self] = 1..N]
+                       /\ pc' = [pc EXCEPT ![self] = "s2"]
+                       /\ UNCHANGED output
+                  ELSE /\ output' = [output EXCEPT ![self] = IF value[self] = bot
+                                                                THEN 0
+                                                                ELSE value[self]]
+                       /\ pc' = [pc EXCEPT ![self] = "Done"]
+                       /\ procs' = procs
+            /\ UNCHANGED << clock, input, round, buffer, crashed, value, recd >>
+
+s2(self) == /\ pc[self] = "s2"
+            /\ IF procs[self] # { }
+                  THEN /\ \E dest \in procs[self]:
+                            /\ buffer' = (        buffer \cup
+                                          {[from |->  self,
+                                            to   |->  dest,
+                                            msg  |-> IF self \in crashed
+                                                      THEN bot
+                                                       ELSE value[self]]})
+                            /\ procs' = [procs EXCEPT ![self] = procs[self] \ {dest}]
+                       /\ pc' = [pc EXCEPT ![self] = "s2"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "s3"]
+                       /\ UNCHANGED << buffer, procs >>
+            /\ UNCHANGED << clock, input, round, crashed, output, value, recd >>
+
+s3(self) == /\ pc[self] = "s3"
+            /\ IF self \notin crashed
+                  THEN /\ procs' = [procs EXCEPT ![self] = 1..N]
+                       /\ recd' = [recd EXCEPT ![self] = { }]
+                       /\ pc' = [pc EXCEPT ![self] = "s4"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "s6"]
+                       /\ UNCHANGED << procs, recd >>
+            /\ UNCHANGED << clock, input, round, buffer, crashed, output, 
+                            value >>
+
+s4(self) == /\ pc[self] = "s4"
+            /\ IF procs[self] # { }
+                  THEN /\ \E source \in Proc:
+                            \E data \in Data \cup {bot}:
+                              /\ [from |->  source,
+                                  to   |->  self,
+                                  msg  |-> data] \in buffer
+                              /\ buffer' = buffer
+                                             \ {[from |->  source,
+                                                 to   |->  self,
+                                                 msg  |-> data]}
+                              /\ recd' = [recd EXCEPT ![self] = recd[self] \cup {data}]
+                              /\ procs' = [procs EXCEPT ![self] = procs[self] \ {source}]
+                       /\ pc' = [pc EXCEPT ![self] = "s4"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "s5"]
+                       /\ UNCHANGED << buffer, procs, recd >>
+            /\ UNCHANGED << clock, input, round, crashed, output, value >>
+
+s5(self) == /\ pc[self] = "s5"
+            /\ IF recd[self] \cap Data = { }
+                  THEN /\ value' = [value EXCEPT ![self] = bot]
+                  ELSE /\ value' = [value EXCEPT ![self] = CHOOSE i \in Data:
+                                                             i \in recd[self] \cap Data]
+            /\ pc' = [pc EXCEPT ![self] = "s6"]
+            /\ UNCHANGED << clock, input, round, buffer, crashed, output, 
+                            procs, recd >>
+
+s6(self) == /\ pc[self] = "s6"
+            /\ round' = [round EXCEPT ![self] = round[self] + 1]
+            /\ pc' = [pc EXCEPT ![self] = "s1"]
+            /\ UNCHANGED << clock, input, buffer, crashed, output, procs, 
+                            value, recd >>
+
+Participant(self) == s1(self) \/ s2(self) \/ s3(self) \/ s4(self)
+                        \/ s5(self) \/ s6(self)
+
+clock_ == /\ pc[N + 1] = "clock_"
+          /\ IF clock < t + 1
+                THEN /\ \A i \in 1..N: round[i] = clock + 1
+                     /\ clock' = clock + 1
+                     /\ pc' = [pc EXCEPT ![N + 1] = "clock_"]
+                ELSE /\ pc' = [pc EXCEPT ![N + 1] = "Done"]
+                     /\ clock' = clock
+          /\ UNCHANGED << input, round, buffer, crashed, output, procs, value, 
+                          recd >>
+
+Clock == clock_
+
+crash == /\ pc[N + 2] = "crash"
+         /\ IF Cardinality(crashed) < t
+               THEN /\ \E x \in (1..N) \ crashed:
+                         crashed' = (crashed \cup {x})
+                    /\ pc' = [pc EXCEPT ![N + 2] = "crash"]
+               ELSE /\ pc' = [pc EXCEPT ![N + 2] = "Done"]
+                    /\ UNCHANGED crashed
+         /\ UNCHANGED << clock, input, round, buffer, output, procs, value, 
+                         recd >>
+
+Crash == crash
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == Clock \/ Crash
+           \/ (\E self \in 1..N: Participant(self))
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-166884a9c4ec2ab81b93439149dfdc07
+
+C1 == \A p1, p2 \in (1..N) \ crashed:
+  (pc[p1] = "Done" /\ pc[p2] = "Done") => (output[p1] = output[p2])
+
+C2 == \A p \in (1..N) \ crashed:
+  ( /\ 1 \notin crashed
+    /\ pc[1] = "Done"
+    /\ pc[p] = "Done" ) => (output[p] = input)
+
+THEOREM Spec => [] C1 /\ [] C2
+=================================================================================
+On derosa.ucsd.edu (dual processor G5)
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+    calculated (optimistic):  8.312778857584233E-8
+    based on the actual fingerprints:  8.652893187423173E-9
+3093758 states generated, 619842 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 81.
+181.963u 2.589s 2:47.51 110.1%  0+0k 2+119io 0pf+0w
+
diff --git a/tlatools/test-model/pcal/Test.tla b/tlatools/test-model/pcal/Test.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cb68cd3b3f9ba05b447bf7428a293b9693cd8ea4
--- /dev/null
+++ b/tlatools/test-model/pcal/Test.tla
@@ -0,0 +1,105 @@
+                
+--algorithm bug
+variables x = 0 ; y = 0 ;
+macro foo()
+   begin if TRUE then if TRUE then y := 22 ;
+                              else y := 42  end if
+                 else  with a = 47 ; b = 77 ; do
+                        y := 27
+                       end with  end if
+   end macro 
+procedure Bar() 
+  begin Q: skip ;
+           foo() ;
+           return 
+  end procedure 
+begin  L1 :   y := 1 ;
+       L3 :   skip ;
+              if x > 0 then foo() 
+                       else x := 17 end if;
+       L2 : assert x = 17 ;
+            foo() ;
+            assert y = 22 
+end algorithm
+
+--------- MODULE Test -------
+(* \* xxxx *) 
+
+\*  (* *)
+EXTENDS Sequences, Naturals, TLC
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-648cb8f5989caacb85bf4050478c7a20
+VARIABLES x, y, pc, stack
+
+vars == << x, y, pc, stack >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ y = 0
+        /\ stack = << >>
+        /\ pc = "L1"
+
+Q == /\ pc = "Q"
+     /\ TRUE
+     /\ IF TRUE
+           THEN /\ IF TRUE
+                      THEN /\ y' = 22
+                      ELSE /\ y' = 42
+           ELSE /\ LET a == 47 IN
+                     LET b == 77 IN
+                       y' = 27
+     /\ pc' = Head(stack).pc
+     /\ stack' = Tail(stack)
+     /\ x' = x
+
+Bar == Q
+
+L1 == /\ pc = "L1"
+      /\ y' = 1
+      /\ pc' = "L3"
+      /\ UNCHANGED << x, stack >>
+
+L3 == /\ pc = "L3"
+      /\ TRUE
+      /\ IF x > 0
+            THEN /\ IF TRUE
+                       THEN /\ IF TRUE
+                                  THEN /\ y' = 22
+                                  ELSE /\ y' = 42
+                       ELSE /\ LET a == 47 IN
+                                 LET b == 77 IN
+                                   y' = 27
+                 /\ x' = x
+            ELSE /\ x' = 17
+                 /\ y' = y
+      /\ pc' = "L2"
+      /\ stack' = stack
+
+L2 == /\ pc = "L2"
+      /\ Assert(x = 17, "Failure of assertion at line 20, column 13.")
+      /\ IF TRUE
+            THEN /\ IF TRUE
+                       THEN /\ y' = 22
+                       ELSE /\ y' = 42
+            ELSE /\ LET a == 47 IN
+                      LET b == 77 IN
+                        y' = 27
+      /\ Assert(y' = 22, "Failure of assertion at line 22, column 13.")
+      /\ pc' = "Done"
+      /\ UNCHANGED << x, stack >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Bar \/ L1 \/ L3 \/ L2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-025c418c95de365cc2a03da9bdd7e60c
+
+============================================
diff --git a/tlatools/test-model/pcal/TestReplace.tla b/tlatools/test-model/pcal/TestReplace.tla
new file mode 100644
index 0000000000000000000000000000000000000000..742eb25a62b72745aebaa295783d1d4655ce3c09
--- /dev/null
+++ b/tlatools/test-model/pcal/TestReplace.tla
@@ -0,0 +1,251 @@
+------------------ MODULE  TestReplace ----------------- 
+EXTENDS Naturals, TLC, Sequences
+
+
+(**********************
+--algorithm TestReplace
+macro IncrementX(u)
+  begin X := X + u 
+  end macro
+
+procedure Bar(u, v)
+ begin a: assert u = v;
+          return;
+ end procedure
+
+procedure Foo1(u, v)
+ variable X = 0; Y = X ;
+ begin
+ a : assert Y = X ;
+ b : while Y = X do 
+         Y := Y - 1;
+     end while ;
+     assert Y = X - 1;
+     with id = X do assert id = 0  end with ;
+     if X = 0
+       then  c: call Bar(X, 0)
+       else  assert FALSE 
+     end if ;
+ d : print <<X, " = 0">> ;
+     IncrementX(X+1) ;
+     assert X = 1 ;
+ e:  return 
+end procedure
+
+procedure Foo2(u, v)
+ variable X = 9; Z = X ;
+ begin
+ a : assert Z = X ;
+ b : while Z = X do 
+         Z := Z - 1;
+     end while ;
+     assert Z = X - 1;
+     with id = X do assert id = 9  end with ;
+     if X = 9
+       then  c: call Bar(X, 9)
+        
+       else  assert FALSE 
+     end if ;
+ d : print <<X, " = 9">> ;
+     IncrementX(X+1) ;
+     assert X = 19 ;
+ e:    return 
+end procedure
+
+begin
+start : call Foo1(1, 2) ;
+b : call Foo2(1, 2) ;
+end algorithm
+
+***********************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a373e52549e281416ed7cb70fa982188
+\* Label a of procedure Bar at line 12 col 11 changed to a_
+\* Label a of procedure Foo1 at line 19 col 6 changed to a_F
+\* Label b of procedure Foo1 at line 20 col 6 changed to b_
+\* Label c of procedure Foo1 at line 26 col 17 changed to c_
+\* Label d of procedure Foo1 at line 29 col 6 changed to d_
+\* Label e of procedure Foo1 at line 32 col 6 changed to e_
+\* Label b of procedure Foo2 at line 39 col 6 changed to b_F
+\* Procedure variable X of procedure Foo1 at line 17 col 11 changed to X_
+\* Parameter u of procedure Bar at line 11 col 15 changed to u_
+\* Parameter v of procedure Bar at line 11 col 18 changed to v_
+\* Parameter u of procedure Foo1 at line 16 col 16 changed to u_F
+\* Parameter v of procedure Foo1 at line 16 col 19 changed to v_F
+CONSTANT defaultInitValue
+VARIABLES pc, stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z
+
+vars == << pc, stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z >>
+
+Init == (* Procedure Bar *)
+        /\ u_ = defaultInitValue
+        /\ v_ = defaultInitValue
+        (* Procedure Foo1 *)
+        /\ u_F = defaultInitValue
+        /\ v_F = defaultInitValue
+        /\ X_ = 0
+        /\ Y = X_
+        (* Procedure Foo2 *)
+        /\ u = defaultInitValue
+        /\ v = defaultInitValue
+        /\ X = 9
+        /\ Z = X
+        /\ stack = << >>
+        /\ pc = "start"
+
+a_ == /\ pc = "a_"
+      /\ Assert(u_ = v_, "Failure of assertion at line 12, column 11.")
+      /\ pc' = Head(stack).pc
+      /\ u_' = Head(stack).u_
+      /\ v_' = Head(stack).v_
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << u_F, v_F, X_, Y, u, v, X, Z >>
+
+Bar == a_
+
+a_F == /\ pc = "a_F"
+       /\ Assert(Y = X_, "Failure of assertion at line 19, column 6.")
+       /\ pc' = "b_"
+       /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z >>
+
+b_ == /\ pc = "b_"
+      /\ IF Y = X_
+            THEN /\ Y' = Y - 1
+                 /\ pc' = "b_"
+            ELSE /\ Assert(Y = X_ - 1, 
+                           "Failure of assertion at line 23, column 6.")
+                 /\ LET id == X_ IN
+                      Assert(id = 0, 
+                             "Failure of assertion at line 24, column 21.")
+                 /\ IF X_ = 0
+                       THEN /\ pc' = "c_"
+                       ELSE /\ Assert(FALSE, 
+                                      "Failure of assertion at line 27, column 14.")
+                            /\ pc' = "d_"
+                 /\ Y' = Y
+      /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, u, v, X, Z >>
+
+c_ == /\ pc = "c_"
+      /\ /\ stack' = << [ procedure |->  "Bar",
+                          pc        |->  "d_",
+                          u_        |->  u_,
+                          v_        |->  v_ ] >>
+                      \o stack
+         /\ u_' = X_
+         /\ v_' = 0
+      /\ pc' = "a_"
+      /\ UNCHANGED << u_F, v_F, X_, Y, u, v, X, Z >>
+
+d_ == /\ pc = "d_"
+      /\ PrintT(<<X_, " = 0">>)
+      /\ X_' = X_ + (X_+1)
+      /\ Assert(X_' = 1, "Failure of assertion at line 31, column 6.")
+      /\ pc' = "e_"
+      /\ UNCHANGED << stack, u_, v_, u_F, v_F, Y, u, v, X, Z >>
+
+e_ == /\ pc = "e_"
+      /\ pc' = Head(stack).pc
+      /\ X_' = Head(stack).X_
+      /\ Y' = Head(stack).Y
+      /\ u_F' = Head(stack).u_F
+      /\ v_F' = Head(stack).v_F
+      /\ stack' = Tail(stack)
+      /\ UNCHANGED << u_, v_, u, v, X, Z >>
+
+Foo1 == a_F \/ b_ \/ c_ \/ d_ \/ e_
+
+a == /\ pc = "a"
+     /\ Assert(Z = X, "Failure of assertion at line 38, column 6.")
+     /\ pc' = "b_F"
+     /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z >>
+
+b_F == /\ pc = "b_F"
+       /\ IF Z = X
+             THEN /\ Z' = Z - 1
+                  /\ pc' = "b_F"
+             ELSE /\ Assert(Z = X - 1, 
+                            "Failure of assertion at line 42, column 6.")
+                  /\ LET id == X IN
+                       Assert(id = 9, 
+                              "Failure of assertion at line 43, column 21.")
+                  /\ IF X = 9
+                        THEN /\ pc' = "c"
+                        ELSE /\ Assert(FALSE, 
+                                       "Failure of assertion at line 47, column 14.")
+                             /\ pc' = "d"
+                  /\ Z' = Z
+       /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, X >>
+
+c == /\ pc = "c"
+     /\ /\ stack' = << [ procedure |->  "Bar",
+                         pc        |->  "d",
+                         u_        |->  u_,
+                         v_        |->  v_ ] >>
+                     \o stack
+        /\ u_' = X
+        /\ v_' = 9
+     /\ pc' = "a_"
+     /\ UNCHANGED << u_F, v_F, X_, Y, u, v, X, Z >>
+
+d == /\ pc = "d"
+     /\ PrintT(<<X, " = 9">>)
+     /\ X' = X + (X+1)
+     /\ Assert(X' = 19, "Failure of assertion at line 51, column 6.")
+     /\ pc' = "e"
+     /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, Z >>
+
+e == /\ pc = "e"
+     /\ pc' = Head(stack).pc
+     /\ X' = Head(stack).X
+     /\ Z' = Head(stack).Z
+     /\ u' = Head(stack).u
+     /\ v' = Head(stack).v
+     /\ stack' = Tail(stack)
+     /\ UNCHANGED << u_, v_, u_F, v_F, X_, Y >>
+
+Foo2 == a \/ b_F \/ c \/ d \/ e
+
+start == /\ pc = "start"
+         /\ /\ stack' = << [ procedure |->  "Foo1",
+                             pc        |->  "b",
+                             X_        |->  X_,
+                             Y         |->  Y,
+                             u_F       |->  u_F,
+                             v_F       |->  v_F ] >>
+                         \o stack
+            /\ u_F' = 1
+            /\ v_F' = 2
+         /\ X_' = 0
+         /\ Y' = X_'
+         /\ pc' = "a_F"
+         /\ UNCHANGED << u_, v_, u, v, X, Z >>
+
+b == /\ pc = "b"
+     /\ /\ stack' = << [ procedure |->  "Foo2",
+                         pc        |->  "Done",
+                         X         |->  X,
+                         Z         |->  Z,
+                         u         |->  u,
+                         v         |->  v ] >>
+                     \o stack
+        /\ u' = 1
+        /\ v' = 2
+     /\ X' = 9
+     /\ Z' = X'
+     /\ pc' = "a"
+     /\ UNCHANGED << u_, v_, u_F, v_F, X_, Y >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Bar \/ Foo1 \/ Foo2 \/ start \/ b
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-424be35f7792f0f2c25e136cc792e4c5
+
+==================================================
+
diff --git a/tlatools/test-model/pcal/TestTabs.tla b/tlatools/test-model/pcal/TestTabs.tla
new file mode 100644
index 0000000000000000000000000000000000000000..975cbbb3a07596aa22292bb2598c999728c34eaf
--- /dev/null
+++ b/tlatools/test-model/pcal/TestTabs.tla
@@ -0,0 +1,51 @@
+------------------------------ MODULE TestTabs ------------------------------ 
+\* Warning: there are evil tabs in this file.
+EXTENDS Naturals, TLC
+(*
+--algorithm TestTabs
+  variables x = 0 ;
+  begin
+l:  x := IF /\ \A i \in {1} : 1 + 1 = 2
+	    /\ \A i \in {1} : 2 + 2 = 4
+  	    /\	\/ \A i \in {1} : 
+		   1 = 0
+		\/ \A i \in {1} : 1 = 2
+ 	        \/ \A i \in {1} : 1 = 1
+          THEN 1
+          ELSE 0 ;
+    assert x = 1 ;
+  end algorithm
+*)
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-df86b294351f95a8207d12196b3732f0
+VARIABLES x, pc
+
+vars == << x, pc >>
+
+Init == (* Global variables *)
+        /\ x = 0
+        /\ pc = "l"
+
+l == /\ pc = "l"
+     /\ x' = (IF /\ \A i \in {1} : 1 + 1 = 2
+                 /\ \A i \in {1} : 2 + 2 = 4
+                 /\  \/ \A i \in {1} :
+                        1 = 0
+                     \/ \A i \in {1} : 1 = 2
+                     \/ \A i \in {1} : 1 = 1
+               THEN 1
+               ELSE 0)
+     /\ Assert(x' = 1, "Failure of assertion at line 16, column 5.")
+     /\ pc' = "Done"
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == l
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ec3da753015679c262e945265fc700f9
+=============================================================================
diff --git a/tlatools/test-model/pcal/TreeBarrier.cfg b/tlatools/test-model/pcal/TreeBarrier.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ef9c9f9df75ac1792e7e68265b19e81d981b7b21
--- /dev/null
+++ b/tlatools/test-model/pcal/TreeBarrier.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANT      exp = 2
+INVARIANT     SafeBarrier
+PROPERTY      LiveBarrier
diff --git a/tlatools/test-model/pcal/TreeBarrier.tla b/tlatools/test-model/pcal/TreeBarrier.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a84d857a4fd0b8afe40e60fc640b0812233f0711
--- /dev/null
+++ b/tlatools/test-model/pcal/TreeBarrier.tla
@@ -0,0 +1,140 @@
+--algorithm TreeBarrier
+   variables arrived = [i \in 1..2 |-> [j \in 1..N |-> 0]];
+             proceed = [i \in 1..2 |-> [j \in 1..N |-> 0]];
+   process i \in 1..N
+       variables b = 1;
+                 p = 0;
+       begin
+prc:   while (TRUE) do
+comp:     skip;
+b1:       if 2*self \leq N then
+b2:          when arrived[b][2*self] = 1; arrived[b][2*self] := 0;
+b3:          when arrived[b][2*self + 1] = 1; arrived[b][2*self + 1] := 0;
+             end if;
+b4:       arrived[b][self] := 1;
+b5:       if self = 1 then
+             p := 1;
+b6:          while (p \leq N) do
+b7:             proceed[b][p] := 1;
+                p := p + 1;
+                end while;
+             end if;
+b8:       when proceed[b][self] = 1; proceed[b][self] := 0;
+b9:       b := IF b = 1 THEN 2 ELSE 1;
+          end while;
+       end process;
+end algorithm
+
+------------------------ MODULE TreeBarrier ------------------------
+
+EXTENDS Naturals, Sequences
+CONSTANT exp
+N == 2^exp-1
+
+--------------------------------------------------------------------
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-f8296f0cc6166098bc809c33e090e03e
+VARIABLES arrived, proceed, pc, b, p
+
+vars == << arrived, proceed, pc, b, p >>
+
+ProcSet == (1..N)
+
+Init == (* Global variables *)
+        /\ arrived = [i \in 1..2 |-> [j \in 1..N |-> 0]]
+        /\ proceed = [i \in 1..2 |-> [j \in 1..N |-> 0]]
+        (* Process i *)
+        /\ b = [self \in 1..N |-> 1]
+        /\ p = [self \in 1..N |-> 0]
+        /\ pc = [self \in ProcSet |-> "prc"]
+
+prc(self) == /\ pc[self] = "prc"
+             /\ pc' = [pc EXCEPT ![self] = "comp"]
+             /\ UNCHANGED << arrived, proceed, b, p >>
+
+comp(self) == /\ pc[self] = "comp"
+              /\ TRUE
+              /\ pc' = [pc EXCEPT ![self] = "b1"]
+              /\ UNCHANGED << arrived, proceed, b, p >>
+
+b1(self) == /\ pc[self] = "b1"
+            /\ IF 2*self \leq N
+                  THEN /\ pc' = [pc EXCEPT ![self] = "b2"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "b4"]
+            /\ UNCHANGED << arrived, proceed, b, p >>
+
+b2(self) == /\ pc[self] = "b2"
+            /\ arrived[b[self]][2*self] = 1
+            /\ arrived' = [arrived EXCEPT ![b[self]][2*self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "b3"]
+            /\ UNCHANGED << proceed, b, p >>
+
+b3(self) == /\ pc[self] = "b3"
+            /\ arrived[b[self]][2*self + 1] = 1
+            /\ arrived' = [arrived EXCEPT ![b[self]][2*self + 1] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "b4"]
+            /\ UNCHANGED << proceed, b, p >>
+
+b4(self) == /\ pc[self] = "b4"
+            /\ arrived' = [arrived EXCEPT ![b[self]][self] = 1]
+            /\ pc' = [pc EXCEPT ![self] = "b5"]
+            /\ UNCHANGED << proceed, b, p >>
+
+b5(self) == /\ pc[self] = "b5"
+            /\ IF self = 1
+                  THEN /\ p' = [p EXCEPT ![self] = 1]
+                       /\ pc' = [pc EXCEPT ![self] = "b6"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "b8"]
+                       /\ p' = p
+            /\ UNCHANGED << arrived, proceed, b >>
+
+b6(self) == /\ pc[self] = "b6"
+            /\ IF (p[self] \leq N)
+                  THEN /\ pc' = [pc EXCEPT ![self] = "b7"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "b8"]
+            /\ UNCHANGED << arrived, proceed, b, p >>
+
+b7(self) == /\ pc[self] = "b7"
+            /\ proceed' = [proceed EXCEPT ![b[self]][p[self]] = 1]
+            /\ p' = [p EXCEPT ![self] = p[self] + 1]
+            /\ pc' = [pc EXCEPT ![self] = "b6"]
+            /\ UNCHANGED << arrived, b >>
+
+b8(self) == /\ pc[self] = "b8"
+            /\ proceed[b[self]][self] = 1
+            /\ proceed' = [proceed EXCEPT ![b[self]][self] = 0]
+            /\ pc' = [pc EXCEPT ![self] = "b9"]
+            /\ UNCHANGED << arrived, b, p >>
+
+b9(self) == /\ pc[self] = "b9"
+            /\ b' = [b EXCEPT ![self] = IF b[self] = 1 THEN 2 ELSE 1]
+            /\ pc' = [pc EXCEPT ![self] = "prc"]
+            /\ UNCHANGED << arrived, proceed, p >>
+
+i(self) == prc(self) \/ comp(self) \/ b1(self) \/ b2(self) \/ b3(self)
+              \/ b4(self) \/ b5(self) \/ b6(self) \/ b7(self) \/ b8(self)
+              \/ b9(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in 1..N: i(self))
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-cbb736d8ea82daa6b485e32bbe870840
+
+
+SafeBarrier == \A p1, p2 \in ProcSet:
+   ((pc[p1] = "comp") /\ (pc[p2] = "comp")) => (b[p1] = b[p2])
+
+LiveBarrier == \A p1 \in ProcSet:
+                   /\ (b[p1] = 1) ~> (b[p1] = 2)
+                   /\ (b[p1] = 2) ~> (b[p1] = 1)
+
+====================================================================
diff --git a/tlatools/test-model/pcal/ULBakery.cfg b/tlatools/test-model/pcal/ULBakery.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9209a00c6288aab93977bdf9e85b204efd16702f
--- /dev/null
+++ b/tlatools/test-model/pcal/ULBakery.cfg
@@ -0,0 +1,7 @@
+SPECIFICATION Spec
+\* Add statements after this line.
+CONSTANTS NumProcs = 2
+          MaxNum = 3
+INVARIANT Invariant
+CONSTRAINT Constraint
+
diff --git a/tlatools/test-model/pcal/ULCallReturn1.tla b/tlatools/test-model/pcal/ULCallReturn1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..20328512f989245d5215a1dd6d58388966d3dbe9
--- /dev/null
+++ b/tlatools/test-model/pcal/ULCallReturn1.tla
@@ -0,0 +1,124 @@
+------------------------------ MODULE ULCallReturn1 ------------------------- 
+EXTENDS Sequences, Naturals, TLC
+
+(*   
+  --algorithm CallReturn1
+    procedure Proc1(arg1 = 0)
+      variable u = 1 ;
+      begin (*p1 :*) u := 2 ;
+                 call Proc2 ( 2 * u ) ;
+            (*p2 :*) assert u = 2;
+                 assert arg1  = 4  ;
+                 call Proc2 ( 2 * u + 1 ) ;
+                 return ;
+      end procedure
+    procedure Proc2(arg2 = 0)
+      variable v = 42 ;
+      begin (*q1 :*) assert v = 42;
+                 assert arg2 \in {4, 5} ;
+                 call Proc3 ( v + arg2 ) ;
+                 return ;
+      end procedure
+    procedure Proc3(arg3 = 0)
+      begin (*r1 :*) assert arg3 \in {46, 47} ;
+                 return ;
+      end procedure
+    begin
+      (*a1 :*) call Proc1( 4 ) ;
+    end algorithm
+
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a699498c8513ed4e4423d961c35407b2
+VARIABLES pc, stack, arg1, u, arg2, v, arg3
+
+vars == << pc, stack, arg1, u, arg2, v, arg3 >>
+
+Init == (* Procedure Proc1 *)
+        /\ arg1 = 0
+        /\ u = 1
+        (* Procedure Proc2 *)
+        /\ arg2 = 0
+        /\ v = 42
+        (* Procedure Proc3 *)
+        /\ arg3 = 0
+        /\ stack = << >>
+        /\ pc = "Lbl_5"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ u' = 2
+         /\ /\ arg2' = 2 * u'
+            /\ stack' = << [ procedure |->  "Proc2",
+                             pc        |->  "Lbl_2",
+                             v         |->  v,
+                             arg2      |->  arg2 ] >>
+                         \o stack
+         /\ v' = 42
+         /\ pc' = "Lbl_3"
+         /\ UNCHANGED << arg1, arg3 >>
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ Assert(u = 2, "Failure of assertion at line 10, column 22.")
+         /\ Assert(arg1  = 4, "Failure of assertion at line 11, column 18.")
+         /\ /\ arg2' = 2 * u + 1
+            /\ stack' = << [ procedure |->  "Proc2",
+                             pc        |->  Head(stack).pc,
+                             v         |->  v,
+                             arg2      |->  arg2 ] >>
+                         \o Tail(stack)
+            /\ u' = Head(stack).u
+         /\ v' = 42
+         /\ pc' = "Lbl_3"
+         /\ UNCHANGED << arg1, arg3 >>
+
+Proc1 == Lbl_1 \/ Lbl_2
+
+Lbl_3 == /\ pc = "Lbl_3"
+         /\ Assert(v = 42, "Failure of assertion at line 17, column 22.")
+         /\ Assert(arg2 \in {4, 5}, 
+                   "Failure of assertion at line 18, column 18.")
+         /\ /\ arg3' = v + arg2
+            /\ stack' = << [ procedure |->  "Proc3",
+                             pc        |->  Head(stack).pc,
+                             arg3      |->  arg3 ] >>
+                         \o Tail(stack)
+            /\ v' = Head(stack).v
+         /\ pc' = "Lbl_4"
+         /\ UNCHANGED << arg1, u, arg2 >>
+
+Proc2 == Lbl_3
+
+Lbl_4 == /\ pc = "Lbl_4"
+         /\ Assert(arg3 \in {46, 47}, 
+                   "Failure of assertion at line 23, column 22.")
+         /\ pc' = Head(stack).pc
+         /\ arg3' = Head(stack).arg3
+         /\ stack' = Tail(stack)
+         /\ UNCHANGED << arg1, u, arg2, v >>
+
+Proc3 == Lbl_4
+
+Lbl_5 == /\ pc = "Lbl_5"
+         /\ /\ arg1' = 4
+            /\ stack' = << [ procedure |->  "Proc1",
+                             pc        |->  "Done",
+                             u         |->  u,
+                             arg1      |->  arg1 ] >>
+                         \o stack
+         /\ u' = 1
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << arg2, v, arg3 >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Proc1 \/ Proc2 \/ Proc3 \/ Lbl_5
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-542b3dd4f4eee627dff4b0b877928865
+=============================================================================
diff --git a/tlatools/test-model/pcal/ULEuclid.cfg b/tlatools/test-model/pcal/ULEuclid.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d3f674e358ba428b8a060e8a4d4eccc010228b50
--- /dev/null
+++ b/tlatools/test-model/pcal/ULEuclid.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+INVARIANT Invariant
+CONSTANTS MaxNum = 20
+
diff --git a/tlatools/test-model/pcal/ULEuclid.tla b/tlatools/test-model/pcal/ULEuclid.tla
new file mode 100644
index 0000000000000000000000000000000000000000..53b0d05cd0106d40b220e174555e35b18314b24e
--- /dev/null
+++ b/tlatools/test-model/pcal/ULEuclid.tla
@@ -0,0 +1,92 @@
+------------------------------ MODULE ULEuclid ------------------------------- 
+(***************************************************************************)
+(* Euclid's algorithm.                                                     *)
+(***************************************************************************)
+
+EXTENDS Naturals, TLC
+
+CONSTANT MaxNum
+
+ASSUME MaxNum > 0
+
+ASSUME /\ Print(<<"Testing Euclid's algorithm on all numbers between 1 and ",
+                   MaxNum>>, TRUE)
+       /\ Print("Most time spent evaluating naive definition of GCD for test",
+                TRUE)
+
+(******
+Adapted from page 8 of the 2nd edition of Robert Sedgewick's "Algorithms".
+
+--algorithm Euclid
+  variables u_ini \in 1 .. MaxNum ; 
+            v_ini \in 1 .. MaxNum ;
+            u = u_ini ; v = v_ini ;
+  begin (*a :*) while u # 0
+              do     if u < v then u := v || v := u ; end if ;
+         (* b: *)  u := u - v;
+            end while ;
+            assert v = GCD(u_ini, v_ini) ;
+            \* print <<"gcd of ", u_ini, v_ini, " equals ", v >> ;
+  end algorithm 
+
+*)
+					
+GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) :
+                /\ x % i = 0 
+                /\ y % i = 0
+                /\ \A j \in (1..x) \cap (1..y) :
+                        /\ x % j = 0 
+                        /\ y % j = 0
+                        => i \geq j
+                      
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-15a96a62d4bb94349f0b7e97b7877563
+VARIABLES u_ini, v_ini, u, v, pc
+
+vars == << u_ini, v_ini, u, v, pc >>
+
+Init == (* Global variables *)
+        /\ u_ini \in 1 .. MaxNum
+        /\ v_ini \in 1 .. MaxNum
+        /\ u = u_ini
+        /\ v = v_ini
+        /\ pc = "Lbl_1"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ IF u # 0
+               THEN /\ IF u < v
+                          THEN /\ /\ u' = v
+                                  /\ v' = u
+                          ELSE /\ TRUE
+                               /\ UNCHANGED << u, v >>
+                    /\ pc' = "Lbl_2"
+               ELSE /\ Assert(v = GCD(u_ini, v_ini), 
+                              "Failure of assertion at line 28, column 13.")
+                    /\ pc' = "Done"
+                    /\ UNCHANGED << u, v >>
+         /\ UNCHANGED << u_ini, v_ini >>
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ u' = u - v
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << u_ini, v_ini, v >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Lbl_1 \/ Lbl_2
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-42ac9bbadc686601495c6cb5a86d3d55
+
+
+Invariant == 
+   (pc = "Done") => (v = GCD(u_ini, v_ini))
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/ULEvenOdd.cfg b/tlatools/test-model/pcal/ULEvenOdd.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..88725dab9155d12e1fbca4531e4f30063b7dc81e
--- /dev/null
+++ b/tlatools/test-model/pcal/ULEvenOdd.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+CONSTANT N = 6
+
diff --git a/tlatools/test-model/pcal/ULEvenOdd.tla b/tlatools/test-model/pcal/ULEvenOdd.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0c3b6cdf01e7d69756c3ea47521c891085d1033d
--- /dev/null
+++ b/tlatools/test-model/pcal/ULEvenOdd.tla
@@ -0,0 +1,116 @@
+--algorithm EvenOdd
+variable result = FALSE;
+procedure Even (xEven = 0)
+begin
+  (*Even1:*) if xEven = 0 then
+           result := TRUE;
+           return;
+         else
+           call Odd(xEven - 1);
+           return;
+         end if;
+  end procedure
+procedure Odd (xOdd = 0)
+begin
+  (*Odd1:*) if xOdd = 0 then result := FALSE;
+        else call Even(xOdd - 1);
+        end if;
+  (*Odd2:*) return;
+  end procedure
+begin
+  (*a1:*) call Even(N);
+  (*a2:*) print result;
+end algorithm
+
+--------------- MODULE ULEvenOdd ---------------
+
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT N
+
+----------------------------------------------
+
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-ead0453df43345041c6d46c21bad39b3
+VARIABLES result, pc, stack, xEven, xOdd
+
+vars == << result, pc, stack, xEven, xOdd >>
+
+Init == (* Global variables *)
+        /\ result = FALSE
+        (* Procedure Even *)
+        /\ xEven = 0
+        (* Procedure Odd *)
+        /\ xOdd = 0
+        /\ stack = << >>
+        /\ pc = "Lbl_4"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ IF xEven = 0
+               THEN /\ result' = TRUE
+                    /\ pc' = Head(stack).pc
+                    /\ xEven' = Head(stack).xEven
+                    /\ stack' = Tail(stack)
+                    /\ xOdd' = xOdd
+               ELSE /\ /\ stack' = << [ procedure |->  "Odd",
+                                        pc        |->  Head(stack).pc,
+                                        xOdd      |->  xOdd ] >>
+                                    \o Tail(stack)
+                       /\ xOdd' = xEven - 1
+                    /\ pc' = "Lbl_2"
+                    /\ UNCHANGED << result, xEven >>
+
+Even == Lbl_1
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ IF xOdd = 0
+               THEN /\ result' = FALSE
+                    /\ pc' = "Lbl_3"
+                    /\ UNCHANGED << stack, xEven >>
+               ELSE /\ /\ stack' = << [ procedure |->  "Even",
+                                        pc        |->  "Lbl_3",
+                                        xEven     |->  xEven ] >>
+                                    \o stack
+                       /\ xEven' = xOdd - 1
+                    /\ pc' = "Lbl_1"
+                    /\ UNCHANGED result
+         /\ xOdd' = xOdd
+
+Lbl_3 == /\ pc = "Lbl_3"
+         /\ pc' = Head(stack).pc
+         /\ xOdd' = Head(stack).xOdd
+         /\ stack' = Tail(stack)
+         /\ UNCHANGED << result, xEven >>
+
+Odd == Lbl_2 \/ Lbl_3
+
+Lbl_4 == /\ pc = "Lbl_4"
+         /\ /\ stack' = << [ procedure |->  "Even",
+                             pc        |->  "Lbl_5",
+                             xEven     |->  xEven ] >>
+                         \o stack
+            /\ xEven' = N
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << result, xOdd >>
+
+Lbl_5 == /\ pc = "Lbl_5"
+         /\ PrintT(result)
+         /\ pc' = "Done"
+         /\ UNCHANGED << result, stack, xEven, xOdd >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Even \/ Odd \/ Lbl_4 \/ Lbl_5
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-20632e604e66ef1ac373dca4eac3efb3
+
+==============================================
+
+
diff --git a/tlatools/test-model/pcal/ULFactorial2.tla b/tlatools/test-model/pcal/ULFactorial2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3205ee538c10648f2caf9fd109ac01ca4c0d4774
--- /dev/null
+++ b/tlatools/test-model/pcal/ULFactorial2.tla
@@ -0,0 +1,126 @@
+-------------------------- MODULE ULFactorial2 --------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(***************************************************************************
+Factorial Algorithm with 2 procedures
+
+--algorithm Factorial
+  variable result = 1;        
+  procedure FactProc(arg1 = 0 )    
+    variable u = 1 ;
+    begin (*p1 :*) if arg1 = 0
+                 then return;     
+                 else result := result * arg1;
+                      call FactProc2 ( arg1 - 1 ) ;
+                      (*b:*) return;
+               end if;
+    end procedure
+  procedure FactProc2(arg2 = 0)    
+    variable u2 = 1 ;
+    begin (*p12 :*) if arg2 = 0
+                 then return;     
+                 else result := result * arg2;
+                      call FactProc ( arg2 - 1 ) ;
+                      return;
+               end if;
+    end procedure
+  begin
+    (*a1 :*) call FactProc( 5 ) ;
+    (*a2 :*) if result = 120  then print <<"Correct =", 120>>;
+                          else print <<"Error = ", result>> ;
+         end if;
+  end algorithm
+***************************************************************************)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-e543cee5733d328853b3f740967ffc6f
+VARIABLES result, pc, stack, arg1, u, arg2, u2
+
+vars == << result, pc, stack, arg1, u, arg2, u2 >>
+
+Init == (* Global variables *)
+        /\ result = 1
+        (* Procedure FactProc *)
+        /\ arg1 = 0
+        /\ u = 1
+        (* Procedure FactProc2 *)
+        /\ arg2 = 0
+        /\ u2 = 1
+        /\ stack = << >>
+        /\ pc = "Lbl_3"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ IF arg1 = 0
+               THEN /\ pc' = Head(stack).pc
+                    /\ u' = Head(stack).u
+                    /\ arg1' = Head(stack).arg1
+                    /\ stack' = Tail(stack)
+                    /\ UNCHANGED << result, arg2, u2 >>
+               ELSE /\ result' = result * arg1
+                    /\ /\ arg2' = arg1 - 1
+                       /\ stack' = << [ procedure |->  "FactProc2",
+                                        pc        |->  Head(stack).pc,
+                                        u2        |->  u2,
+                                        arg2      |->  arg2 ] >>
+                                    \o Tail(stack)
+                       /\ u' = Head(stack).u
+                    /\ u2' = 1
+                    /\ pc' = "Lbl_2"
+                    /\ arg1' = arg1
+
+FactProc == Lbl_1
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ IF arg2 = 0
+               THEN /\ pc' = Head(stack).pc
+                    /\ u2' = Head(stack).u2
+                    /\ arg2' = Head(stack).arg2
+                    /\ stack' = Tail(stack)
+                    /\ UNCHANGED << result, arg1, u >>
+               ELSE /\ result' = result * arg2
+                    /\ /\ arg1' = arg2 - 1
+                       /\ stack' = << [ procedure |->  "FactProc",
+                                        pc        |->  Head(stack).pc,
+                                        u         |->  u,
+                                        arg1      |->  arg1 ] >>
+                                    \o Tail(stack)
+                       /\ u2' = Head(stack).u2
+                    /\ u' = 1
+                    /\ pc' = "Lbl_1"
+                    /\ arg2' = arg2
+
+FactProc2 == Lbl_2
+
+Lbl_3 == /\ pc = "Lbl_3"
+         /\ /\ arg1' = 5
+            /\ stack' = << [ procedure |->  "FactProc",
+                             pc        |->  "Lbl_4",
+                             u         |->  u,
+                             arg1      |->  arg1 ] >>
+                         \o stack
+         /\ u' = 1
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << result, arg2, u2 >>
+
+Lbl_4 == /\ pc = "Lbl_4"
+         /\ IF result = 120
+               THEN /\ PrintT(<<"Correct =", 120>>)
+               ELSE /\ PrintT(<<"Error = ", result>>)
+         /\ pc' = "Done"
+         /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == FactProc \/ FactProc2 \/ Lbl_3 \/ Lbl_4
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-13df185bb6632a27d39dd317f4d5a749
+
+
+Invariant == result \in Nat
+=============================================================================
diff --git a/tlatools/test-model/pcal/ULQuicksortMacro.cfg b/tlatools/test-model/pcal/ULQuicksortMacro.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0ebf8fe12d66c62cbd54cf80207efc9dda94587b
--- /dev/null
+++ b/tlatools/test-model/pcal/ULQuicksortMacro.cfg
@@ -0,0 +1,5 @@
+SPECIFICATION Spec
+PROPERTY Termination
+\* Add statements after this line.
+CONSTANTS ArrayLen = 3
+INVARIANT Invariant
diff --git a/tlatools/test-model/pcal/ULQuicksortMacro.tla b/tlatools/test-model/pcal/ULQuicksortMacro.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d99543dccafba75a8851a7581d5f2f3bfcc0cf8f
--- /dev/null
+++ b/tlatools/test-model/pcal/ULQuicksortMacro.tla
@@ -0,0 +1,134 @@
+-------------------------- MODULE ULQuicksortMacro ------------------------- 
+EXTENDS Naturals, Sequences
+
+CONSTANT ArrayLen
+
+ASSUME ArrayLen \in Nat
+
+PermsOf(Arr) ==
+  LET Automorphism(S) == { f \in [S -> S] : 
+                              \A y \in S : \E x \in S : f[x] = y }
+      f ** g == [x \in DOMAIN g |-> f[g[x]]]
+  IN  { Arr ** f : f \in Automorphism(DOMAIN Arr) }
+
+(*   
+--algorithm QuicksortMacro
+  variables A \in [1..ArrayLen -> 1..ArrayLen];
+            returnVal = 1;
+  macro Partition(lo, hi)
+    begin      with piv \in lo..(hi-1)
+                  do returnVal := piv ;
+                     with Ap \in
+                      {AA \in PermsOf(A) :
+                             (\A i \in 1..(lo-1) : AA[i] = A[i])
+                          /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i])
+                          /\ (\A i \in lo..piv, j \in (piv+1)..hi :
+                                  AA[i] \leq AA[j])}
+                        do A := Ap;
+                     end with ;
+                end with;
+    end macro
+  procedure  QS(qlo = 1, qhi = 1)
+    variable pivot = 1 ;
+    begin (*qs1 :*) if qlo < qhi
+                  then       Partition(qlo, qhi) ;
+                       (*qs2 :*) pivot := returnVal ;
+                       (*qs3 :*) call QS(qlo, pivot) ;
+                       (*qs4 :*) call QS(pivot +1,qhi) ;
+                             return ;
+                  else    return;
+                end if;
+    end procedure
+  begin  (*main :*) call QS(1, Len(A)) ;
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-749f221938b5b2dde2ae6c5d6a23511d
+VARIABLES A, returnVal, pc, stack, qlo, qhi, pivot
+
+vars == << A, returnVal, pc, stack, qlo, qhi, pivot >>
+
+Init == (* Global variables *)
+        /\ A \in [1..ArrayLen -> 1..ArrayLen]
+        /\ returnVal = 1
+        (* Procedure QS *)
+        /\ qlo = 1
+        /\ qhi = 1
+        /\ pivot = 1
+        /\ stack = << >>
+        /\ pc = "Lbl_4"
+
+Lbl_1 == /\ pc = "Lbl_1"
+         /\ IF qlo < qhi
+               THEN /\ \E piv \in qlo..(qhi-1):
+                         /\ returnVal' = piv
+                         /\ \E Ap \in {AA \in PermsOf(A) :
+                                             (\A i \in 1..(qlo-1) : AA[i] = A[i])
+                                          /\ (\A i \in (qhi+1)..Len(A) : AA[i] = A[i])
+                                          /\ (\A i \in qlo..piv, j \in (piv+1)..qhi :
+                                                  AA[i] \leq AA[j])}:
+                              A' = Ap
+                    /\ pivot' = returnVal'
+                    /\ pc' = "Lbl_2"
+                    /\ UNCHANGED << stack, qlo, qhi >>
+               ELSE /\ pc' = Head(stack).pc
+                    /\ pivot' = Head(stack).pivot
+                    /\ qlo' = Head(stack).qlo
+                    /\ qhi' = Head(stack).qhi
+                    /\ stack' = Tail(stack)
+                    /\ UNCHANGED << A, returnVal >>
+
+Lbl_2 == /\ pc = "Lbl_2"
+         /\ /\ qhi' = pivot
+            /\ qlo' = qlo
+            /\ stack' = << [ procedure |->  "QS",
+                             pc        |->  "Lbl_3",
+                             pivot     |->  pivot,
+                             qlo       |->  qlo,
+                             qhi       |->  qhi ] >>
+                         \o stack
+         /\ pivot' = 1
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << A, returnVal >>
+
+Lbl_3 == /\ pc = "Lbl_3"
+         /\ /\ qhi' = qhi
+            /\ qlo' = pivot +1
+         /\ pivot' = 1
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << A, returnVal, stack >>
+
+QS == Lbl_1 \/ Lbl_2 \/ Lbl_3
+
+Lbl_4 == /\ pc = "Lbl_4"
+         /\ /\ qhi' = Len(A)
+            /\ qlo' = 1
+            /\ stack' = << [ procedure |->  "QS",
+                             pc        |->  "Done",
+                             pivot     |->  pivot,
+                             qlo       |->  qlo,
+                             qhi       |->  qhi ] >>
+                         \o stack
+         /\ pivot' = 1
+         /\ pc' = "Lbl_1"
+         /\ UNCHANGED << A, returnVal >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == QS \/ Lbl_4
+           \/ Terminating
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ WF_vars(Next)
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-d263fef6f518e6f97b673c4d40ca9921
+
+Invariant == 
+   (pc = "Done") => \A i, j \in 1..ArrayLen :
+                       (i < j) => A[i] \leq A[j]
+
+
+=============================================================================
diff --git a/tlatools/test-model/pcal/UniprocDefine.tla b/tlatools/test-model/pcal/UniprocDefine.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8d3e3a52a71b5c343a8a65d4c28041dcc7cccd5e
--- /dev/null
+++ b/tlatools/test-model/pcal/UniprocDefine.tla
@@ -0,0 +1,76 @@
+----------------------------- MODULE UniprocDefine -------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+(*   
+--algorithm UniprocDefine
+  variables n = 0 ;
+  define nplus1 == n + 1
+         nplus2 == nplus1 + 1
+  end define ;
+  procedure Foo(a)
+    variable b = 2 ;
+    begin foo : n := nplus2 + a + b ;
+                return ;
+    end procedure ; 
+  begin  main : call Foo(2) ;
+         minor: assert n = 6 ;
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-6a1ac8b08c9ed86b40d0f6e5ef5cbdb0
+CONSTANT defaultInitValue
+VARIABLES n, pc, stack
+
+(* define statement *)
+nplus1 == n + 1
+nplus2 == nplus1 + 1
+
+VARIABLES a, b
+
+vars == << n, pc, stack, a, b >>
+
+Init == (* Global variables *)
+        /\ n = 0
+        (* Procedure Foo *)
+        /\ a = defaultInitValue
+        /\ b = 2
+        /\ stack = << >>
+        /\ pc = "main"
+
+foo == /\ pc = "foo"
+       /\ n' = nplus2 + a + b
+       /\ pc' = Head(stack).pc
+       /\ b' = Head(stack).b
+       /\ a' = Head(stack).a
+       /\ stack' = Tail(stack)
+
+Foo == foo
+
+main == /\ pc = "main"
+        /\ /\ a' = 2
+           /\ stack' = << [ procedure |->  "Foo",
+                            pc        |->  "minor",
+                            b         |->  b,
+                            a         |->  a ] >>
+                        \o stack
+        /\ b' = 2
+        /\ pc' = "foo"
+        /\ n' = n
+
+minor == /\ pc = "minor"
+         /\ Assert(n = 6, "Failure of assertion at line 16, column 17.")
+         /\ pc' = "Done"
+         /\ UNCHANGED << n, stack, a, b >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == Foo \/ main \/ minor
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-bb2646c148c04705a11ba77b1f981f05
+=============================================================================
diff --git a/tlatools/test-model/pcal/bug-05-12-10.tla b/tlatools/test-model/pcal/bug-05-12-10.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fd80b54f278e3e8d394f139e67da4d4005822f8c
--- /dev/null
+++ b/tlatools/test-model/pcal/bug-05-12-10.tla
@@ -0,0 +1,43 @@
+CAUSES the translator to throw an exception
+
+last modified on Sun 11 Dec 2005 at  0:30:16 UT by lamport
+
+---------------------------- MODULE bug-05-12-10 ----------------------------
+EXTENDS Naturals, Sequences, FiniteSets , TLC
+(*
+--algorithm Pcal
+  procedure IsAlgorithm(alg)
+    variable res = FALSE ; i = 0 ;
+    begin IA1: assert \/ /\ alg.type = "uniprocess"
+                         /\ DOMAIN alg = {"type", "name", "defs", 
+                                           "decls", "prcds", "body"}
+                      \/ /\ alg.type = "multiiprocess"
+                         /\ DOMAIN alg = {"type", "name", "defs", 
+                                            "decls", "prcds", "procs"};
+               assert alg.name \in STRING ;
+               assert IsExpr(alg.defs) ;
+               assert IsSeq(alg.decls) ;
+               i := Len(alg.decls) ;
+          IA2: while i > 0 do call IsVarDecl(alg.decls[i]) end while ;
+               i := Len(alg.prcds) ;
+          IA3: while i > 0 do call IsProcedure(alg.prcds[i]) end while ;
+               if alg.type = "uniprocess"
+                 then assert IsNonemptySeq(alg.body) ;
+                             i := Len(alg.body) ;
+                 IA4: while i > 0 do call IsLabeledStmt(alg.prcds[i]) 
+                      end while ;
+                 else assert IsNonemptySeq(alg.procs) ;
+                             i := Len(alg.procs) ;
+                 IA5: while i > 0 do call IsLabeledStmt(alg.procs[i]) 
+                      end while ;
+               end if ;
+          IA6: return ;
+    end procedure
+  begin PC1: call IsAlgorithm(alg) ;
+        Pc2: print "IsAlgorithm(alg) = TRUE"
+  end algorithm
+*)
+
+\* BEGIN TRANSLATION
+\* END TRANSLATION
+=============================================================================
diff --git a/tlatools/test-model/pcal/bug_05_10_03.tla b/tlatools/test-model/pcal/bug_05_10_03.tla
new file mode 100644
index 0000000000000000000000000000000000000000..71d4d55ca983a26ad603c5b03f21607284236410
--- /dev/null
+++ b/tlatools/test-model/pcal/bug_05_10_03.tla
@@ -0,0 +1,47 @@
+--algorithm showBug
+
+process Proc \in 1..2 
+variables x=[f1 |-> 1 , f2|->self ];
+
+begin 
+start: x.f1:=self;
+       assert x.f1 = self;
+end process;
+	
+end algorithm
+
+------------- MODULE bug_05_10_03 ------------
+EXTENDS Naturals, TLC, FiniteSets
+------------------------------------------
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-b9dcdef1a5e6ce1735b61fe6aacca0f9
+VARIABLES pc, x
+
+vars == << pc, x >>
+
+ProcSet == (1..2)
+
+Init == (* Process Proc *)
+        /\ x = [self \in 1..2 |-> [f1 |-> 1 , f2|->self ]]
+        /\ pc = [self \in ProcSet |-> "start"]
+
+start(self) == /\ pc[self] = "start"
+               /\ x' = [x EXCEPT ![self].f1 = self]
+               /\ Assert(x'[self].f1 = self, 
+                         "Failure of assertion at line 8, column 8.")
+               /\ pc' = [pc EXCEPT ![self] = "Done"]
+
+Proc(self) == start(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in 1..2: Proc(self))
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-50c9ab33ebfd9eb7e6aeb12b5bbb6bd1
+==========================================
diff --git a/tlatools/test-model/pcal/bug_05_12_10a.tla b/tlatools/test-model/pcal/bug_05_12_10a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bb525e1e1181f2eee1855a7c7ad3743e1bf81e9c
--- /dev/null
+++ b/tlatools/test-model/pcal/bug_05_12_10a.tla
@@ -0,0 +1,309 @@
+last modified on Thu  2 Feb 2006 at 15:40:27 PST by lamport
+
+The translator was renaming i to i_ in procedure IsAlgorithm, but
+it failed to rename the i in the while test of statement IA3.
+
+--------------------------- MODULE bug_05_12_10a ---------------------------
+EXTENDS Naturals, Sequences, FiniteSets , TLC
+IsSeq(x)         == DOMAIN x = 1..Len(x)
+IsNonemptySeq(x) == IsSeq(x) /\ (Len(x) > 0)
+
+alg == [type |-> "uniprocess",
+        name  |-> "Quicksort", 
+        decls  |-> << >>,
+        defs   |-> <<  >>,
+        prcds  |-> << >>,
+        body  |-> <<1>>]
+
+(* 
+--algorithm Pcal
+  procedure IsAlgorithm(A)
+    variable res = FALSE ; i = 0 ;
+    begin IA1: assert \/ /\ A.type = "uniprocess"
+                         /\ DOMAIN A = {"type", "name", "defs", 
+                                           "decls", "prcds", "body"}
+                      \/ /\ A.type = "multiiprocess"
+                         /\ DOMAIN A = {"type", "name", "defs", 
+                                            "decls", "prcds", "procs"};
+               assert A.name \in STRING ;
+               call IsExpr(A.defs) ;
+          IA2: assert IsSeq(A.decls) ;
+               i := Len(A.decls) ;
+          IA3: while i > 0 do call IsVarDecl(A.decls[i]) ;
+                        IA3a: i := i-1 
+               end while ;
+               i := Len(A.prcds) ;
+          IA4: while i > 0 do call IsProcedure(A.prcds[i]) ;
+                        IA4a: i := i-1 
+               end while ;
+               if A.type = "uniprocess"
+                 then assert IsNonemptySeq(A.body) ;
+                             i := Len(A.body) ;
+                 IA5: while i > 0 do call IsLabeledStmt(A.body[i])  ;
+                               IA5a: i := i-1 
+                      end while ;
+                 else assert IsNonemptySeq(A.procs) ;
+                             i := Len(A.procs) ;
+                 IA6: while i > 0 do call IsProcess(A.procs[i])  ;
+                               IA6a: i := i-1 
+                      end while ;
+               end if ;
+          IA7: return ;
+    end procedure
+  procedure IsExpr(exp)
+    variable i ;
+    begin IE1 : assert IsSeq(exp) ;
+               i := Len(exp) ;
+          IE2: while i > 0 do assert exp[i] \in STRING  ;
+                        IA5a: i := i-1 
+               end while ;
+               return               
+    end procedure
+  procedure IsVarDecl(vdcl)
+    begin IV1 : return 
+    end procedure
+  procedure IsProcedure(prcdr)
+    begin IP1 : return 
+    end procedure
+  procedure IsLabeledStmt(lstmt)
+    begin IL1 : return 
+    end procedure
+  procedure IsProcess(proc)
+    begin IPr1 : return 
+    end procedure
+  begin PC1: call IsAlgorithm(alg) ;
+        Pc2: print "IsAlgorithm(alg) = TRUE"
+  end algorithm
+*)
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-019f5cd9d60f4235bafaf7162f8ed3b4
+\* Label IA5a of procedure IsAlgorithm at line 43 col 38 changed to IA5a_
+\* Procedure variable i of procedure IsAlgorithm at line 21 col 28 changed to i_
+CONSTANT defaultInitValue
+VARIABLES pc, stack, A, res, i_, exp, i, vdcl, prcdr, lstmt, proc
+
+vars == << pc, stack, A, res, i_, exp, i, vdcl, prcdr, lstmt, proc >>
+
+Init == (* Procedure IsAlgorithm *)
+        /\ A = defaultInitValue
+        /\ res = FALSE
+        /\ i_ = 0
+        (* Procedure IsExpr *)
+        /\ exp = defaultInitValue
+        /\ i = defaultInitValue
+        (* Procedure IsVarDecl *)
+        /\ vdcl = defaultInitValue
+        (* Procedure IsProcedure *)
+        /\ prcdr = defaultInitValue
+        (* Procedure IsLabeledStmt *)
+        /\ lstmt = defaultInitValue
+        (* Procedure IsProcess *)
+        /\ proc = defaultInitValue
+        /\ stack = << >>
+        /\ pc = "PC1"
+
+IA1 == /\ pc = "IA1"
+       /\ Assert(\/ /\ A.type = "uniprocess"
+                    /\ DOMAIN A = {"type", "name", "defs",
+                                      "decls", "prcds", "body"}
+                 \/ /\ A.type = "multiiprocess"
+                    /\ DOMAIN A = {"type", "name", "defs",
+                                       "decls", "prcds", "procs"}, 
+                 "Failure of assertion at line 22, column 16.")
+       /\ Assert(A.name \in STRING, 
+                 "Failure of assertion at line 28, column 16.")
+       /\ /\ exp' = A.defs
+          /\ stack' = << [ procedure |->  "IsExpr",
+                           pc        |->  "IA2",
+                           i         |->  i,
+                           exp       |->  exp ] >>
+                       \o stack
+       /\ i' = defaultInitValue
+       /\ pc' = "IE1"
+       /\ UNCHANGED << A, res, i_, vdcl, prcdr, lstmt, proc >>
+
+IA2 == /\ pc = "IA2"
+       /\ Assert(IsSeq(A.decls), 
+                 "Failure of assertion at line 30, column 16.")
+       /\ i_' = Len(A.decls)
+       /\ pc' = "IA3"
+       /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >>
+
+IA3 == /\ pc = "IA3"
+       /\ IF i_ > 0
+             THEN /\ /\ stack' = << [ procedure |->  "IsVarDecl",
+                                      pc        |->  "IA3a",
+                                      vdcl      |->  vdcl ] >>
+                                  \o stack
+                     /\ vdcl' = A.decls[i_]
+                  /\ pc' = "IV1"
+                  /\ i_' = i_
+             ELSE /\ i_' = Len(A.prcds)
+                  /\ pc' = "IA4"
+                  /\ UNCHANGED << stack, vdcl >>
+       /\ UNCHANGED << A, res, exp, i, prcdr, lstmt, proc >>
+
+IA3a == /\ pc = "IA3a"
+        /\ i_' = i_-1
+        /\ pc' = "IA3"
+        /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >>
+
+IA4 == /\ pc = "IA4"
+       /\ IF i_ > 0
+             THEN /\ /\ prcdr' = A.prcds[i_]
+                     /\ stack' = << [ procedure |->  "IsProcedure",
+                                      pc        |->  "IA4a",
+                                      prcdr     |->  prcdr ] >>
+                                  \o stack
+                  /\ pc' = "IP1"
+                  /\ i_' = i_
+             ELSE /\ IF A.type = "uniprocess"
+                        THEN /\ Assert(IsNonemptySeq(A.body), 
+                                       "Failure of assertion at line 40, column 23.")
+                             /\ i_' = Len(A.body)
+                             /\ pc' = "IA5"
+                        ELSE /\ Assert(IsNonemptySeq(A.procs), 
+                                       "Failure of assertion at line 45, column 23.")
+                             /\ i_' = Len(A.procs)
+                             /\ pc' = "IA6"
+                  /\ UNCHANGED << stack, prcdr >>
+       /\ UNCHANGED << A, res, exp, i, vdcl, lstmt, proc >>
+
+IA4a == /\ pc = "IA4a"
+        /\ i_' = i_-1
+        /\ pc' = "IA4"
+        /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >>
+
+IA5 == /\ pc = "IA5"
+       /\ IF i_ > 0
+             THEN /\ /\ lstmt' = A.body[i_]
+                     /\ stack' = << [ procedure |->  "IsLabeledStmt",
+                                      pc        |->  "IA5a_",
+                                      lstmt     |->  lstmt ] >>
+                                  \o stack
+                  /\ pc' = "IL1"
+             ELSE /\ pc' = "IA7"
+                  /\ UNCHANGED << stack, lstmt >>
+       /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, proc >>
+
+IA5a_ == /\ pc = "IA5a_"
+         /\ i_' = i_-1
+         /\ pc' = "IA5"
+         /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >>
+
+IA6 == /\ pc = "IA6"
+       /\ IF i_ > 0
+             THEN /\ /\ proc' = A.procs[i_]
+                     /\ stack' = << [ procedure |->  "IsProcess",
+                                      pc        |->  "IA6a",
+                                      proc      |->  proc ] >>
+                                  \o stack
+                  /\ pc' = "IPr1"
+             ELSE /\ pc' = "IA7"
+                  /\ UNCHANGED << stack, proc >>
+       /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, lstmt >>
+
+IA6a == /\ pc = "IA6a"
+        /\ i_' = i_-1
+        /\ pc' = "IA6"
+        /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >>
+
+IA7 == /\ pc = "IA7"
+       /\ pc' = Head(stack).pc
+       /\ res' = Head(stack).res
+       /\ i_' = Head(stack).i_
+       /\ A' = Head(stack).A
+       /\ stack' = Tail(stack)
+       /\ UNCHANGED << exp, i, vdcl, prcdr, lstmt, proc >>
+
+IsAlgorithm == IA1 \/ IA2 \/ IA3 \/ IA3a \/ IA4 \/ IA4a \/ IA5 \/ IA5a_
+                  \/ IA6 \/ IA6a \/ IA7
+
+IE1 == /\ pc = "IE1"
+       /\ Assert(IsSeq(exp), "Failure of assertion at line 55, column 17.")
+       /\ i' = Len(exp)
+       /\ pc' = "IE2"
+       /\ UNCHANGED << stack, A, res, i_, exp, vdcl, prcdr, lstmt, proc >>
+
+IE2 == /\ pc = "IE2"
+       /\ IF i > 0
+             THEN /\ Assert(exp[i] \in STRING, 
+                            "Failure of assertion at line 57, column 31.")
+                  /\ pc' = "IA5a"
+                  /\ UNCHANGED << stack, exp, i >>
+             ELSE /\ pc' = Head(stack).pc
+                  /\ i' = Head(stack).i
+                  /\ exp' = Head(stack).exp
+                  /\ stack' = Tail(stack)
+       /\ UNCHANGED << A, res, i_, vdcl, prcdr, lstmt, proc >>
+
+IA5a == /\ pc = "IA5a"
+        /\ i' = i-1
+        /\ pc' = "IE2"
+        /\ UNCHANGED << stack, A, res, i_, exp, vdcl, prcdr, lstmt, proc >>
+
+IsExpr == IE1 \/ IE2 \/ IA5a
+
+IV1 == /\ pc = "IV1"
+       /\ pc' = Head(stack).pc
+       /\ vdcl' = Head(stack).vdcl
+       /\ stack' = Tail(stack)
+       /\ UNCHANGED << A, res, i_, exp, i, prcdr, lstmt, proc >>
+
+IsVarDecl == IV1
+
+IP1 == /\ pc = "IP1"
+       /\ pc' = Head(stack).pc
+       /\ prcdr' = Head(stack).prcdr
+       /\ stack' = Tail(stack)
+       /\ UNCHANGED << A, res, i_, exp, i, vdcl, lstmt, proc >>
+
+IsProcedure == IP1
+
+IL1 == /\ pc = "IL1"
+       /\ pc' = Head(stack).pc
+       /\ lstmt' = Head(stack).lstmt
+       /\ stack' = Tail(stack)
+       /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, proc >>
+
+IsLabeledStmt == IL1
+
+IPr1 == /\ pc = "IPr1"
+        /\ pc' = Head(stack).pc
+        /\ proc' = Head(stack).proc
+        /\ stack' = Tail(stack)
+        /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, lstmt >>
+
+IsProcess == IPr1
+
+PC1 == /\ pc = "PC1"
+       /\ /\ A' = alg
+          /\ stack' = << [ procedure |->  "IsAlgorithm",
+                           pc        |->  "Pc2",
+                           res       |->  res,
+                           i_        |->  i_,
+                           A         |->  A ] >>
+                       \o stack
+       /\ res' = FALSE
+       /\ i_' = 0
+       /\ pc' = "IA1"
+       /\ UNCHANGED << exp, i, vdcl, prcdr, lstmt, proc >>
+
+Pc2 == /\ pc = "Pc2"
+       /\ PrintT("IsAlgorithm(alg) = TRUE")
+       /\ pc' = "Done"
+       /\ UNCHANGED << stack, A, res, i_, exp, i, vdcl, prcdr, lstmt, proc >>
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == IsAlgorithm \/ IsExpr \/ IsVarDecl \/ IsProcedure \/ IsLabeledStmt
+           \/ IsProcess \/ PC1 \/ Pc2
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-1e7f8dbaa6644662e3fa38b76045e01d
+=============================================================================
diff --git a/tlatools/test-model/pcal/bug_05_12_16b.tla b/tlatools/test-model/pcal/bug_05_12_16b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9dcfff9d2890738f18223210558ea7d665839047
--- /dev/null
+++ b/tlatools/test-model/pcal/bug_05_12_16b.tla
@@ -0,0 +1,76 @@
+--algorithm Dijkstra1
+  procedure Foo(a = 1) 
+   variable x = 42 ; y = x 
+   begin Foo1: return ;
+   end procedure
+  process P \in 1..3
+    begin 
+    P1: assert x = y ;
+        skip ; 
+    P2: call Foo(17) ;
+    end process;
+
+  end algorithm
+
+----------- MODULE bug_05_12_16b -----------
+EXTENDS Naturals, Sequences, TLC
+ 
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-7f01be0bd1e78f7ef9aee292dd8f66fc
+VARIABLES pc, stack, a, x, y
+
+vars == << pc, stack, a, x, y >>
+
+ProcSet == (1..3)
+
+Init == (* Procedure Foo *)
+        /\ a = [ self \in ProcSet |-> 1]
+        /\ x = [ self \in ProcSet |-> 42]
+        /\ y = [ self \in ProcSet |-> x[self]]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> "P1"]
+
+Foo1(self) == /\ pc[self] = "Foo1"
+              /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+              /\ x' = [x EXCEPT ![self] = Head(stack[self]).x]
+              /\ y' = [y EXCEPT ![self] = Head(stack[self]).y]
+              /\ a' = [a EXCEPT ![self] = Head(stack[self]).a]
+              /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+
+Foo(self) == Foo1(self)
+
+P1(self) == /\ pc[self] = "P1"
+            /\ Assert(x[self] = y[self], 
+                      "Failure of assertion at line 8, column 9.")
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "P2"]
+            /\ UNCHANGED << stack, a, x, y >>
+
+P2(self) == /\ pc[self] = "P2"
+            /\ /\ a' = [a EXCEPT ![self] = 17]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "Foo",
+                                                        pc        |->  "Done",
+                                                        x         |->  x[self],
+                                                        y         |->  y[self],
+                                                        a         |->  a[self] ] >>
+                                                    \o stack[self]]
+            /\ x' = [x EXCEPT ![self] = 42]
+            /\ y' = [y EXCEPT ![self] = x'[self]]
+            /\ pc' = [pc EXCEPT ![self] = "Foo1"]
+
+P(self) == P1(self) \/ P2(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in ProcSet: Foo(self))
+           \/ (\E self \in 1..3: P(self))
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-e3a05375e3bf2d3d55b6d92003f1f2fa
+
+========================================
diff --git a/tlatools/test-model/pcal/bug_05_12_31.tla b/tlatools/test-model/pcal/bug_05_12_31.tla
new file mode 100644
index 0000000000000000000000000000000000000000..79fbadc5e9de7a735bd9cac3f1b8679f1d75c268
--- /dev/null
+++ b/tlatools/test-model/pcal/bug_05_12_31.tla
@@ -0,0 +1,66 @@
+
+------------------------------- MODULE bug_05_12_31 ----------------------------- 
+EXTENDS Naturals, Sequences, TLC
+
+
+(*   
+--algorithm Test
+   procedure P(a = 7) 
+      variable x = a ; y = x+1 ;
+      begin P1: assert a = 1;
+                assert x = a;
+                assert y = a+1;
+                return;
+      end procedure 
+     begin A: call P(1)
+  end algorithm
+*)
+					
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1f8e16f0d9c2b45f0271cd3d8415f9b8
+VARIABLES pc, stack, a, x, y
+
+vars == << pc, stack, a, x, y >>
+
+Init == (* Procedure P *)
+        /\ a = 7
+        /\ x = a
+        /\ y = x+1
+        /\ stack = << >>
+        /\ pc = "A"
+
+P1 == /\ pc = "P1"
+      /\ Assert(a = 1, "Failure of assertion at line 10, column 17.")
+      /\ Assert(x = a, "Failure of assertion at line 11, column 17.")
+      /\ Assert(y = a+1, "Failure of assertion at line 12, column 17.")
+      /\ pc' = Head(stack).pc
+      /\ x' = Head(stack).x
+      /\ y' = Head(stack).y
+      /\ a' = Head(stack).a
+      /\ stack' = Tail(stack)
+
+P == P1
+
+A == /\ pc = "A"
+     /\ /\ a' = 1
+        /\ stack' = << [ procedure |->  "P",
+                         pc        |->  "Done",
+                         x         |->  x,
+                         y         |->  y,
+                         a         |->  a ] >>
+                     \o stack
+     /\ x' = a'
+     /\ y' = x'+1
+     /\ pc' = "P1"
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == pc = "Done" /\ UNCHANGED vars
+
+Next == P \/ A
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(pc = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-6df4c643b7934c47f1761b2b8aced244
+=============================================================================
diff --git a/tlatools/test-model/pcal/bug_06_01_25.tla b/tlatools/test-model/pcal/bug_06_01_25.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5d52e83356e07bb122f08c19108898b4bd4f3709
--- /dev/null
+++ b/tlatools/test-model/pcal/bug_06_01_25.tla
@@ -0,0 +1,228 @@
+
+--algorithm TestAlignment
+   variable x ; y ; z = /\ \A i \in {1} : i > 0 
+                        /\ \A i \in {1} : i > 0 
+                        /\ \A i \in {1} : i > 0 
+   define  foo == /\ \A i \in {1} : i > 0 
+                  /\ \A i \in {1} : i > 0 
+                  /\ \A i \in {1} : i > 0 
+   end define;
+   macro Mac(a) begin x := \A i \in {1} : i > 0 ;
+                      y := \A i \in {1} : i > 0 ;
+                      z := \A i \in {1} : i > 0 ;
+                      assert /\ \A i \in {1} : i > 0 
+                             /\ \A i \in {1} : i > 0 
+                             /\ \A i \in {1} : i > 0  ;
+                      assert a 
+   end macro;
+     
+   procedure P(a = /\ \A i \in {1} : i > 0 
+                   /\ \A i \in {1} : i > 0 
+                   /\ \A i \in {1} : i > 0)
+      begin P1: x := \A i \in {1} : i > 0 ;
+                y := \A i \in {1} : i > 0 ;
+                z := \A i \in {1} : i > 0 ;
+                return
+      end procedure
+   process Q \in {qq \in {1,2} : /\ \A i \in {1} : i > 0 
+                                 /\ \A j \in {1} : j > 0 
+                                 /\ \A k \in {1} : k > 0 }
+     variable w = /\ \A i \in {1} : i > 0 
+                  /\ \A i \in {1} : i > 0 
+                  /\ \A i \in {1} : i > 0 
+      begin P1: if /\ \A i \in {1} : i > 0 
+                   /\ \A i \in {1} : i > 0 
+                   /\ \A i \in {1} : i > 0  then x := \A i \in {1} : i > 0 ;
+                                                 y := \A i \in {1} : i > 0 ;
+                                                 z := \A i \in {1} : i > 0 
+                  else x := \A i \in {1} : i > 0 ;
+                       y := \A i \in {1} : i > 0 ;
+                       z := \A i \in {1} : i > 0                       
+                 end if ;
+           P15:  with k  \in {kk \in {2,3} : /\ \A i \in {1} : i > 0 
+                                             /\ \A i \in {1} : i > 0 
+                                             /\ \A i \in {1} : i > 0 } do
+                   with m = /\ \A i \in {1} : i > 0 
+                            /\ \A i \in {1} : i > 0 
+                            /\ \A i \in {1} : i > 0  do
+                    x := \A i \in {1} : i > 0 ;
+                    y := \A i \in {1} : i > 0 ;
+                    z := \A i \in {1} : i > 0  
+                 end with end with ;
+            P2: while /\ \A i \in {1} : i > 0 
+                      /\ \A i \in {1} : i > 0 
+                      /\ \A i \in {1} : i > 0 
+                      /\ FALSE do
+                   x := \A i \in {1} : i > 0 ;
+                   y := \A i \in {1} : i > 0 ;
+                   z := \A i \in {1} : i > 0 ;
+                   w := /\ \A i \in {1} : i > 0 
+                        /\ \A i \in {1} : i > 0 
+                        /\ \A i \in {1} : i > 0 
+                end while ;
+            P3: call P(/\ \A i \in {1} : i > 0 
+                       /\ \A i \in {1} : i > 0 
+                       /\ \A i \in {1} : i > 0) ;
+            P4: assert /\ \A i \in {1} : i > 0 
+                       /\ \A i \in {1} : i > 0 
+                       /\ \A i \in {1} : i > 0 ;
+                print  /\ \A i \in {1} : i > 0 
+                       /\ \A i \in {1} : i > 0 
+                       /\ \A i \in {1} : i > 0 ;
+            P5: Mac(/\ \A i \in {1} : i > 0 
+                    /\ \A i \in {1} : i > 0 
+                    /\ \A i \in {1} : i > 0 )
+     end process
+   end algorithm
+
+----------- MODULE bug_06_01_25 -----------
+EXTENDS Sequences, Naturals, TLC
+
+ASSUME \forall i \in {} : i \geq 1
+
+\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9ef67f07b10dbbf43fc7d4cb2d8d7b02
+\* Label P1 of procedure P at line 22 col 17 changed to P1_
+CONSTANT defaultInitValue
+VARIABLES x, y, z, pc, stack
+
+(* define statement *)
+foo == /\ \A i \in {1} : i > 0
+       /\ \A i \in {1} : i > 0
+       /\ \A i \in {1} : i > 0
+
+VARIABLES a, w
+
+vars == << x, y, z, pc, stack, a, w >>
+
+ProcSet == ({qq \in {1,2} : /\ \A i \in {1} : i > 0
+                            /\ \A j \in {1} : j > 0
+                            /\ \A k \in {1} : k > 0 })
+
+Init == (* Global variables *)
+        /\ x = defaultInitValue
+        /\ y = defaultInitValue
+        /\ z = (/\ \A i \in {1} : i > 0
+                /\ \A i \in {1} : i > 0
+                /\ \A i \in {1} : i > 0)
+        (* Procedure P *)
+        /\ a = [ self \in ProcSet |-> /\ \A i \in {1} : i > 0
+                                      /\ \A i \in {1} : i > 0
+                                      /\ \A i \in {1} : i > 0]
+        (* Process Q *)
+        /\ w = [self \in {qq \in {1,2} : /\ \A i \in {1} : i > 0
+                                         /\ \A j \in {1} : j > 0
+                                         /\ \A k \in {1} : k > 0 } |-> /\ \A i \in {1} : i > 0
+                                                                       /\ \A i \in {1} : i > 0
+                                                                       /\ \A i \in {1} : i > 0]
+        /\ stack = [self \in ProcSet |-> << >>]
+        /\ pc = [self \in ProcSet |-> "P1"]
+
+P1_(self) == /\ pc[self] = "P1_"
+             /\ x' = (\A i \in {1} : i > 0)
+             /\ y' = (\A i \in {1} : i > 0)
+             /\ z' = (\A i \in {1} : i > 0)
+             /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
+             /\ a' = [a EXCEPT ![self] = Head(stack[self]).a]
+             /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
+             /\ w' = w
+
+P(self) == P1_(self)
+
+P1(self) == /\ pc[self] = "P1"
+            /\ IF /\ \A i \in {1} : i > 0
+                  /\ \A i \in {1} : i > 0
+                  /\ \A i \in {1} : i > 0
+                  THEN /\ x' = (\A i \in {1} : i > 0)
+                       /\ y' = (\A i \in {1} : i > 0)
+                       /\ z' = (\A i \in {1} : i > 0)
+                  ELSE /\ x' = (\A i \in {1} : i > 0)
+                       /\ y' = (\A i \in {1} : i > 0)
+                       /\ z' = (\A i \in {1} : i > 0)
+            /\ pc' = [pc EXCEPT ![self] = "P15"]
+            /\ UNCHANGED << stack, a, w >>
+
+P15(self) == /\ pc[self] = "P15"
+             /\ \E k \in {kk \in {2,3} : /\ \A i \in {1} : i > 0
+                                         /\ \A i \in {1} : i > 0
+                                         /\ \A i \in {1} : i > 0 }:
+                  LET m == /\ \A i \in {1} : i > 0
+                           /\ \A i \in {1} : i > 0
+                           /\ \A i \in {1} : i > 0 IN
+                    /\ x' = (\A i \in {1} : i > 0)
+                    /\ y' = (\A i \in {1} : i > 0)
+                    /\ z' = (\A i \in {1} : i > 0)
+             /\ pc' = [pc EXCEPT ![self] = "P2"]
+             /\ UNCHANGED << stack, a, w >>
+
+P2(self) == /\ pc[self] = "P2"
+            /\ IF /\ \A i \in {1} : i > 0
+                  /\ \A i \in {1} : i > 0
+                  /\ \A i \in {1} : i > 0
+                  /\ FALSE
+                  THEN /\ x' = (\A i \in {1} : i > 0)
+                       /\ y' = (\A i \in {1} : i > 0)
+                       /\ z' = (\A i \in {1} : i > 0)
+                       /\ w' = [w EXCEPT ![self] = /\ \A i \in {1} : i > 0
+                                                   /\ \A i \in {1} : i > 0
+                                                   /\ \A i \in {1} : i > 0]
+                       /\ pc' = [pc EXCEPT ![self] = "P2"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "P3"]
+                       /\ UNCHANGED << x, y, z, w >>
+            /\ UNCHANGED << stack, a >>
+
+P3(self) == /\ pc[self] = "P3"
+            /\ /\ a' = [a EXCEPT ![self] = /\ \A i \in {1} : i > 0
+                                           /\ \A i \in {1} : i > 0
+                                           /\ \A i \in {1} : i > 0]
+               /\ stack' = [stack EXCEPT ![self] = << [ procedure |->  "P",
+                                                        pc        |->  "P4",
+                                                        a         |->  a[self] ] >>
+                                                    \o stack[self]]
+            /\ pc' = [pc EXCEPT ![self] = "P1_"]
+            /\ UNCHANGED << x, y, z, w >>
+
+P4(self) == /\ pc[self] = "P4"
+            /\ Assert(/\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0, 
+                      "Failure of assertion at line 66, column 17.")
+            /\ PrintT(/\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0)
+            /\ pc' = [pc EXCEPT ![self] = "P5"]
+            /\ UNCHANGED << x, y, z, stack, a, w >>
+
+P5(self) == /\ pc[self] = "P5"
+            /\ x' = (\A i \in {1} : i > 0)
+            /\ y' = (\A i \in {1} : i > 0)
+            /\ z' = (\A i \in {1} : i > 0)
+            /\ Assert(/\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0, 
+                      "Failure of assertion at line 13, column 23 of macro called at line 72, column 17.")
+            /\ Assert(/\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0
+                      /\ \A i \in {1} : i > 0, 
+                      "Failure of assertion at line 16, column 23 of macro called at line 72, column 17.")
+            /\ pc' = [pc EXCEPT ![self] = "Done"]
+            /\ UNCHANGED << stack, a, w >>
+
+Q(self) == P1(self) \/ P15(self) \/ P2(self) \/ P3(self) \/ P4(self)
+              \/ P5(self)
+
+(* Allow infinite stuttering to prevent deadlock on termination. *)
+Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
+               /\ UNCHANGED vars
+
+Next == (\E self \in ProcSet: P(self))
+           \/ (\E self \in {qq \in {1,2} : /\ \A i \in {1} : i > 0
+                                           /\ \A j \in {1} : j > 0
+                                           /\ \A k \in {1} : k > 0 }: Q(self))
+           \/ Terminating
+
+Spec == Init /\ [][Next]_vars
+
+Termination == <>(\A self \in ProcSet: pc[self] = "Done")
+
+\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c0ae8bdf7ad8a969ed412d1b64210cb9
+========================================
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/BasicMultiTrace.tla b/tlatools/test-model/simulation/BasicMultiTrace/BasicMultiTrace.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c7d0ec58d042f7ed37c209571739538b8767ad8e
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/BasicMultiTrace.tla
@@ -0,0 +1,63 @@
+------------------------------ MODULE BasicMultiTrace ------------------------------
+EXTENDS Integers
+
+\*
+\* This spec attempts to define a simple state machine that has multiple behaviors. 
+\* Originally added to verify correctness of simulation mode in both single and multi-threaded mode.
+\*
+
+VARIABLE branch, depth
+
+MaxDepth == 5
+Width == 10
+
+Init == 
+    /\ branch = 0
+    /\ depth = 0
+
+Next == 
+    \* Pick a branch
+    \/ /\ branch = 0
+       /\ branch' \in 1..Width /\ UNCHANGED depth
+    \* Traverse down that branch.
+    \/ /\ branch > 0 
+       /\ depth < MaxDepth
+       /\ depth' = depth + 1 /\ UNCHANGED branch
+
+Spec == Init /\ [][Next]_<<branch, depth>>
+
+UnderspecifiedNext == branch' = 0
+UnderspecifiedInit == branch = 0
+
+UnderspecifiedInitSpec == UnderspecifiedInit /\ [][Next]_<<branch, depth>>
+UnderspecifiedNextSpec == Init /\ [][UnderspecifiedNext]_<<branch, depth>>
+
+\* A valid invariant that should be violated by the initial state.
+InvInitState == ~(branch = 0)
+
+\* A valid invariant that should be violated by the spec.
+Inv == ~(depth = 2)
+
+\* A nonsensical invariant that will cause a runtime evaluation error on the initial state.
+InvBadEvalInitState == (0 = "a")
+
+\* A nonsensical invariant that will cause a runtime evaluation error on a non-initial state.
+InvBadEvalNonInitState == IF branch = 0 THEN TRUE ELSE (0 = "a")
+
+\* A valid action property that should be violated by the spec.
+ActionProp == [][branch' > branch]_<<branch, depth>>
+
+\* A nonsensical action property that will cause a runtime evaluation error.
+ActionPropBadEval == [][IF branch = 0 THEN TRUE ELSE 0 = "a"]_<<branch, depth>>
+
+\* A simple state and action constraint, and an invariant that should hold true 
+\* given either constraint.
+StateConstraint == depth < 4
+ActionConstraint == ~(depth' = 4 /\ depth = 3)
+InvConstraint   == depth <= 4
+
+\* A liveness property that should be violated.
+LivenessProp == <>(branch = -1)
+
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MC.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b763c9c194f8988812ce02ebc9f9012fe3a8b442
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MC.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION
+Spec
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCActionProp.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCActionProp.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..02c18eae9509cfeae0f88a0a8d500b6ae3da156e
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCActionProp.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+ActionProp
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCActionPropBadEval.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCActionPropBadEval.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bba9f9740204b197987977900a83e634a9dc9927
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCActionPropBadEval.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+ActionPropBadEval
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvInitState.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvInitState.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d01b36afe2405e3a02bd8a911bd65a427edc8f36
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvInitState.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+InvBadEvalInitState
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvNonInitState.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvNonInitState.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6b7abf91a8e841503ff4472fbac021640a9c6c6d
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvNonInitState.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+InvBadEvalNonInitState
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCInv.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCInv.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCInv.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCInvInitState.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCInvInitState.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..564eb2d977457b3b535e66584faa46db08cd978e
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCInvInitState.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+INVARIANT
+InvInitState
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCLivenessProp.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCLivenessProp.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..2a5e4e9fb0e9b45e8355d4bf71d4f3f396cd7704
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCLivenessProp.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+PROPERTY
+LivenessProp
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecInit.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecInit.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b1919bb49034f496f5204a62eacb6990c13e1ced
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecInit.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+UnderspecifiedInitSpec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecNext.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecNext.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..456896216d24793a361be6563c330eed792e0ab0
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecNext.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+UnderspecifiedNextSpec
+INVARIANT
+Inv
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCWithActionConstraint.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCWithActionConstraint.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8e14f70964e994bb30e651bb896f0903de532b4b
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCWithActionConstraint.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION
+Spec
+INVARIANT
+InvConstraint
+ACTION_CONSTRAINT
+ActionConstraint
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCWithConstraint.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCWithConstraint.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..eb2ec2045cb216aca236f52b19e032281b7610c0
--- /dev/null
+++ b/tlatools/test-model/simulation/BasicMultiTrace/MCWithConstraint.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION
+Spec
+INVARIANT
+InvConstraint
+CONSTRAINT
+StateConstraint
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BenchmarkSpec/BenchmarkSpec.tla b/tlatools/test-model/simulation/BenchmarkSpec/BenchmarkSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..49eca6bb354bab0a5b5a450cbfded41e7c5d71d1
--- /dev/null
+++ b/tlatools/test-model/simulation/BenchmarkSpec/BenchmarkSpec.tla
@@ -0,0 +1,28 @@
+------------------------------ MODULE BenchmarkSpec ------------------------------
+EXTENDS Integers, Sequences
+
+\*
+\* This spec produces a state space with a simple tree-like structure. The maximum depth and branching
+\* factor of the state space tree can be controlled by setting the constant values of the spec. The goal
+\* is to give a spec whose state space structure is easily understandable and controllable, for the sake
+\* of running performance and/or correctness tests of TLC.
+\*
+
+VARIABLE hist
+
+Init == hist = <<>>
+
+CONSTANTS Depth, BranchingFactor
+    
+Next ==
+    \E e \in 1..BranchingFactor :
+    hist' = Append(hist, e)    
+
+Spec == Init /\ [][Next]_<<hist>>
+
+Constraint == Len(hist) < Depth
+
+\* An arbitrary state of max length. It's just a sequence filled with all 1's.
+Inv == hist # [i \in 1..Depth |-> 1]
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/BenchmarkSpec/MCInv.cfg b/tlatools/test-model/simulation/BenchmarkSpec/MCInv.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4606d873bd81a8dc67cc6faf0a8337dd9c7e7421
--- /dev/null
+++ b/tlatools/test-model/simulation/BenchmarkSpec/MCInv.cfg
@@ -0,0 +1,9 @@
+SPECIFICATION
+Spec
+INVARIANT
+Inv
+CONSTANTS
+BranchingFactor = 3
+Depth = 15
+CONSTRAINT
+Constraint
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/NQSpec/MC.cfg b/tlatools/test-model/simulation/NQSpec/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ed49c011a10f683b730d990530404af87d6889ee
--- /dev/null
+++ b/tlatools/test-model/simulation/NQSpec/MC.cfg
@@ -0,0 +1,47 @@
+\* MV CONSTANT declarations
+CONSTANTS
+e1 = e1
+\* MV CONSTANT declarations
+CONSTANTS
+id1 = id1
+\* MV CONSTANT declarations
+CONSTANTS
+d1 = d1
+\* MV CONSTANT declarations
+CONSTANTS
+v1 = v1
+\* MV CONSTANT definitions
+CONSTANT
+EnQers <- const_14476073153412000
+\* MV CONSTANT definitions
+CONSTANT
+Ids <- const_14476073153513000
+\* MV CONSTANT definitions
+CONSTANT
+DeQers <- const_14476073153614000
+\* MV CONSTANT definitions
+CONSTANT
+Data <- const_14476073153715000
+\* CONSTANT declarations
+CONSTANT Busy = Busy
+\* CONSTANT declarations
+CONSTANT NoData = NoData
+\* CONSTANT definitions
+CONSTANT
+InitData <- const_14476073153816000
+\* CONSTANT definition
+CONSTANT
+Done = Done
+NotAnId = NotAnId
+NotAnElement = NotAnElement
+top = top
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144760731543311000
+\* INVARIANT definition
+INVARIANT
+inv_144760731544312000
+\* PROPERTY definition
+PROPERTY
+prop_144760731545313000
+\* Generated on Sun Nov 15 18:08:35 CET 2015
\ No newline at end of file
diff --git a/tlatools/test-model/simulation/NQSpec/MC.tla b/tlatools/test-model/simulation/NQSpec/MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..88d4a9a0075de01213b05a9a2c52d6dafb5709c4
--- /dev/null
+++ b/tlatools/test-model/simulation/NQSpec/MC.tla
@@ -0,0 +1,63 @@
+---- MODULE MC ----
+EXTENDS NQSpecImpliesQSpec, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+e1
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+id1
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+d1
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+v1
+----
+
+\* MV CONSTANT definitions EnQers
+const_14476073153412000 == 
+{e1}
+----
+
+\* MV CONSTANT definitions Ids
+const_14476073153513000 == 
+{id1}
+----
+
+\* MV CONSTANT definitions DeQers
+const_14476073153614000 == 
+{d1}
+----
+
+\* MV CONSTANT definitions Data
+const_14476073153715000 == 
+{v1}
+----
+
+\* CONSTANT definitions @modelParameterConstants:4InitData
+const_14476073153816000 == 
+v1
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144760731543311000 ==
+SpecI
+----
+\* INVARIANT definition @modelCorrectnessInvariants:0
+inv_144760731544312000 ==
+TypeOKI
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144760731545313000 ==
+I!Spec
+----
+=============================================================================
+\* Modification History
+\* Created Sun Nov 15 18:08:35 CET 2015 by markus
diff --git a/tlatools/test-model/simulation/NQSpec/NQSpec.tla b/tlatools/test-model/simulation/NQSpec/NQSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..7affcc75a616acf5e1f1fd441f5eb4fbafc2da0d
--- /dev/null
+++ b/tlatools/test-model/simulation/NQSpec/NQSpec.tla
@@ -0,0 +1,89 @@
+------------------------------- MODULE NQSpec -------------------------------
+EXTENDS Integers, Sequences, TLC
+
+CONSTANTS EnQers, DeQers, Data, InitData, Ids, Busy, NoData
+
+ASSUME InitData \in Data
+
+Done == CHOOSE D : D \notin Data
+\*Busy == CHOOSE D : D \notin Data
+NotAnId == CHOOSE i : i \notin Ids
+\*NoData == CHOOSE D : D \notin Data
+Elements == [data : Data, id : Ids]
+NotAnElement == CHOOSE E : E \notin Elements
+
+VARIABLES enq, deq, after, adding
+vars == <<enq, deq, after, adding>>
+
+elts == DOMAIN after
+
+doneElts == elts \ {adding[e] : e \in EnQers}
+
+TypeOK == /\ enq \in [EnQers -> Data \cup {Done}]
+          /\ deq \in [DeQers -> Data \cup {Busy}]
+          /\ elts \in SUBSET Elements
+          /\ after \in [elts -> SUBSET doneElts]
+          /\ adding \in [EnQers -> Elements \cup {NotAnElement}]
+
+Init ==         
+        /\ TLCSet(42, 1)
+        /\ enq = [e \in EnQers |-> Done]
+        /\ deq = [d \in DeQers |-> InitData]
+        /\ after = << >>
+        /\ adding = [e \in EnQers |-> NotAnElement]
+-----------------------------------------------------------------------------
+
+Assign(var, idx, val) == var' = [var EXCEPT ![idx] = val]
+
+IdsBeingAdded == {adding[el].id : el \in {e \in EnQers : adding[e] # NotAnElement}}
+
+UnusedIds == (Ids \ {el.id : el \in elts}) \ IdsBeingAdded
+
+
+AddElt(el) == 
+  after' = [x \in elts \cup {el} |-> 
+                 IF x = el THEN doneElts
+                           ELSE after[x] ]
+                           
+RemoveElt(el) == 
+  after' = [x \in elts \ {el} |-> after[x] \ {el}]
+
+MinimalElts(After) == {el \in DOMAIN After : After[el] = {}}
+minimalElts == MinimalElts(after)
+      
+BeginEnq(e) == /\ enq[e] = Done 
+               /\ \E D \in Data, id \in UnusedIds : 
+                     LET el == [data |-> D, id |-> id]
+                     IN  /\ Assign(enq, e, D)
+                         /\ AddElt(el)
+                         /\ Assign(adding, e, el)
+               /\ UNCHANGED deq
+                           
+EndEnq(e) == /\ enq[e] # Done
+             /\ Assign(enq, e, Done)
+             /\ Assign(adding, e, NotAnElement)
+             /\ UNCHANGED <<deq, after>>
+
+\*  enq, deq, elts, after, adding
+BeginDeq(d) == /\ deq[d] # Busy
+               /\ Assign(deq, d, Busy)
+               /\ UNCHANGED <<enq, after, adding>>
+               
+EndDeq(d) == /\ deq[d] = Busy
+             /\ \E el \in minimalElts :
+                  /\ RemoveElt(el)
+                  /\ Assign(deq, d, el.data)
+             /\ UNCHANGED <<enq, adding>>
+             
+Next == \/ \E e \in EnQers : BeginEnq(e)  \/ EndEnq(e)
+        \/ \E d \in DeQers : BeginDeq(d)  \/ EndDeq(d)
+
+Liveness == /\ \A e \in EnQers : WF_vars(BeginEnq(e)  \/ EndEnq(e))
+            /\ \A d \in DeQers :  WF_vars(BeginDeq(d)  \/ EndDeq(d))
+            
+Spec == Init /\ [][Next]_vars  /\ Liveness
+
+=============================================================================
+\* Modification History
+\* Last modified Sat Nov 14 16:00:53 PST 2015 by lamport
+\* Created Thu Nov 05 15:07:25 PST 2015 by lamport
diff --git a/tlatools/test-model/simulation/NQSpec/NQSpecImpliesQSpec.tla b/tlatools/test-model/simulation/NQSpec/NQSpecImpliesQSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2197c3620e3a2d900ef513d986a489b4c9e52543
--- /dev/null
+++ b/tlatools/test-model/simulation/NQSpec/NQSpecImpliesQSpec.tla
@@ -0,0 +1,212 @@
+------------------------- MODULE NQSpecImpliesQSpec -------------------------
+EXTENDS NQSpec, FiniteSets, TLC
+
+\*NoData == NotData
+NatSetMax(S) == IF S = {} THEN 0 ELSE CHOOSE n \in S : \A m \in S : n >=m
+VARIABLE p, \* The prophecy variable = sequence of next values read,
+            \* of length Cardinality(elts)
+         Q, \* The queue of QSpec, except as Elements with their Ids,
+            \* and except that it is changed only on the actions
+            \* of NQSpec.  Added as a history variable.
+         s,
+            \* A stuttering variable to add the actions
+            \* corresponding to the DoEnq and DoDeq actions of QSpec
+         enqInner, deqInner
+            \* History variables implementing the corresponding variables
+            \* of  QSpec, except deqInner gets set to an Element el instead
+            \* of el.data
+
+\* The order of adding the variables is:
+\*   s, p, Q, {queue, enqInner, deqInner}
+varsI == <<vars, p, Q,  s, enqInner, deqInner>>
+
+top == CHOOSE t : t \notin {}
+bot == [EndEnq |-> TRUE, EndDeq |-> TRUE]
+
+TypeOKI == /\ TypeOK
+           /\ Q \in Seq(Elements)
+           /\ p \in Seq(Elements)
+           /\ s \in {top} 
+                 \cup [type : {"EndDeq"}, name : DeQers \X Elements, val : {FALSE, TRUE}]
+                 \cup [type : {"EndEnq"}, name : EnQers, val : {TRUE}]
+                 \cup [type : {"BeginEnq"}, name : EnQers, val : Seq(Elements)] 
+           /\ enqInner \in [EnQers -> {Done, Busy}]
+           /\ deqInner \in [DeQers -> {NoData} \cup Elements]
+
+InitI == /\ Init
+         /\ Q = << >>
+         /\ enqInner = [e \in EnQers |-> Done]
+         /\ \E i \in Ids : deqInner = [d \in DeQers |-> [data |-> InitData,
+                                                         id |-> i]]
+         /\ p = << >>
+         /\ s = top
+         /\ TLCSet(1, {})
+
+RemoveFrom(E, After) == [x \in (DOMAIN After) \  E |-> After[x] \ E]
+RECURSIVE IsConsistent(_, _)
+IsConsistent(seq, After) ==
+  IF seq = << >>
+    THEN TRUE
+    ELSE LET hd == Head(seq)
+             Elts == DOMAIN After
+         IN /\ (hd \in DOMAIN After) /\ (After[hd] = {})
+            /\ IsConsistent(Tail(seq),
+                            RemoveFrom({hd}, After))
+                            
+IsBlocking(el, After) == LET Elts == DOMAIN After
+                             MinElts == MinimalElts(After)
+                         IN  (el \notin MinElts) /\ (doneElts # {})
+
+
+AllEnabled == s = top
+
+PostStutterEnabled(type, name) == (s # top) /\ (s.type = type) /\ (s.name = name)
+PreStutterEnabled(type, name) ==  (s # top) =>
+                                     /\ (s.type = type) /\ (s.name = name)
+                                     /\ s.val # bot[s.type]
+AfterPreStutterEnabled(type, name) == /\ s # top
+                                      /\ (s.type = type) /\ (s.name = name)
+                                      /\ s.val = bot[s.type]
+                              
+SetStutter(type, name, val) == 
+   /\ (s # top) =>  Assert(s.type = type /\ s.name = name,
+                           "SetStutter not executed by owner of stuttering variable")
+   /\ s' = [type |-> type, name |-> name, val |-> val]
+   
+Prefix(n, seq) == [i \in 1..n |-> seq[i]]
+Suffix(n, seq) == [i \in 1..(Len(seq)-n) |-> seq[i+n]]
+SeqElts(seq) == {seq[i] : i \in 1..Len(seq)}
+
+PInv == /\ Cardinality(elts) = Len(p)
+
+QInv == /\ Cardinality(SeqElts(Q)) = Len(Q) 
+        /\ SeqElts(Q) \subseteq elts
+        /\ IsConsistent(Q, after)
+        /\ LET n == NatSetMax({i \in 1..Len(Q) : Prefix(i, Q) = Prefix(i, p)})
+           IN (n < Len(Q)) => IsBlocking(p[n+1], 
+                                         RemoveFrom(SeqElts(Prefix(n, Q)), after))  
+
+Writer(elt) == CHOOSE e \in EnQers : adding[e] = elt
+
+BeginEnqI(e) == /\ AllEnabled 
+                /\ BeginEnq(e) 
+                /\ \E el \in Elements : p' = Append(p, el)
+\*                /\ TLCSet(1, {p'} \cup TLCGet(1))
+\*                /\ IF (Elements \X Elements \subseteq TLCGet(1))
+\*                     THEN /\ PrintT(TLCGet(1))
+\*                          /\ Assert (FALSE, "There")
+\*                     ELSE TRUE
+\*\*                /\ PrintT(<<e, p, p', after'>>)
+                /\ LET RECURSIVE ToAdd(_, _)
+                       ToAdd(i, After) == 
+                         IF (i > Len(p')) \/ p'[i] \notin MinimalElts(After)
+                           THEN << >> 
+                           ELSE <<p'[i]>> \o ToAdd(i+1, RemoveFrom({p'[i]}, After))
+                       TA == ToAdd(Len(Q)+1, RemoveFrom(SeqElts(Q), after'))
+                   IN  IF TA # << >>
+                         THEN SetStutter("BeginEnq", e, TA) 
+                         ELSE s' = s
+               /\ Assign(enqInner, e, Busy)
+               /\ UNCHANGED <<Q, deqInner>>
+                 
+BeginEnqIDo(e) == /\ PostStutterEnabled("BeginEnq", e)
+                  /\ Q' = Append(Q, Head(s.val))
+                  /\ Assign(enqInner, Writer(Head(s.val)), Done)
+                  /\ IF Tail(s.val) # << >> THEN SetStutter("BeginEnq", e, Tail(s.val))
+                                            ELSE s' = top
+                  /\ UNCHANGED <<vars, p, deqInner>>
+                  
+DoEnqI(e) == /\ PreStutterEnabled("EndEnq", e)
+             /\ enq[e] # Done 
+             /\ IF enqInner[e] # Done \* adding[e] \notin SeqElts(Q)
+                 THEN /\ Q' = Append(Q, adding[e])
+                      /\ Assign(enqInner, e, Done)
+                 ELSE UNCHANGED <<Q, enqInner>>
+             /\ SetStutter("EndEnq", e, TRUE)
+             /\ UNCHANGED <<vars, p, deqInner>>
+                              
+EndEnqI(e) ==  /\ AfterPreStutterEnabled("EndEnq", e) 
+               /\ EndEnq(e) 
+               /\ s' = top
+               /\ UNCHANGED << p, Q, enqInner, deqInner>>
+
+BeginDeqI(d) == /\ AllEnabled
+                /\ BeginDeq(d) 
+                /\ Assign(deqInner, d, NoData)
+                /\ UNCHANGED <<p, Q, s, enqInner>>
+
+\* OLD DEFS:
+\*DoDeqI(d) == /\ PreStutterEnabled("EndDeq", d)
+\*             /\ deq[d] = Busy
+\*             /\ minimalElts # {}
+\*             /\ IF s = top /\ Q = << >>
+\*                  THEN /\ \E el \in minimalElts : 
+\*                             /\ Q' = <<el>>
+\*                             /\ Assign(enqInner, Writer(el), Done)                      
+\*                       /\ SetStutter("EndDeq", d, FALSE)
+\*                       /\ UNCHANGED deqInner
+\*                  ELSE /\ Q' = Tail(Q)
+\*                       /\ SetStutter("EndDeq", d, TRUE)
+\*                       /\ Assign(deqInner, d, Head(Q))
+\*                       /\ UNCHANGED enqInner
+\*             /\ UNCHANGED <<vars, p>>
+\*
+\*EndDeqI(d) == /\ AfterPreStutterEnabled("EndDeq", d) 
+\*              /\ EndDeq(d) 
+\*              /\ p = << >> \/ Head(p) = deqInner[d]
+\*              /\ p' = Tail(p)
+\*              /\ s' = top
+\*              /\ UNCHANGED <<Q, enqInner, deqInner>>
+(***************************************************************************
+XEndDeq(d, el) defined so that
+
+   EndDeq(d) == \E el \in Elements : XEndDeq(d, el)
+ ***************************************************************************)
+XEndDeq(d, el) == /\ deq[d] = Busy
+                  /\ el \in minimalElts
+                  /\ RemoveElt(el)
+                  /\ Assign(deq, d, el.data)
+             /\ UNCHANGED <<enq, adding>>
+
+DoDeqI(d, el) == /\ PreStutterEnabled("EndDeq", <<d, el>>)
+                 /\ deq[d] = Busy
+                 /\ el \in minimalElts
+                 /\  \/ p = << >> /\ p' = << >> /\ Assert(FALSE, "shouldn't happen")
+                     \/ Head(p) = el /\ p' = Tail(p)
+                 /\ IF s = top /\ Q = << >>
+                      THEN \* I believe this case should never occur
+                           /\ Q' = <<el>>
+                           /\ Assign(enqInner, Writer(el), Done)                      
+                           /\ SetStutter("EndDeq", <<d, el>>,  FALSE)
+                           /\ UNCHANGED deqInner
+                      ELSE /\ el = Head(Q)
+                           /\ Q' = Tail(Q)
+                           /\ SetStutter("EndDeq", <<d, el>>, TRUE)
+                           /\ Assign(deqInner, d, Head(Q))
+                           /\ UNCHANGED enqInner
+                 /\ UNCHANGED <<vars>>
+
+EndDeqI(d, el) == /\ AfterPreStutterEnabled("EndDeq", <<d, el>>) 
+                  /\ XEndDeq(d, el) 
+\*                  /\ p = << >> \/ Head(p) = deqInner[d]
+\*                  /\ p' = Tail(p)
+                  /\ s' = top
+                  /\ UNCHANGED <<Q, enqInner, deqInner, p>>
+
+NextI == \/ \E e \in EnQers : BeginEnqI(e) \/ BeginEnqIDo(e) 
+                                  \/ DoEnqI(e) \/ EndEnqI(e)
+         \/ \E d \in DeQers : \/ BeginDeqI(d) 
+                              \/ \E el \in Elements : DoDeqI(d, el) \/ EndDeqI(d, el)
+
+SpecI == InitI /\ [][NextI]_varsI    
+-----------------------------------------------------------------------------
+queue == [i \in 1..Len(Q) |-> Q[i].data]
+
+deqInnerBar == [d \in DeQers |-> IF deqInner[d] = NoData THEN NoData
+                                                         ELSE deqInner[d].data]
+I == INSTANCE QSpec WITH deqInner <- deqInnerBar
+            
+=============================================================================
+\* Modification History
+\* Last modified Sat Nov 14 17:54:40 PST 2015 by lamport
+\* Created Fri Nov 06 11:05:33 PST 2015 by lamport
diff --git a/tlatools/test-model/simulation/NQSpec/QSpec.tla b/tlatools/test-model/simulation/NQSpec/QSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4d3a7962b5121e4e4d08f855a99c682921f1fad8
--- /dev/null
+++ b/tlatools/test-model/simulation/NQSpec/QSpec.tla
@@ -0,0 +1,68 @@
+------------------------------- MODULE QSpec -------------------------------
+EXTENDS Integers, Sequences, TLC
+
+CONSTANTS EnQers, DeQers, Data, Done, Busy, NoData, InitData
+
+ASSUME /\ Done     \notin Data
+       /\ Busy     \notin Data
+       /\ NoData   \notin Data
+       /\ InitData \in Data
+
+VARIABLES enq, deq, queue, enqInner, deqInner
+vars == <<enq, deq, queue, enqInner, deqInner>>
+
+TypeOK == /\ enq \in [EnQers -> Data \cup {Done}]
+          /\ deq \in [DeQers -> Data \cup {Busy}]
+          /\ queue \in Seq(Data)
+          /\ enqInner \in [EnQers -> {Done, Busy}]
+          /\ deqInner \in [DeQers -> {NoData} \cup Data]
+
+Init == \* /\ PrintT("begin")
+        /\ enq = [e \in EnQers |-> Done]
+        /\ deq = [d \in DeQers |-> InitData]
+        /\ queue = << >>
+        /\ enqInner = [e \in EnQers |-> Done] \* /\ PrintT("foo")
+        /\ deqInner = [d \in DeQers |-> InitData] \* /\ PrintT("bar")
+        
+BeginEnq(e) == /\ enq[e] = Done 
+               /\ \E D \in Data : enq' = [enq EXCEPT ![e] = D]
+               /\ enqInner' = [enqInner EXCEPT ![e] = Busy]
+               /\ UNCHANGED <<deq, queue, deqInner>>
+               
+DoEnq(e) == /\ enqInner[e] = Busy
+            /\ queue' = Append(queue, enq[e])
+            /\ enqInner' = [enqInner EXCEPT ![e] = Done]
+            /\ UNCHANGED <<deq, enq, deqInner>>
+            
+EndEnq(e) == /\ enq[e] # Done
+             /\ enqInner[e] = Done
+             /\ enq' = [enq EXCEPT ![e] = Done]
+             /\ UNCHANGED <<deq, queue, enqInner, deqInner>>
+
+BeginDeq(d) == /\ deq[d] # Busy
+               /\ deq' = [deq EXCEPT ![d] = Busy]
+               /\ deqInner' = [deqInner EXCEPT ![d] = NoData]
+               /\ UNCHANGED <<enq, queue, enqInner>>
+               
+DoDeq(d) == /\ deq[d] = Busy
+            /\ deqInner[d] = NoData
+            /\ queue # << >>
+            /\ deqInner' = [deqInner EXCEPT ![d] = Head(queue)]
+            /\ queue' = Tail(queue)
+            /\ UNCHANGED <<enq, deq, enqInner>>
+
+EndDeq(d) == /\ deq[d] = Busy
+             /\ deqInner[d] # NoData
+             /\ deq' = [deq EXCEPT ![d] = deqInner[d]]
+             /\ UNCHANGED <<enq, queue, enqInner, deqInner>>
+             
+Next == 
+    /\ TLCSet(42, TLCGet(42))
+    /\  \/ \E e \in EnQers : BeginEnq(e) \/ DoEnq(e) \/ EndEnq(e)
+        \/ \E d \in DeQers : BeginDeq(d) \/ DoDeq(d) \/ EndDeq(d)
+        
+Spec == Init /\ [][Next]_vars
+=============================================================================
+\* Modification History
+\* Last modified Sat Nov 14 12:56:11 PST 2015 by lamport
+\* Created Wed Nov 04 15:29:04 PST 2015 by lamport
diff --git a/tlatools/test-model/suite/etest1.cfg b/tlatools/test-model/suite/etest1.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/etest1.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/etest1.tla b/tlatools/test-model/suite/etest1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9004a1858d319b2ec4505829f97471d35a64ae01
--- /dev/null
+++ b/tlatools/test-model/suite/etest1.tla
@@ -0,0 +1,20 @@
+--------------- MODULE etest1 -------------
+
+(* Test that TLC complains if an operator is called with too many arguments. *)
+
+EXTENDS TLC
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == 
+  /\ Print("Should complain that operator Foo is called with too many arguments", TRUE)
+  /\ x = TRUE
+
+Next == UNCHANGED x
+
+
+Foo(a) == a 
+
+Inv ==  Foo(x, x)
+
+=========================================
diff --git a/tlatools/test-model/suite/etest10.cfg b/tlatools/test-model/suite/etest10.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/etest10.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/etest10.tla b/tlatools/test-model/suite/etest10.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0063d717a09aee8328ea1507c344499339974d35
--- /dev/null
+++ b/tlatools/test-model/suite/etest10.tla
@@ -0,0 +1,15 @@
+--------------- MODULE etest10  -------------
+
+\* Test that TLC doesn't accept x \in Int if x not a number
+
+EXTENDS TLC, Integers
+
+VARIABLE x
+
+Init == /\ x = "a"
+        /\ Print("Should report error here", TRUE)
+        /\ Print(x \in Int, TRUE)
+
+Next == /\ x'=x
+        /\ Print("Test failed--error not reported", TRUE)
+=========================================
diff --git a/tlatools/test-model/suite/etest11.cfg b/tlatools/test-model/suite/etest11.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/etest11.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/etest11.tla b/tlatools/test-model/suite/etest11.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f948b4cc37429f5897bf4142704b2527200bb863
--- /dev/null
+++ b/tlatools/test-model/suite/etest11.tla
@@ -0,0 +1,15 @@
+--------------- MODULE etest11  -------------
+
+\* Test that TLC doesn't accept x \in STRING if x not a string
+
+EXTENDS TLC
+
+VARIABLE x
+
+Init == /\ x = 1
+        /\ Print("Should report error here", TRUE)
+        /\ Print(x \in STRING, TRUE)
+
+Next == /\ x'=x
+        /\ Print("Test failed--error not reported", TRUE)
+=========================================
diff --git a/tlatools/test-model/suite/etest12.cfg b/tlatools/test-model/suite/etest12.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/etest12.tla b/tlatools/test-model/suite/etest12.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d471244d2690616b9468300c04402d238a99224f
--- /dev/null
+++ b/tlatools/test-model/suite/etest12.tla
@@ -0,0 +1,9 @@
+-------------------------- MODULE etest12 --------------------------------
+EXTENDS Naturals, TLC, FiniteSets
+\* Test of error reporting when computing Cardinality of a huge set.
+
+ASSUME
+     IF Cardinality ([0..2 -> 1..2000]) = Cardinality ([2..4 -> 1..2000]) 
+       THEN Print("Test 22 OK", TRUE)
+       ELSE Assert(FALSE, "Test 22 Failed")
+==========================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/etest13.cfg b/tlatools/test-model/suite/etest13.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/etest13.tla b/tlatools/test-model/suite/etest13.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e0d4e62c966367ddb93813ddb332358a98cea9e3
--- /dev/null
+++ b/tlatools/test-model/suite/etest13.tla
@@ -0,0 +1,9 @@
+-------------------------- MODULE etest13 --------------------------------
+EXTENDS Naturals, TLC, FiniteSets
+\* Test of error reporting when computing Cardinality of a huge set.
+
+ASSUME
+     IF Cardinality ([0..2 -> 1..2000]) = Cardinality ([2..4 -> 1..2000]) 
+       THEN Print("Test 22 OK", TRUE)
+       ELSE Assert(FALSE, "Test 22 Failed")
+==========================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/etest14.cfg b/tlatools/test-model/suite/etest14.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/etest14.tla b/tlatools/test-model/suite/etest14.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e93ce4a819c7dc16c6c52de0b521146af97d4f8d
--- /dev/null
+++ b/tlatools/test-model/suite/etest14.tla
@@ -0,0 +1,6 @@
+----------------------  MODULE etest14 -----------------------------
+
+\* Test of CHOOSEing element of empty set.
+ASSUME
+  /\ (CHOOSE y \in {} : TRUE) = (CHOOSE y \in {} : TRUE)
+=====================================================================
diff --git a/tlatools/test-model/suite/etest15.cfg b/tlatools/test-model/suite/etest15.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/etest15.tla b/tlatools/test-model/suite/etest15.tla
new file mode 100644
index 0000000000000000000000000000000000000000..af25c8cc9469be17cbb43b5e8a906a9aeaf2e9a8
--- /dev/null
+++ b/tlatools/test-model/suite/etest15.tla
@@ -0,0 +1,10 @@
+----------------------  MODULE etest15 -----------------------------
+\* Test that TLC distinguishes <<x>> from x in function expressions.
+
+EXTENDS Naturals
+
+S == {<<y>> : y \in {1, 2, 3, 4}}
+
+ASSUME  [<<y>> \in S |-> y+1][2] = [<<y>> \in S |-> y+1][2] 
+  
+=====================================================================
diff --git a/tlatools/test-model/suite/etest16.cfg b/tlatools/test-model/suite/etest16.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/etest16.tla b/tlatools/test-model/suite/etest16.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d0d388ae3858ddef0d3abccf3e991eb0f0c0df5d
--- /dev/null
+++ b/tlatools/test-model/suite/etest16.tla
@@ -0,0 +1,11 @@
+--------------- MODULE etest16 -----------
+
+\* Test of detecting duplicate record elements
+
+Foo == [a |-> 1, b |-> 2, a |-> 1]
+
+Bar == [a : {1}, b : {2}, a : {1}]
+=============================================================================
+
+
+
diff --git a/tlatools/test-model/suite/etest2.cfg b/tlatools/test-model/suite/etest2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/etest2.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/etest2.tla b/tlatools/test-model/suite/etest2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3b133bf9277d08b23ca8ca201d014ca207dee145
--- /dev/null
+++ b/tlatools/test-model/suite/etest2.tla
@@ -0,0 +1,20 @@
+--------------- MODULE etest2 -------------
+
+(* Test that TLC complains if a operator is called with too few arguments. *)
+
+EXTENDS TLC
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == 
+  /\ Print("Should complain that operator Foo is called with too few arguments", TRUE)
+  /\ x = TRUE
+
+Next == UNCHANGED x
+
+
+Foo(a, b) == a /\ b
+
+Inv ==  Foo(x)
+
+=========================================
diff --git a/tlatools/test-model/suite/etest3.cfg b/tlatools/test-model/suite/etest3.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042
--- /dev/null
+++ b/tlatools/test-model/suite/etest3.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/etest3.tla b/tlatools/test-model/suite/etest3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5172acf98e888eee5f38b2b90fcea7151ececdd4
--- /dev/null
+++ b/tlatools/test-model/suite/etest3.tla
@@ -0,0 +1,22 @@
+--------------- MODULE etest3  -------------
+
+(* Testing deadlock and number of states found. *)
+
+EXTENDS TLC, Naturals, Sequences, FiniteSets
+
+VARIABLE x, y, z
+
+Init == /\ Print("This test should deadlock after finding two states", TRUE)
+        /\ x = {}
+        /\ y = {}
+        /\ z = 1
+
+Next == /\ x = {}
+        /\ x' = {1}        
+        /\ y' \in SUBSET x'
+        /\ z' = z
+        /\ y' = {}
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/etest4.cfg b/tlatools/test-model/suite/etest4.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c95fce6e2a4a0a1137f1e83ec8efe3d039acfede
--- /dev/null
+++ b/tlatools/test-model/suite/etest4.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT
+    A = ModelValue
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/etest4.tla b/tlatools/test-model/suite/etest4.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e6b4fbdff6637f772a9dfd18a2295c79e5323e74
--- /dev/null
+++ b/tlatools/test-model/suite/etest4.tla
@@ -0,0 +1,22 @@
+--------------- MODULE etest4  -------------
+
+(* Evaluates m[v] for model-value m. *)
+
+EXTENDS TLC, Naturals, Sequences, FiniteSets
+
+VARIABLE x
+CONSTANT A
+
+
+
+Req == [  mask |-> A  ]
+Init == /\ x = 1
+        /\ Print("Should report error for applying model value to arg", TRUE)
+        /\ Req.mask[1] = 1
+        /\ Print("Should never get this far", TRUE)
+
+Next == UNCHANGED x
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/etest5.cfg b/tlatools/test-model/suite/etest5.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042
--- /dev/null
+++ b/tlatools/test-model/suite/etest5.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/etest5.tla b/tlatools/test-model/suite/etest5.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a3c736d7c1c70ebe9e14583e700c205d931e48b3
--- /dev/null
+++ b/tlatools/test-model/suite/etest5.tla
@@ -0,0 +1,19 @@
+--------------- MODULE etest5  -------------
+
+(* M!Ident for nonexistent module M. *)
+
+EXTENDS TLC, Naturals
+
+VARIABLE x
+
+Init == /\ x = 1
+        /\ Print("Should report error for nonexistent module M", TRUE)
+
+Next == /\ UNCHANGED x
+        /\ IF M!Init THEN TRUE
+                     ELSE TRUE
+        /\ Print("Should not get this far", TRUE)
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/etest6.cfg b/tlatools/test-model/suite/etest6.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0cc6c2124ed19c2b3250dc1dfa1ab569eca3c252
--- /dev/null
+++ b/tlatools/test-model/suite/etest6.cfg
@@ -0,0 +1,13 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  CONSTRAINT Constraint
+
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/etest6.tla b/tlatools/test-model/suite/etest6.tla
new file mode 100644
index 0000000000000000000000000000000000000000..73b610103641896adb0d5ef427f26a33cc4b03aa
--- /dev/null
+++ b/tlatools/test-model/suite/etest6.tla
@@ -0,0 +1,25 @@
+--------------- MODULE etest6 -------------
+
+(* Defining x' in terms of x' *)
+
+EXTENDS TLC, Integers, Sequences, FiniteSets
+
+VARIABLE x
+
+Init == /\ Print("Should complain about undefined value of x.", TRUE)
+        /\ x = [i \in {1,2} |-> i]
+
+
+Action(c) == c' = [c EXCEPT ![1] = (c[2])']
+
+Next ==  \/ /\ x[1] = 1
+            /\ Action(x)
+            /\ Print("Should not get this far", TRUE)
+
+         \/ UNCHANGED x
+
+Inv ==  TRUE
+         
+
+Constraint == TRUE
+=========================================
diff --git a/tlatools/test-model/suite/etest7.cfg b/tlatools/test-model/suite/etest7.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8f3f0653e8f0d314ebb13afe19cfb1158ca370f1
--- /dev/null
+++ b/tlatools/test-model/suite/etest7.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT 
+     C <- Foo
+
diff --git a/tlatools/test-model/suite/etest7.tla b/tlatools/test-model/suite/etest7.tla
new file mode 100644
index 0000000000000000000000000000000000000000..862ef69ee0d4c48e3772bfb3432ad66eebadd673
--- /dev/null
+++ b/tlatools/test-model/suite/etest7.tla
@@ -0,0 +1,21 @@
+--------------- MODULE etest7  -------------
+
+(* Test replacement of constant with nonconstant operator. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+CONSTANT C
+
+VARIABLE x
+
+Foo == x
+
+Next == x' = C
+
+Init == /\ Print("TLC should complain about replacing the constant C", TRUE)
+        /\ Print("by the nonconstant Foo", TRUE)
+        /\ x = 2
+
+Inv ==  x = 2
+
+=========================================
diff --git a/tlatools/test-model/suite/etest8.cfg b/tlatools/test-model/suite/etest8.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/etest8.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/etest8.tla b/tlatools/test-model/suite/etest8.tla
new file mode 100644
index 0000000000000000000000000000000000000000..11bbfb68d7a2b4c28e72ca5878954274d9304c39
--- /dev/null
+++ b/tlatools/test-model/suite/etest8.tla
@@ -0,0 +1,21 @@
+--------------- MODULE etest8  -------------
+
+(* Test that simulation mode doesn't check for deadlock. *)
+
+EXTENDS TLC
+
+
+VARIABLE x
+
+Init == /\ x \in {"a", "b"}
+        /\ Print("This spec can deadlock, but shouldn't.", TRUE)
+
+Next == \/ /\ x = "b"
+           /\ x' = "c"
+        \/ /\ x = "c"
+           /\ x' = "d"
+        \/ /\ x = "d"
+           /\ x' = "d"
+           /\ Assert(FALSE, "Test successfully completed.")
+
+=========================================
diff --git a/tlatools/test-model/suite/etest9.cfg b/tlatools/test-model/suite/etest9.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/etest9.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/etest9.tla b/tlatools/test-model/suite/etest9.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c860dd62aa9aeda4765ab0c9dbdbe0a4dea38bb8
--- /dev/null
+++ b/tlatools/test-model/suite/etest9.tla
@@ -0,0 +1,15 @@
+--------------- MODULE etest9  -------------
+
+\* Test that TLC doesn't accept x \in Nat if x not a number
+
+EXTENDS TLC, Naturals
+
+VARIABLE x
+
+Init == /\ x = "a"
+        /\ Print("Should report error here", TRUE)
+        /\ Print(x \in Nat, TRUE)
+
+Next == /\ x'=x
+        /\ Print("Test failed--error not reported", TRUE)
+=========================================
diff --git a/tlatools/test-model/suite/test.cfg b/tlatools/test-model/suite/test.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test.tla b/tlatools/test-model/suite/test.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bd4950a64a48d49da9591248c5f03ad3c26cb57d
--- /dev/null
+++ b/tlatools/test-model/suite/test.tla
@@ -0,0 +1,9 @@
+------------- MODULE Foo  --------------
+EXTENDS Naturals
+
+Foo == \A x \in {1} : lab(x) :: x = 1
+
+Bar == Foo!lab(1)
+
+ASSUME Bar
+=============================================
diff --git a/tlatools/test-model/suite/test1.cfg b/tlatools/test-model/suite/test1.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test1.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test1.tla b/tlatools/test-model/suite/test1.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9bc722cc36f846eb80debae15b06d5af65d95a44
--- /dev/null
+++ b/tlatools/test-model/suite/test1.tla
@@ -0,0 +1,77 @@
+--------------- MODULE test1 -------------
+
+(* test equality of various representations of sets.    *)
+
+EXTENDS Naturals, TLC
+
+VARIABLE x
+
+
+Type == x \in {1}
+Inv  == 
+ /\ IF {1, 2, 3} # {3, 2, 2, 1}
+      THEN Assert(FALSE, "Failed Test 1")
+      ELSE Print("Test 1 OK", TRUE)
+
+ /\ IF  {1, 2, 3} # {i \in {5, 4, 3, 2, 1} : i < 4}
+      THEN Assert(FALSE, "Failed Test 2")
+      ELSE Print("Test 2 OK", TRUE)
+
+ /\ IF {1, 2, 3} # {i-3 : i \in {4, 6, 5, 6, 5, 6}}
+      THEN Assert(FALSE, "Failed Test 3")
+      ELSE Print("Test 3 OK", TRUE)
+
+ /\ IF SUBSET {1, 2, 3} # {{}, {1}, {2,1,2}, {3,1}, {2}, {3}, {3,2,3}, {1,2,3}}
+      THEN Assert(FALSE, "Failed Test 4")
+      ELSE Print("Test 4 OK", TRUE)
+
+ /\ IF {1,2,3} # UNION (SUBSET {1, 2, 3})
+      THEN Assert(FALSE, "Failed Test 5")
+      ELSE Print("Test 5 OK", TRUE)    
+
+ /\ IF {1,2,3} # UNION {{1}, {2}, {3, 2, 1}}
+      THEN Assert(FALSE, "Failed Test 6")
+      ELSE Print("Test 6 OK", TRUE)
+
+ /\ IF {1,2,3} # {1} \cup {3, 2} \cup {}
+      THEN Assert(FALSE, "Failed Test 7")
+      ELSE Print("Test 7 OK", TRUE)
+
+ /\ IF {1,2,3} # {5, 4, 3, 2, 1} \cap {1, 3, 0, 2}
+      THEN Assert(FALSE, "Failed Test 8")
+      ELSE Print("Test 8 OK", TRUE)
+
+ /\ IF {1,2,3} \subseteq {5, 3, 3, 4, 2}
+      THEN Assert(FALSE, "Failed Test 9")
+      ELSE Print("Test 9 OK", TRUE)
+
+ /\ IF {1,2,3} # {5, 3, 3, 4, 2,1} \ {4, 5}
+      THEN Assert(FALSE, "Failed Test 10")
+      ELSE Print("Test 10 OK", TRUE)
+
+ /\ IF {1,2,3} # 1..3
+      THEN Assert(FALSE, "Failed Test 11")
+      ELSE Print("Test 11 OK", TRUE)
+
+ /\ IF {1,2,3} # DOMAIN <<"a", "b", "c">>
+      THEN Assert(FALSE, "Failed Test 12")
+      ELSE Print("Test 12 OK", TRUE)
+
+ /\ IF {1,2,3} # DOMAIN [i \in {3,2,2,1} |-> i+1]
+      THEN Assert(FALSE, "Failed Test 13")
+      ELSE Print("Test 13 OK", TRUE)
+
+ /\ IF {"a", "b", "c"} # DOMAIN [a |-> 1, b |-> 2, c |-> 3]
+      THEN Assert(FALSE, "Failed Test 14")
+      ELSE Print("Test 14 OK", TRUE)
+
+ /\ IF {} # [{1, 2, 3} -> {}]
+      THEN Assert(FALSE, "Failed Test 15")
+      ELSE Print("Test 15 OK", TRUE)
+
+ /\ Print("Test Completed", TRUE)
+
+Init == x = 1
+
+Next == UNCHANGED x
+=========================================
diff --git a/tlatools/test-model/suite/test10.cfg b/tlatools/test-model/suite/test10.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test10.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test10.tla b/tlatools/test-model/suite/test10.tla
new file mode 100644
index 0000000000000000000000000000000000000000..7e35ec086d4fab2dbef27399250da6487a85467d
--- /dev/null
+++ b/tlatools/test-model/suite/test10.tla
@@ -0,0 +1,160 @@
+--------------- MODULE test10 -------------
+
+(* Test of function definition and application *)
+
+EXTENDS Integers, TLC
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+f1[i \in Int]  == i+2
+f2[i, j \in Int] == i+j
+f3[i, j \in Int, <<k,l>> \in Int \X Int, m \in {5, 7,8}] == 
+        i + 2*j + 3*k + 4*l + 5*m
+f4[i, j \in Int] == [a |-> i, b |-> j]
+
+f5[i, j, k \in Int] == i+j+k
+
+f6[i, j, k \in Int, <<l, m, n>> \in Int \X Int \X Int, p \in {5,7,8}] ==
+        i + 2*j + 3*k + 4*l + 5*m + 6*n + 7*p
+
+Inv  ==  
+
+LET r1 == [a |-> f1, b |-> f2]
+    g1 == [f4 EXCEPT ![1,2].a = 22, ![1,3].b = 33]
+    g2 == [[<<i, j>> \in (1..10) \X (1..10) 
+            |-> [a |-> i, b |-> j]] EXCEPT ![1,2].a = 22, ![1,3].b = 33]
+IN
+  /\ IF f1[3] # 5
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF f2[3,4] # 7
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF f2[<<3,4>>] # 7
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF f3[1, 2, <<3, 4>>, 5] # 55
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF f3[<<1, 2, <<3, 4>>, 5>>] # 55
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+
+  /\ IF [i \in Int |-> i+2][3] # 5
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF [i, j \in Int |-> i+j][3,4] # 7
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF [i, j \in Int |-> i+j][<<3,4>>] # 7
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF [i, j \in Int, <<k,l>> \in Int \X Int, m \in {5,7,8} 
+         |-> i + 2*j + 3*k + 4*l + 5*m][1, 2, <<3, 4>>, 5] # 55
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF [i, j \in Int, <<k,l>> \in Int \X Int, m \in {5,7,8} 
+         |-> i + 2*j + 3*k + 4*l + 5*m][<<1, 2, <<3, 4>>, 5>>] # 55
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF [f1 EXCEPT ![3] = [i \in Int |-> @ + i]][3][7] # 12
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF [f1 EXCEPT ![3] = [i \in Int |-> @ + i],  ![3][6] = 44][3][6] # 44
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF [f1 EXCEPT ![3] = [i \in Int |-> @ + i], ![3][6] = 44][3][7] # 12
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF f4[3,4].a # 3
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17][3,4].b # 17
+       THEN Assert(FALSE, "Test 15 Failed")
+       ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17][3,4].a # 3
+       THEN Assert(FALSE, "Test 16 Failed")
+       ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17][2,4].b # 4
+       THEN Assert(FALSE, "Test 17 Failed")
+       ELSE Print("Test 17 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18][3,4].b # 17
+       THEN Assert(FALSE, "Test 18 Failed")
+       ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18][3,4].a # 18
+       THEN Assert(FALSE, "Test 19 Failed")
+       ELSE Print("Test 19 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18, ![3,4].b= @+5][3,4].b # 22
+       THEN Assert(FALSE, "Test 20 Failed")
+       ELSE Print("Test 20 OK", TRUE)
+
+  /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18, ![<<3,4>>].b= @+5][3,4].b # 22
+       THEN Assert(FALSE, "Test 21 Failed")
+       ELSE Print("Test 21 OK", TRUE)
+
+  /\ IF g1[1,3].a # 1 \/ g1[1,3].a # 1
+       THEN Assert(FALSE, "Test 22 Failed")
+       ELSE Print("Test 22 OK", TRUE)
+
+  /\ IF g2[1,3].a # 1 \/ g2[1,3].a # 1
+       THEN Assert(FALSE, "Test 23 Failed")
+       ELSE Print("Test 23 OK", TRUE)
+
+  /\ IF g1[1,2].a # 22 \/ g1[1,2].a # 22
+       THEN Assert(FALSE, "Test 24 Failed")
+       ELSE Print("Test 24 OK", TRUE)
+
+  /\ IF g2[1,2].a # 22 \/ g2[1,2].a # 22
+       THEN Assert(FALSE, "Test 25 Failed")
+       ELSE Print("Test 25 OK", TRUE)
+
+  /\ IF f5[3,4,5] # 12
+       THEN Assert(FALSE, "Test 26 Failed")
+       ELSE Print("Test 26 OK", TRUE)
+
+  /\ IF f5[<<3,4,5>>] # 12
+       THEN Assert(FALSE, "Test 27 Failed")
+       ELSE Print("Test 27  OK", TRUE)
+
+  /\ IF f6[1, 2, 3, <<4,5,6>>, 7] # 140
+       THEN Assert(FALSE, "Test 28 Failed")
+       ELSE Print("Test 28 OK", TRUE)
+
+  /\ IF f6[<<1, 2, 3, <<4,5,6>>, 7>>] # 140
+       THEN Assert(FALSE, "Test 29 Failed")
+       ELSE Print("Test 29 OK", TRUE)
+
+  /\ IF [i, j, k \in Int |-> i+j+k][3,4,5] # 12
+       THEN Assert(FALSE, "Test 30 Failed")
+       ELSE Print("Test 30 OK", TRUE)
+
+  /\ IF [i, j, k \in Int |-> i+j+k][<<3,4,5>>] # 12
+       THEN Assert(FALSE, "Test 31 Failed")
+       ELSE Print("Test 31 OK", TRUE)
+
+
+
+
+=========================================
diff --git a/tlatools/test-model/suite/test11.cfg b/tlatools/test-model/suite/test11.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test11.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test11.tla b/tlatools/test-model/suite/test11.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b388dd57d86dc71f7dc5c24b7fd2aeb8f9eb52cd
--- /dev/null
+++ b/tlatools/test-model/suite/test11.tla
@@ -0,0 +1,120 @@
+--------------- MODULE test11 -------------
+
+(* Test of DOMAIN and Function Sets *)
+
+EXTENDS Integers, TLC, FiniteSets
+
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+f1[i \in Int] == i+3
+
+S == [1..4 -> Nat]
+
+Inv  ==  
+  /\ IF 5 \notin DOMAIN f1
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF 5 \notin DOMAIN [i \in Int |-> i+3]
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF {1,2,3} \notin DOMAIN [T \in SUBSET Int |-> T \cup {1}]
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF Cardinality(DOMAIN [i \in {1,3,5} |-> i+7]) # 3
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF Cardinality(DOMAIN [f \in [{1,3,5} -> {1,2}] |-> f[3]]) # 8
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF Cardinality(DOMAIN [f \in {g \in [{1,3,5} -> {1,2}] : g[3]=1} 
+                              |-> f[3]]) # 4
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF [i \in {1,3, 5} |-> i-2] \notin 
+            {g \in [{1,3,5} -> -2..3] : g[3]=1}
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF ~ ((DOMAIN [a |-> 1, b |-> 2]) \subseteq {"a", "b", "cde"})
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF <<1, 2, 3, 4>> \notin S
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF <<1, 2, -3, 4>> \in S
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF <<1, 2, 4>> \in S
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF [i \in {1, 2} |-> i] \in S
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF [i \in {1, 2, 3, 4} |-> i+1] \notin S
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF [i \in {1, 2, 3, 4} |-> i-2] \in S
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF [i \in 1..100 |-> i+1]    \notin [1..100 -> Nat]
+       THEN Assert(FALSE, "Test 15 Failed")
+       ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF [i \in 1..10  |-> i+1]    \in [1..100 -> Nat]
+       THEN Assert(FALSE, "Test 16 Failed")
+       ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF [i \in {99, 100} |-> i+1] \in [1..100 -> Nat]
+       THEN Assert(FALSE, "Test 17 Failed")
+       ELSE Print("Test 17 OK", TRUE)
+
+  /\ IF [i \in {"a", "b"} |-> i]  \in [1..100 -> Nat]
+       THEN Assert(FALSE, "Test 18 Failed")
+       ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF [a |-> 1, b |-> 2]        \in [1..100 -> Nat]
+       THEN Assert(FALSE, "Test 19 Failed")
+       ELSE Print("Test 19 OK", TRUE)
+
+  /\ Print("Test 20: Enumeration of [{1+1,2} -> {3-1,2,1+1}]", TRUE)
+  /\ \A f \in [{1+1,2} -> {3-1,2,1+1}] : Print(f, TRUE)
+
+  /\ Print(<<"Test 21: evaluation of [{1+1,2} -> {3-1,2,1+1}] = ", 
+                [{1+1,2} -> {3-1,2,1+1}]>>, TRUE)
+
+  /\ IF /\ Cardinality ([0..1 -> 1..2000]) = 4000000
+        /\ Cardinality (1..8099) = 8099
+       THEN Print("Test 22 OK", TRUE)
+       ELSE Assert(FALSE, "Test 22 Failed")
+
+  /\ IF DOMAIN [i \in {1,2}, j \in {3,4} |-> i+j] # {1,2} \X {3, 4}
+       THEN Assert(FALSE, "Test 23 Failed")
+       ELSE Print("Test 23 OK", TRUE)
+
+  /\ IF DOMAIN [i \in {1,2}, j \in {3,4}, k \in {5,6} |-> i+j+k] 
+                    # {1,2} \X {3, 4} \X {5, 6}
+       THEN Assert(FALSE, "Test 24 Failed")
+       ELSE Print("Test 24 OK", TRUE)
+
+  /\ IF <<1,3,5>> \notin 
+           DOMAIN [i \in {1,2}, j \in {3,4}, k \in {5,6} |-> i+j+k] 
+       THEN Assert(FALSE, "Test 25 Failed")
+       ELSE Print("Test 25 OK", TRUE)
+=========================================
diff --git a/tlatools/test-model/suite/test12.cfg b/tlatools/test-model/suite/test12.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test12.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test12.tla b/tlatools/test-model/suite/test12.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cde8c843e67a637bb2314e823570bf6336f20153
--- /dev/null
+++ b/tlatools/test-model/suite/test12.tla
@@ -0,0 +1,120 @@
+--------------- MODULE test12 -------------
+
+(* Test of Record constructs. *)
+
+EXTENDS Integers, TLC, FiniteSets
+
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+
+Inv  ==  
+LET r1 == [l1 |-> 1, l2 |-> 2, l3 |-> 3]
+   r2 == [l1 |-> r1, l2 |-> [r1 EXCEPT !.l2 = 22], l3 |-> [r1 EXCEPT !.l3 = 33]]
+   r3 == [l1 |-> [i \in Int |-> i+1], 
+          l2 |-> [i , j \in Int |-> [m1 |->i+2, m2 |-> j+3]]]
+   
+   r4 == [a |-> [i \in 1..1 |-> i],
+          b |-> [i \in 1..1 |-> 2]]
+
+   S1 == [l1 : Int, l2 : [{1,2} -> {i \in Int : i > 5}]]
+   S2(j) == [l1 : {1,3}, l2 : {f \in [{1,2} -> 1..10] : f[1] > j /\ f[2] > j}]
+   S3 == [l1 : Int, l2 : SUBSET [{1,2} -> {2,3}]]
+
+IN
+  /\ IF r1.l1 # 1 \/ r1.l1 # 1
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF r1["l1"] # 1 \/ r1["l1"] # 1
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF [l1 |-> 1, l2 |-> 2, l3 |-> 3].l1 # 1
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF [l1 |-> 1, l2 |-> 2, l3 |-> 3]["l1"] # 1
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF [1l |-> 1, 2l |-> 2, 3l |-> 3]["2l"] # 2
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF r2.l1 # r1 \/ r2.l1 # r1
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF r2.l2.l2 # 22 \/ r2.l2.l2 # 22
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF r2.l2.l1 # 1 \/ r2.l2.l1 # 1
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF [r2 EXCEPT !.l2.l1 = 44, !.l2.l3=55].l2.l1 # 44
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF [r2 EXCEPT !.l2.l1 = 44, !.l2.l3=55].l2.l2 # 22
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF r3.l2[5,6].m1 # 7 \/ r3.l2[5,6].m1 # 7
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF [i , j \in Int |-> [m1 |->i+2, m2 |-> j+3]][5,6].m2 # 9
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF r3.l2[5,6].m2 # 9 \/ r3.l2[5,6].m2 # 9
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF [l1 |-> 1, l2 |-> [i \in {1,2} |-> i+5]] \notin S1
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF [l1 |-> 1, l2 |-> [i \in {1,2} |-> i+4]] \in S1
+       THEN Assert(FALSE, "Test 15 Failed")
+       ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF ~(S2(5) \subseteq S1) \/ ~(S2(5) \subseteq S1)
+       THEN Assert(FALSE, "Test 16 Failed")
+       ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF S2(4) \subseteq S1 \/ S2(4) \subseteq S1
+       THEN Assert(FALSE, "Test 17 Failed")
+       ELSE Print("Test 17 OK", TRUE)
+  /\ IF Cardinality(S2(5)) # 50 \/ Cardinality(S2(5)) # 50
+       THEN Assert(FALSE, "Test 18 Failed")
+       ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF Cardinality([l1 : {1,2}, 
+                     l2 : {1, 2, 3, 4, 4, 4, 4}, l3 : {3, 3, 2, 1}]) # 24
+       THEN Assert(FALSE, "Test 19 Failed")
+       ELSE Print("Test 19 OK", TRUE)
+
+  /\ IF ~(\A r \in S2(5) : r.l2[1] > 5) \/ ~(\A r \in S2(5) : r.l2[1] > 5)
+       THEN Assert(FALSE, "Test 20 Failed")
+       ELSE Print("Test 20 OK", TRUE)
+
+  /\ IF [l1 |-> -4, l2 |-> {<<3,2>>}] \notin S3
+       THEN Assert(FALSE, "Test 21 Failed")
+       ELSE Print("Test 21 OK", TRUE)
+
+  /\ IF [l1 |-> -4, l2 |-> {<<3,4>>}] \in S3
+       THEN Assert(FALSE, "Test 22 Failed")
+       ELSE Print("Test 22 OK", TRUE)
+
+  /\ IF [r4 EXCEPT !["a"][1] = 2]["a"][1] # 2
+       THEN Assert(FALSE, "Test 23 Failed")
+       ELSE Print("Test 23 OK", TRUE)
+
+=========================================
diff --git a/tlatools/test-model/suite/test13.cfg b/tlatools/test-model/suite/test13.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test13.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test13.tla b/tlatools/test-model/suite/test13.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a72f9b93132ade5d59c03cbade567a57c246ac8b
--- /dev/null
+++ b/tlatools/test-model/suite/test13.tla
@@ -0,0 +1,70 @@
+--------------- MODULE test13 -------------
+
+(* Test of Tuples and Cartesian products. *)
+
+EXTENDS Integers, TLC, FiniteSets, Sequences
+
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+Inv  ==  
+LET S1 == {1,2,3} \X {"a", "b", "c", "d"}
+    S2 == Int \X [{1,2,3} -> {4, 5, 6}]
+    S3 == {1,2,3} \X {"a", "b", "c", "d"} \X {4,5}
+    S4 == Int \X [{1,2,3} -> {4, 5, 6}] \X Seq(Int)
+
+IN
+  /\ IF Cardinality(S1) # 12 \/ Cardinality(S3) # 24
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF \/ <<-5, <<4,4,4>> >> \notin S2
+        \/ <<-5, <<4,4,4>>, <<-1,-2,-3>> >>  \notin S4
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF <<-5, -4>> \notin Int \X Int
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF <<-5, <<4,4,3>>>> \in S2
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF {<<"a", "b", "c">>[i] : i \in 1..3} 
+          # DOMAIN [a |-> 1, b |-> 2, c |-> {}]
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF [i \in {1, 2} |-> IF i = 1 THEN "a" ELSE 22] # <<"a", 22>>
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF [i \in {1, 2} |-> IF i = 1 THEN "a" ELSE 22] =  <<"a", 23>>
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF <<1,2>> \notin Seq(Int)
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF <<1,2>> \notin Seq({n \in Int : n < 22})
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF <<1,2>> \in Seq({n \in Int : n > 22})
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF <<-5, -4, -3>> \notin Int \X Int \X Int
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF <<-5, <<4,4,3>>, <<1,-1>> >> \in S4
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+ =========================================
diff --git a/tlatools/test-model/suite/test14.cfg b/tlatools/test-model/suite/test14.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test14.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test14.tla b/tlatools/test-model/suite/test14.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4cd3b1783d7ffbf7ea9c5f6cade784c6b79d925f
--- /dev/null
+++ b/tlatools/test-model/suite/test14.tla
@@ -0,0 +1,54 @@
+--------------- MODULE test14 -------------
+
+(* Test of IF and CASE and nested junctions *)
+
+EXTENDS Integers, TLC, FiniteSets, Sequences
+
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+Inv  ==  
+
+  /\ IF (IF TRUE THEN IF FALSE THEN 3 ELSE 2 ELSE 1) # 2
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF (CASE FALSE -> 1 [] TRUE -> CASE FALSE -> 2 [] TRUE -> 3 [] OTHER -> 4)
+          # 3
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF (CASE FALSE -> 1 [] FALSE -> CASE FALSE -> 2 [] TRUE -> 3 
+               [] OTHER -> 4 [] TRUE -> 5) # 5
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF (CASE FALSE -> 1 [] TRUE -> CASE FALSE -> 2 [] FALSE -> 3 
+          [] OTHER -> 4 [] TRUE -> 5) # 4
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF (CASE FALSE -> 1 [] FALSE -> CASE TRUE -> 2 [] TRUE -> 3 
+                 [] OTHER -> 4 [] OTHER -> 5) # 5
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF (CASE TRUE -> 1 [] TRUE -> CASE TRUE -> 2 [] TRUE -> 3 
+            [] OTHER -> 4 [] OTHER -> 5) \notin {1, 2, 3}
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF  ~ \/ /\ 1=3
+              /\ "a" = 1
+           \/ /\ 1 = 2
+              /\ "b" = 1
+           \/ /\ 1 = 1
+              /\ 2 = 2
+           \/ "c" = 1
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+=========================================
diff --git a/tlatools/test-model/suite/test15.cfg b/tlatools/test-model/suite/test15.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test15.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test15.tla b/tlatools/test-model/suite/test15.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e4d977e74e918a8f2956f3673701651262911d45
--- /dev/null
+++ b/tlatools/test-model/suite/test15.tla
@@ -0,0 +1,78 @@
+--------------- MODULE test15 -------------
+(* Test of handling the empty set *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+
+
+Inv ==  
+  /\ IF SUBSET {} # {{}}
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF UNION {} # {}
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF {y \in {} : y = y} # {}
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF [y \in {} |-> y] # << >>
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF (\E S \in {} : TRUE) # FALSE
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF (\A S \in {} : FALSE) # TRUE
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF DOMAIN <<>> # {}
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF 1..0 # {}
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF <<>> \notin Seq({})
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF <<{}>> \in Seq ({})
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF \E S \in SUBSET {} : S # {}
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+  /\ IF [{} -> {1, 2, 3}] # {<<>>}
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF [{1, 2, 3} -> {}] # {}
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+
+  /\ IF (UNION {{1,2,3}, {}}) # (UNION {{1,2,3}}) 
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF 1..0 # {}
+       THEN Assert(FALSE, "Test 15 Failed")
+       ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF 1..0 # 5..2
+       THEN Assert(FALSE, "Test 16 Failed")
+       ELSE Print("Test 16 OK", TRUE)
+
+=========================================
diff --git a/tlatools/test-model/suite/test16.cfg b/tlatools/test-model/suite/test16.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test16.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test16.tla b/tlatools/test-model/suite/test16.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4a5dcc10ca9724d64afc857f3e6f3d4ee083cf7b
--- /dev/null
+++ b/tlatools/test-model/suite/test16.tla
@@ -0,0 +1,40 @@
+--------------- MODULE test16 -------------
+(* Test of handling of one-tuples *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+S == {<<y>> : y \in {1, 2, 3, 4}}
+
+Inv ==  
+
+  /\ IF <<1>> \notin S
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF ~(\E <<y>> \in S : y = 2) 
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF ~(\E y \in S : y[1] = 2) 
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF {<<y>> \in S : y > 3} # {<<4>>}
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF <<1>> \o <<2>> # <<1, 2>>
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF [<<y>> \in S |-> y+1][<<2>>] # 3
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+
+=========================================
diff --git a/tlatools/test-model/suite/test17.cfg b/tlatools/test-model/suite/test17.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test17.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test17.tla b/tlatools/test-model/suite/test17.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d6975f3f805075b4678a5a13c09d48feb550fc1e
--- /dev/null
+++ b/tlatools/test-model/suite/test17.tla
@@ -0,0 +1,131 @@
+--------------- MODULE test17 -------------
+
+(* Test of handling of handling expressions with primes. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x, y
+
+Init == /\ Print("There should be two distinct states", TRUE)
+        /\ x = 1
+        /\ y = 1
+
+f[i \in Nat ] == x'+ x + 2*i
+g == [i \in {x'} |-> x+i]
+
+
+Foo(a) == a' = x+1
+
+Inv == TRUE
+
+TNext == 
+  /\ x' = x+1
+  /\ x' = 2
+  /\ \/ UNCHANGED y
+
+     \/ /\ Print("Starting Test 1", TRUE)
+        /\ y' = IF x' = 2 
+                  THEN 1 
+                   ELSE Assert(FALSE, "Failed Test 1")
+
+     \/ /\ Print("Starting Test 2", TRUE)
+        /\ IF x' = 2 
+             THEN y' = 1 
+             ELSE Assert(FALSE, "Failed Test 2")
+
+     \/ /\ Print("Starting Test 3", TRUE)
+        /\ x' # 2
+        /\ Assert(FALSE, "Failed Test 3")
+
+     \/ /\ Print("Starting Test 4", TRUE)
+        /\ ~(\E i \in {x'} : i = 2)
+        /\ Assert(FALSE, "Failed Test 4")
+
+     \/ /\ Print("Starting Test 5", TRUE)
+        /\ ~ (\A i \in {x'} : i = 2)
+        /\ Assert(FALSE, "Failed Test 5")
+
+     \/ /\ Print("Starting Test 6", TRUE)
+        /\ (CHOOSE i \in {x'} : TRUE) # 2
+        /\ Assert(FALSE, "Failed Test 6")
+
+     \/ /\ Print("Starting Test 7", TRUE)
+        /\ 2 \notin {x'}
+        /\ Assert(FALSE, "Failed Test 7")
+
+     \/ /\ Print("Starting Test 8", TRUE)
+        /\ {i \in {x'} : TRUE} # {2}
+        /\ Assert(FALSE, "Failed Test 8")
+
+     \/ /\ Print("Starting Test 9", TRUE)
+        /\ {i' : i \in {x'}} # {2}
+        /\ Assert(FALSE, "Failed Test 9")
+
+     \/ /\ Print("Starting Test 10", TRUE)
+        /\ {2} \notin SUBSET {x'}
+        /\ Assert(FALSE, "Failed Test 10")
+
+     \/ /\ Print("Starting Test 11", TRUE)
+        /\ {2} # UNION {{x'}, {}}
+        /\ Assert(FALSE, "Failed Test 11")
+
+     \/ /\ Print("Starting Test 12", TRUE)
+        /\ f[1] # 5
+        /\ Assert(FALSE, "Failed Test 12")
+
+     \/ /\ Print("Starting Test 13", TRUE)
+        /\ f[x'] # 7
+        /\ Assert(FALSE, "Failed Test 13")
+
+     \/ /\ Print("Starting Test 14", TRUE)
+        /\ [f EXCEPT ![x'] = x'+x+3][2] # 6
+        /\ Assert(FALSE, "Failed Test 14")
+
+     \/ /\ Print("Starting Test 15", TRUE)
+        /\ [f EXCEPT ![2] = x'+x+3][x'] # 6
+        /\ Assert(FALSE, "Failed Test 15")
+
+     \/ /\ Print("Starting Test 16", TRUE)
+        /\ [{x'-1} -> {x'}] # {<<2>>}
+        /\ Assert(FALSE, "Failed Test 16")
+
+     \/ /\ Print("Starting Test 17", TRUE)
+        /\ [{x',x} -> {x'}] # {<<2, 2>>}
+        /\ Assert(FALSE, "Failed Test 17")
+
+     \/ /\ Print("Starting Test 18", TRUE)
+        /\ [{x',x} -> {x}] # {<<1, 1>>}
+        /\ Assert(FALSE, "Failed Test 18")
+
+     \/ /\ Print("Starting Test 19", TRUE)
+        /\ x'=2 => x'=3
+        /\ Assert(FALSE, "Failed Test 19")
+
+     \/ /\ Print("Starting Test 20", TRUE)
+        /\ CASE x'= x+1 -> FALSE
+             [] x'# x+1 -> TRUE
+        /\ Assert(FALSE, "Failed Test 20")
+
+     \/ /\ Print("Starting Test 21", TRUE)
+        /\ DOMAIN g # {2}
+        /\ Assert(FALSE, "Failed Test 21")
+
+     \/ /\ Print("Starting Test 22", TRUE)
+        /\ g[2] # 3
+        /\ Assert(FALSE, "Failed Test 22")
+
+     \/ /\ Print("Starting Test 23", TRUE)
+        /\ CASE x' # 2 -> Assert(FALSE, "Failed Test 23")
+             [] x' = 2 -> y' = 1
+
+     \/ /\ Print("Starting Test 24", TRUE)
+        /\ ~Foo(x)
+        /\ Assert(FALSE, "Failed Test 24")
+
+     \/ /\ Print("Starting Test 25", TRUE)
+        /\ ~Foo(x)
+        /\ Assert(FALSE, "Failed Test 25")
+
+
+Next == TNext \/ UNCHANGED <<x, y>>
+=========================================
diff --git a/tlatools/test-model/suite/test18.cfg b/tlatools/test-model/suite/test18.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test18.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test18.tla b/tlatools/test-model/suite/test18.tla
new file mode 100644
index 0000000000000000000000000000000000000000..de889dc7a9ccc0fb5f583aeabd4e8ce506de8412
--- /dev/null
+++ b/tlatools/test-model/suite/test18.tla
@@ -0,0 +1,64 @@
+--------------- MODULE test18 -------------
+
+(* Test of fingerprinting. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x, y, z, w, u
+
+f == [i \in {1,2}, j \in {3,4} |-> i+j]
+g == [p \in {1,2} \X {3,4} |-> p[1] + p[2]]
+m == [i \in {3,4} |-> <<i, 7>>]
+
+Init == /\ Print("There should be only one distinct state", TRUE)
+        /\ x = [a |-> 1, b |-> 2]
+        /\ y = <<3, 4>>
+        /\ z = {1, 2, 3}
+        /\ w = f
+        /\ u = [m EXCEPT ![4] = <<4, 8>>]
+        /\ IF y # [i \in {1, 2} |-> IF i = 1 THEN 3 ELSE 4]
+             THEN Print("Error in comparing seq and fcn", TRUE)
+             ELSE TRUE
+        
+
+Inv == /\ TRUE
+
+Next == 
+  \/ UNCHANGED <<x, y, z, w, u>>
+
+  \/ /\ x' = [i \in {"a", "b"} |-> IF i = "a" THEN 1 ELSE 2]
+     /\ UNCHANGED <<y, z, w, u>>
+
+  \/ /\ y' = [i \in {1, 2} |-> IF i = 1 THEN 3 ELSE 4]
+     /\ UNCHANGED <<x, z, w, u>>
+
+  \/ /\ y' = <<3>> \o <<4>>
+     /\ UNCHANGED <<x, z, w, u>>  
+
+  \/ /\ y' = [y EXCEPT ![2]=4]
+     /\ UNCHANGED <<x, z, w, u>>
+
+  \/ /\ y' = [<<3, 7>> EXCEPT ![2]=4]
+     /\ UNCHANGED <<x, z, w, u>>
+
+  \/ /\ y' = <<3>> \o <<4>>
+     /\ UNCHANGED <<x, z, w, u>>
+
+  \/ /\ z' = {3, 3, 2, 2, 1}
+     /\ UNCHANGED <<x, y, w, u>>
+
+  \/ /\ z' = 1..3
+     /\ UNCHANGED <<x, y, w, u>>
+
+  \/ /\ z' = {i - 3 : i \in {4, 5, 6, 4, 5}}
+     /\ UNCHANGED <<x, y, w, u>>
+
+  \/ /\ z' = DOMAIN <<"a", "b", 4>>
+     /\ UNCHANGED <<x, y, w, u>>
+
+  \/ /\ w' = g
+     /\ UNCHANGED <<x, y, z, u>>
+
+  \/ /\ u' = [m EXCEPT ![4][2] = @+1]
+     /\ UNCHANGED <<x, y, z, w>>         
+=========================================
diff --git a/tlatools/test-model/suite/test19.cfg b/tlatools/test-model/suite/test19.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test19.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test19.tla b/tlatools/test-model/suite/test19.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9d4ac22b6d9d3e8b317a150ba1abb11811944f4c
--- /dev/null
+++ b/tlatools/test-model/suite/test19.tla
@@ -0,0 +1,15 @@
+--------------- MODULE test19 -------------
+
+(* Test of large initial state space *)
+
+EXTENDS Naturals, Sequences
+
+VARIABLE x, y, z 
+Init == /\ x \in 1..100
+        /\ y \in 1..200
+        /\ z \in 1..20
+
+Next == UNCHANGED <<x, y, z>>
+
+Inv == TRUE
+=========================================
diff --git a/tlatools/test-model/suite/test2.cfg b/tlatools/test-model/suite/test2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d32cfa879ee7389879ef2ad6bdf490a5e474c707
--- /dev/null
+++ b/tlatools/test-model/suite/test2.cfg
@@ -0,0 +1,12 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT
+    a = a
+    b = b
+    c = c
diff --git a/tlatools/test-model/suite/test2.tla b/tlatools/test-model/suite/test2.tla
new file mode 100644
index 0000000000000000000000000000000000000000..45ff6602c2d7208ae5df4b1914f195fd666178ef
--- /dev/null
+++ b/tlatools/test-model/suite/test2.tla
@@ -0,0 +1,78 @@
+--------------- MODULE test2 -------------
+
+(* test equality of various representations of functions. *)
+
+EXTENDS Naturals, TLC
+
+VARIABLE x
+CONSTANT a, b, c
+f == [i \in {1,2}, j \in {3,4} |-> i+j]
+g == [p \in {1,2} \X {3,4} |-> p[1] + p[2]]
+h == [i \in {1,2} |-> [j \in {3,4} |-> i+j]]
+
+m == [i \in {3,4} |-> <<i, 7>>]
+
+
+Type == x \in {1}
+Inv  == 
+ /\ TRUE
+
+ /\ IF [i \in {3, 2, 1} |-> i+2] # <<3, 4, 5>>
+      THEN Assert(FALSE, "Failed Test 1") 
+      ELSE Print("Test 1 OK", TRUE)
+
+ /\ IF [i \in {"a", "b", "c"} |-> 
+         CASE i = "a" -> 1 [] i = "b" -> 2 [] i = "c" -> 3]
+         # [c |-> 3, a |-> 1, b |-> 2]
+      THEN Assert(FALSE, "Failed Test 2")
+      ELSE Print("Test 2 OK", TRUE)
+
+ /\ IF [c |-> 3, a |-> 1, b |-> 2] # 
+        [[a |-> 1, b |-> 3, c |-> 4] EXCEPT !.b =2 , !.c=3]
+      THEN Assert(FALSE, "Failed Test 3") 
+      ELSE Print("Test 3 OK", TRUE)
+
+ /\ IF <<3, 4, 5>> # [<<3, 42, 75>> EXCEPT ![3]=5, ![2]=4]
+      THEN Assert(FALSE, "Failed Test 4") 
+      ELSE Print("Test 4 OK", TRUE)
+
+ /\ IF [i \in {3, 2, 1} |-> i+2] # [ j \in 1..3 |-> j+2]
+      THEN Assert(FALSE, "Failed Test 5") 
+      ELSE Print("Test 5 OK", TRUE)
+
+ /\ IF [i \in {} |-> i] # <<>>
+      THEN Assert(FALSE, "Failed Test 6") 
+      ELSE Print("Test 6 OK", TRUE)
+
+ /\ IF {i \in [{1,2} -> {a, b, c}] : i[1] = a} # {<<a, a>>, <<a, b>>, <<a, c>>}
+      THEN Assert(FALSE, "Failed Test 7") 
+      ELSE Print("Test 7 OK", TRUE)
+
+ /\ IF [{"a", "bc"} -> {1, 2, 3}] # [bc : {1,2,3}, a : {1,2,3}]
+      THEN Assert(FALSE, "Failed Test 8") 
+      ELSE Print("Test 8 OK", TRUE)
+
+ /\ IF f # g
+      THEN Assert(FALSE, "Test 9 Failed")
+      ELSE Print("Test 9 OK", TRUE)
+
+ /\ IF f = h
+      THEN Assert(FALSE, "Test 10 Failed")
+      ELSE Print("Test 10 OK", TRUE)
+
+ /\ IF [m EXCEPT ![4] = <<4, 8>>] # [m EXCEPT ![4][2] = @+1]
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+ /\ IF m#[m EXCEPT ![4][2] = 7]
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+ /\ IF f # [f EXCEPT ![1,3] = 4]
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+Init == x = 1
+
+Next == UNCHANGED x
+=========================================
diff --git a/tlatools/test-model/suite/test20.cfg b/tlatools/test-model/suite/test20.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test20.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test20.tla b/tlatools/test-model/suite/test20.tla
new file mode 100644
index 0000000000000000000000000000000000000000..984160fbd869ab16f92daa1fed3e5d210f6ccd78
--- /dev/null
+++ b/tlatools/test-model/suite/test20.tla
@@ -0,0 +1,85 @@
+--------------- MODULE test20 -------------
+
+(* Test of ENABLED *)
+
+EXTENDS Naturals, Sequences, TLC
+
+VARIABLE x, y
+
+Init == x = 1 /\ y = 1
+
+Step(p) == x' = p
+
+Foo(A) == ENABLED((x' = x) /\ (A'=x+1))
+
+Next == 
+  \/ /\ x'=x
+     /\ y'=y
+
+  \/ /\ Print("Test N1 begun", TRUE)
+     /\ ~ENABLED (x'=2)
+     /\ Assert(FALSE, "Test N1 Failed")
+
+  \/ IF ENABLED (x'=2)
+       THEN /\ Print("Test N2 Passed", TRUE)
+            /\ UNCHANGED <<x, y>>
+       ELSE Assert(FALSE, "Test N2 Failed")
+  \/ IF ENABLED Step(17)
+       THEN /\ Print("Test N3 Passed", TRUE)
+            /\  UNCHANGED <<x, y>>
+       ELSE Assert(FALSE, "Test N3 Failed")
+
+  \/ IF \A p \in 1..3 : ENABLED Step(p)
+       THEN /\ Print("Test N4 Passed", TRUE)
+            /\  UNCHANGED <<x, y>>
+       ELSE Assert(FALSE, "Test N4 Failed")
+
+Inv == 
+  /\ IF ENABLED <<x'=1 /\ y'=y>>_<<x,y>>
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF ~ ENABLED (x'="a" /\ y'=y)
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF ENABLED (x > 2)
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF ~ENABLED (x=1)
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF ENABLED ((x > 2) /\ Next)
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF ~ENABLED ((x=1) /\ Next)
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF ~ ENABLED (x'=1) 
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF Foo(x)
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ LET A == x
+     IN  IF Foo(A) 
+           THEN Assert(FALSE, "Test 9 Failed")
+           ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF ~Foo(y) 
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ LET A == x
+     IN  IF ~Foo(y) 
+           THEN Assert(FALSE, "Test 11 Failed")
+           ELSE Print("Test 11 OK", TRUE)
+
+
+=========================================
diff --git a/tlatools/test-model/suite/test201.cfg b/tlatools/test-model/suite/test201.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438
--- /dev/null
+++ b/tlatools/test-model/suite/test201.cfg
@@ -0,0 +1,2 @@
+
+
diff --git a/tlatools/test-model/suite/test201.tla b/tlatools/test-model/suite/test201.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b2d3d5752a44c8ecb35d5802c3b0a7f1834cfc3f
--- /dev/null
+++ b/tlatools/test-model/suite/test201.tla
@@ -0,0 +1,20 @@
+-------------------- MODULE test201 ----------------
+\* Test of lambda expressions
+
+EXTENDS Naturals, TLC
+Op(A(_)) == A(4)
+
+bar == Op(LAMBDA x : x^2)
+
+ASSUME /\ Op(LAMBDA x : {x,2} ) = {4,2}
+       /\ PrintT("Test1 OK")
+
+ASSUME /\ bar = 16 
+       /\ PrintT("Test2 OK")
+
+foo == INSTANCE test201a WITH A <- LAMBDA y : y^3, b <- 4
+
+ASSUME /\ foo!def = 64
+       /\ PrintT("Test201 OK")
+=================================================
+
diff --git a/tlatools/test-model/suite/test201a.cfg b/tlatools/test-model/suite/test201a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438
--- /dev/null
+++ b/tlatools/test-model/suite/test201a.cfg
@@ -0,0 +1,2 @@
+
+
diff --git a/tlatools/test-model/suite/test201a.tla b/tlatools/test-model/suite/test201a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..1f5c21d50552d1fa1a74e50dab58a381f7acb211
--- /dev/null
+++ b/tlatools/test-model/suite/test201a.tla
@@ -0,0 +1,7 @@
+-------------------- MODULE test201a ----------------
+
+CONSTANT A(_), b
+
+def == A(b)
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test202.cfg b/tlatools/test-model/suite/test202.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438
--- /dev/null
+++ b/tlatools/test-model/suite/test202.cfg
@@ -0,0 +1,2 @@
+
+
diff --git a/tlatools/test-model/suite/test202.tla b/tlatools/test-model/suite/test202.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bf779d2986e617f83e4070bf003f0cf88ea007ef
--- /dev/null
+++ b/tlatools/test-model/suite/test202.tla
@@ -0,0 +1,32 @@
+-------------------- MODULE test202 ----------------
+\* Test of recursion
+
+EXTENDS Naturals, TLC
+
+RECURSIVE g(_)
+f(a) == IF a = 0 THEN 1 ELSE a * g(a-1)
+g(a) == f(a) 
+
+
+ASSUME /\ f(0) = 1
+       /\ f(4) = 24
+       /\ \A i \in 1..5 : f(i) = i * f(i-1)
+       /\ PrintT("Test 1 OK")
+
+ASSUME /\ g(0) = 1
+       /\ g(4) = 24
+       /\ \A i \in 1..5 : g(i) = i * g(i-1)
+       /\ PrintT("Test 2 OK")
+
+RECURSIVE fact
+fact1[n \in Nat] == fact[n]
+fact[n \in Nat] == IF n = 0 THEN 1 ELSE n * fact1[n-1]
+
+ASSUME /\ fact1[3] = 6
+       /\ fact[4] = 24
+       /\ \A i \in 0..5 : fact[i] = g(i)     
+       /\ PrintT("Test 3 OK")
+
+
+=================================================
+
diff --git a/tlatools/test-model/suite/test203.cfg b/tlatools/test-model/suite/test203.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438
--- /dev/null
+++ b/tlatools/test-model/suite/test203.cfg
@@ -0,0 +1,2 @@
+
+
diff --git a/tlatools/test-model/suite/test203.tla b/tlatools/test-model/suite/test203.tla
new file mode 100644
index 0000000000000000000000000000000000000000..47ba23369b9232e869c454ec290ea756d2ee3c43
--- /dev/null
+++ b/tlatools/test-model/suite/test203.tla
@@ -0,0 +1,17 @@
+-------------------- MODULE test203 ----------------
+\* Test of recursion
+
+EXTENDS Naturals, TLC
+
+RECURSIVE g(_)
+
+foo(n) == INSTANCE test203a WITH A <- g, b <- n
+
+g(n) == IF n = 0 THEN 1 ELSE n * foo(n-1)!h
+
+ASSUME /\ g(0) = 1
+       /\ g(4) = 24
+       /\ \A i \in 1..5 : g(i) = i * g(i-1)
+       /\ PrintT("Test1 OK")
+=================================================
+
diff --git a/tlatools/test-model/suite/test203a.cfg b/tlatools/test-model/suite/test203a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438
--- /dev/null
+++ b/tlatools/test-model/suite/test203a.cfg
@@ -0,0 +1,2 @@
+
+
diff --git a/tlatools/test-model/suite/test203a.tla b/tlatools/test-model/suite/test203a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..4e3cc2422ced3efb13590461bf82d745063cb183
--- /dev/null
+++ b/tlatools/test-model/suite/test203a.tla
@@ -0,0 +1,8 @@
+-------------------- MODULE test203a ----------------
+
+CONSTANT A(_), b
+
+h == A(b) 
+
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test204.cfg b/tlatools/test-model/suite/test204.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/tlatools/test-model/suite/test204.cfg
@@ -0,0 +1 @@
+
diff --git a/tlatools/test-model/suite/test204.tla b/tlatools/test-model/suite/test204.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b65c21c4c88718c8b9eaa17d6419854a753c2200
--- /dev/null
+++ b/tlatools/test-model/suite/test204.tla
@@ -0,0 +1,178 @@
+--------------- MODULE test204 -------------
+
+(***************************************************************************)
+(* Test definability and use of all user-defined operators as prefix       *)
+(* operators, as in +(a, b).  It does not work with binary minus.          *)
+(***************************************************************************)
+
+z - y == <<1, z, y>>
+\* ASSUME -(4, 6) = <<1, 4, 6>>
+z + y == <<2, z, y>>
+ASSUME +(4, 6) = <<2, 4, 6>>
+z * y == <<3, z, y>>
+ASSUME *(4, 6) = <<3, 4, 6>>
+z / y == <<4, z, y>>
+ASSUME /(4, 6) = <<4, 4, 6>>
+z ^ y == <<5, z, y>>
+ASSUME ^(4, 6) = <<5, 4, 6>>
+z < y == <<6, z, y>>
+ASSUME <(4, 6) = <<6, 4, 6>>
+z > y == <<7, z, y>>
+ASSUME >(4, 6) = <<7, 4, 6>>
+z .. y == <<8, z, y>>
+ASSUME ..(4, 6) = <<8, 4, 6>>
+z ... y == <<9, z, y>>
+ASSUME ...(4, 6) = <<9, 4, 6>>
+z | y == <<10, z, y>>
+ASSUME |(4, 6) = <<10, 4, 6>>
+z || y == <<11, z, y>>
+ASSUME ||(4, 6) = <<11, 4, 6>>
+z & y == <<12, z, y>>
+ASSUME &(4, 6) = <<12, 4, 6>>
+z && y == <<13, z, y>>
+ASSUME &&(4, 6) = <<13, 4, 6>>
+z $ y == <<14, z, y>>
+ASSUME $(4, 6) = <<14, 4, 6>>
+z $$ y == <<15, z, y>>
+ASSUME $$(4, 6) = <<15, 4, 6>>
+z ?? y == <<16, z, y>>
+ASSUME ??(4, 6) = <<16, 4, 6>>
+z %% y == <<17, z, y>>
+ASSUME %%(4, 6) = <<17, 4, 6>>
+z ## y == <<18, z, y>>
+ASSUME ##(4, 6) = <<18, 4, 6>>
+z ++ y == <<19, z, y>>
+ASSUME ++(4, 6) = <<19, 4, 6>>
+z -- y == <<20, z, y>>
+ASSUME --(4, 6) = <<20, 4, 6>>
+z ** y == <<21, z, y>>
+ASSUME **(4, 6) = <<21, 4, 6>>
+z // y == <<22, z, y>>
+ASSUME //(4, 6) = <<22, 4, 6>>
+z ^^ y == <<23, z, y>>
+ASSUME ^^(4, 6) = <<23, 4, 6>>
+z @@ y == <<23, z, y>> 
+ASSUME @@(4, 6) = <<23, 4, 6>>
+z !! y == <<24, z, y>>
+ASSUME !!(4, 6) = <<24, 4, 6>>
+z |- y == <<25, z, y>>
+ASSUME |-(4, 6) = <<25, 4, 6>>
+z |= y == <<26, z, y>>
+ASSUME |=(4, 6) = <<26, 4, 6>>
+z -| y == <<27, z, y>>
+ASSUME -|(4, 6) = <<27, 4, 6>>
+z =| y == <<28, z, y>>
+ASSUME =|(4, 6) = <<28, 4, 6>>
+z <: y == <<29, z, y>>
+ASSUME <:(4, 6) = <<29, 4, 6>>
+z :> y == <<29, z, y>>
+ASSUME :>(4, 6) = <<29, 4, 6>>
+z := y == <<30, z, y>>
+ASSUME :=(4, 6) = <<30, 4, 6>>
+z ::= y == <<31, z, y>>
+ASSUME ::=(4, 6) = <<31, 4, 6>>
+z \uplus y == <<32, z, y>>
+ASSUME \uplus(4, 6) = <<32, 4, 6>>
+z \sqcap y == <<33, z, y>>
+ASSUME \sqcap(4, 6) = <<33, 4, 6>>
+z \sqcup y == <<34, z, y>>
+ASSUME \sqcup(4, 6) = <<34, 4, 6>>
+z \div y == <<35, z, y>>
+ASSUME \div(4, 6) = <<35, 4, 6>>
+z \wr y == <<36, z, y>>
+ASSUME \wr(4, 6) = <<36, 4, 6>>
+z \star y == <<37, z, y>>
+ASSUME \star(4, 6) = <<37, 4, 6>>
+z \bigcirc y == <<38, z, y>>
+ASSUME \bigcirc(4, 6) = <<38, 4, 6>>
+z \bullet y == <<39, z, y>>
+ASSUME \bullet(4, 6) = <<39, 4, 6>>
+z \prec y == <<40, z, y>>
+ASSUME \prec(4, 6) = <<40, 4, 6>>
+z \succ y == <<41, z, y>>
+ASSUME \succ(4, 6) = <<41, 4, 6>>
+z \preceq y == <<42, z, y>>
+ASSUME \preceq(4, 6) = <<42, 4, 6>>
+z \succeq y == <<43, z, y>>
+ASSUME \succeq(4, 6) = <<43, 4, 6>>
+z \sim y == <<44, z, y>>
+ASSUME \sim(4, 6) = <<44, 4, 6>>
+z \simeq y == <<45, z, y>>
+ASSUME \simeq(4, 6) = <<45, 4, 6>>
+z \ll y == <<46, z, y>>
+ASSUME \ll(4, 6) = <<46, 4, 6>>
+z \gg y == <<47, z, y>>
+ASSUME \gg(4, 6) = <<47, 4, 6>>
+z \asymp y == <<48, z, y>>
+ASSUME \asymp(4, 6) = <<48, 4, 6>>
+z \subset y == <<49, z, y>>
+ASSUME \subset(4, 6) = <<49, 4, 6>>
+z \supset y == <<50, z, y>>
+ASSUME \supset(4, 6) = <<50, 4, 6>>
+z \supseteq y == <<51, z, y>>
+ASSUME \supseteq(4, 6) = <<51, 4, 6>>
+z \approx y == <<52, z, y>>
+ASSUME \approx(4, 6) = <<52, 4, 6>>
+z \cong y == <<53, z, y>>
+ASSUME \cong(4, 6) = <<53, 4, 6>>
+z \sqsubset y == <<54, z, y>>
+ASSUME \sqsubset(4, 6) = <<54, 4, 6>>
+z \sqsupset y == <<55, z, y>>
+ASSUME \sqsupset(4, 6) = <<55, 4, 6>>
+z \sqsubseteq y == <<56, z, y>>
+ASSUME \sqsubseteq(4, 6) = <<56, 4, 6>>
+z \sqsupseteq y == <<57, z, y>>
+ASSUME \sqsupseteq(4, 6) = <<57, 4, 6>>
+z \doteq y == <<58, z, y>>
+ASSUME \doteq(4, 6) = <<58, 4, 6>>
+z \propto y == <<59, z, y>>
+ASSUME \propto(4, 6) = <<59, 4, 6>>
+
+z ^+ == <<60, z>>
+ASSUME ^+(4) = <<60, 4>>
+z ^* == <<61, z>>
+ASSUME ^*(4) = <<61, 4>>
+z ^# == <<62, z>>
+ASSUME ^#(4) = <<62, 4>>
+-. z == <<63, z>>
+ASSUME -.(4) = <<63, 4>>
+
+z \leq y == <<64, z, y>>
+ASSUME \leq(4, 6) = <<64, 4, 6>>
+ASSUME <=(4, 6) = <<64, 4, 6>>
+ASSUME =<(4, 6) = <<64, 4, 6>>
+
+z \geq y == <<65, z, y>>
+ASSUME \geq(4, 6) = <<65, 4, 6>>
+ASSUME >=(4, 6) = <<65, 4, 6>>
+
+z \oplus y == <<66, z, y>>
+ASSUME \oplus(4, 6) = <<66, 4, 6>>
+ASSUME (+) (4, 6) = <<66, 4, 6>>
+
+z \ominus y == <<67, z, y>>
+ASSUME \ominus(4, 6) = <<67, 4, 6>>
+ASSUME (-) (4, 6) = <<67, 4, 6>>
+
+
+z \odot y == <<68, z, y>>
+ASSUME \odot(4, 6) = <<68, 4, 6>>
+ASSUME (.) (4, 6) = <<68, 4, 6>>
+
+z \otimes y == <<69, z, y>>
+ASSUME \otimes(4, 6) = <<69, 4, 6>>
+ASSUME (\X) (4, 6) = <<69, 4, 6>>
+
+z \oslash y == <<70, z, y>>
+ASSUME \oslash(4, 6) = <<70, 4, 6>>
+ASSUME (/) (4, 6) = <<70, 4, 6>>
+
+z \circ y == <<71, z, y>>
+ASSUME \circ(4, 6) = <<71, 4, 6>>
+ASSUME \o (4, 6) = <<71, 4, 6>>
+
+ASSUME \in(1, {1})
+
+ASSUME \cup({1}, {2}) = {1,2}
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test205.cfg b/tlatools/test-model/suite/test205.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/tlatools/test-model/suite/test205.cfg
@@ -0,0 +1 @@
+
diff --git a/tlatools/test-model/suite/test205.tla b/tlatools/test-model/suite/test205.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c057b6abba236aa393e4df701a08f4fee26edac3
--- /dev/null
+++ b/tlatools/test-model/suite/test205.tla
@@ -0,0 +1,175 @@
+--------------- MODULE test205 -------------
+
+(***************************************************************************)
+(* Test use of notation R!+(a, b) for instantiated *-fix operators.        *)
+(***************************************************************************)
+
+F == INSTANCE test204
+
+\*z - y == <<1, z, y>>
+ASSUME F!-(4, 6) = <<1, 4, 6>>
+\*z + y == <<2, z, y>>
+ASSUME F!+(4, 6) = <<2, 4, 6>>
+\*z * y == <<3, z, y>>
+ASSUME F!*(4, 6) = <<3, 4, 6>>
+\*z / y == <<4, z, y>>
+ASSUME F!/(4, 6) = <<4, 4, 6>>
+\*z ^ y == <<5, z, y>>
+ASSUME F!^(4, 6) = <<5, 4, 6>>
+\*z < y == <<6, z, y>>
+ASSUME F!<(4, 6) = <<6, 4, 6>>
+\*z > y == <<7, z, y>>
+ASSUME F!>(4, 6) = <<7, 4, 6>>
+\*z .. y == <<8, z, y>>
+ASSUME F!..(4, 6) = <<8, 4, 6>>
+\*z ... y == <<9, z, y>>
+ASSUME F!...(4, 6) = <<9, 4, 6>>
+\*z | y == <<10, z, y>>
+ASSUME F!|(4, 6) = <<10, 4, 6>>
+\*z || y == <<11, z, y>>
+ASSUME F!||(4, 6) = <<11, 4, 6>>
+\*z & y == <<12, z, y>>
+ASSUME F!&(4, 6) = <<12, 4, 6>>
+\*z && y == <<13, z, y>>
+ASSUME F!&&(4, 6) = <<13, 4, 6>>
+\*z $ y == <<14, z, y>>
+ASSUME F!$(4, 6) = <<14, 4, 6>>
+\*z $$ y == <<15, z, y>>
+ASSUME F!$$(4, 6) = <<15, 4, 6>>
+\*z ?? y == <<16, z, y>>
+ASSUME F!??(4, 6) = <<16, 4, 6>>
+\*z %% y == <<17, z, y>>
+ASSUME F!%%(4, 6) = <<17, 4, 6>>
+\*z ## y == <<18, z, y>>
+ASSUME F!##(4, 6) = <<18, 4, 6>>
+\*z ++ y == <<19, z, y>>
+ASSUME F!++(4, 6) = <<19, 4, 6>>
+\*z -- y == <<20, z, y>>
+ASSUME F!--(4, 6) = <<20, 4, 6>>
+\*z ** y == <<21, z, y>>
+ASSUME F!**(4, 6) = <<21, 4, 6>>
+\*z // y == <<22, z, y>>
+ASSUME F!//(4, 6) = <<22, 4, 6>>
+\*z ^^ y == <<23, z, y>>
+ASSUME F!^^(4, 6) = <<23, 4, 6>>
+\*z @@ y == <<23, z, y>> 
+ASSUME F!@@(4, 6) = <<23, 4, 6>>
+\*z !! y == <<24, z, y>>
+ASSUME F ! !!(4, 6) = <<24, 4, 6>>
+\*z |- y == <<25, z, y>>
+ASSUME F!|-(4, 6) = <<25, 4, 6>>
+\*z |= y == <<26, z, y>>
+ASSUME F!|=(4, 6) = <<26, 4, 6>>
+\*z -| y == <<27, z, y>>
+ASSUME F!-|(4, 6) = <<27, 4, 6>>
+\*z =| y == <<28, z, y>>
+ASSUME F!=|(4, 6) = <<28, 4, 6>>
+\*z <: y == <<29, z, y>>
+ASSUME F!<:(4, 6) = <<29, 4, 6>>
+\*z :> y == <<29, z, y>>
+ASSUME F!:>(4, 6) = <<29, 4, 6>>
+\*z := y == <<30, z, y>>
+ASSUME F!:=(4, 6) = <<30, 4, 6>>
+\*z ::= y == <<31, z, y>>
+ASSUME F!::=(4, 6) = <<31, 4, 6>>
+\*z \uplus y == <<32, z, y>>
+ASSUME F!\uplus(4, 6) = <<32, 4, 6>>
+\*z \sqcap y == <<33, z, y>>
+ASSUME F!\sqcap(4, 6) = <<33, 4, 6>>
+\*z \sqcup y == <<34, z, y>>
+ASSUME F!\sqcup(4, 6) = <<34, 4, 6>>
+\*z \div y == <<35, z, y>>
+ASSUME F!\div(4, 6) = <<35, 4, 6>>
+\*z \wr y == <<36, z, y>>
+ASSUME F!\wr(4, 6) = <<36, 4, 6>>
+\*z \star y == <<37, z, y>>
+ASSUME F!\star(4, 6) = <<37, 4, 6>>
+\*z \bigcirc y == <<38, z, y>>
+ASSUME F!\bigcirc(4, 6) = <<38, 4, 6>>
+\*z \bullet y == <<39, z, y>>
+ASSUME F!\bullet(4, 6) = <<39, 4, 6>>
+\*z \prec y == <<40, z, y>>
+ASSUME F!\prec(4, 6) = <<40, 4, 6>>
+\*z \succ y == <<41, z, y>>
+ASSUME F!\succ(4, 6) = <<41, 4, 6>>
+\*z \preceq y == <<42, z, y>>
+ASSUME F!\preceq(4, 6) = <<42, 4, 6>>
+\*z \succeq y == <<43, z, y>>
+ASSUME F!\succeq(4, 6) = <<43, 4, 6>>
+\*z \sim y == <<44, z, y>>
+ASSUME F!\sim(4, 6) = <<44, 4, 6>>
+\*z \simeq y == <<45, z, y>>
+ASSUME F!\simeq(4, 6) = <<45, 4, 6>>
+\*z \ll y == <<46, z, y>>
+ASSUME F!\ll(4, 6) = <<46, 4, 6>>
+\*z \gg y == <<47, z, y>>
+ASSUME F!\gg(4, 6) = <<47, 4, 6>>
+\*z \asymp y == <<48, z, y>>
+ASSUME F!\asymp(4, 6) = <<48, 4, 6>>
+\*z \subset y == <<49, z, y>>
+ASSUME F!\subset(4, 6) = <<49, 4, 6>>
+\*z \supset y == <<50, z, y>>
+ASSUME F!\supset(4, 6) = <<50, 4, 6>>
+\*z \supseteq y == <<51, z, y>>
+ASSUME F!\supseteq(4, 6) = <<51, 4, 6>>
+\*z \approx y == <<52, z, y>>
+ASSUME F!\approx(4, 6) = <<52, 4, 6>>
+\*z \cong y == <<53, z, y>>
+ASSUME F!\cong(4, 6) = <<53, 4, 6>>
+\*z \sqsubset y == <<54, z, y>>
+ASSUME F!\sqsubset(4, 6) = <<54, 4, 6>>
+\*z \sqsupset y == <<55, z, y>>
+ASSUME F!\sqsupset(4, 6) = <<55, 4, 6>>
+\*z \sqsubseteq y == <<56, z, y>>
+ASSUME F!\sqsubseteq(4, 6) = <<56, 4, 6>>
+\*z \sqsupseteq y == <<57, z, y>>
+ASSUME F!\sqsupseteq(4, 6) = <<57, 4, 6>>
+\*z \doteq y == <<58, z, y>>
+ASSUME F!\doteq(4, 6) = <<58, 4, 6>>
+\*z \propto y == <<59, z, y>>
+ASSUME F!\propto(4, 6) = <<59, 4, 6>>
+
+\*z ^+ == <<60, z>>
+ASSUME F!^+(4) = <<60, 4>>
+\*z ^* == <<61, z>>
+ASSUME F!^*(4) = <<61, 4>>
+\*z ^# == <<62, z>>
+ASSUME F!^#(4) = <<62, 4>>
+\*-. z == <<63, z>>
+ASSUME F!-.(4) = <<63, 4>>
+
+\*z \leq y == <<64, z, y>>
+ASSUME F!\leq(4, 6) = <<64, 4, 6>>
+ASSUME F!<=(4, 6) = <<64, 4, 6>>
+ASSUME F!=<(4, 6) = <<64, 4, 6>>
+
+\*z \geq y == <<65, z, y>>
+ASSUME F!\geq(4, 6) = <<65, 4, 6>>
+ASSUME F!>=(4, 6) = <<65, 4, 6>>
+
+\*z \oplus y == <<66, z, y>>
+ASSUME F!\oplus(4, 6) = <<66, 4, 6>>
+ASSUME F!(+) (4, 6) = <<66, 4, 6>>
+
+\*z \ominus y == <<67, z, y>>
+ASSUME F!\ominus(4, 6) = <<67, 4, 6>>
+ASSUME F!(-) (4, 6) = <<67, 4, 6>>
+
+
+\*z \odot y == <<68, z, y>>
+ASSUME F!\odot(4, 6) = <<68, 4, 6>>
+ASSUME F!(.) (4, 6) = <<68, 4, 6>>
+
+\*z \otimes y == <<69, z, y>>
+ASSUME F!\otimes(4, 6) = <<69, 4, 6>>
+ASSUME F!(\X) (4, 6) = <<69, 4, 6>>
+
+\*z \oslash y == <<70, z, y>>
+ASSUME F!\oslash(4, 6) = <<70, 4, 6>>
+ASSUME F!(/) (4, 6) = <<70, 4, 6>>
+
+\*z \circ y == <<71, z, y>>
+ASSUME F!\circ(4, 6) = <<71, 4, 6>>
+ASSUME F!\o (4, 6) = <<71, 4, 6>>
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test206.cfg b/tlatools/test-model/suite/test206.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8f848aa991a48bf066529f284b3990008f6100d4
--- /dev/null
+++ b/tlatools/test-model/suite/test206.cfg
@@ -0,0 +1 @@
+CONSTANT C = 4
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test206.tla b/tlatools/test-model/suite/test206.tla
new file mode 100644
index 0000000000000000000000000000000000000000..16f9e534db5b450cdc3242ad42bf6f9baa6848fb
--- /dev/null
+++ b/tlatools/test-model/suite/test206.tla
@@ -0,0 +1,238 @@
+------------------------------- MODULE test206 ------------------------------ 
+\* Test of labels and their use in subexpression selectors.
+
+EXTENDS TLC, Integers
+
+CONSTANT C  \* Test with C = 4
+
+Op(Arg(_), p) == 
+   \A x \in {1,2}, <<y, z>> \in {<<3, 4>>} :
+                      lab(x,y,z) ::  LET a ++ b ==  
+                                          Arg(a) + b + x + 2*y + 3*z
+                                     IN  (Arg(1) + Arg(p) + C + 
+                                            2*x  + 4*y + 6*z) =
+                                         1 ++ ( label2 :: p ++ C)
+\*                                IN /\label2::  PrintT("FOO")
+\*                                   /\ PrintT(Arg(1))
+\*                                   /\ PrintT("FOO2")
+\*                                   /\ PrintT(<<p, Arg(p)>>)
+\*                                   /\ PrintT("FOO3")
+\*                                   /\ PrintT(<<p, C>>)
+\*                                   /\ PrintT("FOO3a")
+\*                                   /\ PrintT(1++2)
+\*                                   /\ PrintT(p++C)
+\*                                   /\ PrintT("FOO4")
+\*                                   /\ PrintT(<<x, y, z>>)
+
+Foo(u) == 2*u 
+
+ASSUME Foo(5) = Foo(5)!:
+
+THEOREM Thm == ASSUME FALSE
+               PROVE  lab:: C > 0
+
+THEOREM Thm2 == lab3 :: TRUE
+
+
+ASSUME Foo(25) = 50
+
+Bar == Op(Foo, 9)!lab(1,2,3)
+
+ASSUME Op(Foo, 9) 
+
+ASSUME Thm2!lab3
+
+ASSUME Bar
+
+ASSUME Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) 
+
+Op3(A(_,_,_), a1, a2, a3) == A(a1, a2, a3)
+
+Op1(a) == \A x \in {} : lab(x) :: IF TRUE
+                             THEN LET Op1a(c) == 4 * c + a*x
+                                  IN  a + Op1a(x) 
+                             ELSE 1
+SOp1(a) == a
+OOp1(A(_), a) == A(a)
+ASSUME OOp1(SOp1, 2) = 2
+
+SOp1a(a) == {x \in {1,2} : a = x}
+OOp2(A(_,_), a, b) == A(a, b)
+ASSUME OOp2(SOp1a!@, 2, 2) = TRUE
+
+SOp1b(a) == {x + a : x \in {1,2}}
+ASSUME OOp2(SOp1b!@, 2, 3) = 5
+
+SOp1c(a) == {(LET c == a * x IN x + a)  : x \in {1,2}}
+ASSUME OOp2(SOp1c!@!c, 2, 3) = 2 * 3
+
+SOp1d(a) == {(LET c(y) == (a * x) + y IN x + a)  : x \in {1,2}}
+OOp3(A(_,_,_), a, b, c) == A(a, b, c)
+ASSUME OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4
+
+OOp4(A(_,_,_,_), a, b, c, d) == A(a, b, c, d)
+
+
+\* Foo1 == Op1(2)!(3)!2!1
+ASSUME Op1(2)!(3)!2!1 = 2 + 4*3 + 6
+ASSUME Op1(2)!(3)!2!Op1a(4) = 16 + 6
+ASSUME Op3(Op1!@!2!Op1a, 2, 3, 4) = 22
+
+COp(q, p) == 
+   CHOOSE x : lab(x) ::  LET a ++ b ==  {a, b, q, p, x}
+                         IN  <<q, 99 ++ p >>
+
+
+ASSUME COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5}
+
+ASSUME COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >>
+ASSUME COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >>
+ASSUME COp(1, 2)!(3)!1!>> = {99, 1, 2, 3}
+
+OOp5(A(_, _, _, _, _), a1, a2, a3, a4, a5) == A(a1, a2, a3, a4, a5)
+
+ASSUME OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5}
+
+Inst == INSTANCE test206a WITH A5 <- COp!@!++
+ASSUME Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME Inst!Bar(3)!>> = 3
+ASSUME Inst!Bar2(3)!>> = 3
+ASSUME Inst!Bar3!>> = 2
+ASSUME Inst!Bar4(1)!(2)!<< = 2
+ASSUME Inst!Bar4(1)!(2)!>> = 1
+ASSUME Inst!Bar4(1)!lab(2)!<< = 2
+ASSUME Inst!Bar4(1)!lab(2)!>> = 1
+ASSUME OOp2(Inst!Bar4!@!<<, 1, 2) = 2
+ASSUME OOp2(Inst!Bar4!@!>>, 1, 2) = 1
+ASSUME OOp2(Inst!Bar4!lab!<<, 1, 2) = 2
+ASSUME OOp2(Inst!Bar4!lab!>>, 1, 2) = 1
+ASSUME Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>>
+
+Inst2(A5(_, _, _, _, _)) ==  INSTANCE test206a 
+ASSUME Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+
+
+COp2(q, p) == 
+   CHOOSE x : lab(x) ::  LET a ++ b ==  {lab(y):: y + a + b : y \in {1}}
+                         IN  <<q, 99 ++ p >>
+
+ASSUME COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30
+ASSUME COp2(1,2)!(3)!++(9,10)!lab(11) = 30
+ASSUME COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30
+OOp6(A(_, _, _, _, _, _), a1, a2, a3, a4, a5, a6) == A(a1, a2, a3, a4, a5, a6)
+ASSUME OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30
+
+\* Let's test all the different kinds of operators
+
+Def1 == {1, 2, 3, 4}
+ASSUME Def1!2 = 2
+
+Def2 == {x \in {1, 2, 3} : x > 17}
+ASSUME Def2!1 = {1, 2, 3}
+ASSUME Def2!(14)!<< = 14
+
+Def3 == {<<x, y>> : x \in {1}, y \in {2}}
+ASSUME Def3!2!1 = 2
+ASSUME Def3!(1, 2)!<< = 1
+
+Def4 == SUBSET UNION {{1}, {2}}
+ASSUME Def4!<<!<<!2 = {2}
+
+Def5 == [i \in 1..10 |-> i+1][3]
+ASSUME Def5!>> = 3
+ASSUME Def5!<<[4] = 5
+ASSUME Def5!<<!1!>> = 10
+
+Def6 == [[i \in 1..10 |-> i+1] EXCEPT ![2] = 1, ![3] = 2]
+ASSUME Def6!1[3] = 4
+\* The following two subexpression expressions are now
+\* considered to be illegal because they could contain
+\* a "@".
+\* ASSUME Def6!2=1
+\* ASSUME Def6!3=2
+
+Def7 == 3.a
+ASSUME Def7!1 = 3
+ASSUME Def7!2 = "a"
+
+Def8 == [a |-> 1, b |-> 2]
+ASSUME Def8!2 = 2
+
+Def9 == [a : 1, b : 2]
+ASSUME Def9!2 = 2
+
+Def10 == <<1, 2, 3>>
+ASSUME Def10!2 = 2
+
+Def11 == {1} \X {2} \X {3}
+ASSUME Def11!3!1 = 3
+
+Def12 == IF 2=3 THEN 2 ELSE 3
+ASSUME /\ Def12!1!<< = 2
+       /\ Def12!2 = 2
+       /\ Def12!3 = 3
+
+Def13 == \A x : CASE x=1 -> 1
+                  [] x=2 -> 2
+                  [] OTHER -> 3
+ASSUME /\ Def13!(42)!1!<<!<< = 42
+       /\ Def13!(42)!1!>> = 1
+       /\ Def13!(42)!2!>> = 2
+       /\ Def13!(42)!3!>> = 3
+
+Def14 == \A x \in {1} : x = 1
+ASSUME /\ Def14!(2)!<< = 2
+       /\ Def14!1!1 = 1
+
+Def15 == \A x \in {1}, y \in {2} : (x = 1) /\ (y = 2)
+ASSUME /\ Def15!(2,3)!<<!<< = 2
+       /\ Def15!(2,3)!>>!<< = 3
+       /\ Def15!2!<< = 2
+
+Def16 == \E x \in {1}, <<y, z>> \in {<<2,3>>} : /\ ((x = 1))
+                                                /\ y = 2 
+                                                /\ (z=3)
+ASSUME /\ Def16!(2,3,4)!1!1 = 2
+       /\ Def16!(2,3,4)!2!1 = 3
+       /\ Def16!2!1!<< = 2
+
+Def17 == \E x , y, z : /\ ((x = 1))
+                       /\ y = 2 
+                       /\ (z=3)
+ASSUME /\ Def17!(2,3,4)!1!1 = 2
+       /\ Def17!(2,3,4)!2!1 = 3
+
+Def18 == CHOOSE x : (x=1)
+ASSUME Def18!(2)!<< = 2
+
+Def19 == CHOOSE x \in {42}: (x=1)
+ASSUME /\ Def19!(2)!<< = 2
+       /\ Def19!1!1 = 42
+
+Def20 == [12]_(23)
+ASSUME /\ Def20!1 = 12
+       /\ Def20!>> = 23
+
+Def21 == <<12>>_(23)
+ASSUME /\ Def21!1 = 12
+       /\ Def21!>> = 23
+
+Def22 == WF_(12)(23)
+ASSUME /\ Def22!1 = 12
+       /\ Def22!>> = 23
+
+Def23 == \EE x , y, z : /\ ((x = 1))
+                        /\ y = 2 
+                        /\ (z=3)
+ASSUME /\ Def23!(2,3,4)!1!1 = 2
+       /\ Def23!(2,3,4)!2!1 = 3
+
+
+=============================================================================
+
+last modified on Tue  9 March 2010 at 11:49:54 PST by lamport
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test206a.cfg b/tlatools/test-model/suite/test206a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8f848aa991a48bf066529f284b3990008f6100d4
--- /dev/null
+++ b/tlatools/test-model/suite/test206a.cfg
@@ -0,0 +1 @@
+CONSTANT C = 4
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test206a.tla b/tlatools/test-model/suite/test206a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..add74af1a0e13af6987c95283dacbef9eab2d75c
--- /dev/null
+++ b/tlatools/test-model/suite/test206a.tla
@@ -0,0 +1,12 @@
+----------------------------- MODULE test206a -----------------------------
+
+CONSTANT A5(_, _, _, _, _)
+
+Foo(a1, a2, a3, a4, a5) == A5(a1, a2, a3, a4, a5) 
+Bar(a) == <<1, a>>
+Bar2(a) == <<1, lab::a>>
+Bar3 == <<1, 2>>
+Bar4(a) == {x \in {1} : lab(x) :: <<x, a>>}
+Bar5(a) == {x \in {1} : lab(x) :: LET b ++ c == <<a, b, c, x>>
+                                  IN 42 }
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test207.cfg b/tlatools/test-model/suite/test207.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/tlatools/test-model/suite/test207.cfg
@@ -0,0 +1 @@
+
diff --git a/tlatools/test-model/suite/test207.tla b/tlatools/test-model/suite/test207.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c8b6cd6974c96fd1d3289c707bca5039404a12f5
--- /dev/null
+++ b/tlatools/test-model/suite/test207.tla
@@ -0,0 +1,38 @@
+\* SANY2 test.
+\* Test of scope of NEW declarations.  They should be restricted
+\* to the statement's proof, except for SUFFICES which should be 
+\* visible to the rest of the current proof.
+
+-------------------- MODULE test207 ----------------
+EXTENDS TLC
+a + b == <<a, b>>
+THEOREM Thm == ASSUME NEW J PROVE lab :: TRUE
+<1>1 ASSUME NEW I
+     PROVE  Thm!lab
+  <2>1 I = J
+  <2>2 QED
+<1> DEFINE I == 1
+<1>2 ASSUME NEW K
+     PROVE  TRUE
+<1>3 SUFFICES ASSUME NEW K
+              PROVE  TRUE
+  <2>1. \A K : TRUE
+  <2>2. SUFFICES ASSUME NEW L
+                 PROVE  TRUE
+  <2>3. QED
+<1>3a. DEFINE L == 0
+<1>4.  K = 0      \* 12 June 14 Changed from <*>4 which is now illegal.
+  <2>1. DEFINE F == 7
+  <2>4. TRUE   \* 12 June 14 Changed from <*>4 which is now illegal.
+  <2>2. QED
+     BY <2>4
+<1>4a. DEFINE F == 42
+<1>5 QED
+  BY <1>4
+J == 0
+I == 0
+F == 1
+
+ASSUME PrintT("SANY2 Test")
+
+=============================
diff --git a/tlatools/test-model/suite/test208.cfg b/tlatools/test-model/suite/test208.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/tlatools/test-model/suite/test208.cfg
@@ -0,0 +1 @@
+
diff --git a/tlatools/test-model/suite/test208.tla b/tlatools/test-model/suite/test208.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8f938c37b8b8d648da0e14862f7362d0fc60bf0a
--- /dev/null
+++ b/tlatools/test-model/suite/test208.tla
@@ -0,0 +1,203 @@
+\* SANY2 test.
+\* Test to make sure that all keywords are useable as record fields.
+
+-------------------- MODULE test208 ----------------
+bar == 17
+
+LEMMA TRUE
+PROPOSITION TRUE
+ASSUMPTION TRUE
+
+Foo == 
+/\  bar.NEW \cup  [bar EXCEPT !.NEW = 0]
+/\  bar.SF_ \cup  [bar EXCEPT !.SF_ = 0]
+/\  bar.WF_ \cup  [bar EXCEPT !.WF_ = 0]
+/\  bar.THEN \cup  [bar EXCEPT !.THEN = 0]
+/\ bar.BY \cup [bar EXCEPT !.BY = 0]      \* 68 XXXXXX
+/\ bar.HAVE \cup [bar EXCEPT !.HAVE = 0]
+/\ bar.QED \cup [bar EXCEPT !.QED = 0]
+/\ bar.TAKE \cup [bar EXCEPT !.TAKE = 0]
+
+/\ bar.DEF \cup [bar EXCEPT !.DEF = 0]
+/\ bar.HIDE \cup [bar EXCEPT !.HIDE = 0]
+/\ bar.RECURSIVE \cup [bar EXCEPT !.RECURSIVE = 0] \* XXXXX 82
+/\ bar.USE \cup [bar EXCEPT !.USE = 0]
+
+/\ bar.DEFINE \cup [bar EXCEPT !.DEFINE = 0]
+/\ bar.PROOF \cup [bar EXCEPT !.PROOF = 0]
+/\ bar.WITNESS \cup [bar EXCEPT !.WITNESS = 0]
+/\ bar.PICK \cup [bar EXCEPT !.PICK = 0]
+
+/\ bar.DEFS \cup [bar EXCEPT !.DEFS = 0]
+/\ bar.PROVE \cup [bar EXCEPT !.PROVE = 0]
+/\ bar.SUFFICES \cup [bar EXCEPT !.SUFFICES = 0]
+/\ bar.NEW \cup [bar EXCEPT !.NEW = 0]
+
+/\ bar.LAMBDA \cup [bar EXCEPT !.LAMBDA = 0]
+/\ bar.LEMMA \cup [bar EXCEPT !.LEMMA = 0]
+/\ bar.PROPOSITION \cup [bar EXCEPT !.PROPOSITION = 0]
+/\ bar.STATE \cup [bar EXCEPT !.STATE = 0]
+/\ bar.ACTION \cup [bar EXCEPT !.ACTION = 0]
+/\ bar.TEMPORAL \cup [bar EXCEPT !.TEMPORAL = 0]
+/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0]
+
+/\ bar.OBVIOUS \cup [bar EXCEPT !.OBVIOUS = 0]
+/\ bar.OMITTED \cup [bar EXCEPT !.OMITTED = 0]
+
+/\ bar.ASSUME \cup [bar EXCEPT !.ASSUME = 0]
+/\ bar.ELSE \cup [bar EXCEPT !.ELSE = 0]
+/\ bar.LOCAL \cup [bar EXCEPT !.LOCAL = 0]
+/\ bar.UNION \cup [bar EXCEPT !.UNION = 0]
+     
+/\ bar.ASSUMPTION \cup [bar EXCEPT !.ASSUMPTION = 0]
+/\ bar.ENABLED \cup [bar EXCEPT !.ENABLED = 0]
+/\ bar.MODULE \cup [bar EXCEPT !.MODULE = 0]
+/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0]
+   
+/\ bar.AXIOM \cup [bar EXCEPT !.AXIOM = 0]
+/\ bar.EXCEPT \cup [bar EXCEPT !.EXCEPT = 0]
+/\ bar.OTHER \cup [bar EXCEPT !.OTHER = 0]
+/\ bar.VARIABLES \cup [bar EXCEPT !.VARIABLES = 0]
+  
+/\ bar.CASE \cup [bar EXCEPT !.CASE = 0]
+/\ bar.EXTENDS \cup [bar EXCEPT !.EXTENDS = 0]
+/\ bar.SF_ \cup [bar EXCEPT !.SF_ = 0]
+/\ bar.WF_ \cup [bar EXCEPT !.WF_ = 0]
+      
+/\ bar.CHOOSE \cup [bar EXCEPT !.CHOOSE = 0]
+/\ bar.IF \cup [bar EXCEPT !.IF = 0]        \* XXXXX
+/\ bar.SUBSET \cup [bar EXCEPT !.SUBSET = 0]
+/\ bar.WITH \cup [bar EXCEPT !.WITH = 0]
+ 
+/\ bar.CONSTANT \cup [bar EXCEPT !.CONSTANT = 0]
+/\ bar.IN \cup [bar EXCEPT !.IN = 0]
+/\ bar.THEN \cup [bar EXCEPT !.THEN = 0]    \* XXXXX
+               
+/\ bar.CONSTANTS \cup [bar EXCEPT !.CONSTANTS = 0]
+/\ bar.INSTANCE \cup [bar EXCEPT !.INSTANCE = 0]
+/\ bar.THEOREM \cup [bar EXCEPT !.THEOREM = 0] \* XXXXX
+       
+/\ bar.DOMAIN \cup [bar EXCEPT !.DOMAIN = 0]
+/\ bar.LET \cup [bar EXCEPT !.LET = 0]
+/\ bar.UNCHANGED \cup [bar EXCEPT !.UNCHANGED = 0]
+================================
+
+
+
+THEOREM Bar == ASSUME NEW y, ASSUME TRUE PROVE FALSE
+               PROVE  TRUE
+
+
+THEOREM TRUE
+ <1>1. TAKE x \in << a:: {}, {}>> 
+ <1>2. <1>1!a
+ <1>3. <1>1!1
+ <1> QED
+
+
+==================================================
+a+b == <<a, b>>
+THEOREM TRUE
+<1>2 TRUE
+  <33>1 HAVE ~ TRUE
+  <33>2 TAKE Id, Id1
+  <33>3 TAKE Id4 \in {1}, <<Id2, Id3>> \in {2}
+  <33>123 FooBar == <<Id, Id1, Id4, Id2, Id3>>
+  <33>4 WITNESS {1} 
+  <33>5 WITNESS {1} \in {2}, {3}
+  <33>-... PICK x : ~ FALSE
+          <+>*.   PICK y \in {1} : ~ FALSE
+          <*>* QED
+  <33>7 PICK y \in {1} : <33>5!1!1 + <33>3!1
+    PROOF <55>1... TRUE
+          <*>2.... USE <33>5, <33>7!1!1, <33>7!(42)!<< DEF +
+          <55>2 QED
+  <33>7a Foo == << <33>7!(1)!<< , <33>7!>> >>
+  <33>8 PICK z \in {1}, <<y1, z1>> \in {2} : ~ FALSE
+  <33>9 PICK w, u : <<1, 2, 3>>
+    <34>* DEFINE A == <<x, y, z, y1, z1, w, u>>
+    <34>*. B == A!1
+    <34> QED
+  <33>44 DEFINE A == <<x, y, z, y1, z1, w, u>>
+  <33>*. B == <33>9!(1,2)!3
+  <33>. BBC(Op(_,_)) == Op(1,2)
+        DD == BBC(<33>9!@)
+  <33>22 ASSUME TRUE PROVE FALSE
+  <33>  BBB == \A xxxx \in {} : TRUE
+  <33>14. INSTANCE Test1
+  <*>  AA == INSTANCE Test1
+  <33>  B3 == AA!Foo3!Def2
+  <*>a ASSUME FALSE PROVE TRUE 
+  <33>b CASE TRUE
+  <33>c SUFFICES TRUE
+  <33>. SUFFICES ASSUME FALSE PROVE TRUE
+  <33>2a. QED
+<1>14 HAVE <<1, 2, 3>>
+<1>14a havedef == <1>14!1!3
+<*> TAKE Id7 \in {1}
+<*> WITNESS {1} \in {2}
+<*> PICK x2 \in {1}, y2 \in {2} : ~ FALSE
+   <+> PICK x3, y3 : ~ FALSE
+   <*>*. DEFINE A2 == 1
+   <*>2 B2 == 17
+   <*> ASSUME FALSE PROVE TRUE 
+   <*> CASE TRUE
+   <*> TRUE
+   <*> SUFFICES ASSUME FALSE PROVE TRUE
+   <2> QED
+<1>... DEFINE A == 1
+<1> B == 17
+<1> INSTANCE Test1
+<1> AA == INSTANCE Test2 WITH B <- 1
+<1>3. QED
+
+===============================
+
+\* Foo == \A x , y, z : TRUE
+\* Bar == \A x \in {}, y, z \in {}, <<u, v>> \in {} : TRUE
+\* Inst(a) == INSTANCE Test1
+\* Foo == Inst(7)!Bar1
+THEOREM Thm2 == TRUE
+ <1>2 QED
+       PROOF USE <1>2
+             QED
+====================
+ PROOF 
+\* DEFINE Def1 == TRUE
+ USE Thm2!:  \* DEF Thm2 , Inst!Bar2
+\* Def2 == TRUE
+ QED
+\*   PROOF USE Def1 DEFS Def2
+\*         QED
+=================================
+THEOREM Thm3 == FALSE
+ PROOF DEFINE A == B
+       PROVE /\ TRUE
+             /\ 1=1
+         BY Thm2
+       QED
+==========================================================
+THEOREM 1
+ PROOF QED
+       PROOF OBVIOUS
+=================================================
+=============================================================================
+
+BY [.]
+USE DEF FALSE
+HIDE 
+<1>abc FALSE
+      BY MODULE M DEF MODULE M, 2 [.]
+A == B
+DEFINE F == INSTANCE M
+A[x \in S] == B
+INSTANCE M
+<1>2. QED
+(********
+  PROOF ASSUME TRUE PROVE FALSE
+          <1>1. TRUE
+          <1>2. QED
+        HAVE TRUE
+        QED
+********)
+=============================================================================
diff --git a/tlatools/test-model/suite/test209.cfg b/tlatools/test-model/suite/test209.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/tlatools/test-model/suite/test209.cfg
@@ -0,0 +1 @@
+
diff --git a/tlatools/test-model/suite/test209.tla b/tlatools/test-model/suite/test209.tla
new file mode 100644
index 0000000000000000000000000000000000000000..70d8ad7998d6035eaca0e392f88c6af640c93c9e
--- /dev/null
+++ b/tlatools/test-model/suite/test209.tla
@@ -0,0 +1,293 @@
+\* Test of positional subexpression naming.
+-------------------- MODULE test209 ----------------
+EXTENDS Naturals, Sequences
+
+xx == <<11, <<22,33>>, 33>>
+F(a) == /\ TRUE
+        /\ TRUE
+        /\ Len(xx[a]) > 0
+        /\ TRUE
+
+ASSUME F(2)!3 = (Len(xx[2]) > 0)
+ASSUME F(2)!3!1 = Len(xx[2]) 
+ASSUME F(2)!3!1!1 = xx[2]
+ASSUME F(2)!3!1!1!2 = 2
+ASSUME F(2)!3!1!1!1 = xx
+ASSUME F(2)!3!<< = Len(xx[2]) 
+ASSUME F(2)!3!<<!<< = xx[2]
+ASSUME F(2)!3!<<!<<!>> = 2
+ASSUME F(2)!3!<<!<<!<< = xx
+
+G == [xx EXCEPT ![1] = 42, ![2] = 55]
+H == xx.foo
+I == [fld1 |-> 1, fld2 |->2, fld3 |-> 3]
+J == [fld1 : 1, fld2 :2, fld3 : 3]
+
+ASSUME G!1 = xx
+\* Selection of EXCEPT fields disabled because of "@" problem.
+\* ASSUME G!2 = 42
+\* ASSUME G!3 = 55
+ASSUME H!1 = xx
+ASSUME H!2 = "foo"
+\* ASSUME I!3 = "fld3"
+ASSUME I!3  = 3
+
+\* ASSUME J!3 = "fld3"
+ASSUME J!3 = 3
+
+K == IF 41 THEN 42 ELSE 43
+ASSUME K!1 = 41
+ASSUME K!2 = 42
+ASSUME K!3 = 43
+
+L == CASE 1 -> 11 []
+          2 -> 22 []
+         OTHER -> 33
+
+ASSUME L!1!1 = 1
+ASSUME L!1!2 = 11
+ASSUME L!2!1 = 2
+ASSUME L!2!2 = 22
+ASSUME L!3!2 = 33
+
+M == LET Z == 23 IN Z
+Z == 44
+ASSUME M!1 = 23
+
+NN == 55
+N == [44]_NN
+O == <<44>>_NN
+P == WF_NN(44)
+Q == SF_NN(44)
+
+ASSUME N!1 = 44
+ASSUME N!2 = 55
+ASSUME O!1 = 44
+ASSUME O!2 = 55
+ASSUME P!1 = 55
+ASSUME P!2 = 44
+ASSUME Q!1 = 55
+ASSUME Q!2 = 44
+
+R == \E x \in {1}, y \in {2} : x + y  > 2
+ASSUME R!1 = {1}
+ASSUME R!2 = {2}
+ASSUME R!(1,2)!<< = 3
+
+S == [x, y \in {1, 2}, z \in {3} |-> x+y+z]
+ASSUME S!1 = {1, 2}
+ASSUME S!2 = {3}
+ASSUME S!(2, 3, 4) = 2+3+4
+
+T(Op(_)) == Op(42)
+U  == /\ LET Op(x) == x+1
+         IN  44
+      /\ TRUE
+ASSUME T(U!1!Op) = 43
+ASSUME U!1!Op(42) = 43
+ASSUME U!1!Op(42)!: = 43
+V == \A x \in {} : 2*x
+ASSUME T(V!@) = 84
+ASSUME T(LAMBDA x : 2*x) = 84
+
+=============================================================================
+
+CONSTANT bar
+
+LEMMA TRUE
+PROPOSITION TRUE
+ASSUMPTION TRUE
+
+Foo == 
+/\  bar.NEW \cup  [bar EXCEPT !.NEW = 0]
+/\  bar.SF_ \cup  [bar EXCEPT !.SF_ = 0]
+/\  bar.WF_ \cup  [bar EXCEPT !.WF_ = 0]
+/\  bar.THEN \cup  [bar EXCEPT !.THEN = 0]
+/\ bar.BY \cup [bar EXCEPT !.BY = 0]      \* 68 XXXXXX
+/\ bar.HAVE \cup [bar EXCEPT !.HAVE = 0]
+/\ bar.QED \cup [bar EXCEPT !.QED = 0]
+/\ bar.TAKE \cup [bar EXCEPT !.TAKE = 0]
+
+/\ bar.DEF \cup [bar EXCEPT !.DEF = 0]
+/\ bar.HIDE \cup [bar EXCEPT !.HIDE = 0]
+/\ bar.RECURSIVE \cup [bar EXCEPT !.RECURSIVE = 0] \* XXXXX 82
+/\ bar.USE \cup [bar EXCEPT !.USE = 0]
+
+/\ bar.DEFINE \cup [bar EXCEPT !.DEFINE = 0]
+/\ bar.PROOF \cup [bar EXCEPT !.PROOF = 0]
+/\ bar.WITNESS \cup [bar EXCEPT !.WITNESS = 0]
+/\ bar.PICK \cup [bar EXCEPT !.PICK = 0]
+
+/\ bar.DEFS \cup [bar EXCEPT !.DEFS = 0]
+/\ bar.PROVE \cup [bar EXCEPT !.PROVE = 0]
+/\ bar.SUFFICES \cup [bar EXCEPT !.SUFFICES = 0]
+/\ bar.NEW \cup [bar EXCEPT !.NEW = 0]
+
+/\ bar.LAMBDA \cup [bar EXCEPT !.LAMBDA = 0]
+/\ bar.LEMMA \cup [bar EXCEPT !.LEMMA = 0]
+/\ bar.PROPOSITION \cup [bar EXCEPT !.PROPOSITION = 0]
+/\ bar.STATE \cup [bar EXCEPT !.STATE = 0]
+/\ bar.ACTION \cup [bar EXCEPT !.ACTION = 0]
+/\ bar.TEMPORAL \cup [bar EXCEPT !.TEMPORAL = 0]
+/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0]
+
+/\ bar.OBVIOUS \cup [bar EXCEPT !.OBVIOUS = 0]
+/\ bar.OMITTED \cup [bar EXCEPT !.OMITTED = 0]
+
+/\ bar.ASSUME \cup [bar EXCEPT !.ASSUME = 0]
+/\ bar.ELSE \cup [bar EXCEPT !.ELSE = 0]
+/\ bar.LOCAL \cup [bar EXCEPT !.LOCAL = 0]
+/\ bar.UNION \cup [bar EXCEPT !.UNION = 0]
+     
+/\ bar.ASSUMPTION \cup [bar EXCEPT !.ASSUMPTION = 0]
+/\ bar.ENABLED \cup [bar EXCEPT !.ENABLED = 0]
+/\ bar.MODULE \cup [bar EXCEPT !.MODULE = 0]
+/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0]
+   
+/\ bar.AXIOM \cup [bar EXCEPT !.AXIOM = 0]
+/\ bar.EXCEPT \cup [bar EXCEPT !.EXCEPT = 0]
+/\ bar.OTHER \cup [bar EXCEPT !.OTHER = 0]
+/\ bar.VARIABLES \cup [bar EXCEPT !.VARIABLES = 0]
+  
+/\ bar.CASE \cup [bar EXCEPT !.CASE = 0]
+/\ bar.EXTENDS \cup [bar EXCEPT !.EXTENDS = 0]
+/\ bar.SF_ \cup [bar EXCEPT !.SF_ = 0]
+/\ bar.WF_ \cup [bar EXCEPT !.WF_ = 0]
+      
+/\ bar.CHOOSE \cup [bar EXCEPT !.CHOOSE = 0]
+/\ bar.IF \cup [bar EXCEPT !.IF = 0]        \* XXXXX
+/\ bar.SUBSET \cup [bar EXCEPT !.SUBSET = 0]
+/\ bar.WITH \cup [bar EXCEPT !.WITH = 0]
+ 
+/\ bar.CONSTANT \cup [bar EXCEPT !.CONSTANT = 0]
+/\ bar.IN \cup [bar EXCEPT !.IN = 0]
+/\ bar.THEN \cup [bar EXCEPT !.THEN = 0]    \* XXXXX
+               
+/\ bar.CONSTANTS \cup [bar EXCEPT !.CONSTANTS = 0]
+/\ bar.INSTANCE \cup [bar EXCEPT !.INSTANCE = 0]
+/\ bar.THEOREM \cup [bar EXCEPT !.THEOREM = 0] \* XXXXX
+       
+/\ bar.DOMAIN \cup [bar EXCEPT !.DOMAIN = 0]
+/\ bar.LET \cup [bar EXCEPT !.LET = 0]
+/\ bar.UNCHANGED \cup [bar EXCEPT !.UNCHANGED = 0]
+================================
+
+
+
+THEOREM Bar == ASSUME NEW y, ASSUME TRUE PROVE FALSE
+               PROVE  TRUE
+
+
+THEOREM TRUE
+ <1>1. TAKE x \in << a:: {}, {}>> 
+ <1>2. <1>1!a
+ <1>3. <1>1!1
+ <1> QED
+
+
+==================================================
+a+b == <<a, b>>
+THEOREM TRUE
+<1>2 TRUE
+  <33>1 HAVE ~ TRUE
+  <33>2 TAKE Id, Id1
+  <33>3 TAKE Id4 \in {1}, <<Id2, Id3>> \in {2}
+  <33>123 FooBar == <<Id, Id1, Id4, Id2, Id3>>
+  <33>4 WITNESS {1} 
+  <33>5 WITNESS {1} \in {2}, {3}
+  <33>-... PICK x : ~ FALSE
+          <+>*.   PICK y \in {1} : ~ FALSE
+          <*>* QED
+  <33>7 PICK y \in {1} : <33>5!1!1 + <33>3!1
+    PROOF <55>1... TRUE
+          <*>2.... USE <33>5, <33>7!1!1, <33>7!(42)!<< DEF +
+          <55>2 QED
+  <33>7a Foo == << <33>7!(1)!<< , <33>7!>> >>
+  <33>8 PICK z \in {1}, <<y1, z1>> \in {2} : ~ FALSE
+  <33>9 PICK w, u : <<1, 2, 3>>
+    <34>* DEFINE A == <<x, y, z, y1, z1, w, u>>
+    <34>*. B == A!1
+    <34> QED
+  <33>44 DEFINE A == <<x, y, z, y1, z1, w, u>>
+  <33>*. B == <33>9!(1,2)!3
+  <33>. BBC(Op(_,_)) == Op(1,2)
+        DD == BBC(<33>9!@)
+  <33>22 ASSUME TRUE PROVE FALSE
+  <33>  BBB == \A xxxx \in {} : TRUE
+  <33>14. INSTANCE Test1
+  <*>  AA == INSTANCE Test1
+  <33>  B3 == AA!Foo3!Def2
+  <*>a ASSUME FALSE PROVE TRUE 
+  <33>b CASE TRUE
+  <33>c SUFFICES TRUE
+  <33>. SUFFICES ASSUME FALSE PROVE TRUE
+  <33>2a. QED
+<1>14 HAVE <<1, 2, 3>>
+<1>14a havedef == <1>14!1!3
+<*> TAKE Id7 \in {1}
+<*> WITNESS {1} \in {2}
+<*> PICK x2 \in {1}, y2 \in {2} : ~ FALSE
+   <+> PICK x3, y3 : ~ FALSE
+   <*>*. DEFINE A2 == 1
+   <*>2 B2 == 17
+   <*> ASSUME FALSE PROVE TRUE 
+   <*> CASE TRUE
+   <*> TRUE
+   <*> SUFFICES ASSUME FALSE PROVE TRUE
+   <2> QED
+<1>... DEFINE A == 1
+<1> B == 17
+<1> INSTANCE Test1
+<1> AA == INSTANCE Test2 WITH B <- 1
+<1>3. QED
+
+===============================
+
+\* Foo == \A x , y, z : TRUE
+\* Bar == \A x \in {}, y, z \in {}, <<u, v>> \in {} : TRUE
+\* Inst(a) == INSTANCE Test1
+\* Foo == Inst(7)!Bar1
+THEOREM Thm2 == TRUE
+ <1>2 QED
+       PROOF USE <1>2
+             QED
+====================
+ PROOF 
+\* DEFINE Def1 == TRUE
+ USE Thm2!:  \* DEF Thm2 , Inst!Bar2
+\* Def2 == TRUE
+ QED
+\*   PROOF USE Def1 DEFS Def2
+\*         QED
+=================================
+THEOREM Thm3 == FALSE
+ PROOF DEFINE A == B
+       PROVE /\ TRUE
+             /\ 1=1
+         BY Thm2
+       QED
+==========================================================
+THEOREM 1
+ PROOF QED
+       PROOF OBVIOUS
+=================================================
+=============================================================================
+
+BY [.]
+USE DEF FALSE
+HIDE 
+<1>abc FALSE
+      BY MODULE M DEF MODULE M, 2 [.]
+A == B
+DEFINE F == INSTANCE M
+A[x \in S] == B
+INSTANCE M
+<1>2. QED
+(********
+  PROOF ASSUME TRUE PROVE FALSE
+          <1>1. TRUE
+          <1>2. QED
+        HAVE TRUE
+        QED
+********)
+=============================================================================
diff --git a/tlatools/test-model/suite/test21.cfg b/tlatools/test-model/suite/test21.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test21.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test21.tla b/tlatools/test-model/suite/test21.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fbf1187b3b272a306d395f7aa65133829d47488d
--- /dev/null
+++ b/tlatools/test-model/suite/test21.tla
@@ -0,0 +1,83 @@
+--------------- MODULE test21 -------------
+
+(* Test of priming and variable confusion *)
+
+EXTENDS Naturals, Sequences, TLC
+
+Bar(z)    == z' = 2
+Foo(y)    == y = 2
+FooBar(y) == y = 1
+
+VARIABLE x, y
+
+LetTest == LET s == x=1
+           IN  s
+
+
+Op(a) ==  x+a
+
+Init == /\ x = 1
+        /\ y = 3
+Next == 
+
+  \/ /\ x = 1
+     /\ x'=x+1
+     /\ y' = y
+     /\ IF Bar(x)
+          THEN Print("Test 1 OK", TRUE)
+          ELSE Assert(FALSE, "Test 1 Failed")
+
+  \/ /\ x = 1
+     /\ x'=x+1
+     /\ y' = y
+     /\ IF Foo(x)'
+          THEN Print("Test 2 OK", TRUE)
+          ELSE Assert(FALSE, "Test 2 Failed")
+
+  \/ /\ x = 1
+     /\ x'=x+1
+     /\ y' = y
+     /\ IF FooBar(x)
+          THEN Print("Test 3 OK", TRUE)
+          ELSE Assert(FALSE, "Test 3 Failed")
+     /\ IF LetTest' THEN Assert(FALSE, "Test 4 Failed")
+                    ELSE Print("Test 4 OK", TRUE)
+     /\ LET a == x
+        IN  IF a' = 2 THEN Print("Test 5 OK", TRUE)
+                      ELSE Assert(FALSE, "Test 5 Failed")
+
+     /\ IF FooBar(x)' THEN Assert(FALSE, "Test 6 Failed")
+                      ELSE Print("Test 6 OK", TRUE)
+
+     /\ IF FooBar(x') THEN Assert(FALSE, "Test 7 Failed")
+                      ELSE Print("Test 7 OK", TRUE)
+
+  \/ IF (x = 1)
+       THEN /\ x'=x+1
+            /\ y'=y
+            /\ Print("Test 8 OK", TRUE)
+       ELSE /\ x'=x+1
+            /\ y'=y
+            /\ Print("Test 12 OK", FALSE)  
+
+  \/ /\ x = 1
+     /\ x'=x+1
+     /\ y'=y
+     /\ IF Op(1)' = 3
+          THEN Print("Test 9 OK", TRUE)
+          ELSE Assert(FALSE, "Test 9 Failed")
+     /\ IF LET LetOp(a) ==  x+a
+           IN  LetOp(1)' = 3
+          THEN Print("Test 10 OK", TRUE)
+          ELSE Assert(FALSE, "Test 10 Failed")
+
+     /\ IF (LET LetOp(a) ==  x+a
+            IN  LetOp(1) = 3     )'
+          THEN Print("Test 11 OK", TRUE)
+          ELSE Assert(FALSE, "Test 11 Failed")
+
+  \/ UNCHANGED <<x,y>>
+
+Inv == TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test210.cfg b/tlatools/test-model/suite/test210.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test210.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test210.tla b/tlatools/test-model/suite/test210.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8bde6fb282f8fc24860c529ff00f3fb58f3ed1be
--- /dev/null
+++ b/tlatools/test-model/suite/test210.tla
@@ -0,0 +1,293 @@
+\* This module is meant to produce parsing errors when evaluating any
+\* subexpression reference ref in an expression of the form ref <=>
+\* ILLEGAL and any label labeling "ILLEGAL"
+
+
+-------------------- MODULE test210 --------------
+ILLEGAL == FALSE
+LEGAL   == TRUE
+
+ASSUME assump == /\ lab :: LEGAL
+                 /\ TRUE
+
+AssDef == /\ assump!lab <=> LEGAL
+          /\ assump!1   <=> LEGAL
+
+THEOREM foo == ASSUME  NEW x \in labx :: LEGAL,
+                       NEW y \in laby :: LEGAL,
+                       labz :: LEGAL,
+                       ASSUME TRUE PROVE  labi :: LEGAL
+                       PROVE labu :: LEGAL
+<1>1. ASSUME  LEGAL,
+              NEW q \in labx :: LEGAL,
+              NEW r \in laby :: LEGAL
+              PROVE /\ foo!3 <=> LEGAL
+                    /\ foo!labz  <=> LEGAL
+                    /\ foo!5 <=> LEGAL
+                    /\ foo!labu  <=> LEGAL
+                    /\ foo!labi  <=> LEGAL
+  BY <1>1!labx <=> LEGAL, <1>1!laby <=> LEGAL, <1>1!4 <=> LEGAL,
+     foo!4!2 <=> LEGAL, foo!4!1 <=> LEGAL
+
+<1>1a. /\ <1>1!labx <=> LEGAL
+       /\ <1>1!laby <=> ILLEGAL
+       /\ <1>1!1    <=> LEGAL
+       /\ <1>1!4    <=> ILLEGAL
+
+<1>2. SUFFICES ASSUME  NEW q \in labx :: LEGAL,
+                       TRUE,
+                       NEW r \in laby :: LEGAL,
+                       ASSUME lab1 :: LEGAL,
+                              NEW a \in lab2 :: LEGAL,
+                              NEW b \in lab3 :: ILLEGAL
+                       PROVE lab4 :: ILLEGAL
+                PROVE /\ foo!3 <=> LEGAL
+                      /\ foo!labz  <=> LEGAL
+                      /\ foo!5 <=> LEGAL
+                      /\ foo!labu  <=> LEGAL
+        
+<1>2a. /\ <1>2!laby <=> LEGAL
+       /\ <1>2!labx <=> LEGAL
+       /\ <1>2!lab1 <=> LEGAL
+       /\ <1>2!lab2 <=> LEGAL
+       /\ <1>2!1!5 <=> LEGAL
+       /\ <1>2!1!5!2  <=> LEGAL
+       /\ <1>2!2    <=> ILLEGAL
+
+<1>3. SUFFICES 1 = 2
+
+<1>4. QED
+
+Bar == /\ foo!labx <=> LEGAL
+       /\ foo!laby <=> ILLEGAL
+       /\ foo!labi <=> ILLEGAL
+       /\ foo!labu <=> ILLEGAL
+       /\ foo!3    <=> ILLEGAL
+==================
+
+CONSTANT x
+THEOREM TRUE
+<1>1. x = 1
+<1>4. QED
+================
+<2>21 bar == 1 foobar == 2
+<2>31 foo
+  BY DEF <2>21
+(****
+<2>22 WITNESS TRUE
+<2>32 <2>22
+<2>23 TAKE x \in {}
+<2>33 <2>23
+<2>24 PICK y \in {} : TRUE
+<2>34 <2>24
+<2>25 CASE TRUE
+<2>35 <2>25
+*****)
+<2>3 QED
+  <3>1 TRUE\* <2>3
+     BY <2>31
+  <3>2 QED
+=================
+VARIABLE y
+Foo(x) == INSTANCE Test1 
+LOCAL fcn[x \in {}] == x
+USE MODULE Test, MODULE Test1 DEFS (* MODULE Test, *) MODULE Test1, Foo
+Bar == Foo(1)!-(1, 2)
+================================
+
+THEOREM ASSUME ASSUME P PROVE
+        PROVE  P
+BY Foo \* DEFS Foo
+
+THEOREM foo7 == TRUE
+ <0>1. TRUE
+ <*>.... <0>1
+     <007>1. TRUE
+     <07>2. QED
+ <000>3 QED
+
+=================
+\* Foo == <1>1 
+THEOREM foobar == TRUE
+<*>1. TRUE
+PROOF
+ <+>1. TRUE
+   <+> TRUE
+   <*>11. TRUE
+   <*>12 TRUE
+   <2>13. TRUE
+   <2>14 TRUE 
+   <2>2a TRUE
+   <*>2 <2>2a
+   <*>3. TRUE
+   <*> QED
+ <*>2. QED
+<0>4. TRUE
+<0>1a <0>4
+<0>2. QED
+
+THEOREM foo == ASSUME CONSTANT a,
+                     VARIABLE c,
+                      STATE b
+               PROVE  TRUE
+PROOF
+<+>1. TRUE
+       PROOF <+>a. TRUE
+             <1>3. TRUE
+             <*> Test == <1>a
+
+             <1>4. QED
+              <+> <1>3
+              <2> QED
+<0>... QED
+
+AXIOM TRUE
+
+THEOREM ASSUME CONSTANT a,
+               CONSTANT b \in {} ,
+               VARIABLE c,
+               STATE d,
+               ACTION e,
+               TEMPORAL f
+        PROVE  e = e
+
+THEOREM ASSUME NEW a,
+               NEW CONSTANT b \in {} ,
+               NEW VARIABLE c,
+               NEW STATE d,
+               NEW ACTION e,
+               NEW TEMPORAL f
+        PROVE  TRUE
+
+OBVIOUS
+
+THEOREM TRUE
+OMITTED
+
+THEOREM TRUE
+PROOF OBVIOUS
+
+THEOREM TRUE
+PROOF OMITTED
+
+a+b == <<a, b>>
+
+THEOREM TRUE
+BY TRUE DEF +
+
+THEOREM TRUE
+BY TRUE DEFS +
+
+THEOREM TRUE
+PROOF BY TRUE DEFS +
+
+
+
+THEOREM TRUE
+<1>ab.... TRUE
+  PROOF
+  <33>0 TRUE
+        BY <1>ab
+  <33>1 HAVE ~ (TRUE /\ <33>0)
+  <33>2 TAKE Id, Id1
+  <33>3 TAKE Id4 \in {1}, <<Id2, Id3>> \in {2}
+  <33>123 FooBar == <<Id, Id1, Id4, Id2, Id3>>
+  <33>4 WITNESS {1} \cup <33>1!1!1!2
+  <33>5 WITNESS {1} \in {2}, {3}
+  <33>-... PICK x : ~ FALSE
+          <+>*.   PICK y \in {1} : ~ FALSE
+          <*>* QED
+  <33>7 PICK y \in {1} : <33>5!1!1 + <33>3!1
+    PROOF <*>1... TRUE
+          <*>2.... USE <*>1, <33>5, <33>7!1!1, <33>7!(42)!<< DEF +
+
+          <*>3 QED
+  <33>7a Foo == << <33>7!(1)!<< , <33>7!>> >>
+  <33>8 PICK z \in {1}, <<y1, z1>> \in {2} : ~ FALSE
+  <33>9 PICK w, u : <<1, 2, 3>>
+    <34>* DEFINE A == <<x, y, z, y1, z1>>
+    <34>*. B == A!1
+           C == A
+    <34> QED
+  <33>44 DEFINE A == <<x, y, z, y1, z1, w, u>>
+  <33>*. B == <33>9!(1,2)!3
+  <33>. BBC(Op(_,_)) == Op(1,2)
+        DD == BBC(<33>9!@)
+  <33>22 ASSUME TRUE PROVE FALSE
+  <33>  BBB == \A xxxx \in {} : TRUE
+  <33>14. INSTANCE Test1
+  <*>  AA == INSTANCE Test1
+  <*>a ASSUME FALSE PROVE TRUE 
+  <33>b CASE TRUE /\ <33>22!1
+  <33>c SUFFICES TRUE
+  <33>. SUFFICES ASSUME FALSE PROVE TRUE
+  <33>2a. QED
+<1>14 HAVE <<1, 2, 3>>
+<1>14a havedef == <1>14!1!3
+<*> TAKE Id7 \in {1}
+<*> WITNESS {1} \in {2}
+<*> PICK x2 \in {1}, y2 \in {2} : ~ FALSE
+   <+> PICK x3, y3 : ~ FALSE
+   <*>*. DEFINE A2 == 1
+   <*>2 B2 == 17
+   <*> ASSUME FALSE PROVE TRUE 
+   <*> CASE TRUE
+   <*> TRUE
+   <*> SUFFICES ASSUME FALSE PROVE TRUE
+   <2> QED
+<1>... DEFINE A == 1
+<1> B == 17
+<1> INSTANCE Test1 WITH x <- 27
+<1> AA == INSTANCE Test2 WITH B <- 1
+<1>3. QED
+
+===============================
+
+\* Foo == \A x , y, z : TRUE
+\* Bar == \A x \in {}, y, z \in {}, <<u, v>> \in {} : TRUE
+\* Inst(a) == INSTANCE Test1
+\* Foo == Inst(7)!Bar1
+THEOREM Thm2 == TRUE
+ <1>2 QED
+       PROOF USE <1>2
+             QED
+====================
+ PROOF 
+\* DEFINE Def1 == TRUE
+ USE Thm2!:  \* DEF Thm2 , Inst!Bar2
+\* Def2 == TRUE
+ QED
+\*   PROOF USE Def1 DEFS Def2
+\*         QED
+=================================
+THEOREM Thm3 == FALSE
+ PROOF DEFINE A == B
+       PROVE /\ TRUE
+             /\ 1=1
+         BY Thm2
+       QED
+==========================================================
+THEOREM 1
+ PROOF QED
+       PROOF OBVIOUS
+=================================================
+=============================================================================
+
+BY [.]
+USE DEF FALSE
+HIDE 
+<1>abc FALSE
+      BY MODULE M DEF MODULE M, 2 [.]
+A == B
+DEFINE F == INSTANCE M
+A[x \in S] == B
+INSTANCE M
+<1>2. QED
+(********
+  PROOF ASSUME TRUE PROVE FALSE
+          <1>1. TRUE
+          <1>2. QED
+        HAVE TRUE
+        QED
+********)
+=============================================================================
diff --git a/tlatools/test-model/suite/test211.tla b/tlatools/test-model/suite/test211.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2b7f396c514fdb48a5364b0ae090e590c325b0c9
--- /dev/null
+++ b/tlatools/test-model/suite/test211.tla
@@ -0,0 +1,1114 @@
+\* Test of moderately large proof.
+
+`. last modified on Wed  4 March 2009 at 17:55:12 PST by lamport 
+   Major changes made 21 Sep 2007:
+     variable chosen renamed learned
+     action Choose renamed Learn
+   Major changes made 22 Sep 2007:
+     removed variable noccmd
+.'
+
+
+--------------------------------- MODULE test211 --------------------------------
+EXTENDS Integers, FiniteSets
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(*                                 CONSTANTS                               *)
+(*                                                                         *)
+(* For convenience, we take configuration numbers, instance numbers, and   *)
+(* ballot numbers all to be the set of naturals.  However, to make the     *)
+(* meanings of our formulas easier to understand, we use different names   *)
+(* for these three equal sets.                                             *)
+(***************************************************************************)
+CNum == Nat  \* The set of configuration numbers.
+INum == Nat  \* The set of instance numbers.
+BNum == Nat  \* The set of ballot numbers.
+
+CONSTANT Acc   \* The set of acceptors
+
+(***************************************************************************)
+(* We define Config to be the set of configurations, where a configuration *)
+(* is a record with a cnum field that is the configuration number field    *)
+(* and a quorums field that is a set of quorums, which is a set of         *)
+(* pairwise non-disjoint sets of acceptors.                                *)
+(***************************************************************************)
+Config ==  [cnum    : CNum, 
+            quorums : {C \in SUBSET SUBSET Acc : 
+                         \A P1, P2 \in C : P1 \cap P2 # {}}]
+
+CONSTANTS Cmd,           \* The set of commands (choosable values).
+
+          ConfigCmd(_),  \* ConfigCmd(C) is the command that sets the 
+                         \* current configuration to C.                    
+
+          InitConfig     \* The initial configuration
+
+(***************************************************************************)
+(* We assume that ConfigCmd(C) is a distinct command for every             *)
+(* configuration C, and that InitConfig is a configuration with cnum 0.    *)
+(***************************************************************************)
+ASSUME ConstantAssumption ==
+  /\ \A C \in Config : ConfigCmd(C) \in  Cmd
+  /\ \A C, D \in Config : (C # D) => (ConfigCmd(C) # ConfigCmd(D))    
+  /\ InitConfig \in Config
+  /\ InitConfig.cnum = 0
+
+(***************************************************************************)
+(* The configuration of a configuration command.                           *)
+(***************************************************************************)
+ConfigOfCmd(c) == CHOOSE C \in Config : c = ConfigCmd(C)
+
+
+(***************************************************************************)
+(* CCmd is the set of reconfiguration commands.                            *)
+(***************************************************************************)
+CCmd == {ConfigCmd(C) : C \in Config}
+
+(***************************************************************************)
+(* Vote is the set of votes, where a vote is a record with cnum            *)
+(* (configuration number) field and cmd (command) field.  We define init   *)
+(* and `none' to be two different values a that are not votes.             *)
+(***************************************************************************)
+Vote == [cnum : CNum, cmd : Cmd]
+init == CHOOSE v : v \notin Vote 
+none == CHOOSE v : v \notin Vote \cup {init}
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(*                                   VARIABLES                             *)
+(*                                                                         *)
+(* The algorithm has two variables avote and `learned', where:             *)
+(*                                                                         *)
+(* - avote[a][i][b] is either the vote cast by acceptor a in instance i    *)
+(*   for ballot b.  It initially equals init from which it can be set to   *)
+(*   a vote, to the value `none' indicating that `a' abstains in that      *)
+(*   ballot.                                                               *)
+(*                                                                         *)
+(* - `learned' is the set of pairs <<i, cmd>> indicating that command cmd  *)
+(*   has been learned in instance i.                                       *)
+(***************************************************************************)
+VARIABLE avote, learned
+
+(***************************************************************************)
+(* BOUND IDENTIFIER CONVENTIONS                                            *)
+(*                                                                         *)
+(* We use the following conventions for the names used in quantifiers and  *)
+(* operator parameters:                                                    *)
+(*                                                                         *)
+(*   `a' : An acceptor                                                     *)
+(*                                                                         *)
+(*   i : An instance number.                                               *)
+(*                                                                         *)
+(*   b : A ballot number                                                   *)
+(*                                                                         *)
+(*   C : A configuration                                                   *)
+(*                                                                         *)
+(*   c, cmd : A command                                                    *)
+(*                                                                         *)
+(*   n, cnum : A configuration number.                                     *)
+(*                                                                         *)
+(* Multiple identifiers of the same type are named in an obvious way--for  *)
+(* example b and bb, or c1 and c2.                                         *)
+(***************************************************************************)
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(*                           STATE PREDICATES                              *)
+(*                                                                         *)
+(* The initial predicate asserts that avote[a][i][b] = init for all `a',   *)
+(* i, and b, and that `learned' contains the single element <<-1, c>>      *)
+(* where c is the command that sets the current configuration to           *)
+(* InitConfig.                                                             *)
+(***************************************************************************)
+Init == /\ avote  = [a \in Acc |-> [i \in INum |-> [b \in BNum |-> init]]]
+        /\ learned = {<<-1, ConfigCmd(InitConfig)>>}
+
+(***************************************************************************)
+(* The following state predicate is true iff `a' voted in instance i at    *)
+(* ballot b for command c with configuration number n.                     *)
+(***************************************************************************)
+Voted(a, i, b, n, c) == /\ avote[a][i][b] \in Vote
+                        /\ avote[a][i][b] = [cnum |-> n, cmd |-> c]
+
+(***************************************************************************)
+(* A type correctness assertion, which is an invariant of the algorithm.   *)
+(* It is trivial to check that this is true in the initial state and       *)
+(* preserved by every step of the algorithm.                               *)
+(***************************************************************************)
+TypeOK == 
+  /\ (**********************************************************************)
+     (* avote is a function with the correct domain and range.             *)
+     (**********************************************************************)
+     avote \in [Acc -> [INum -> [BNum -> Vote \cup {init, none}] ] ]
+     
+  /\ (**********************************************************************)
+     (* An acceptor votes only for a new configuration with a              *)
+     (* configuration number 1 greater than that of the current            *)
+     (* configuration.                                                     *)
+     (**********************************************************************)
+     \A a \in Acc, i \in INum, b \in BNum, n \in CNum, C \in Config :
+        Voted(a, i, b, n, ConfigCmd(C)) => (C.cnum = n+1)
+
+  /\ (**********************************************************************)
+     (* `learned' is a set of elements of the proper type.                 *)
+     (**********************************************************************)
+     learned \subseteq (INum \cup {-1}) \X Cmd
+
+  /\ (**********************************************************************)
+     (* There is exactly one element of the form <<-1, c>> \in learned,    *)
+     (* where c is the command that sets the configuration to InitConfig.  *)
+     (**********************************************************************)
+     \A c \in Cmd : (<<-1, c>> \in learned) <=> (c = ConfigCmd(InitConfig))     
+
+
+(***************************************************************************)
+(* The following state predicates are the usual ones for Paxos for a       *)
+(* particular instance i and a fixed configuration C.                      *)
+(***************************************************************************)
+ChosenInBal(i, b, C, cmd) ==
+  (*************************************************************************)
+  (* True iff command cmd is chosen in ballot b.                           *)
+  (*************************************************************************)
+  \E Q \in C.quorums : 
+    \A a \in Q : Voted(a, i, b, C.cnum, cmd)
+
+ChosenIn(i, C, cmd) == \E b \in BNum : ChosenInBal(i, b, C, cmd) 
+  (*************************************************************************)
+  (* True iff command cmd is chosen.                                       *)
+  (*************************************************************************)
+
+ChoosableAt(i, b, C, cmd) ==
+  (*************************************************************************)
+  (* True iff command c has been chosen or could still be chosen in ballot *)
+  (* b.                                                                    *)
+  (*************************************************************************)
+  \E Q \in C.quorums : 
+    \A a \in Q : \/ Voted(a, i, b, C.cnum, cmd) 
+                 \/ avote[a][i][b] = init
+
+UnchoosableBefore(i, b, C, cmd) ==
+  (*************************************************************************)
+  (* True iff it is not possible for command c ever to be chosen in any    *)
+  (* ballot numbered less than b.                                          *)
+  (*************************************************************************)
+  \A bb \in 0 .. (b-1): ~ ChoosableAt(i, bb, C, cmd)
+
+SafeAt(i, b, C, cmd) ==
+  (*************************************************************************)
+  (* True iff it is safe for an acceptor to vote for command cmd in ballot *)
+  (* b (because no other command can be chosen with a smaller ballot       *)
+  (* number).                                                              *)
+  (*************************************************************************)
+  \A c \in Cmd \ {cmd} : UnchoosableBefore(i, b, C, c)
+
+
+(***************************************************************************)
+(* The following state predicate asserts that it is safe to try to choose  *)
+(* a command in ballot b of instance i using configuration C. It assserts  *)
+(* that C was learned in some instance ii < i and that in every instance j *)
+(* with ii < j < i, no reconfiguration command can possibly be chosen in a *)
+(* ballot < b and no acceptor can vote for a reconfiguration command in    *)
+(* ballot b.                                                               *)
+(***************************************************************************)
+ConfigOKAt(i, b, C) ==
+  \E ii \in -1 .. (i-1) :
+    /\ <<ii, ConfigCmd(C)>> \in learned
+    /\ \A j \in (ii+1)..(i-1), c \in CCmd : 
+         /\ UnchoosableBefore(j, b, C, c)
+         /\ \A a \in Acc : ~Voted(a, j, b, C.cnum, c)
+            (***************************************************************)
+            (* Note: a ConfigOK formula is used as an enabling condition   *)
+            (* in the action by which an acceptor votes.  In practice, the *)
+            (* real enabling condition will be the stronger formula        *)
+            (*                                                             *)
+            (*     \A a \in Acc, n \in BNum : ~ Voted(a, j, b, n, c)       *)
+            (*                                                             *)
+            (* However, my proof seems to requires the weaker formula.  I  *)
+            (* think the algorithm remains correct with the weaker         *)
+            (* precondition.                                               *)
+            (***************************************************************)
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(*                                 ACTIONS                                 *)
+(*                                                                         *)
+(* The following is the action in which acceptor `a' votes in instance i   *)
+(* at ballot b for command cmd using configuration numbere cnum.  The      *)
+(* first three conjuncts are enabling conditions.  In an implementation,   *)
+(* acceptor `a' will perform this action only if it receives of a phase2a  *)
+(* message containing i, b, cnum, and cmd from the leader of ballot b.     *)
+(* It is up to the leader to ensure that the second and third conjuncts    *)
+(* will be satisfied when that message is received.  After preforming the  *)
+(* action (meaning after the new value of avote[a][i][b] is written in     *)
+(* stable storage), acceptor `a' will send a phase2b message.  The         *)
+(* purpose of those phase2b messages is to enable some process to learn    *)
+(* if command cmd has been chosen.                                         *)
+(***************************************************************************)
+VoteFor(a, i, b, cnum, cmd) ==
+  /\ (**********************************************************************)
+     (* `a' has not already voted or abstained in this ballot.             *)
+     (**********************************************************************)
+     avote[a][i][b] = init 
+
+  /\ (**********************************************************************)
+     (* Any other acceptor that has voted in ballot b of instance i has    *)
+     (* voted for cmd with configuration number cnum.                      *)
+     (**********************************************************************)
+     \A aa \in Acc \ {a} :
+       (avote[aa][i][b] \in Vote) =>  
+         (avote[aa][i][b] = [cnum |-> cnum, cmd |-> cmd])
+
+  /\ (**********************************************************************)
+     (* It is safe to try to choose a command with ballot b in instance i  *)
+     (* for some configuration C with number cnum.  Moreover, if cmd is a  *)
+     (* reconfiguration command, then no configuration command can ever be *)
+     (* chosen with configuration C in any instance after i with a ballot  *)
+     (* number < b.                                                        *)
+     (**********************************************************************)
+     \E C \in Config : 
+        /\ C.cnum = cnum
+        /\ ConfigOKAt(i, b, C)
+        /\ SafeAt(i, b, C, cmd)
+        /\ \A newC \in Config : 
+              (cmd = ConfigCmd(newC))
+                 => /\ newC.cnum = cnum + 1
+                    /\ \A ii \in INum, c \in Cmd :
+                          (ii > i) => 
+                             /\ UnchoosableBefore(ii, b, C, c)
+                             /\ \/ ChosenInBal(i, b, C, cmd)
+                                \/ \A aa \in Acc :
+                                     avote[aa][ii][b] \notin Vote
+            (***************************************************************)
+            (* When the leader sends the Phase2a message for this vote,    *)
+            (* the last disjunct will be true.  However, it can send       *)
+            (* Phase2a messages in instance ii once it knows that cmd is   *)
+            (* chosen in ballot b.  In that case, the preceding disjunct   *)
+            (* will be true when the first Phase2a message arrives at `a'. *)
+            (***************************************************************)
+                          
+  /\ (**********************************************************************)
+     (* Set avote[a][i][b] to a vote with configuration number cnum and    *)
+     (* command cmd.                                                       *)
+     (**********************************************************************)
+     avote' = [avote EXCEPT ![a][i][b] = [cnum |-> cnum, cmd |-> cmd]]
+
+  /\ (**********************************************************************)
+     (* Leave learned unchanged.                                            *)
+     (**********************************************************************)
+     UNCHANGED learned
+
+(***************************************************************************)
+(* The following action is one in which acceptor `a' abstains (votes       *)
+(* `none') for every pair <<i, b>> in the set S of instance number, ballot *)
+(* number pairs in which it has not yet voted.  This action is always      *)
+(* enabled.  In an actual implementation, acceptor `a' performs this       *)
+(* action for the set S equal to the set of <<i, b>> with b < b0 and i any *)
+(* instance number when it receives a phase1b message with ballot          *)
+(* number b0.  It also performs the action for S the set of all <<i, b>>   *)
+(* with b < b0 when it receives a phase2a message for instance i with      *)
+(* ballot number b0 before it performs the Vote action.  This action will  *)
+(* be a no-op (will perform a stuttering step) if `a' discards the phase1a *)
+(* or phase2a message because it has already seen one with a ballow number *)
+(* \geq b0.                                                                *)
+(***************************************************************************)
+Abstain(a, S) ==
+  /\ avote' = [avote EXCEPT ![a] =
+                 [i \in INum |-> [b \in BNum |-> IF /\ <<i, b>> \in S
+                                                    /\ avote[a][i][b] = init
+                                                   THEN none
+                                                   ELSE avote[a][i][b] ] ] ]
+  /\ UNCHANGED learned
+
+(***************************************************************************)
+(* The action by which a command cmd is learned in instance i at ballot b. *)
+(* It occurs if cmd is chosen for that instance and ballot by a            *)
+(* configuration C for which it is safe to choose commands in that         *)
+(* instance and ballot.                                                    *)
+(***************************************************************************)
+Learn(i, b, cmd) ==
+  /\ \E C \in Config : /\ ConfigOKAt(i, b, C)
+                       /\ ChosenInBal(i, b, C, cmd)
+                       /\ learned' = learned \cup {<<i, cmd>>}
+  /\ UNCHANGED avote
+
+
+(***************************************************************************)
+(* The next-state action is one that performs any of the Vote, Abstain, or *)
+(* Learn steps described above.                                            *)
+(***************************************************************************)
+Next == \/ \E a \in Acc:
+             \/ \E S \in SUBSET (INum \X BNum) : Abstain(a, S) 
+             \/ \E i \in INum, b \in BNum, cnum \in CNum, cmd \in Cmd : 
+                  VoteFor(a, i, b, cnum, cmd)
+        \/ \E i \in INum, b \in BNum, cmd \in Cmd : Learn(i, b, cmd)
+-----------------------------------------------------------------------------
+(***************************************************************************)
+(* The standard TLA+ formula that represents the complete algorithm, with  *)
+(* no liveness assumptions on when actions must be performed.              *)
+(***************************************************************************)
+Spec == Init /\ [][Next]_<<avote, learned>>
+
+(***************************************************************************)
+(* The fact that TypeOK is an invariant of the algorithm is expressed      *)
+(* formally by:                                                            *)
+(***************************************************************************)
+THEOREM Spec => []TypeOK
+
+(***************************************************************************)
+(* The following is the invariant that expresses the desired safety        *)
+(* property, and the theorem asserting that it is an invariant.            *)
+(***************************************************************************)
+SafetyInv == \A ch1, ch2 \in learned : (ch1[1] = ch2[1]) => (ch1 = ch2)
+THEOREM Spec => []SafetyInv
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* We first prove some stability results.  A predicate P is generally      *)
+(* called stable if it can't be made false by executing a step.  When      *)
+(* formalized in an untyped system, this generally assumes that the        *)
+(* starting state is type-correct.                                         *)
+(***************************************************************************)
+Stable(P) == TypeOK /\ P /\ Next => P'
+
+THEOREM Stability ==
+ /\(*1*)  \A i \in INum, c \in Cmd : Stable(<<i, c>> \in learned)
+ /\(*2*)  \A a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd:
+            /\(*2.1*)  Stable( Voted(a, i, b, n, c) )
+            /\(*2.2*)  Stable( /\ ~Voted(a, i, b, n, c)
+                               /\ avote[a][i][b] # init  )
+ /\(*3*)  \A i \in INum, b \in BNum, c \in Cmd, C \in Config: 
+            /\(*3.1*)  Stable( ChosenInBal(i, b, C, c) )
+            /\(*3.2*)  Stable( ~ChoosableAt(i, b, C, c) )
+            /\(*3.3*)  Stable( UnchoosableBefore(i, b, C, c) )
+            /\(*3.4*)  Stable( SafeAt(i, b, C, c) )
+
+(***************************************************************************)
+(* Proof: 1 and 2 are obvious since no action removes an element from      *)
+(* `learned' and once an acceptor has voted or abstained in a ballot, it   *)
+(* cannot retract that decision.  The proof of 3 uses 1 and 2 and the fact *)
+(* that the conjunction or disjunction (and hence \A and \E) of stable     *)
+(* predicates are stable.  The only part that takes a little work is 3.2,  *)
+(* which requires first rewriting ~ChoosableAt(i, b, C, c) as              *)
+(*                                                                         *)
+(*    \A Q \in C.quorums :                                                 *)
+(*      \E a \in Q : ~Voted(a, i, b, C.cnum, c) /\ avote[a][i][b] # init   *)
+(*                                                                         *)
+(* and applying 2.1.                                                       *)
+(***************************************************************************)
+
+
+(***************************************************************************)
+(* We define Max(S) to be the maximum of the set S of numbers, if S is     *)
+(* non-empty.                                                              *)
+(***************************************************************************)
+Max(S) == CHOOSE k \in S : \A j \in S : k \geq j
+
+
+(***************************************************************************)
+(* Suppose that m+1 reconfiguration commands have been chosen and they     *)
+(* can be numbered C_0, ...  , C_m such that each C_j with j>0 was chosen  *)
+(* in ballot b_j of instance i_j using configuration C_(j-1) with          *)
+(* i_1 < i_2 < ...  < i_m.  Let i_0 = -1 and b_0 = 0, and let c_j be the   *)
+(* command that chooses new configuration C_j.  Then the following         *)
+(* definitions define                                                      *)
+(*                                                                         *)
+(*   maxcfg  = m                                                           *)
+(*                                                                         *)
+(*   cfgl[j] = <<i_j, c_j>>                                                *)
+(*                                                                         *)
+(*   cfgi[j] = i_j                                                         *)
+(*                                                                         *)
+(*   cfgc[j] = c_j                                                         *)
+(*                                                                         *)
+(*   cfgC[j] = C_j                                                         *)
+(*                                                                         *)
+(*   cfgb[j] = b_j                                                         *)
+(***************************************************************************)
+learnedRcfg == { ln \in learned : ln[2] \in CCmd } 
+maxcfg == Cardinality(learnedRcfg) - 1
+cfgl == [j \in 0..maxcfg |-> CHOOSE ln \in learnedRcfg : ln[2].cnum = j ]
+cfgi == [j \in 0..maxcfg |-> cfgl[j][1]]
+cfgc == [j \in 0..maxcfg |-> cfgl[j][2]]
+cfgC == [j \in 0..maxcfg |-> ConfigOfCmd(cfgc[j])]
+
+RECURSIVE cfgb  \* This statement warns that the definition of cfgb is recursive.
+cfgb == [j \in 0..maxcfg |->
+          IF j = 0 THEN 0
+                   ELSE CHOOSE b \in BNum : 
+                          /\ ChosenInBal(cfgi[j], b, cfgC[j-1], cfgc[j])
+                          /\ ConfigOKAt(cfgi[j], b, cfgC[j-1])     ]
+                        (***************************************************)
+                        (* The choice isn't necessarily unique, but it     *)
+                        (* doesn't matter which b is chosen.               *)
+                        (***************************************************)
+                        
+
+(***************************************************************************)
+(* The definitions of cfgl, etc. define what they are supposed to only if  *)
+(* the set of chosen reconfiguration commands satisfies a certain          *)
+(* property.  The following predicate asserts that they really do satisfy  *)
+(* those properties.  More precisely, it asserts enough so that those      *)
+(* properties follow from it and the definitions.                          *)
+(***************************************************************************)
+CfgOK ==
+  /\ cfgl \in [0..maxcfg -> learnedRcfg]
+  /\ cfgb \in [0..maxcfg -> BNum]
+  /\ cfgi[0] = -1
+  /\ cfgC[0] = InitConfig
+  /\ cfgb[0] = 0
+  /\ \A j \in 0..maxcfg :  /\ <<cfgi[j], cfgc[j]>> \in learnedRcfg
+                           /\ cfgC[j].cnum = j
+  /\ \A j \in 1..maxcfg :  /\ ChosenInBal(cfgi[j], cfgb[j], cfgC[j-1], cfgc[j])
+                           /\ ConfigOKAt(cfgi[j], cfgb[j], cfgC[j-1])
+  /\ \A j \in 1..maxcfg, i \in INum :
+         (i > cfgi[j]) => \A c \in Cmd : UnchoosableBefore(i, cfgb[j], cfgC[j-1], c)
+
+(***************************************************************************)
+(* The following theorem asserts that the desired properties of cfgi, etc. *)
+(* not explicitly stated in CfgOK follow from it and the definitions.      *)
+(* (This theorem isn't used explicitly, but it's probably used             *)
+(* implicitly.)                                                            *)
+(***************************************************************************)
+THEOREM TypeOK /\ CfgOK =>
+          /\ cfgi \in [0..maxcfg -> INum \cup {-1}] 
+          /\ cfgC \in [0..maxcfg -> Config]
+          /\ cfgb \in [0..maxcfg -> BNum ]
+          /\ \A j\in 0..maxcfg : cfgc[j] = ConfigCmd(cfgC[j])
+          /\ learnedRcfg = {cfgl[j] : j \in 0..maxcfg}
+  PROOF OBVIOUS
+
+(***************************************************************************)
+(* The following stability property is used in the invariance proof.       *)
+(***************************************************************************)
+THEOREM CfgStability ==
+    CfgOK => 
+       \A i \in INum, b \in BNum, C \in Config :
+         /\ \A a \in Acc, c \in Cmd : Stable( /\ Voted(a, i, b, C.cnum, c)
+                                              /\ ConfigOKAt(i, b, C)  )
+         /\ \A c \in Cmd : Stable( /\ ChosenInBal(i, b, C, c)
+                                   /\ ConfigOKAt(i, b, C)  )
+
+<1>1 HAVE CfgOK
+  (*************************************************************************)
+  (* This statement means that we're assuming CfgOK and proving the        *)
+  (* formula that it's supposed to imply.                                  *)
+  (*************************************************************************)
+  
+<1>2. TAKE i \in INum, b \in BNum, C \in Config
+  (*************************************************************************)
+  (* This statement means that our current goal is a formula of the form   *)
+  (*                                                                       *)
+  (*   \A i \in INum, b \in BNum, C \in Config : P                         *)
+  (*                                                                       *)
+  (* and we're going to prove it by introducing these new constants i, b,  *)
+  (* and C and proving P.                                                  *)
+  (*************************************************************************)
+  
+
+<1>3. ASSUME CONSTANT a \in Acc, 
+             CONSTANT c \in Cmd
+      PROVE Stable( /\ Voted(a, i, b, C.cnum, c)
+                    /\ ConfigOKAt(i, b, C)  )
+
+  <2>1. SUFFICES ASSUME Next, Voted(a, i, b, C.cnum, c), 
+                        ConfigOKAt(i, b, C)
+                 PROVE  ConfigOKAt(i, b, C)'
+
+    (***********************************************************************)
+    (* Note: This statement is read "it suffices to assume ... and         *)
+    (*       prove ..."                                                    *)
+    (*                                                                     *)
+    (* Proof: By definition of stability and the fact that Voted(...)  is  *)
+    (* stable.                                                             *)
+    (***********************************************************************)
+  <2>2. PICK ii \in -1 .. (i-1) :
+             /\ <<ii, ConfigCmd(C)>> \in learned
+             /\ \A j \in (ii+1)..(i-1), c4 \in CCmd : 
+                  /\ UnchoosableBefore(j, b, C, c4)
+                  /\ \A aa \in Acc : ~ Voted(aa, j, b, C.cnum, c4)
+    (***********************************************************************)
+    (* By the assumption ConfigOKAt(...)  of <2>1 and the definition of    *)
+    (* ConfigOKAt.                                                         *)
+    (***********************************************************************)
+    
+  <2>3. SUFFICES ASSUME CONSTANT j \in (ii+1)..(i-1), 
+                        CONSTANT c4 \in CCmd,
+                        CONSTANT aa \in Acc,
+                        ~ Voted(aa, j, b, C.cnum, c4),
+                        Voted(aa, j, b, C.cnum, c4)'
+                 PROVE  FALSE
+    (***********************************************************************)
+    (* Proof: By <2>1 we're assuming ConfigOKAt(i, b, C) and have to prove *)
+    (* ConfigOKAt(i, b, C)'.  ConfiguOKAt(...)  is the disjunction and     *)
+    (* conjunction of formulas all of which are stable except for          *)
+    (*                                                                     *)
+    (*   \A a \in Acc: ~ Voted(a, j, b, C.cnum, c),                        *)
+    (*                                                                     *)
+    (* So we just have to show that ~ Voted(aa, j, b, n, c4) is stable for *)
+    (* every aa, j, c4, and n.  The proof is by contradiction.             *)
+    (***********************************************************************)
+    
+  <2>4. VoteFor(aa, j, b, C.cnum, c4) 
+     (**********************************************************************)
+     (* Proof: By the assumptions of <2>3, because this VoteFor action is  *)
+     (* the only subaction of Next that can make                           *)
+     (* Voted(aa, j, b, C.cnum, c) become true.                            *)
+     (**********************************************************************)
+     
+  <2>5. PICK CC \in Config : 
+           /\ CC.cnum = C.cnum
+           /\ ConfigOKAt(j, b, CC)
+           /\ ChosenInBal(j, b, CC, c4)
+     (**********************************************************************)
+     (* Proof: The required CC exists by <2>4 and the third conjunct of    *)
+     (* VoteFor(aa, j, b, C.cnum, c4), using the assumption c4 \in CCmd of *)
+     (* <2>3 and the assumption Voted(a, i, b, C.cnum, c) of <2>1, which   *)
+     (* implies avote[a][i][b] \in Vote, so the final disjunct at the end  *)
+     (* of that conjunct is false.                                         *)
+     (**********************************************************************)
+
+  <2>6. CC = C
+     (**********************************************************************)
+     (* This follows from ConfigOKAt(j, b, CC) (from <2>5) and             *)
+     (* ConfigOKAt(i, b, C) (from <2>1, which imply that ConfigCmd(CC) and *)
+     (* ConfigCmd(C) are both learned commands.  It then follows from      *)
+     (* CC.cnum = C.cnum (from <2>5) and CfigOK (<1>1), which implies that *)
+     (* chosen reconfiguration commands have unique numbers, that CC = C.  *)
+     (**********************************************************************)
+
+  <2>7. \A a3 \in Acc : ~Voted(a3, j, b, C.cnum, c4)
+    (***********************************************************************)
+    (* Proof: By the last conjunct of <2>2, since <2>3 implies             *)
+    (* j \in (ii+1)..(i-1) and c4 \in CCmd                                 *)
+    (***********************************************************************)
+    
+  <2>8. QED
+    (***********************************************************************)
+    (* Proof: ChosenInBal(j, b, CC, c4) (from <2>5) implies Voted(aa, b,   *)
+    (* CC.cnum, c4) aa.  By <2>6 (CC = C), this contradicts <2>7.          *)
+    (***********************************************************************)
+    
+<1>4. ASSUME CONSTANT c1 \in Cmd   \* Bug in parser prevents reuse of symbol c
+      PROVE  Stable( /\ ChosenInBal(i, b, C, c1)
+                      /\ ConfigOKAt(i, b, C)  )
+  (*************************************************************************)
+  (* Proof: Since ChosenInBal(i, b, C, c1) implies                         *)
+  (* Voted(a, i, b, C.cnum, c1), for some `a', this follows easily from    *)
+  (* <1>3 and the stability of ChosenInBal(i, b, C, c1).                   *)
+  (*************************************************************************)
+  BY <1>4!2 + 1
+  
+<1>5. QED
+  BY <1>1, <1>2, <1>3, <1>4
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* We now define the inductive invariant that is the heart of the safety   *)
+(* proof.                                                                  *)
+(***************************************************************************)
+Inv ==
+ /\(*1*)  TypeOK 
+
+ /\(*2*)  \A a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd :
+            Voted(a, i, b, n, c) =>
+                   \A aa \in Acc, nn \in CNum, cc \in Cmd :
+                      Voted(aa, i, b, nn, cc) => (<<nn, cc>> = <<n, c>>)
+
+ /\(*3*)  \A i \in INum, c \in Cmd :
+             (<<i, c>> \in learned) => 
+               \E b \in BNum, C \in Config : /\ ConfigOKAt(i, b, C)
+                                             /\ ChosenInBal(i, b, C, c)
+
+ /\(*4*)  CfgOK
+
+ /\(*5*)  \A a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd :
+            Voted(a, i, b, n, c) => 
+              \E C \in Config :
+                /\ C.cnum = n
+                /\ ConfigOKAt(i, b, C)
+                /\ SafeAt(i, b, C, c)
+                /\ c \in CCmd => /\ ConfigOfCmd(c).cnum = n+1
+                                 /\ \A j \in INum, cc \in Cmd :
+                                       (j > i) => UnchoosableBefore(j, b, C, cc)
+                /\ ChoosableAt(i, b, C, c) =>
+                     C = cfgC[ Max({j \in 0..maxcfg : cfgi[j] < i}) ]
+
+
+(***************************************************************************)
+(* We first prove that this Inv is strong enough--that is, it implies the  *)
+(* predicate that we want to show is invariant.                            *)
+(***************************************************************************)
+THEOREM Inv => SafetyInv
+
+<1>1. SUFFICES ASSUME Inv,
+                      CONSTANT i \in INum,
+                      CONSTANT c1 \in Cmd,
+                      CONSTANT c2 \in Cmd,
+                      <<i, c1>> \in learned /\ <<i, c2>> \in learned
+               PROVE  c1 = c2
+  PROOF OBVIOUS
+
+<1>2. PICK C1, C2 \in Config, b1, b2 \in BNum :
+        /\ ConfigOKAt(i, b1, C1)
+        /\ ChosenInBal(i, b1, C1, c1)
+        /\ ConfigOKAt(i, b2, C2)
+        /\ ChosenInBal(i, b2, C2, c2)
+  (*************************************************************************)
+  (* Proof: By <<i, c1>> and <<i, c2>> in `learned' (from <1>1) and Inv!3 *)
+  (* (which is assumed by <1>1).                                           *)
+  (*************************************************************************)
+
+<1>3. PICK a1, a2 \in Acc : /\ Voted(a1, i, b1, C1.cnum, c1)
+                            /\ Voted(a2, i, b2, C2.cnum, c2)        
+  (*************************************************************************)
+  (* Proof: By <1>2, since ChosenInBal implies at least one vote.          *)
+  (*************************************************************************)
+
+<1>4. PICK CC1, CC2 \in Config :  
+        /\ CC1.cnum = C1.cnum
+        /\ ConfigOKAt(i, b1, CC1)
+        /\ ChoosableAt(i, b1, CC1, c1) =>
+              CC1 = cfgC[Max ({j \in 0 .. maxcfg : cfgi[j] < i})]
+        /\ CC2.cnum = C2.cnum
+        /\ ConfigOKAt(i, b2, CC2)
+        /\ ChoosableAt(i, b2, CC2, c2) =>
+              CC2 = cfgC[Max ({j \in 0 .. maxcfg : cfgi[j] < i})]
+  (*************************************************************************)
+  (* Proof: By <1>3 and conjunction 5 of Inv.                             *)
+  (*************************************************************************)
+
+<1>5. (CC1 = C1) /\ (CC2 = C2)
+  (*************************************************************************)
+  (* Proof: ConfigOKAt(i, b1, C1) (1st conjunct of <1>2) and               *)
+  (* ConfigOKAt(i, b1, CC1) (2nd conjunct of <1>4) imply that <<i1, C1>>   *)
+  (* and <<ii1, CC1>> are in `learned' for some i1 and ii1, and C1.cnum =  *)
+  (* CC1.cnum (<1>4!1).  By CfgOK, this implies that C1 = CC1.  Similarly, *)
+  (* C2 = CC2.                                                             *)
+  (*************************************************************************)
+
+<1>6.  C1 = C2 
+  (*************************************************************************)
+  (* Proof: We have ChosenInBal(i, b1, C1, c1) (second conjunct of <1>2),  *)
+  (* which by definition of ChosenInBal and ChoosableAt implies            *)
+  (* ChoosableAt(i, b1, C1, c1).  By <1>5 and the 3rd conjunct of <1>4,    *)
+  (* this shows that                                                       *)
+  (*                                                                       *)
+  (*    C1 = cfgC[Max ({j \in 0 .. maxcfg : cfgi[j] < i})]                 *)
+  (*                                                                       *)
+  (* A similar argument shows that C2 equals the same expression.          *)
+  (*************************************************************************)
+
+<1>7. QED
+  (*************************************************************************)
+  (* Proof: <1>6 and <1>2 imply ChosenInBal(i, b1, C, c1) and              *)
+  (* ChosenInBal(i, b2, C, c2) for C = C1 = C2.  The same simple reasoning *)
+  (* used in the proof of ordinary Paxos (with a fixed configuration C)    *)
+  (* shows that this implies c1 = c2.                                      *)
+  (*************************************************************************)
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* Here is the heart of the proof--the proof that Inv is an inductive      *)
+(* invariant.                                                              *)
+(***************************************************************************)
+THEOREM InductiveInvariance == Inv /\ Next => Inv'
+<1>1. HAVE Inv /\ Next
+
+<1>2. Inv!1'
+  (*************************************************************************)
+  (* It is straighforward to show that TypeOK /\ Next => TypeOK'.          *)
+  (*************************************************************************)
+
+\* <1> USE TypeOK
+\* On 4 Mar 2009 it became illegal to use arbitrary expressions as
+\* facts in a USE or HIDE--making this USE illegal
+
+<1>2a. TypeOK
+  PROOF OBVIOUS
+<1> USE <1>2a  
+  (*************************************************************************)
+  (* This statement means that from now on, we are free to use TypeOK      *)
+  (* without explicitly mentioning that we're using it.  In an informal    *)
+  (* proof like this one, there are undoubtedly lots of places where we    *)
+  (* assume other things without mentioning them.  If we've been careful,  *)
+  (* then we haven't assumed anything that we're not allowed to.           *)
+  (*************************************************************************)
+  
+<1>3. Inv!2' 
+  (*************************************************************************)
+  (* Proof: Next and Inv!2 true and Inv!2' false implies that              *)
+  (* VoteFor(a1, i, b, n, c) /\ Voted(a2, i, b, nn, cc) is true for some   *)
+  (* a2 # a1 and cc # c.  The second conjunct of VoteFor(a1, i, b, n, c)   *)
+  (* implies that this is impossible.                                      *)
+  (*************************************************************************)
+  
+<1>4. Inv!3'
+  (*************************************************************************)
+  (* Proof: Next and Inv!3 true and Inv!3' false implies that there is     *)
+  (* some i, bb, and c such that Learn(i, bb, c) is true but               *)
+  (*                                                                       *)
+  (*   \E b \in BNum, C \in Config : /\ ConfigOKAt(i, b, C)                *)
+  (*                                 /\ ChosenInBal(i, b, C, c)            *)
+  (*                                                                       *)
+  (* is false.  The definition of Learn(i, bb, c) implies that this is     *)
+  (* impossible.                                                           *)
+  (*************************************************************************)
+  
+<1>5. Inv!4' 
+  <2>1. CASE learnedRcfg' = learnedRcfg
+    <3>1. maxcfg' = maxcfg /\ cfgl' = cfgl
+      (*********************************************************************)
+      (* Proof: By case assumption <2>1 becuase maxcfg is defined in terms *)
+      (* of learnedRcfg and cfgl is defined in terms of maxcfg and         *)
+      (* learnedRcfg.                                                      *)
+      (*********************************************************************)
+    <3>2. cfgi' = cfgi /\ cfgc' = cfgc /\ cfgC' = cfgc /\ cfgb' = cfgb
+      (*********************************************************************)
+      (* Proof: By <3>1, since All these values are defined in terms of    *)
+      (* cfgl and maxcfg.                                                  *)
+      (*********************************************************************)
+    <3>3. QED
+      (*********************************************************************)
+      (* Proof: CfgOK' follows by assumption <2>1, <3>1, <3>2, the         *)
+      (* stability of                                                      *)
+      (*                                                                   *)
+      (*   /\ ChosenInBal(cfgi[j], cfgb[j], cfgC[j-1], cfgc[j])            *)
+      (*   /\ ConfigOKAt(cfgi[j], cfgb[j], cfgC[j-1])                      *)
+      (*                                                                   *)
+      (* for all j (by <1>1 and Theorem CfgStability) and the stability of *)
+      (*                                                                   *)
+      (*   UnchoosableBefore(i, cfgb[j], cfgC[j-1], c)                     *)
+      (*                                                                   *)
+      (* for all j, c (by Theorem Stability).                              *)
+      (*********************************************************************)
+       
+  <2>2. CASE learnedRcfg' # learnedRcfg 
+
+    <3>1. PICK i \in INum, b \in BNum, C \in Config : Learn(i, b, ConfigCmd(C))
+      (*********************************************************************)
+      (* Proof: Case assumption <2>2 and the definition of Next.           *)
+      (*********************************************************************)
+    <3>  DEFINE c == ConfigCmd(C)
+      
+    <3>2. PICK k \in 0..maxcfg :
+            /\ ConfigOKAt(i, b, cfgC[k])
+            /\ ChosenInBal(i, b, cfgC[k], c)
+            /\ learned' = learned \cup {<<i, c>>}
+      (*********************************************************************)
+      (* Proof: By <3>1, the definition of Learn, and CfgOK (Inv!4), which *)
+      (* implies that the configuration whose existence is implied by the  *)
+      (* definition Learn(i, b, c) must be one of the cfgC[j].             *)
+      (*********************************************************************)
+   <3>3. i > cfgi[k]
+     (**********************************************************************)
+     (* Proof: By ChosenInBal(i, b, cfgC[k], c) (<3>2), definition of      *)
+     (* ConfigOKAt, and CfgOK                                              *)
+     (**********************************************************************)
+      
+   <3>4. k = maxcfg
+      <4>1. SUFFICES ASSUME k < maxcfg
+                     PROVE  FALSE
+        (*******************************************************************)
+        (* Proof by contradiction.                                         *)
+        (*******************************************************************)
+
+      <4> HAVE k < maxcfg
+
+      <4>2. CASE i > cfgi[k+1]
+        <5>1. b < cfgb[k+1]
+          <6>1. /\ UnchoosableBefore(cfgi[k+1], b, cfgC[k], cfgc[k+1])      
+                /\ \A a \in Acc: ~Voted(a, cfgi[k+1], b, cfgC[k].cnum, cfgc[k+1])
+            (***************************************************************)
+            (* Proof: By ConfigOKAt(i, b, cfgC[k]) (<3>2), case assumption *)
+            (* <4>2, and cfgi[k] < cfgi[k+1] (by CfgOK).                   *)
+            (***************************************************************)
+          <6>2. QED
+            (***************************************************************)
+            (* Proof: Since CfgOK implies                                  *)
+            (*                                                             *)
+            (*     ChosenInBal(cfgi[k+1], cfgb[k+1], cfgC[k], cfgc[k+1])   *)
+            (*                                                             *)
+            (* <6>1 implies that cfgb[k+1] cannot be \leq b.               *)
+            (***************************************************************)
+
+        <5>2. UnchoosableBefore(i, cfgb[k+1], cfgC[k], c)
+          (*****************************************************************)
+          (* Proof: By the last conjunct of CfgOK, substituting k+1 for j, *)
+          (* using case assumption <4>2.                                   *)
+          (*****************************************************************)
+
+        <5>3. QED
+          (*****************************************************************)
+          (* Proof: <5>1 and <5>2 contradict <3>2, which implies           *)
+          (* ChosenInBal(i, b, cfgC[k], c).                                *)
+          (*****************************************************************)
+          
+      <4>3. CASE i < cfgi[k+1]
+        <5>1. b > cfgb[k+1]
+          <6>1. /\ UnchoosableBefore(i, cfgb[k+1], cfgC[k], c)
+                /\ \A a \in Acc : ~Voted(a, i, cfgb[k+1], cfgC[k].cnum, c)
+            (***************************************************************)
+            (* Proof: By CfgOK, which implies                              *)
+            (* ConfigOKAt(cfgi[k+1], cfgb[k+1], cfgC[k]) and the           *)
+            (* definition of ConfigOKAt, since case assumption <4>3 and    *)
+            (* <3>3 allows us to substitute i for j in the definition.     *)
+            (***************************************************************)
+          <6>2. QED
+            (***************************************************************)
+            (* Proof: <3>2 implies ChosenInBal(i, b, cfgC[k], c), which    *)
+            (* would contradict <6>1 if b \leq cfgb[k+1].                  *)
+            (***************************************************************)
+
+        <5>2. PICK a \in Acc : Voted(a, i, b, cfgC[k].cnum, c)
+          (*****************************************************************)
+          (* Proof: By ChosenInBal(i, b, cfgC[k], c) (from <3>2).          *)
+          (*****************************************************************)
+          
+        <5>3. PICK CC \in Config :
+                /\ CC.cnum = cfgC[k].cnum
+                /\ ConfigOKAt(i, b, CC)
+                /\ UnchoosableBefore(cfgi[k+1], b, CC, cfgc[k+1])
+          (*****************************************************************)
+          (* Proof: By <5>2 and the Inv!5, using case assumption <4>3 to   *)
+          (* justify substituting cfgc[k+1] for j in the 4th conjunct of   *)
+          (* the body of the \E C \in Config expression.                   *)
+          (*****************************************************************)
+
+        <5>4. CC = cfgC[k]          
+          (*****************************************************************)
+          (* Proof: ConfigOKAt(i, b, CC) (from <5>3) and CfgOK implies CC  *)
+          (* equals cfgC[j] for some j, and CC.cnum = cfgC[k].cnum (from   *)
+          (* <5>3) implies j = k.                                          *)
+          (*****************************************************************)
+
+        <5>5. QED 
+          (*****************************************************************)
+          (* Proof: <5>3 and <5>4 imply                                    *)
+          (*                                                               *)
+          (*      UnchoosableBefore(cfgi[k+1], b, cfg[k], cfgc[k+1])       *)
+          (*                                                               *)
+          (* and by b > cfgb[k+1] (from <5>1) this contradicts CfgOK,      *)
+          (* which implies                                                 *)
+          (*                                                               *)
+          (*      ChosenInBal(cfgi[k+1], cfgb[k+1], cfgC[k], cfgc[k+1])    *)
+          (*****************************************************************)
+          
+      <4>4. CASE i = cfgi[k+1]
+        <5>1. ChosenInBal(i, b, cfgC[k], c)
+          BY <3>2
+
+        <5>2. ChosenInBal(cfgi[k+1], cfgb[k+1], cfgC[k], cfgc[k+1])
+          BY CfgOK
+
+        <5>3. c = cfgc[k+1]
+          (*****************************************************************)
+          (* Proof: By same simple reasoning used to prove the consistency *)
+          (* of ordinary Paxos (for instance i = cfgi[k+1] and the (fixed) *)
+          (* configuration cfgC[k]).                                       *)
+          (*****************************************************************)
+
+        <5>4. QED
+          (*****************************************************************)
+          (* Proof: CfgOK implies <<cfgi[k+1], cfgc[k+1]>> \in `learned',  *)
+          (* so Learn(i, b, c) implies that `learned' is unchanged,        *)
+          (* contradicting case assumption <2>2 (learnedRcfg' #            *)
+          (* learnedRcfg).                                                 *)
+          (*****************************************************************)
+          
+      <4>5. QED        
+        BY <4>2, <4>3, <4>4
+
+    <3>5. /\(*1*)  <<i, c>> \in learned' \ {learned}
+          /\(*2*)  C.cnum = maxcfg + 1
+          /\(*3*)  ChosenInBal(i, b, cfgC[maxcfg], c)
+          /\(*4*)  ConfigOKAt(i, b, cfgC[maxcfg])
+          /\(*5*)  \A ii \in INum :
+                     (ii > i) => 
+                        \A cc \in Cmd : UnchoosableBefore(ii, b, cfgC[maxcfg], cc)
+      <4>1.  <3>5!1
+        (*******************************************************************)
+        (* Proof: By <3>1 and case assumption <2>2.                        *)
+        (*******************************************************************)
+        
+      <4>2.  <3>5!3
+        BY <3>2
+
+      <4>3.  <3>5!4
+        BY <3>2
+
+      <4>4. /\ C.cnum = cfgC[maxcfg].cnum + 1
+            /\ \A ii \in INum, cc \in Cmd :
+                  (ii > i) => UnchoosableBefore(ii, b, cfgC[maxcfg], cc)
+
+        <5>1. PICK a \in Acc : Voted(a, i, b, cfgC[maxcfg], c)
+          BY <4>2
+        <5>2. PICK CC \in Config :
+                /\ CC.cnum = cfgC[maxcfg]
+                /\ ConfigOKAt(i, b, CC)
+                /\ C.cnum = cfgC[maxcfg].cnum + 1
+                /\ \A j \in INum, cc \in Cmd :
+                      (j > i) => UnchoosableBefore(j, b, CC, cc)
+          (*****************************************************************)
+          (* Proof: By <5>1 and Inv!5, since c \in CCmd and C =            *)
+          (* ConfigOfCmd(c).                                               *)
+          (*****************************************************************)
+        <5>3. CC = cfgC[maxcfg]
+          (*****************************************************************)
+          (* Proof: BY <5>2, since ConfigOKAt(i, b, CC) implies            *)
+          (* <<j, CmdOfConfig(CC)>> in `learned', so it equals cfgC[j] for *)
+          (* some j, and CC.cnum = cfgC[maxcfg] implies j = maxcfg.        *)
+          (*****************************************************************)
+        <5>4. QED
+          BY <5>2, <5>3
+          
+      <4>5. <3>5!5
+
+      <4>6. QED
+        BY <4>1, <4>2, <4>3, <4>4, <4>5
+
+    <3>6. QED
+      (*********************************************************************)
+      (* Proof: <3>5!1 and <3>5!2 imply that maxcfg' = maxcfg+1 and        *)
+      (*                                                                   *)
+      (*   cfgl' = [j \in 0..maxcfg' |->                                   *)
+      (*              IF j < maxcfg THEN cfgl[j] ELSE <<i, c>>]            *)
+      (*                                                                   *)
+      (* from which it follows that the functions cfgi', cfgc', cfgC',     *)
+      (* and cfgb' are the expected "extensions" of cfgi, cfgc, cfgC, and  *)
+      (* cfgb, where <3>5!3 and <3>5!4 imply the existence of the b in     *)
+      (* BNum satisfying the CHOOSE expression in the definition of        *)
+      (* cfgb'[maxcfg'].  This proves the first 5 conjuncts of CfgOK'.     *)
+      (* The remaining three conjuncts are all \A j formulas.  For j <     *)
+      (* maxcfg', they follow from CfgOK. For j = maxcfg', they follow     *)
+      (* from <3>5.                                                        *)
+      (*********************************************************************)
+  <2>3. QED
+    BY <2>1, <2>2
+
+<1>6. Inv!5'
+  <2>1. TAKE a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd 
+  <2>2. HAVE Voted(a, i, b, n, c)'
+  <2>3. CASE Voted(a, i, b, n, c)
+    (***********************************************************************)
+    (* Proof: This case follows from the Theorems Stability and            *)
+    (* CfgStability, which imply the stability of all the non-constant     *)
+    (* subformuulas in the \E C \in Config expression in Inv!5.            *)
+    (***********************************************************************)
+  <2>4. CASE ~Voted(a, i, b, n, c)
+    <3>1. VoteFor(a, i, b, n, c)
+      (*********************************************************************)
+      (* Proof: Next, <2>2 and case assumption <2>4, implies this VoteFor  *)
+      (* action.                                                           *)
+      (*********************************************************************)
+      
+    <3>2. SUFFICES  (* to prove *)
+            \E C \in Config :
+                /\ C.cnum = n
+                /\ ConfigOKAt(i, b, C)
+                /\ SafeAt(i, b, C, c)
+                /\ c \in CCmd => /\ ConfigOfCmd(c).cnum = n+1
+                                 /\ \A j \in INum, cc \in Cmd :
+                                       (j > i) => UnchoosableBefore(j, b, C, cc)
+                /\ ChoosableAt(i, b, C, c) =>
+                     C = cfgC[ Max({j \in 0..maxcfg : cfgi[j] < i}) ]
+      (*********************************************************************)
+      (* Proof: For all other tuples <<aa, ii, bb, nn, cc>>, the body of   *)
+      (* the \A of Inv!5' follows from Inv!5 and Theorems Stability and    *)
+      (* CfgStability.                                                     *)
+      (*********************************************************************)
+
+    <3>3. PICK C \in Config :
+            /\ C.cnum = n
+            /\ ConfigOKAt(i, b, C)
+            /\ SafeAt(i, b, C, c)
+            /\ c \in CCmd => /\ ConfigOfCmd(c).cnum = n+1
+                             /\ \A j \in INum, cc \in Cmd :
+                                       (j > i) => UnchoosableBefore(j, b, C, cc)
+      (*********************************************************************)
+      (* Proof: The existence of C follows from <3>1 and the definition of *)
+      (* VoteFor.                                                          *)
+      (*********************************************************************)
+
+    <3>4. ASSUME ChoosableAt(i, b, C, c),
+                 C # cfgC[ Max({j \in 0..maxcfg : cfgi[j] < i}) ]
+          PROVE  FALSE
+      <4>1. PICK j \in 0..maxcfg : C = cfgC[j]
+        (*******************************************************************)
+        (* Proof: By CfgOK and ConfigOKAt(i, b, C), which imply that C is  *)
+        (* one of the cfgC[j], and                                         *)
+        (*******************************************************************)
+      <4>2. i > cfgi[j]
+        (*******************************************************************)
+        (* Proof: <3>3 and <4>1 imply ConfigOKAt(i, b, cfgC[j]), which     *)
+        (* implies i > cfgi[j].                                            *)
+        (*******************************************************************)
+      <4>3. i > cfgi[j+1]
+        (*******************************************************************)
+        (* Proof: <4>1, <4>2, and the assumption <3>4!2.                   *)
+        (*******************************************************************)
+      <4>4. cfgb[j+1] > b
+        (*******************************************************************)
+        (* Proof: Since and i > cfgi[j+1] > cfgi[j] (by <4>3 and CfgOK),   *)
+        (* ConfigOKAt(i, b, cfgC[j]) (which holds by <3>3 and <4>1)        *)
+        (* implies UnchoosableBefore(cfgi[j+1], b, cfgC[j], cfgc[j+1]) and *)
+        (* ~ChosenInBal(cfgi[j+1], b, cfgC[j], cfgc[j+1]).  Since CfgOK    *)
+        (* implies ChosenInBal(cfgi[j+1], cfgb[j+1], cfgC[j], cfgc[j+1]),  *)
+        (* we cannot have b \geq cfgb[j+1].                                *)
+        (*******************************************************************)
+      <4>5. QED
+        (*******************************************************************)
+        (* Proof: The last conjunct of CfgOK implies                       *)
+        (*                                                                 *)
+        (*   UnchoosableBefore(i, cfgb[j+1], cfgC[j], c)                   *)
+        (*                                                                 *)
+        (* By <4>4 and <4>1, this contradicts ChoosableAt(i, b, C, c),     *)
+        (* which is assumption <3>4!1.                                     *)
+        (*******************************************************************)
+    <3>5. QED
+      BY <3>2, <3>3, <3>4
+      
+  <2>5. QED
+    BY <2>3, <2>4
+
+<1>7. QED
+  BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>6 
+
+=============================================================================
+
+
+TLA+2 problems:
+
+ - Can't use "!" selectors to name "c \in S" in 
+   ASSUME CONSTANT c \in S
+   PARTIALLY FIXED 10 Oct 2007 to allow "CONSTANT c \in S" to
+   be used as a fact.
+
+ - <i>j. PICK x ...  makes x declared in the proof of <i>j, which
+   is wrong, since it's not legal to use it there.
+   FIXED 10 Oct 2007
+
+ - For subexpression naming, PICK is treated like a \A, even though
+   the variables being picked are being declared externally to the
+   expression.  Thus, we have to write
+
+    <3>4. PICK x : P(x)
+    <3>5. ...
+      BY <3>4!(x)  \* This names P(x)
+
+   This seems wrong, since x is declared in the scope of the use.
+   However, the !(x) can't be omitted if we want to refer to P inside
+   the proof of <3>4.  Since we don't want the naming convention to
+   depend on where the name appears, we don't have much choice but to
+   let it stand as is.
+
+ - Doesn't allow labels in numbered steps.  This seems to be a bug,
+   since the parser allows positional naming of subexpressions of
+   a numbered step.  FIXED 10 Oct 2007
+
+ - Bug: declaration of NEWs introduced in an ASSUME/PROVE step leak
+   out into the rest of the proof.  (They should only for a 
+   SUFFICE ASSUME/PROVE step.) FIXED 10 Oct 2007
+
+ - Bug: The error message for duplicate step numbers reports the position
+   as the entire containing proof body, not the step.  This indicates
+   that some semantic node is being created with the entire proof body
+   rather than just its own part of the spec as its syntactic node.
+   FIXED 10 Oct 2007
+=============================================================================
diff --git a/tlatools/test-model/suite/test212.cfg b/tlatools/test-model/suite/test212.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test212.tla b/tlatools/test-model/suite/test212.tla
new file mode 100644
index 0000000000000000000000000000000000000000..886fe8e0aba2014d6ae394130e5baa181e20eaba
--- /dev/null
+++ b/tlatools/test-model/suite/test212.tla
@@ -0,0 +1,33 @@
+--------------------------- MODULE test212 --------------------------
+\* Test of Leibniz checking
+\*   Uses the fact that you can't substitute a non-Leibniz operator
+\*   for a constant operator.
+VARIABLE x
+
+Leibniz(a2) == {a2 , x'}
+NonLeibniz2(a, b) == <<a, b'>>
+L ==  INSTANCE test212b WITH y <- x
+
+NonLeibniz(a1) == a1'
+Leibniz2(a, b) == <<a, b, x'>>
+
+I(a) == INSTANCE test212b WITH y <- a
+LL == INSTANCE test212b WITH y <- ENABLED (x'=x)
+NonL(a) ==INSTANCE test212b WITH y <- ENABLED a
+
+\* Legal
+L1 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- Leibniz2
+L2 == INSTANCE test212a WITH Op <- LAMBDA a : I(a)!Leibniz(1), 
+                             Op2 <- Leibniz2
+L3 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- I!Leibniz
+L4 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- LL!Leibniz2
+L5 == INSTANCE test212a WITH Op <- LL!Leibniz, Op2 <- Leibniz2
+
+\* Illegal
+I1 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- NonLeibniz2
+I2 == INSTANCE test212a WITH Op <- NonLeibniz, Op2 <- Leibniz2
+I3 == INSTANCE test212a WITH Op <- NonL!NoArg, Op2 <- Leibniz2
+I4 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- L!NonLeibniz2
+I5 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- NonL!Leibniz
+I6 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- I!NonLeibniz
+==================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test212a.tla b/tlatools/test-model/suite/test212a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..175fea251f1af826e385c7502a6b02b0ed34d7fd
--- /dev/null
+++ b/tlatools/test-model/suite/test212a.tla
@@ -0,0 +1,5 @@
+-------------------------- MODULE test212a -------------------------
+
+CONSTANT Op(_) , Op2(_,_) 
+
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test212b.tla b/tlatools/test-model/suite/test212b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c6027040a338c85e81f4fecaad02b100943cebcc
--- /dev/null
+++ b/tlatools/test-model/suite/test212b.tla
@@ -0,0 +1,9 @@
+-------------------------- MODULE test212b -------------------------
+VARIABLE y
+NoArg == y'
+Leibniz(a4) == <<a4, y>>
+Leibniz2(a44, a45) == <<a44, a45, y>>
+NonLeibniz(a5) == ENABLED a5
+NonLeibniz2(a6, a7) == {a6', a7}
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test213.cfg b/tlatools/test-model/suite/test213.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test213.tla b/tlatools/test-model/suite/test213.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b16ab4c8616d80bc47be3c071be5090b0c132668
--- /dev/null
+++ b/tlatools/test-model/suite/test213.tla
@@ -0,0 +1,47 @@
+------------------------------- MODULE test213 ------------------------------ 
+\* Test that 
+\* - A non-temporal theorem or proof step cannot have a
+\*   temporal formula in its proof.
+\* - A declared constant that appears in an ASSUME or an ASSUME/PROVE
+\*   cannot be instantiated with a temporal formula 
+\*
+\* Parsing only; produces errors.
+
+VARIABLE x
+
+I1 == INSTANCE test213b WITH C <- x', D <- x' 
+I2 == INSTANCE test213b WITH C <- [](x=0), D <- TRUE \* ERROR
+I3 == INSTANCE test213b WITH C <- TRUE, D <- [](x=0) \* ERROR
+
+THEOREM [](x=0)
+<1>1. CASE FALSE
+  <2>1. [](x=0)
+  <2>2. QED
+    <3>1. [](x # 0)
+      BY [](x # 0)
+    <3>2. QED
+<1>2. x=0        \* ERROR
+  <2>1. (x=0)   
+  <2>2. QED
+    <3>1. [](x=0)  
+    <3>2. QED
+<1>3. CASE TRUE
+ <2>1. CASE x=0  \* ERROR
+ <2>1a. CASE FALSE
+ <2>2. HAVE x=0  \* ERROR
+ <2>2a HAVE TRUE 
+ <2>3. WITNESS 3 \in x \* ERROR
+ <2>4. WITNESS 2 \in {}
+ <2>5. QED
+<1>4. QED
+  <2>1. [](x=0)
+  <2>2. QED
+ 
+
+
+THEOREM x=0  \* ERROR
+ <1>1. x'=0     \* ERROR
+   BY [](x=0)   \* This BY causes an error, though it needn't 
+ <1>2. [](x=0)  \* this causes error in THEOREM
+ <1>3. QED
+=============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test213b.tla b/tlatools/test-model/suite/test213b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..099e566da5f15f4cfa6f67e8b0246a70ccc94933
--- /dev/null
+++ b/tlatools/test-model/suite/test213b.tla
@@ -0,0 +1,7 @@
+------------------------------ MODULE test213b ------------------------------ 
+CONSTANT C, D
+ASSUME C = {}
+
+THEOREM ASSUME D
+        PROVE  TRUE
+=============================================================================
diff --git a/tlatools/test-model/suite/test214.cfg b/tlatools/test-model/suite/test214.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test214.tla b/tlatools/test-model/suite/test214.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c2be50d9d42b446fec0f1fb51588a4b4237d1087
--- /dev/null
+++ b/tlatools/test-model/suite/test214.tla
@@ -0,0 +1,16 @@
+------------------------------ MODULE test214 ------------------------------ 
+\* Test that USE and HIDE cannot contain arbitrary expressions as 
+\* facts.
+
+THEOREM bar == <<1, 2>>
+
+USE bar, bar!1 (*ERROR*)
+         
+
+HIDE bar, TRUE (*ERROR*)
+
+THEOREM xx == ASSUME TRUE PROVE FALSE 
+ <1>2. USE TRUE (*ERROR*), bar , xx , xx!1 (*ERROR*)
+ <1>3. QED
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test215.cfg b/tlatools/test-model/suite/test215.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test215.tla b/tlatools/test-model/suite/test215.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b4d5f7845d1db4f8c0f29aea9931bb0e3ba91ca5
--- /dev/null
+++ b/tlatools/test-model/suite/test215.tla
@@ -0,0 +1,29 @@
+------------------------- MODULE test215 ------------------------- 
+\* Test of constraints on levels of temporal operators that
+\* outlaw any form of raw TLA.
+
+VARIABLE x
+
+a == x ~> []x
+b == x' ~> []x    \* ERROR
+c == []x ~> x'    \* ERROR
+
+d == x -+-> []x
+e == x' -+-> []x  \* ERROR
+f == []x -+-> x'  \* ERROR
+
+g == <><<x'>>_x
+h == <>(x')       \* ERROR
+
+foo == [][x']_x
+
+bar == [](x')    \* ERROR
+
+m == \A i \in {}, j \in x : []x
+n == \A i \in {}, j \in x' : []x   \* ERROR
+
+mm == \E i \in {}, j \in x : []x
+nn == \E i \in {}, j \in x' : []x   \* ERROR
+
+m2 == \A i \in x' : x'
+=======================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test216.cfg b/tlatools/test-model/suite/test216.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..87210cf3d20b205f9d171b521f757ef61f2f98ff
--- /dev/null
+++ b/tlatools/test-model/suite/test216.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION   Spec1 \* Spec
+CONSTANT C=6
+INVARIANT Inv Inv2
+PROPERTIES Prop1 Prop2 Prop3 Prop4 Prop5
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test216.tla b/tlatools/test-model/suite/test216.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e54d2f85b7e0d3b8e7422ab9b1ba590d2e490bd1
--- /dev/null
+++ b/tlatools/test-model/suite/test216.tla
@@ -0,0 +1,72 @@
+---------------------------- MODULE test216 ------------------------
+EXTENDS TLC, Naturals
+
+INSTANCE test216a WITH D <- 42
+Foo == INSTANCE test216a WITH D <- 43
+FooG == INSTANCE test216a WITH D <- 42
+VARIABLE x
+CONSTANT C
+
+THEOREM Thm1 == x \in 0..C
+ 
+THEOREM Thm1Prime == x' \in 0..C
+
+THEOREM Thm1a == 1+1=2
+Bar == x \in 0..C
+Init == Bar \/ Thm1!: \/ (Thm1 /\ ENABLED Thm1Prime!:)
+Next == x'=(x+1)%C  /\  Thm1!: 
+             /\ Thm1Prime!: /\ Thm1 /\ Thm1Prime
+Spec == /\ Init 
+        /\ [][Next]_x
+        /\ WF_x(Next)
+
+
+
+
+Inv == Thm1
+Inv2 == Thm1!<< \in Thm1!>>
+
+THEOREM Thm1b == Spec => /\ []Inv 
+                         /\  []<>(x=0)
+
+Spec1 == Thm1b!1
+\* need to build two tests, since there are two specs
+
+THEOREM Thm1c == [][Next]_x
+Spec2 == Init /\ Thm1c \* Doesn't work
+
+Thm1d == []<>(x=0)
+Prop3 == Thm1d
+
+Prop1 == Thm1b!2!1
+Prop2 == Thm1b!2!2
+
+ASSUME Thm2
+ASSUME ~ Foo!Thm2
+ASSUME ~Foo!Thm3
+ASSUME ~Foo!I!Thm3
+ASSUME  FooG!Thm2
+ASSUME FooG!Thm3 
+ASSUME FooG!I!Thm3
+ASSUME Thm2!:
+ASSUME ~ Foo!Thm2!:
+ASSUME Thm2!1 = 42
+ASSUME Foo!Thm2!1 = 43
+
+THEOREM Thm3!:
+THEOREM ~ Foo!Thm3!:
+ASSUME Foo!Thm3!2!2 = 42
+ASSUME Foo!I!Thm3!2!2 = 42
+ASSUME I!Thm3!2!2 = 42
+ASSUME Thm3!2!2 = 42
+
+
+J == INSTANCE test216c
+Prop4 == J!ThmC!1 /\ J!ThmC!2 /\ J!ThmC2!:
+
+Thm1aDef == Thm1a
+
+INSTANCE test216c
+ThmC3Def == ThmC3
+Prop5 == ThmC!1 /\ ThmC!2   /\ ThmC3!: /\ Thm1aDef /\ ThmC3Def
+====================================================================
diff --git a/tlatools/test-model/suite/test216a.tla b/tlatools/test-model/suite/test216a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bd7e81a7f7135e6f0c521c0fe0e10ad6ab545f39
--- /dev/null
+++ b/tlatools/test-model/suite/test216a.tla
@@ -0,0 +1,15 @@
+---------------------------- MODULE test216a ------------------------
+EXTENDS Naturals
+CONSTANT D
+
+ASSUME Ass2 == D + 0 = 42 
+THEOREM Thm2 == D + 0 = 42
+Thm2Def == D + 0 = 42
+
+INSTANCE test216b WITH E <- D, y <- 0   
+  \* 12 June 2014: Changed from Test216b, which is now illegal
+I == INSTANCE test216b WITH E <- D, y <- 0
+  \* 12 June 2014: Changed from Test216b, which is now illegal
+
+
+====================================================================
diff --git a/tlatools/test-model/suite/test216b.tla b/tlatools/test-model/suite/test216b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e576811063d60194ff88e1a84535b2c8df319426
--- /dev/null
+++ b/tlatools/test-model/suite/test216b.tla
@@ -0,0 +1,7 @@
+---------------------------- MODULE test216b ------------------------
+CONSTANT E, y
+
+THEOREM Thm3 == y \in {y} /\ E = 42
+
+
+====================================================================
diff --git a/tlatools/test-model/suite/test216c.tla b/tlatools/test-model/suite/test216c.tla
new file mode 100644
index 0000000000000000000000000000000000000000..80175592a06892a00b77745635ca7c34780e210c
--- /dev/null
+++ b/tlatools/test-model/suite/test216c.tla
@@ -0,0 +1,15 @@
+------------------ MODULE test216c ---------------------
+EXTENDS Naturals
+VARIABLE x
+
+InitC == x = 0
+NextC == x'=(x+1) %6
+
+SpecC == [][NextC]_x
+
+THEOREM ThmC == SpecC => [](x \in Nat)
+
+THEOREM ThmC2 == [](x \in Nat)
+
+THEOREM ThmC3 == 1+1 = 2
+======================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test217.cfg b/tlatools/test-model/suite/test217.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test217.tla b/tlatools/test-model/suite/test217.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f9028c3320e16e4d3b138a711e42b0929187bbe4
--- /dev/null
+++ b/tlatools/test-model/suite/test217.tla
@@ -0,0 +1,14 @@
+Sany should produce two level errors when parsing this module.
+
+---- MODULE test217 ----
+    \* 12 June 2014: Changed module name from Test217, which is now illegal
+EXTENDS test217a
+    \* 12 June 2014: Changed from Test217a, which is now illegal
+VARIABLE x
+
+I(z) == INSTANCE test217a WITH y <- z
+    \* 12 June 2014: Changed from Test217a, which is now illegal
+
+FooBar == I(x')!Foo
+THEOREM I({x'})!Foo
+===============================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test217a.tla b/tlatools/test-model/suite/test217a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8617c81c5013269b6071dd14549d5930a40c697c
--- /dev/null
+++ b/tlatools/test-model/suite/test217a.tla
@@ -0,0 +1,5 @@
+---- MODULE test217a ----
+VARIABLE y
+
+THEOREM Foo == y'=0
+=========================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test218.tla b/tlatools/test-model/suite/test218.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ab3fb749971531ec4e114cdce17c8b40c92383a3
--- /dev/null
+++ b/tlatools/test-model/suite/test218.tla
@@ -0,0 +1,11 @@
+Check fix to handling of ThmOrAssumpDefNode in Tool.java
+on 23 Oct 2012.
+
+---- MODULE test218 ----
+EXTENDS Test218a, TLC
+
+I(z) == INSTANCE Test218a WITH y <- z
+
+ASSUME PrintT(I(55)!Foo)
+
+===============================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test219.cfg b/tlatools/test-model/suite/test219.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test219.tla b/tlatools/test-model/suite/test219.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2d17f4b8388b4b797c8865562b804eba5a2f82cb
--- /dev/null
+++ b/tlatools/test-model/suite/test219.tla
@@ -0,0 +1,227 @@
+------------------------------- MODULE test219 ------------------------------ 
+\* Test of use of labels from instantiated theorems, and repeat
+\* test of some instantiations
+
+EXTENDS TLC , Integers
+
+I(x) == INSTANCE test219a WITH C <- 4  
+J == INSTANCE test219a WITH C <- 4  
+INSTANCE test219a WITH C <- 4 
+
+LOCAL K(x) == INSTANCE test219a WITH C <- 4  
+LOCAL L == INSTANCE test219a WITH C <- 4  
+
+
+ASSUME J!Thm2!lab3
+ASSUME I(55)!Thm2!lab3
+ASSUME L!Thm2!lab3
+ASSUME K(55)!Thm2!lab3
+ASSUME Thm2!lab3
+
+
+ASSUME Op(Foo, 9) 
+
+ASSUME Thm2!lab3
+
+ASSUME Bar
+
+ASSUME Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) 
+ASSUME OOp1(SOp1, 2) = 2
+ASSUME OOp2(SOp1b!@, 2, 3) = 5
+ASSUME OOp2(SOp1c!@!c, 2, 3) = 2 * 3
+ASSUME OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4
+ASSUME Op1(2)!(3)!2!1 = 2 + 4*3 + 6
+ASSUME Op1(2)!(3)!2!Op1a(4) = 16 + 6
+ASSUME Op3(Op1!@!2!Op1a, 2, 3, 4) = 22
+ASSUME COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5}
+ASSUME COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >>
+ASSUME COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >>
+ASSUME COp(1, 2)!(3)!1!>> = {99, 1, 2, 3}
+ASSUME OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5}
+ASSUME Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME Inst!Bar(3)!>> = 3
+ASSUME Inst!Bar2(3)!>> = 3
+ASSUME Inst!Bar3!>> = 2
+ASSUME Inst!Bar4(1)!(2)!<< = 2
+ASSUME Inst!Bar4(1)!(2)!>> = 1
+ASSUME Inst!Bar4(1)!lab(2)!<< = 2
+ASSUME Inst!Bar4(1)!lab(2)!>> = 1
+ASSUME OOp2(Inst!Bar4!@!<<, 1, 2) = 2
+ASSUME OOp2(Inst!Bar4!@!>>, 1, 2) = 1
+ASSUME OOp2(Inst!Bar4!lab!<<, 1, 2) = 2
+ASSUME OOp2(Inst!Bar4!lab!>>, 1, 2) = 1
+ASSUME Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>>
+ASSUME Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30
+ASSUME COp2(1,2)!(3)!++(9,10)!lab(11) = 30
+ASSUME COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30
+ASSUME OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30
+ASSUME Def1!2 = 2
+ASSUME Def2!(14)!<< = 14
+ASSUME Def3!2!1 = 2
+ASSUME Def3!(1, 2)!<< = 1
+ASSUME Def4!<<!<<!2 = {2}
+ASSUME Def5!>> = 3
+ASSUME Def5!<<[4] = 5
+ASSUME Def5!<<!1!>> = 10
+ASSUME Def6!1[3] = 4
+ASSUME Def7!1 = 3
+ASSUME Def7!2 = "a"
+ASSUME Def8!2 = 2
+ASSUME Def9!2 = 2
+ASSUME Def10!2 = 2
+ASSUME Def11!3!1 = 3
+
+ASSUME L!Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) 
+ASSUME L!OOp1(SOp1, 2) = 2
+ASSUME L!OOp2(SOp1b!@, 2, 3) = 5
+ASSUME L!OOp2(SOp1c!@!c, 2, 3) = 2 * 3
+ASSUME L!OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4
+ASSUME L!Op1(2)!(3)!2!1 = 2 + 4*3 + 6
+ASSUME L!Op1(2)!(3)!2!Op1a(4) = 16 + 6
+ASSUME L!Op3(Op1!@!2!Op1a, 2, 3, 4) = 22
+ASSUME L!COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5}
+ASSUME L!COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >>
+ASSUME L!COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >>
+ASSUME L!COp(1, 2)!(3)!1!>> = {99, 1, 2, 3}
+ASSUME L!OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME L!COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5}
+ASSUME L!Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME L!Inst!Bar(3)!>> = 3
+ASSUME L!Inst!Bar2(3)!>> = 3
+ASSUME L!Inst!Bar3!>> = 2
+ASSUME L!Inst!Bar4(1)!(2)!<< = 2
+ASSUME L!Inst!Bar4(1)!(2)!>> = 1
+ASSUME L!Inst!Bar4(1)!lab(2)!<< = 2
+ASSUME L!Inst!Bar4(1)!lab(2)!>> = 1
+ASSUME L!OOp2(Inst!Bar4!@!<<, 1, 2) = 2
+ASSUME L!OOp2(Inst!Bar4!@!>>, 1, 2) = 1
+ASSUME L!OOp2(Inst!Bar4!lab!<<, 1, 2) = 2
+ASSUME L!OOp2(Inst!Bar4!lab!>>, 1, 2) = 1
+ASSUME L!Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME L!Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME L!OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>>
+ASSUME L!Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME L!COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30
+ASSUME L!COp2(1,2)!(3)!++(9,10)!lab(11) = 30
+ASSUME L!COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30
+ASSUME L!OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME L!OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME L!OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30
+ASSUME L!Def1!2 = 2
+ASSUME L!Def2!(14)!<< = 14
+ASSUME L!Def3!2!1 = 2
+ASSUME L!Def3!(1, 2)!<< = 1
+ASSUME L!Def4!<<!<<!2 = {2}
+ASSUME L!Def5!>> = 3
+ASSUME L!Def5!<<[4] = 5
+ASSUME L!Def5!<<!1!>> = 10
+ASSUME L!Def6!1[3] = 4
+ASSUME L!Def7!1 = 3
+ASSUME L!Def7!2 = "a"
+ASSUME L!Def8!2 = 2
+ASSUME L!Def9!2 = 2
+ASSUME L!Def10!2 = 2
+ASSUME L!Def11!3!1 = 3
+
+ASSUME I(55)!Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) 
+ASSUME I(55)!OOp1(SOp1, 2) = 2
+ASSUME I(55)!OOp2(SOp1b!@, 2, 3) = 5
+ASSUME I(55)!OOp2(SOp1c!@!c, 2, 3) = 2 * 3
+ASSUME I(55)!OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4
+ASSUME I(55)!Op1(2)!(3)!2!1 = 2 + 4*3 + 6
+ASSUME I(55)!Op1(2)!(3)!2!Op1a(4) = 16 + 6
+ASSUME I(55)!Op3(Op1!@!2!Op1a, 2, 3, 4) = 22
+ASSUME I(55)!COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5}
+ASSUME I(55)!COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >>
+ASSUME I(55)!COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >>
+ASSUME I(55)!COp(1, 2)!(3)!1!>> = {99, 1, 2, 3}
+ASSUME I(55)!OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME I(55)!COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5}
+ASSUME I(55)!Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME I(55)!Inst!Bar(3)!>> = 3
+ASSUME I(55)!Inst!Bar2(3)!>> = 3
+ASSUME I(55)!Inst!Bar3!>> = 2
+ASSUME I(55)!Inst!Bar4(1)!(2)!<< = 2
+ASSUME I(55)!Inst!Bar4(1)!(2)!>> = 1
+ASSUME I(55)!Inst!Bar4(1)!lab(2)!<< = 2
+ASSUME I(55)!Inst!Bar4(1)!lab(2)!>> = 1
+ASSUME I(55)!OOp2(Inst!Bar4!@!<<, 1, 2) = 2
+ASSUME I(55)!OOp2(Inst!Bar4!@!>>, 1, 2) = 1
+ASSUME I(55)!OOp2(Inst!Bar4!lab!<<, 1, 2) = 2
+ASSUME I(55)!OOp2(Inst!Bar4!lab!>>, 1, 2) = 1
+ASSUME I(55)!Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME I(55)!Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>>
+ASSUME I(55)!OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>>
+ASSUME I(55)!Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+ASSUME I(55)!COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30
+ASSUME I(55)!COp2(1,2)!(3)!++(9,10)!lab(11) = 30
+ASSUME I(55)!COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30
+ASSUME I(55)!OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME I(55)!OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30
+ASSUME I(55)!OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30
+ASSUME I(55)!Def1!2 = 2
+ASSUME I(55)!Def2!(14)!<< = 14
+ASSUME I(55)!Def3!2!1 = 2
+ASSUME I(55)!Def3!(1, 2)!<< = 1
+ASSUME I(55)!Def4!<<!<<!2 = {2}
+ASSUME I(55)!Def5!>> = 3
+ASSUME I(55)!Def5!<<[4] = 5
+ASSUME I(55)!Def5!<<!1!>> = 10
+ASSUME I(55)!Def6!1[3] = 4
+ASSUME I(55)!Def7!1 = 3
+ASSUME I(55)!Def7!2 = "a"
+ASSUME I(55)!Def8!2 = 2
+ASSUME I(55)!Def9!2 = 2
+ASSUME I(55)!Def10!2 = 2
+ASSUME I(55)!Def11!3!1 = 3
+
+ASSUME /\ Def12!1!<< = 2
+       /\ Def12!2 = 2
+       /\ Def12!3 = 3
+ASSUME /\ Def13!(42)!1!<<!<< = 42
+       /\ Def13!(42)!1!>> = 1
+       /\ Def13!(42)!2!>> = 2
+       /\ Def13!(42)!3!>> = 3
+
+ASSUME /\ Def14!(2)!<< = 2
+       /\ Def14!1!1 = 1
+
+ASSUME /\ Def15!(2,3)!<<!<< = 2
+       /\ Def15!(2,3)!>>!<< = 3
+       /\ Def15!2!<< = 2
+
+ASSUME /\ Def16!(2,3,4)!1!1 = 2
+       /\ Def16!(2,3,4)!2!1 = 3
+       /\ Def16!2!1!<< = 2
+
+ASSUME /\ Def17!(2,3,4)!1!1 = 2
+       /\ Def17!(2,3,4)!2!1 = 3
+
+ASSUME Def18!(2)!<< = 2
+
+ASSUME /\ Def19!(2)!<< = 2
+       /\ Def19!1!1 = 42
+
+ASSUME /\ Def20!1 = 12
+       /\ Def20!>> = 23
+
+ASSUME /\ Def21!1 = 12
+       /\ Def21!>> = 23
+
+ASSUME /\ Def22!1 = 12
+       /\ Def22!>> = 23
+
+ASSUME /\ Def23!(2,3,4)!1!1 = 2
+       /\ Def23!(2,3,4)!2!1 = 3
+
+
+=============================================================================
+
+last modified on Thu  1 November 2012 at 10:11:43 PST by lamport
+==============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test219a.cfg b/tlatools/test-model/suite/test219a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tlatools/test-model/suite/test219a.tla b/tlatools/test-model/suite/test219a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..efbdb448b87ea70fac806c8b0329ad875d804370
--- /dev/null
+++ b/tlatools/test-model/suite/test219a.tla
@@ -0,0 +1,239 @@
+------------------------------- MODULE test219a ------------------------------ 
+\* Test of labels and their use in subexpression selectors.
+
+EXTENDS TLC, Integers
+
+CONSTANT C
+\* C  == 4
+
+Op(Arg(_), p) == 
+   \A x \in {1,2}, <<y, z>> \in {<<3, 4>>} :
+                      lab(x,y,z) ::  LET a ++ b ==  
+                                          Arg(a) + b + x + 2*y + 3*z
+                                     IN  (Arg(1) + Arg(p) + C + 
+                                            2*x  + 4*y + 6*z) =
+                                         1 ++ ( label2 :: p ++ C)
+\*                                IN /\label2::  PrintT("FOO")
+\*                                   /\ PrintT(Arg(1))
+\*                                   /\ PrintT("FOO2")
+\*                                   /\ PrintT(<<p, Arg(p)>>)
+\*                                   /\ PrintT("FOO3")
+\*                                   /\ PrintT(<<p, C>>)
+\*                                   /\ PrintT("FOO3a")
+\*                                   /\ PrintT(1++2)
+\*                                   /\ PrintT(p++C)
+\*                                   /\ PrintT("FOO4")
+\*                                   /\ PrintT(<<x, y, z>>)
+
+Foo(u) == 2*u 
+
+\* ASSUME Foo(5) = Foo(5)!:
+
+THEOREM Thm == ASSUME FALSE
+               PROVE  lab:: C > 0
+
+THEOREM Thm2 == lab3 :: TRUE
+
+
+\* ASSUME Foo(25) = 50
+
+Bar == Op(Foo, 9)!lab(1,2,3)
+
+\* ASSUME Op(Foo, 9) 
+\* 
+\* ASSUME Thm2!lab3
+\* 
+\* ASSUME Bar
+\* 
+\* ASSUME Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) 
+
+Op3(A(_,_,_), a1, a2, a3) == A(a1, a2, a3)
+
+Op1(a) == \A x \in {} : lab(x) :: IF TRUE
+                             THEN LET Op1a(c) == 4 * c + a*x
+                                  IN  a + Op1a(x) 
+                             ELSE 1
+SOp1(a) == a
+OOp1(A(_), a) == A(a)
+\* ASSUME OOp1(SOp1, 2) = 2
+
+SOp1a(a) == {x \in {1,2} : a = x}
+OOp2(A(_,_), a, b) == A(a, b)
+\* ASSUME OOp2(SOp1a!@, 2, 2) = TRUE
+
+SOp1b(a) == {x + a : x \in {1,2}}
+\* ASSUME OOp2(SOp1b!@, 2, 3) = 5
+
+SOp1c(a) == {(LET c == a * x IN x + a)  : x \in {1,2}}
+\* ASSUME OOp2(SOp1c!@!c, 2, 3) = 2 * 3
+
+SOp1d(a) == {(LET c(y) == (a * x) + y IN x + a)  : x \in {1,2}}
+OOp3(A(_,_,_), a, b, c) == A(a, b, c)
+\* ASSUME OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4
+
+OOp4(A(_,_,_,_), a, b, c, d) == A(a, b, c, d)
+
+
+\* Foo1 == Op1(2)!(3)!2!1
+\* ASSUME Op1(2)!(3)!2!1 = 2 + 4*3 + 6
+\* ASSUME Op1(2)!(3)!2!Op1a(4) = 16 + 6
+\* ASSUME Op3(Op1!@!2!Op1a, 2, 3, 4) = 22
+
+COp(q, p) == 
+   CHOOSE x : lab(x) ::  LET a ++ b ==  {a, b, q, p, x}
+                         IN  <<q, 99 ++ p >>
+
+
+\* ASSUME COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5}
+
+\* ASSUME COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >>
+\* ASSUME COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >>
+\* ASSUME COp(1, 2)!(3)!1!>> = {99, 1, 2, 3}
+
+OOp5(A(_, _, _, _, _), a1, a2, a3, a4, a5) == A(a1, a2, a3, a4, a5)
+
+\* ASSUME OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+\* ASSUME COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5}
+
+Inst == INSTANCE test206a WITH A5 <- COp!@!++
+\* ASSUME Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+\* ASSUME Inst!Bar(3)!>> = 3
+\* ASSUME Inst!Bar2(3)!>> = 3
+\* ASSUME Inst!Bar3!>> = 2
+\* ASSUME Inst!Bar4(1)!(2)!<< = 2
+\* ASSUME Inst!Bar4(1)!(2)!>> = 1
+\* ASSUME Inst!Bar4(1)!lab(2)!<< = 2
+\* ASSUME Inst!Bar4(1)!lab(2)!>> = 1
+\* ASSUME OOp2(Inst!Bar4!@!<<, 1, 2) = 2
+\* ASSUME OOp2(Inst!Bar4!@!>>, 1, 2) = 1
+\* ASSUME OOp2(Inst!Bar4!lab!<<, 1, 2) = 2
+\* ASSUME OOp2(Inst!Bar4!lab!>>, 1, 2) = 1
+\* ASSUME Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>>
+\* ASSUME Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>>
+\* ASSUME OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>>
+
+Inst2(A5(_, _, _, _, _)) ==  INSTANCE test206a 
+\* ASSUME Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5}
+
+
+COp2(q, p) == 
+   CHOOSE x : lab(x) ::  LET a ++ b ==  {lab(y):: y + a + b : y \in {1}}
+                         IN  <<q, 99 ++ p >>
+
+\* ASSUME COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30
+\* ASSUME COp2(1,2)!(3)!++(9,10)!lab(11) = 30
+\* ASSUME COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30
+OOp6(A(_, _, _, _, _, _), a1, a2, a3, a4, a5, a6) == A(a1, a2, a3, a4, a5, a6)
+\* ASSUME OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30
+\* ASSUME OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30
+\* ASSUME OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30
+
+\* Let's test all the different kinds of operators
+
+Def1 == {1, 2, 3, 4}
+\* ASSUME Def1!2 = 2
+
+Def2 == {x \in {1, 2, 3} : x > 17}
+\* ASSUME Def2!1 = {1, 2, 3}
+\* ASSUME Def2!(14)!<< = 14
+
+Def3 == {<<x, y>> : x \in {1}, y \in {2}}
+\* ASSUME Def3!2!1 = 2
+\* ASSUME Def3!(1, 2)!<< = 1
+
+Def4 == SUBSET UNION {{1}, {2}}
+\* ASSUME Def4!<<!<<!2 = {2}
+
+Def5 == [i \in 1..10 |-> i+1][3]
+\* ASSUME Def5!>> = 3
+\* ASSUME Def5!<<[4] = 5
+\* ASSUME Def5!<<!1!>> = 10
+
+Def6 == [[i \in 1..10 |-> i+1] EXCEPT ![2] = 1, ![3] = 2]
+\* ASSUME Def6!1[3] = 4
+\* The following two subexpression expressions are now
+\* considered to be illegal because they could contain
+\* a "@".
+\* ASSUME Def6!2=1
+\* ASSUME Def6!3=2
+
+Def7 == 3.a
+\* ASSUME Def7!1 = 3
+\* ASSUME Def7!2 = "a"
+
+Def8 == [a |-> 1, b |-> 2]
+\* ASSUME Def8!2 = 2
+
+Def9 == [a : 1, b : 2]
+\* ASSUME Def9!2 = 2
+
+Def10 == <<1, 2, 3>>
+\* ASSUME Def10!2 = 2
+
+Def11 == {1} \X {2} \X {3}
+\* ASSUME Def11!3!1 = 3
+
+Def12 == IF 2=3 THEN 2 ELSE 3
+\* ASSUME /\ Def12!1!<< = 2
+\*       /\ Def12!2 = 2
+\*       /\ Def12!3 = 3
+
+Def13 == \A x : CASE x=1 -> 1
+                  [] x=2 -> 2
+                  [] OTHER -> 3
+\* ASSUME /\ Def13!(42)!1!<<!<< = 42
+\*       /\ Def13!(42)!1!>> = 1
+\*       /\ Def13!(42)!2!>> = 2
+\*       /\ Def13!(42)!3!>> = 3
+
+Def14 == \A x \in {1} : x = 1
+\* ASSUME /\ Def14!(2)!<< = 2
+\*       /\ Def14!1!1 = 1
+
+Def15 == \A x \in {1}, y \in {2} : (x = 1) /\ (y = 2)
+\* ASSUME /\ Def15!(2,3)!<<!<< = 2
+\*       /\ Def15!(2,3)!>>!<< = 3
+\*       /\ Def15!2!<< = 2
+
+Def16 == \E x \in {1}, <<y, z>> \in {<<2,3>>} : /\ ((x = 1))
+                                                /\ y = 2 
+                                                /\ (z=3)
+\* ASSUME /\ Def16!(2,3,4)!1!1 = 2
+\*       /\ Def16!(2,3,4)!2!1 = 3
+\*       /\ Def16!2!1!<< = 2
+
+Def17 == \E x , y, z : /\ ((x = 1))
+                       /\ y = 2 
+                       /\ (z=3)
+\* ASSUME /\ Def17!(2,3,4)!1!1 = 2
+\*       /\ Def17!(2,3,4)!2!1 = 3
+
+Def18 == CHOOSE x : (x=1)
+\* ASSUME Def18!(2)!<< = 2
+
+Def19 == CHOOSE x \in {42}: (x=1)
+\* ASSUME /\ Def19!(2)!<< = 2
+\*       /\ Def19!1!1 = 42
+
+Def20 == [12]_(23)
+\* ASSUME /\ Def20!1 = 12
+\*       /\ Def20!>> = 23
+
+Def21 == <<12>>_(23)
+\* ASSUME /\ Def21!1 = 12
+\*       /\ Def21!>> = 23
+
+Def22 == WF_(12)(23)
+\* ASSUME /\ Def22!1 = 12
+\*     /\ Def22!>> = 23
+
+Def23 == \EE x , y, z : /\ ((x = 1))
+                        /\ y = 2 
+                        /\ (z=3)
+\* ASSUME /\ Def23!(2,3,4)!1!1 = 2
+\*       /\ Def23!(2,3,4)!2!1 = 3
+
+
+=============================================================================
+
+last modified on Wed 31 October 2012 at 14:45:24 PST by lamport
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test22.cfg b/tlatools/test-model/suite/test22.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test22.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test22.tla b/tlatools/test-model/suite/test22.tla
new file mode 100644
index 0000000000000000000000000000000000000000..7f40faf963139cdfb4f01ee341e52c36b6b00b03
--- /dev/null
+++ b/tlatools/test-model/suite/test22.tla
@@ -0,0 +1,40 @@
+--------------- MODULE test22 -------------
+
+(* Test of fingerprinting. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x, y
+
+S == {1}
+T == {2, 3, 4}
+
+
+
+Init ==  /\ Print("Distinct States should equal Distinct Initial States", TRUE)
+         /\ x \in [S -> [S -> SUBSET T]]
+         /\ y \in [S -> [S -> SUBSET [a : {2}, b : {3, 4, 5}]]]
+
+Inv == IF [S -> [S -> SUBSET [b : {4, 4, 3, 5, 4}, a : {2, 2}]]] # 
+               [S -> [S -> SUBSET [a : {2}, b : {3, 4, 5}]]]
+          THEN Assert(FALSE, "Test 1 Failed")
+          ELSE TRUE
+
+Next ==     
+  \/ UNCHANGED <<x, y>>
+
+  \/ /\ x' \in [S -> [S -> SUBSET {4, 4, 4, 3, 2, 2}]]
+     /\ UNCHANGED y
+
+  \/ /\ y' \in [S -> [S -> SUBSET [b : {4, 4, 3, 5, 4}, a : {2, 2}]]]
+     /\ UNCHANGED x
+
+  \/ /\ y' = [y EXCEPT ![1][1] = {[b |-> 4, a |-> 2]}]
+     /\ UNCHANGED x
+
+  \/ /\ y' = [y EXCEPT ![1][1] = {[b |-> 3, a |-> 2]}]
+     /\ UNCHANGED x
+
+  \/ /\ y' = [y EXCEPT ![1][1] = {[b |-> 5, a |-> 2]}]
+     /\ UNCHANGED x
+=========================================
diff --git a/tlatools/test-model/suite/test220.cfg b/tlatools/test-model/suite/test220.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3f4b98ea5accf7e48d18d975a5c1822b0c00f4d3
--- /dev/null
+++ b/tlatools/test-model/suite/test220.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+
+  CHECK_DEADLOCK FALSE
+
+  INVARIANT Inv
diff --git a/tlatools/test-model/suite/test220.tla b/tlatools/test-model/suite/test220.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c5a085d4e478b09b49d7098fd55594a2d31fad56
--- /dev/null
+++ b/tlatools/test-model/suite/test220.tla
@@ -0,0 +1,22 @@
+--------------- MODULE test220  -------------
+
+(* Testing deadlock and number of states found. *)
+
+EXTENDS TLC, Naturals, Sequences, FiniteSets
+
+VARIABLE x, y, z
+
+Init == /\ Print("This test should not deadlock when configured with CHECK_DEADLOCK=FALSE", TRUE)
+        /\ x = {}
+        /\ y = {}
+        /\ z = 1
+
+Next == /\ x = {}
+        /\ x' = {1}        
+        /\ y' \in SUBSET x'
+        /\ z' = z
+        /\ y' = {}
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test23.cfg b/tlatools/test-model/suite/test23.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6b7101b241f4a4d2db964521a677ea247a42e163
--- /dev/null
+++ b/tlatools/test-model/suite/test23.cfg
@@ -0,0 +1,5 @@
+INIT      Init
+NEXT      Next
+INVARIANT Inv
+CONSTANT
+  SubBag <- MCSubBag
diff --git a/tlatools/test-model/suite/test23.tla b/tlatools/test-model/suite/test23.tla
new file mode 100644
index 0000000000000000000000000000000000000000..301039f8e8fe1f484e3c7edf5f0fdaa9556903fe
--- /dev/null
+++ b/tlatools/test-model/suite/test23.tla
@@ -0,0 +1,284 @@
+--------------- MODULE test23 -------------
+
+(* Test of definitions from the Bags module *)
+
+EXTENDS Naturals, FiniteSets, TLC
+
+IsABag(B) == 
+  (************************************************************************)
+  (* True iff B is a bag.                                                 *)
+  (************************************************************************)
+  B \in [DOMAIN B -> {n \in Nat : n > 0}]
+
+BagToSet(B) == DOMAIN B
+  (************************************************************************)
+  (* The set of elements at least one copy of which is in B.              *)
+  (************************************************************************)
+
+SetToBag(S) == [e \in S |-> 1]  
+  (************************************************************************)
+  (* The bag that contains one copy of every element of the set S.        *)
+  (************************************************************************)
+  
+BagIn(e,B) == e \in BagToSet(B)
+  (************************************************************************)
+  (* The \in operator for bags.                                           *)
+  (************************************************************************)
+
+EmptyBag == SetToBag({})
+
+BagCup(B1, B2)  ==
+  (************************************************************************)
+  (* The union of bags B1 and B2.                                         *)
+  (************************************************************************)
+  [e \in (DOMAIN B1) \cup (DOMAIN B2) |->
+      (IF e \in DOMAIN B1 THEN B1[e] ELSE 0) 
+    + (IF e \in DOMAIN B2 THEN B2[e] ELSE 0) ]
+
+B1 ++ B2 == BagCup(B1, B2)
+  
+BagDiff(B1, B2)  == 
+  (************************************************************************)
+  (* The bag B1 with the elements of B2 removed--that is, with one copy   *)
+  (* of an element removed from B1 for each copy of the same element in   *)
+  (* B2.  If B2 has at least as many copies of e as B1, then B1 -- B2    *)
+  (* has no copies of e.                                                  *)
+  (************************************************************************)
+  LET B == [e \in DOMAIN B1 |-> IF e \in DOMAIN B2 THEN B1[e] - B2[e]
+                                                   ELSE B1[e]]
+  IN  [e \in {d \in DOMAIN B : B[d] > 0} |-> B[e]]
+
+B1 -- B2 == BagDiff(B1, B2)
+
+LOCAL Sum(f) ==
+        (******************************************************************)
+        (* The sum of f[x] for all x in DOMAIN f.  The definition assumes *)
+        (* that f is a Nat-valued function and that f[x] equals 0 for all *)
+        (* but a finite number of elements x in DOMAIN f.                 *)
+        (******************************************************************)
+        LET D == {e \in DOMAIN f : f[e] > 0}
+            DSum[S \in SUBSET DOMAIN f] ==
+               LET elt == CHOOSE e \in S : TRUE
+               IN  IF S = {} THEN 0
+                             ELSE f[elt] + DSum[S \ {elt}]
+        IN  DSum[DOMAIN f]
+
+BagUnion(S) ==
+  (************************************************************************)
+  (* The bag union of all elements of the set S of bags.                  *)
+  (************************************************************************)
+  [e \in UNION {BagToSet(B) : B \in S} |->
+    Sum( [B \in S |-> IF BagIn(e, B) THEN B[e] ELSE 0] ) ]
+
+B1 \sqsubseteq B2  ==
+  (************************************************************************)
+  (* The subset operator for bags.  B1 \sqsubseteq B2 iff, for all e, bag *)
+  (* B2 has at least as many copies of e as bag B1 does.                  *)
+  (************************************************************************)
+  /\ (DOMAIN B1) \subseteq (DOMAIN B2)
+  /\ \A e \in DOMAIN B1 : B1[e] \leq B2[e]
+
+SubBag(B) ==
+  (************************************************************************)
+  (* The set of all subbags of bag B.                                     *)
+  (************************************************************************)
+  LET AllBagsOfSubset == 
+        (******************************************************************)
+        (* The set of all bags SB such that BagToSet(SB) \subseteq        *)
+        (* BagToSet(B).                                                   *)
+        (******************************************************************)
+        UNION {[SB -> {n \in Nat : n > 0}] : SB \in SUBSET BagToSet(B)}  
+  IN  {SB \in AllBagsOfSubset : \A e \in DOMAIN SB : SB[e] \leq B[e]}
+
+BagOfAll(F(_), B) ==
+  (************************************************************************)
+  (* The bag analog of the set {F(x) : x \in B} for a set B. It's the bag *)
+  (* that contains, for each element e of B, one copy of F(e) for every   *)
+  (* copy of e in B. This defines a bag iff, for any value v, the set of  *)
+  (* e in B such that F(e) = v is finite.                                 *)
+  (************************************************************************)
+  [e \in {F(d) : d \in BagToSet(B)} |-> 
+     Sum( [d \in BagToSet(B) |-> IF F(d) = e THEN B[d] ELSE 0] ) ]
+
+BagCardinality(B) ==
+  (************************************************************************)
+  (* If B is a finite bag (one such that BagToSet(B) is a finite set),    *)
+  (* then this is its cardinality (the total number of copies of elements *)
+  (* in B).  Its value is unspecified if B is infinite.                   *)
+  (************************************************************************)
+  Sum(B)
+
+CopiesIn(e, B) ==
+  (************************************************************************)
+  (* If B is a bag, then CopiesIn(e, B) is the number of copies of e in   *)
+  (* B. If ~BagIn(e, B), then CopiesIn(e, B) = 0.                         *)
+  (************************************************************************)
+  IF BagIn(e, B) THEN B[e] ELSE 0
+
+
+
+Max(a, b) == IF a < b THEN b ELSE a
+MaxCopies(B) ==
+  (*************************************************************************)
+  (* Maximum of B[e] s.t. e \in DOMAIN B                                   *)
+  (*************************************************************************)
+  LET MC[S \in SUBSET(DOMAIN B)] ==
+        LET elt == CHOOSE elt \in S : TRUE
+        IN  IF S = {} THEN 0
+                      ELSE Max(B[elt], MC[S \ {elt}])
+  IN MC[DOMAIN B]
+
+MCSubBag(B) ==
+  (*************************************************************************)
+  (* Definition of SubBag that TLC can handle.                             *)
+  (*************************************************************************)
+  LET AllBagsOfSubset == 
+        (******************************************************************)
+        (* The set of all bags SB such that BagToSet(SB) \subseteq        *)
+        (* BagToSet(B).                                                   *)
+        (******************************************************************)
+        UNION {[SB -> {n \in 1..MaxCopies(B) : n > 0}] : SB \in SUBSET BagToSet(B)}  
+  IN  {SB \in AllBagsOfSubset : \A e \in DOMAIN SB : SB[e] \leq B[e]}
+         
+VARIABLES x
+
+(* S == {"a"} *)
+S == {"a", "b", "c"} 
+
+B1 == SetToBag(S)
+B2 == BagCup(SetToBag(S), SetToBag({"a"}))
+B3 == [i \in S |-> 3]
+B4 == BagUnion({B1, B2, B3})
+B9 == BagCup(SetToBag(S), SetToBag({"a"}))
+
+F1(e) == CASE e = "a" -> "fa"
+           [] e = "b" -> "fa"
+           [] e = "c" -> "fc"
+           [] e = "d" -> "fc"
+
+Init == /\ Print("Computing Init", TRUE) 
+        /\ x = 1
+
+Next == UNCHANGED x
+
+Inv ==
+  /\ Print("Computing Inv", TRUE) 
+  /\ Print(<<"B1", B1>>, TRUE)
+  /\ Print(SetToBag({"a"}), TRUE)
+
+  /\ LET B7    == (DOMAIN B1) \cup (DOMAIN SetToBag({"a"})) 
+         e1(e, BB) == (IF e \in DOMAIN BB THEN BB[e] ELSE 0)
+     IN /\ IF B7 = {}
+            THEN Assert(FALSE, "Failed Test 0a")
+            ELSE Print("Test 0a OK", TRUE)
+
+        /\ IF [e \in B7 |-> 2]["a"] # 2
+             THEN Assert(FALSE, "Failed Test 0b")
+             ELSE Print("Test 0b OK", TRUE)
+
+        /\ IF [e \in B7 |-> e1(e, B1)]["a"] # 1
+             THEN Assert(FALSE, "Failed Test 0c")
+             ELSE Print("Test 0c OK", TRUE)
+
+        /\ IF [e \in B7 |-> e1(e, SetToBag({"a"}))]["a"] # 1
+             THEN Assert(FALSE, "Failed Test 0d")
+             ELSE Print("Test 0d OK", TRUE)
+
+        /\ IF [e \in B7 |-> e1(e, B1) + e1(e,SetToBag({"a"}))]["a"] # 2
+             THEN Assert(FALSE, "Failed Test 0e")
+             ELSE Print("Test 0e OK", TRUE)
+
+  /\ IF B9["a"] # 2
+       THEN Assert(FALSE, "Failed Test 1aa")
+       ELSE Print("Test 1aa OK", TRUE)
+  /\ Print("Finished test 1aa", TRUE)
+
+  /\ IF B2["a"] # 2
+       THEN Assert(FALSE, "Failed Test 1a")
+       ELSE Print("Test 1a OK", TRUE)
+  /\ Print("Finished test 1a", TRUE)
+
+  /\ IF BagToSet(B9) # S
+       THEN Assert(FALSE, "Failed Test 1b")
+       ELSE Print("Test 1b OK", TRUE)
+
+  /\ IF BagToSet(B2) # S
+       THEN Assert(FALSE, "Failed Test 1c")
+       ELSE Print("Test 1c OK", TRUE)
+
+  /\ IF BagToSet(B1 -- SetToBag({"a"})) # {"c", "b"}
+       THEN Assert(FALSE, "Failed Test 2")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF BagToSet(B3) # BagToSet(B1)
+       THEN Assert(FALSE, "Failed Test 3")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF BagIn("a", B1 -- SetToBag({"a"}))
+       THEN Assert(FALSE, "Failed Test 4")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF ~BagIn("a", B2)
+       THEN Assert(FALSE, "Failed Test 5")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF EmptyBag # B1 -- B1
+       THEN Assert(FALSE, "Failed Test 6")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF ~(B1 \sqsubseteq B2)
+       THEN Assert(FALSE, "Failed Test 7")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF ~(B2 \sqsubseteq B3)
+       THEN Assert(FALSE, "Failed Test 8")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF B2 \sqsubseteq B1
+       THEN Assert(FALSE, "Failed Test 9")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF BagCup(B1, SetToBag({"d"})) \sqsubseteq B1
+       THEN Assert(FALSE, "Failed Test 10")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF ~(B1 \sqsubseteq (B1 ++ SetToBag({"d"})) )
+       THEN Assert(FALSE, "Failed Test 11")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF BagToSet(B2 ++ SetToBag({"d"})) # S \cup {"d"}
+       THEN Assert(FALSE, "Failed Test 12")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF Cardinality(SubBag(B1)) # Cardinality(SUBSET S)
+       THEN Assert(FALSE, "Failed Test 13")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF Cardinality(SubBag(B3)) # 64
+       THEN Assert(FALSE, "Failed Test 14")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF CopiesIn("fa", BagOfAll(F1, B1)) # 2
+       THEN Assert(FALSE, "Failed Test 15")
+       ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF CopiesIn("a", BagOfAll(F1, B1)) # 0
+       THEN Assert(FALSE, "Failed Test 16")
+       ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF CopiesIn("fa", BagOfAll(F1, B3)) # 6
+       THEN Assert(FALSE, "Failed Test 17")
+       ELSE Print("Test 17 OK", TRUE)
+
+  /\ IF BagCardinality(B1) # 3
+       THEN Assert(FALSE, "Failed Test 18")
+       ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF BagCardinality(B3) # 9
+       THEN Assert(FALSE, "Failed Test 19")
+       ELSE Print("Test 19 OK", TRUE)
+
+==========================================================================
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test23bis.cfg b/tlatools/test-model/suite/test23bis.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6b7101b241f4a4d2db964521a677ea247a42e163
--- /dev/null
+++ b/tlatools/test-model/suite/test23bis.cfg
@@ -0,0 +1,5 @@
+INIT      Init
+NEXT      Next
+INVARIANT Inv
+CONSTANT
+  SubBag <- MCSubBag
diff --git a/tlatools/test-model/suite/test23bis.tla b/tlatools/test-model/suite/test23bis.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c8324c3c20a6582895b47c8015d9ed47cc972a3b
--- /dev/null
+++ b/tlatools/test-model/suite/test23bis.tla
@@ -0,0 +1,146 @@
+--------------- MODULE test23 -------------
+
+EXTENDS Naturals, FiniteSets, Bags, TLC
+
+VARIABLES x
+
+(* S == {"a"} *)
+S == {"a", "b", "c"} 
+
+B1 == SetToBag(S)
+B2 == BagCup(SetToBag(S), SetToBag({"a"}))
+B3 == [i \in S |-> 3]
+B4 == BagUnion({B1, B2, B3})
+B9 == BagCup(SetToBag(S), SetToBag({"a"}))
+
+F1(e) == CASE e = "a" -> "fa"
+           [] e = "b" -> "fa"
+           [] e = "c" -> "fc"
+           [] e = "d" -> "fc"
+
+Init == /\ Print("Computing Init", TRUE) 
+        /\ x = 1
+
+Next == UNCHANGED x
+
+Inv ==
+  /\ Print("Computing Inv", TRUE) 
+  /\ Print(<<"B1", B1>>, TRUE)
+  /\ Print(SetToBag({"a"}), TRUE)
+
+  /\ LET B7    == (DOMAIN B1) \cup (DOMAIN SetToBag({"a"})) 
+         e1(e, BB) == (IF e \in DOMAIN BB THEN BB[e] ELSE 0)
+     IN /\ IF B7 = {}
+            THEN Assert(FALSE, "Failed Test 0a")
+            ELSE Print("Test 0a OK", TRUE)
+
+        /\ IF [e \in B7 |-> 2]["a"] # 2
+             THEN Assert(FALSE, "Failed Test 0b")
+             ELSE Print("Test 0b OK", TRUE)
+
+        /\ IF [e \in B7 |-> e1(e, B1)]["a"] # 1
+             THEN Assert(FALSE, "Failed Test 0c")
+             ELSE Print("Test 0c OK", TRUE)
+
+        /\ IF [e \in B7 |-> e1(e, SetToBag({"a"}))]["a"] # 1
+             THEN Assert(FALSE, "Failed Test 0d")
+             ELSE Print("Test 0d OK", TRUE)
+
+        /\ IF [e \in B7 |-> e1(e, B1) + e1(e,SetToBag({"a"}))]["a"] # 2
+             THEN Assert(FALSE, "Failed Test 0e")
+             ELSE Print("Test 0e OK", TRUE)
+
+  /\ IF B9["a"] # 2
+       THEN Assert(FALSE, "Failed Test 1aa")
+       ELSE Print("Test 1aa OK", TRUE)
+  /\ Print("Finished test 1aa", TRUE)
+
+  /\ IF B2["a"] # 2
+       THEN Assert(FALSE, "Failed Test 1a")
+       ELSE Print("Test 1a OK", TRUE)
+  /\ Print("Finished test 1a", TRUE)
+
+  /\ IF BagToSet(B9) # S
+       THEN Assert(FALSE, "Failed Test 1b")
+       ELSE Print("Test 1b OK", TRUE)
+
+  /\ IF BagToSet(B2) # S
+       THEN Assert(FALSE, "Failed Test 1c")
+       ELSE Print("Test 1c OK", TRUE)
+
+  /\ IF BagToSet(B1 -- SetToBag({"a"})) # {"c", "b"}
+       THEN Assert(FALSE, "Failed Test 2")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF BagToSet(B3) # BagToSet(B1)
+       THEN Assert(FALSE, "Failed Test 3")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF BagIn("a", B1 -- SetToBag({"a"}))
+       THEN Assert(FALSE, "Failed Test 4")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF ~BagIn("a", B2)
+       THEN Assert(FALSE, "Failed Test 5")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF EmptyBag # B1 -- B1
+       THEN Assert(FALSE, "Failed Test 6")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF ~(B1 \sqsubseteq B2)
+       THEN Assert(FALSE, "Failed Test 7")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF ~(B2 \sqsubseteq B3)
+       THEN Assert(FALSE, "Failed Test 8")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF B2 \sqsubseteq B1
+       THEN Assert(FALSE, "Failed Test 9")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF BagCup(B1, SetToBag({"d"})) \sqsubseteq B1
+       THEN Assert(FALSE, "Failed Test 10")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF ~(B1 \sqsubseteq (B1 ++ SetToBag({"d"})) )
+       THEN Assert(FALSE, "Failed Test 11")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF BagToSet(B2 ++ SetToBag({"d"})) # S \cup {"d"}
+       THEN Assert(FALSE, "Failed Test 12")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF Cardinality(SubBag(B1)) # Cardinality(SUBSET S)
+       THEN Assert(FALSE, "Failed Test 13")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF Cardinality(SubBag(B3)) # 64
+       THEN Assert(FALSE, "Failed Test 14")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF CopiesIn("fa", BagOfAll(F1, B1)) # 2
+       THEN Assert(FALSE, "Failed Test 15")
+       ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF CopiesIn("a", BagOfAll(F1, B1)) # 0
+       THEN Assert(FALSE, "Failed Test 16")
+       ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF CopiesIn("fa", BagOfAll(F1, B3)) # 6
+       THEN Assert(FALSE, "Failed Test 17")
+       ELSE Print("Test 17 OK", TRUE)
+
+  /\ IF BagCardinality(B1) # 3
+       THEN Assert(FALSE, "Failed Test 18")
+       ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF BagCardinality(B3) # 9
+       THEN Assert(FALSE, "Failed Test 19")
+       ELSE Print("Test 19 OK", TRUE)
+
+==========================================================================
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test24.cfg b/tlatools/test-model/suite/test24.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd
--- /dev/null
+++ b/tlatools/test-model/suite/test24.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test24.tla b/tlatools/test-model/suite/test24.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9e24ff44d5f5d98b015f01bf310a2cf96da1ba5e
--- /dev/null
+++ b/tlatools/test-model/suite/test24.tla
@@ -0,0 +1,71 @@
+--------------- MODULE test24 -------------
+
+(* Test of UNCHANGED *)
+
+EXTENDS Naturals, TLC
+
+VARIABLES x, y
+
+Init == /\ x = [i \in 1..3 |-> i]
+        /\ y = 1
+Next == 
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ \A i \in 1..3 : (x[i])' = x[i]
+     /\ Print("Test 1 OK", TRUE)
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ \A i \in 1..3 : UNCHANGED x[i]
+     /\ Print("Test 2 OK", TRUE)
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ \A i \in 1..3 : UNCHANGED [a |-> x[i], b |-> x[i]+1]
+     /\ Print("Test 3 OK", TRUE)
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ IF UNCHANGED \E i \in 1..3 : x[i]=3
+          THEN Print("Test 4 OK", TRUE)
+          ELSE Assert(FALSE, "Test 4 Failed")
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ IF UNCHANGED \A i \in 1..3 : x[i]=3
+          THEN Print("Test 5 OK", TRUE)
+          ELSE Assert(FALSE, "Test 5 Failed")
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ IF UNCHANGED CHOOSE i \in DOMAIN x : x[i] = 2
+          THEN Print("Test 6 OK", TRUE)
+          ELSE Assert(FALSE, "Test 6 Failed")
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ IF UNCHANGED {x[i] : i \in 1..2}
+          THEN Print("Test 7 OK", TRUE)
+          ELSE Assert(FALSE, "Test 7 Failed")
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ IF UNCHANGED {i \in 3..5 : \E j \in 1..3 : x[j]=i}
+          THEN Print("Test 8 OK", TRUE)
+          ELSE Assert(FALSE, "Test 8 Failed")
+
+  \/ /\ UNCHANGED <<x, y>>
+     /\ IF UNCHANGED DOMAIN x
+          THEN Print("Test 9 OK", TRUE)
+          ELSE Assert(FALSE, "Test 9 Failed")
+
+  \/ /\ (y=1) /\ (y' = y+1)
+     /\ UNCHANGED <<x>>
+     /\ IF UNCHANGED {x}
+          THEN Print("Test 10 OK", TRUE)
+          ELSE Assert(FALSE, "Test 10 Failed")
+
+  \/ /\ (y=1) /\ (y' = y+1)
+     /\ UNCHANGED <<x>>
+     /\ IF UNCHANGED {1, 2, y}
+          THEN Print("Test 11 OK", TRUE)
+          ELSE Assert(FALSE, "Test 11 Failed")
+
+Inv == TRUE
+==========================================================================
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test25.cfg b/tlatools/test-model/suite/test25.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4ffaa30df7d616b53602ccd0633e211c7a778449
--- /dev/null
+++ b/tlatools/test-model/suite/test25.cfg
@@ -0,0 +1,12 @@
+  INIT Init
+  
+  NEXT Next
+
+  INVARIANT Inv
+
+  CONSTANT
+     s1 = s1
+     t1 = t1
+     S = {s1, s2, s3}
+     T = {t1, t2}
+     c = c
diff --git a/tlatools/test-model/suite/test25.tla b/tlatools/test-model/suite/test25.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f948d11468a1a99c96d324f902df107300c2d010
--- /dev/null
+++ b/tlatools/test-model/suite/test25.tla
@@ -0,0 +1,30 @@
+---------------------- MODULE test25 ----------------------
+
+(* test of fingerprinting elements of [S -> [B -> C]]  *)
+
+EXTENDS Naturals, Sequences, TLC
+
+CONSTANT S, T, s1, t1, c
+
+VARIABLES x, y, z, w
+
+Init ==  /\ Print("Should find only one state", TRUE)
+         /\ x = [i \in 1..3 |-> [j \in {4,5} |-> i+j]]
+         /\ y = [a |-> <<"b", "c">>, b |-> <<"c", "d", "e">>]
+         /\ z = [i \in S |-> [j \in {2,3} |-> j+1]]
+         /\ w = [i \in S |-> [j \in T |-> c]]
+
+Inv == TRUE
+
+Next == 
+  \/ UNCHANGED <<x, y, z, w>>
+
+  \/ /\ x' = << [j \in {4,5} |-> 1+j], [j \in {4,5} |-> 2+j], [j \in {4,5} |-> 3+j]>>
+     /\ y' = [i \in {"a", "b"} |->
+               IF i = "a" THEN [j \in {1,2} |-> IF j = 1 THEN "b" ELSE "c"]
+                          ELSE [<<"x", "d", "e">> EXCEPT ![1]= "c"]]
+     /\ z' = [[z EXCEPT ![s1] = [@ EXCEPT ![3] = 77]] EXCEPT ![s1][3] = 4]
+     /\ w' = [[w EXCEPT ![s1][t1] = s1] EXCEPT ![s1][t1] = c]
+      
+
+===========================================================
diff --git a/tlatools/test-model/suite/test26.cfg b/tlatools/test-model/suite/test26.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e2bf187389678424ffd4335584ec990172d1030d
--- /dev/null
+++ b/tlatools/test-model/suite/test26.cfg
@@ -0,0 +1,3 @@
+INIT      Init
+NEXT      Next
+INVARIANT Inv
diff --git a/tlatools/test-model/suite/test26.tla b/tlatools/test-model/suite/test26.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c5c569383bf9e97e33e8d3cebab1ca759fabe894
--- /dev/null
+++ b/tlatools/test-model/suite/test26.tla
@@ -0,0 +1,47 @@
+--------------- MODULE test26 -------------
+
+(* Test of LET *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+
+Init ==  x = 0
+
+FcnSubst(f, e(_)) ==
+  (* A recursive definition of [i \in DOMAIN f |-> e(i)] *)
+  LET Range == {f[i] : i \in DOMAIN f}
+      FSet == UNION { [U -> Range] : U \in SUBSET DOMAIN f}
+      FSub[g \in FSet] == 
+        IF g = << >> 
+          THEN g
+          ELSE LET el == CHOOSE el \in DOMAIN g : TRUE
+               IN  [ i \in DOMAIN g |-> 
+                     IF i = el 
+                      THEN e(i)
+                      ELSE FSub[[j \in (DOMAIN g) \ {el} |-> f[j]]][i]]
+  IN FSub[f]
+
+
+Inv == 
+
+  /\ LET A == {}
+         B == A
+     IN  IF B = {}
+           THEN Print("Test 1 OK", TRUE)
+           ELSE Assert(FALSE, "Test 1 Failed")
+
+  /\ LET A == {}
+         B == A
+         C[i \in B] == 17
+     IN  IF C = << >> 
+           THEN Print("Test 2 OK", TRUE)
+           ELSE Assert(FALSE, "Test 2 Failed")
+
+  /\ LET e(i) == i+1
+     IN  IF FcnSubst(<<1,2,3>>, e) = <<2,3,4>>
+           THEN Print("Test 3 OK", TRUE)
+           ELSE Assert(FALSE, "Test 3 Failed")
+           
+Next ==  UNCHANGED x
+=========================================
diff --git a/tlatools/test-model/suite/test27.cfg b/tlatools/test-model/suite/test27.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..1b488723acf2908759c2ebde0150fa06a8606089
--- /dev/null
+++ b/tlatools/test-model/suite/test27.cfg
@@ -0,0 +1,12 @@
+INIT  Init
+NEXT  Next
+INVARIANT Inv
+CONSTANT
+  Data          = {d1}
+  InitData      = {d1}
+  Addr          = {a1, a2}
+  N             = 2
+  InitData      = {d1}
+  InLen         = 2
+  OutLen        = 1
+  ND            = ND
diff --git a/tlatools/test-model/suite/test27.tla b/tlatools/test-model/suite/test27.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f6e7a57e64ab2d70606c70c0ab6d85850af373d9
--- /dev/null
+++ b/tlatools/test-model/suite/test27.tla
@@ -0,0 +1,121 @@
+--------------------------- MODULE test27 ----------------------------------
+(***************************************************************************)
+(* A fingerprint test that models LazyCache.                               *)
+(***************************************************************************)
+
+EXTENDS Naturals, Sequences, FiniteSets, TLC
+
+CONSTANT Addr,         (* A set                         *)
+         Data,         (* A set                         *)
+         InitData,     (* A subset of Data              *)
+         ND,           (* A model value                 *)
+         N,            (* The number of processors      *)
+         InLen, OutLen (* Maximum lengths of in and out *)
+
+VARIABLE mem, c, in, out
+
+Proc == 1..N
+
+
+Exp(a, m) ==
+  (*************************************************************************)
+  (* Recursive definition of a^n.                                          *)
+  (*************************************************************************)
+  LET E[n \in Nat] == IF n = 0 THEN 1 ELSE a * E[n-1]
+  IN  E[m]
+
+CountSeq(S, len) ==
+  (*************************************************************************)
+  (* Number of elements in Seq(S) of length \leq len.                      *)
+  (*************************************************************************)
+  LET CS[n \in Nat] == 
+        (*******************************************************************)
+        (* Recursive definition of CountSeq(S, n).  There are              *)
+        (* (Cardinality(S))^n sequences of length n.                       *)
+        (*******************************************************************)
+         IF n = 0 THEN 1
+                  ELSE Exp(Cardinality(S), n) + CS[n-1]       
+  IN  CS[len]
+
+
+Op  ==  (Data \X Addr) \cup {<<d, a, "*">> : d \in Data, a \in Addr}
+                            (***********************************************)
+                            (* Have to write Data \X Addr \X {"*"}         *)
+                            (* in this way.                                *)
+                            (***********************************************)
+                            
+Restrict(f) == 
+    { g \in  [Addr -> Data \cup  { ND  }] :
+                   \A  a \in  Addr: g[a] \in  { f[a] , ND  } } 
+
+Init == /\ Print(LET D == Cardinality(Data)
+                     A == Cardinality(Addr)
+                     P == Cardinality(Proc)
+                 IN <<"Number of distinct states =", 
+                        Exp(D, A)                    (* No. of values of mem *)
+                      * Exp(D+1, P * A)              (* No. of values of c   *)
+                      * Exp(CountSeq(Op, InLen), P)  (* No. of values of in  *)
+                      * Exp(CountSeq(Data \X Addr, OutLen), P) (* No. of values of out *)
+                    >>, TRUE)
+        /\ mem \in  [Addr -> InitData] 
+        /\ c \in  [Proc -> Restrict(mem)] 
+        /\ in  = [i \in  Proc |-> <<>>]    
+        /\ out = [i \in  Proc |-> <<>>]                  
+
+Inv ==
+  /\ IF mem \in [Addr -> Data] 
+       THEN TRUE
+       ELSE /\ Print("Conj 1 False", TRUE)
+            /\ FALSE
+  /\ IF c   \in [Proc -> [Addr -> (Data \cup {ND})] ] 
+       THEN TRUE
+       ELSE /\ Print("Conj 2 False", TRUE)
+            /\ FALSE
+  /\ IF in  \in [Proc -> Seq(Op)]
+       THEN TRUE
+       ELSE /\ Print("Conj 3 False", TRUE)
+            /\ FALSE
+  /\ IF \A p \in Proc: Len(in[p]) \leq InLen
+       THEN TRUE
+       ELSE /\ Print("Conj 4 False", TRUE)
+            /\ FALSE
+  /\ IF out \in [Proc -> Seq(Data \X Addr)] 
+       THEN TRUE
+       ELSE /\ Print("Conj 5 False", TRUE)
+            /\ FALSE
+  /\ IF \A p \in Proc: Len(out[p]) \leq OutLen
+       THEN TRUE
+       ELSE /\ Print("Conj 6 False", TRUE)
+            /\ FALSE
+
+Next ==
+  \/ UNCHANGED <<mem, c, in, out>>
+
+  \/ /\ \E a \in Addr, d \in Data : mem' = [mem EXCEPT ![a] = d]
+     /\ UNCHANGED <<c, in, out>>
+
+  \/ \E p \in Proc : 
+
+        \/ /\ \E a \in Addr,  d \in Data \cup {ND} :
+                  c' = [c EXCEPT ![p][a] = d]
+           /\ UNCHANGED <<mem,  in, out>>
+
+        \/ /\ Len(out[p]) < OutLen
+           /\ \E o \in Data \X Addr : out' = [out EXCEPT ![p] = @ \o <<o>>]
+           /\ UNCHANGED <<mem, c, in>>
+
+        \/ /\ Len(in[p]) < InLen
+           /\ \E o \in Op : in' = [in EXCEPT ![p] = @ \o <<o>>]
+           /\ UNCHANGED <<mem, c, out>>
+
+        \/ /\ out[p] # << >> 
+           /\ out' = [out EXCEPT ![p] = Tail(@)]
+           /\ UNCHANGED <<mem, c, in>>
+
+        \/ /\ in[p] # << >> 
+           /\ in' = [in EXCEPT ![p] = Tail(@)]
+           /\ UNCHANGED <<mem, c, out>>
+
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test28.cfg b/tlatools/test-model/suite/test28.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test28.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test28.tla b/tlatools/test-model/suite/test28.tla
new file mode 100644
index 0000000000000000000000000000000000000000..55b36d6e15561e17f719ae2cc07ed3e8490563c6
--- /dev/null
+++ b/tlatools/test-model/suite/test28.tla
@@ -0,0 +1,31 @@
+--------------- MODULE test28  -------------
+\* Some random test
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+
+BdedSeq(S, n) == UNION {[1..i -> S] : i \in 0..n}
+
+Init == /\ x = 2
+        /\ x > 1 
+        /\ Print("Test 1 OK", TRUE)
+        /\ 1..x = {1,2}
+        /\ Print("Test 2 OK", TRUE)
+        /\ {i + 1 : i \in {x}} = {3}
+        /\ Print("Test 3 OK", TRUE)
+        /\ {i \in {x} : i = 2} = {2}
+        /\ Print("Test 4 OK", TRUE)
+        /\ \E i \in {x} : i = 2
+        /\ Print("Test 5 OK", TRUE)
+        /\ \E i \in 1..x : i < x
+        /\ Print("Test 6 OK", TRUE)
+        /\ IF BdedSeq({x}, 2) # {<<>>, <<2>>, <<2,2>>}
+             THEN Print("Failed Test 7", TRUE)
+             ELSE Print("Test 7 OK", TRUE)
+
+Next == UNCHANGED x
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test29.cfg b/tlatools/test-model/suite/test29.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test29.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test29.tla b/tlatools/test-model/suite/test29.tla
new file mode 100644
index 0000000000000000000000000000000000000000..86a5fbbd934b994310ba0b5cb63343fc0e273330
--- /dev/null
+++ b/tlatools/test-model/suite/test29.tla
@@ -0,0 +1,30 @@
+--------------- MODULE test29  -------------
+
+\* Testing proper evaluation of primed variables in action
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+
+
+Init == x = 2
+
+Next == /\ x' = 2
+        /\ x' > 1 
+        /\ Print("Test 1 OK", TRUE)
+        /\ 1..x' = {1,2}
+        /\ Print("Test 2 OK", TRUE)
+        /\ {i + 1 : i \in {x'}} = {3}
+        /\ Print("Test 3 OK", TRUE)
+        /\ {i \in {x'} : i = 2} = {2}
+        /\ Print("Test 4 OK", TRUE)
+        /\ \E i \in {x'} : i = 2
+        /\ Print("Test 5 OK", TRUE)
+        /\ \E i \in 1..x' : i < x'
+        /\ Print("Test 6 OK", TRUE)
+
+
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test3.cfg b/tlatools/test-model/suite/test3.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d32cfa879ee7389879ef2ad6bdf490a5e474c707
--- /dev/null
+++ b/tlatools/test-model/suite/test3.cfg
@@ -0,0 +1,12 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT
+    a = a
+    b = b
+    c = c
diff --git a/tlatools/test-model/suite/test3.tla b/tlatools/test-model/suite/test3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cba6800e84ce49d26b2d17f42587e1514d1e18d5
--- /dev/null
+++ b/tlatools/test-model/suite/test3.tla
@@ -0,0 +1,121 @@
+--------------- MODULE test3 -------------
+
+(* Test of function application. *)
+
+EXTENDS Naturals, TLC
+
+VARIABLE x
+CONSTANT a, b, c
+
+Type == x \in {1}
+Inv  == 
+ /\ TRUE
+
+ /\ IF [i \in {3, 2, 1} |-> i+1][3] # 4
+      THEN Assert(FALSE, "Test 1")
+      ELSE Print("Test 1", TRUE)
+
+ /\ IF <<a, b, c>>[2] # b
+      THEN Assert(FALSE, "Test 2")
+      ELSE Print("Test 2", TRUE)
+
+ /\ IF [a |-> 1, b |-> 42, c |-> 99]["b"] # 42
+      THEN Assert(FALSE, "Test 3" )
+      ELSE Print("Test 3" , TRUE) 
+
+ /\ IF [i \in Nat |-> i+1][3] # 4
+      THEN Assert(FALSE, "Test 4" )
+      ELSE Print("Test 4" , TRUE) 
+
+ /\ IF [[i \in Nat |-> i+1] EXCEPT ![42] = 3, ![42] = 5][42] # 5
+      THEN Assert(FALSE, "Test 5" )
+      ELSE Print("Test 5" , TRUE) 
+
+ /\ IF [[i \in Nat |-> i+1] EXCEPT ![42] = 3, ![42] = 5][79] # 80
+      THEN Assert(FALSE, "Test 6" )
+      ELSE Print("Test 6" , TRUE) 
+
+ /\ IF [i \in Nat |-> [j \in Nat |-> 2*i + j]][5][3] # 13
+      THEN Assert(FALSE, "Test 7" )
+      ELSE Print("Test 7" , TRUE) 
+
+ /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] EXCEPT ![5] = 11][5] # 11
+      THEN Assert(FALSE, "Test 8" )
+      ELSE Print("Test 8" , TRUE) 
+ /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] EXCEPT ![5][3] = @+1][5][3] # 14
+      THEN Assert(FALSE, "Test 9" )
+      ELSE Print("Test 9" , TRUE) 
+
+ /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] 
+            EXCEPT ![5][4] = @+2, ![5][3] = @+1][5][3] # 14
+      THEN Assert(FALSE, "Test 10" )
+      ELSE Print("Test 10" , TRUE) 
+
+ /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] 
+            EXCEPT ![5][3] = @+1, ![5][4] = @+2][5][3] # 14
+      THEN Assert(FALSE, "Test 11" )
+      ELSE Print("Test 11" , TRUE) 
+
+ /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] 
+        EXCEPT ![5][3] = @+1, ![5][3] = @+2][5][3] # 16
+      THEN Assert(FALSE, "Test 12" )
+      ELSE Print("Test 12" , TRUE) 
+
+ /\ IF [i \in Nat, j \in STRING |-> <<i+1, j>>][22, "b"] # <<23, "b">>
+      THEN Assert(FALSE, "Test 13" )
+      ELSE Print("Test 13" , TRUE) 
+
+ /\ IF [i \in Nat, j \in STRING |-> <<i+1, j>>][<<22, "b">>] # <<23, "b">>
+      THEN Assert(FALSE, "Test 14" )
+      ELSE Print("Test 14" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+        EXCEPT ![22, "c"] = 15] [22, "b"] # <<23, "b">>
+      THEN Assert(FALSE, "Test 15" )
+      ELSE Print("Test 15" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+          EXCEPT ![22, "c"] = 15] [22, "c"] # 15
+      THEN Assert(FALSE, "Test 16" )
+      ELSE Print("Test 16" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+        EXCEPT ![22, "c"] = 15] [<<22, "c">>] # 15
+      THEN Assert(FALSE, "Test 17" )
+      ELSE Print("Test 17" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+          EXCEPT ![22, "c"] = 15, ![22, "d"] = 33] [22, "c"] # 15
+      THEN Assert(FALSE, "Test 18" )
+      ELSE Print("Test 18" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+          EXCEPT ![22, "d"] = 33, ![22, "c"] = 15] [22, "c"] # 15
+      THEN Assert(FALSE, "Test 19" )
+      ELSE Print("Test 19" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+          EXCEPT ![22, "d"] = 33, ![22, "d"] = 15] [22, "d"] # 15
+      THEN Assert(FALSE, "Test 20" )
+      ELSE Print("Test 20" , TRUE) 
+
+ /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] 
+        EXCEPT ![22, "d"] = @[1]] [22, "d"] # 23
+      THEN Assert(FALSE, "Test 21" )
+      ELSE Print("Test 21" , TRUE) 
+
+
+ /\ IF [[i \in Nat, j \in Nat |-> <<i+1, j>>] 
+         EXCEPT ![22, 3] = @[1]] [22, 3] # 23
+      THEN Assert(FALSE, "Test 22" )
+      ELSE Print("Test 22" , TRUE) 
+
+ /\ Print("Tests completed", TRUE)
+
+
+ /\ Print("Tests completed", TRUE)
+
+Init == x = 1
+
+Next == UNCHANGED x
+=========================================
diff --git a/tlatools/test-model/suite/test30-true.cfg b/tlatools/test-model/suite/test30-true.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..392d8232df0571fe24d200364209c46f8ae4dc1a
--- /dev/null
+++ b/tlatools/test-model/suite/test30-true.cfg
@@ -0,0 +1,15 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT 
+     P    <- PRep
+     ++   <- PlusPlus
+     PLen <- Len
+     Plus <- +
+     Seq  <- MCSeq
+     \o   <- MCCat
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test30-true.tla b/tlatools/test-model/suite/test30-true.tla
new file mode 100644
index 0000000000000000000000000000000000000000..39aa0937add02d93fbb437e307f2fc24759b7155
--- /dev/null
+++ b/tlatools/test-model/suite/test30-true.tla
@@ -0,0 +1,47 @@
+--------------- MODULE test31  -------------
+
+(* Test replacement of and by infix operators and overridden operators. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+
+CONSTANT P(_,_), _++_ , Plus(_, _), PLen(_)
+
+PlusPlus(a, b) == <<a, b>>
+
+PRep(a, b) == {a, b}
+
+MCSeq(a) = {a}
+
+MCCat(a, b) == a + b
+
+Init == /\ x = 1
+        /\ IF P(2, x+3) = {2, 4}
+             THEN Print("Test 1 OK", TRUE)
+             ELSE Assert(FALSE, "Test 1 Failed")
+        /\ IF (2++(x+3)) = <<2, 4>>
+             THEN Print("Test 2 OK", TRUE)
+             ELSE Assert(FALSE, "Test 2 Failed")
+        /\ IF PLen(<<1, 2, 3>>) = 3
+             THEN Print("Test 3 OK", TRUE)
+             ELSE Assert(FALSE, "Test 3 Failed")
+        /\ IF Plus(2, x+3) = 6
+             THEN Print("Test 4 OK", TRUE)
+             ELSE Assert(FALSE, "Test 4 Failed")
+        /\ IF Seq(22) = {22}
+             THEN Print("Test 5 OK", TRUE)
+             ELSE Assert(FALSE, "Test 5 Failed")
+        /\ IF 1 \o 2 = 3                          (* Huh???? *)
+             THEN Print("Test 6 OK", TRUE)
+             ELSE Assert(FALSE, "Test 6 Failed")
+        /\ LET a \prec b == a < b
+           IN  IF 1 \prec 2
+                 THEN Print("Test 7 OK", TRUE)
+                 ELSE Assert(FALSE, "Test 7 Failed")
+
+Next == UNCHANGED x
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test30.cfg b/tlatools/test-model/suite/test30.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..eecec9a4f071796a4236f4bf1bf5a1a1e115560e
--- /dev/null
+++ b/tlatools/test-model/suite/test30.cfg
@@ -0,0 +1,15 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT 
+     P    <- PRep
+     PLen <- Len
+     Seq  <- MCSeq
+     ++   <- PlusPlus
+     Plus <- + 
+     \o   <- +
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test30.tla b/tlatools/test-model/suite/test30.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9be35d89a743e1d9f919d6ded96a944984e268c1
--- /dev/null
+++ b/tlatools/test-model/suite/test30.tla
@@ -0,0 +1,47 @@
+--------------- MODULE test30  -------------
+
+(* Test replacement of and by infix operators and overridden operators. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+
+CONSTANT P(_,_), _++_ , Plus(_, _), PLen(_)
+
+PlusPlus(a, b) == <<a, b>>
+
+PRep(a, b) == {a, b}
+
+MCSeq(a) == {a}
+
+MCCat(a, b) == a + b
+
+Init == /\ x = 1
+        /\ IF P(2, x+3) = {2, 4}
+             THEN Print("Test 1 OK", TRUE)
+             ELSE Assert(FALSE, "Test 1 Failed")
+        /\ IF (2++(x+3)) = <<2, 4>>
+             THEN Print("Test 2 OK", TRUE)
+             ELSE Assert(FALSE, "Test 2 Failed")
+        /\ IF PLen(<<1, 2, 3>>) = 3
+             THEN Print("Test 3 OK", TRUE)
+             ELSE Assert(FALSE, "Test 3 Failed")
+        /\ IF Plus(2, x+3) = 6
+             THEN Print("Test 4 OK", TRUE)
+             ELSE Assert(FALSE, "Test 4 Failed")
+        /\ IF Seq(22) = {22}
+             THEN Print("Test 5 OK", TRUE)
+             ELSE Assert(FALSE, "Test 5 Failed")
+        /\ IF 1 \o 2 = 3                          (* Huh???? *)
+             THEN Print("Test 6 OK", TRUE)
+             ELSE Assert(FALSE, "Test 6 Failed")
+        /\ LET a \prec b == a < b
+           IN  IF 1 \prec 2
+                 THEN Print("Test 7 OK", TRUE)
+                 ELSE Assert(FALSE, "Test 7 Failed")
+
+Next == UNCHANGED x
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test31.cfg b/tlatools/test-model/suite/test31.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042
--- /dev/null
+++ b/tlatools/test-model/suite/test31.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test31.tla b/tlatools/test-model/suite/test31.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2b7a423e5886ff892a1ef38240362c49d658af25
--- /dev/null
+++ b/tlatools/test-model/suite/test31.tla
@@ -0,0 +1,20 @@
+--------------- MODULE test31  -------------
+
+EXTENDS TLC, Naturals, Sequences, FiniteSets
+
+VARIABLE x, y
+
+Init == /\ Print("This test should generate 3 distinct state", TRUE)
+        /\ x = 1
+        /\ y = 1
+
+Next == \/ /\ Print("1", TRUE)
+           /\ x' \in {1, 2, 3}
+           /\ y' = 1
+           /\ Print(<<"Generated state", x', y'>>, TRUE)
+
+        \/ UNCHANGED <<x, y>>
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test32.cfg b/tlatools/test-model/suite/test32.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042
--- /dev/null
+++ b/tlatools/test-model/suite/test32.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test32.tla b/tlatools/test-model/suite/test32.tla
new file mode 100644
index 0000000000000000000000000000000000000000..87a6664bf99a0b9f191409038712442737a9c418
--- /dev/null
+++ b/tlatools/test-model/suite/test32.tla
@@ -0,0 +1,32 @@
+--------------- MODULE test32  -------------
+
+(* Test of [A]_e and <<A>>_e *)
+
+EXTENDS TLC, Naturals, Sequences, FiniteSets
+
+VARIABLE x, y
+
+Init == /\ x = 1
+        /\ y = 1
+
+Act1 == /\ x' = 1
+        /\ y' = 1
+
+Next == \/ /\ UNCHANGED <<x, y>>
+           /\ IF [x # x]_<<x,y>>
+                THEN Print("Test1 OK", TRUE)
+                ELSE Assert(FALSE, "Test 1 Failed") 
+           /\ IF <<Act1>>_<<x,y>>
+                THEN Assert(FALSE, "Test 2 Failed")
+                ELSE Print("Test2 OK", TRUE)
+
+        \/ /\ <<x'= 1 /\ y'=1>>_y
+           /\ Assert(FALSE, "Test 3 Failed")
+
+        \/ /\ Print("Test 4 started -- should finish", TRUE)
+           /\ [FALSE]_<<x,y>>
+           /\ Print("Test 4 completed" , TRUE)
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/test33.cfg b/tlatools/test-model/suite/test33.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042
--- /dev/null
+++ b/tlatools/test-model/suite/test33.cfg
@@ -0,0 +1,9 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test33.tla b/tlatools/test-model/suite/test33.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9e003bb6c16936af9961f44fd48f0c48d1235449
--- /dev/null
+++ b/tlatools/test-model/suite/test33.tla
@@ -0,0 +1,23 @@
+--------------- MODULE test33  -------------
+
+(* Test of large sets of functions *)
+
+EXTENDS TLC, Naturals
+
+VARIABLE x
+
+Init == /\ Print(<<"Computing", 7^6, "States">>, TRUE)
+        /\ x \in [1..6 -> 1..7]   
+
+Next == UNCHANGED x
+
+Inv ==  TRUE
+=========================================
+
+Works for 7^6 = 117649 states on dix in     43.049u   3.729s  1:16.97 60.7%  
+                                           (1529 states/sec)
+Works for 6^7 = 279936 states on dix in    113.333u  19.494s  3:37.24 61.1%
+                                           (1289 states/sec)
+works for 7^7 = 823543 states on rowdy in  655.835u 158.050s 11:03.11 122.7%
+                                           (1242 states/sec)
+Doesn't work for 7^7 = 823543 states on dix
diff --git a/tlatools/test-model/suite/test34.cfg b/tlatools/test-model/suite/test34.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..df8f60f24b3d0c5ab136521162c93d401c5e7be4
--- /dev/null
+++ b/tlatools/test-model/suite/test34.cfg
@@ -0,0 +1,12 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT a = a
+           b = b
+           c = c
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test34.tla b/tlatools/test-model/suite/test34.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9dc008ae6c95f853eca91dc9f4dd8263026c4456
--- /dev/null
+++ b/tlatools/test-model/suite/test34.tla
@@ -0,0 +1,25 @@
+--------------- MODULE test34  -------------
+
+(* Test Model Values *)
+
+EXTENDS TLC, Naturals, Sequences, FiniteSets
+
+CONSTANTS a, b, c
+
+VARIABLE x
+
+Init == x = 1
+
+Next == UNCHANGED x
+
+Inv ==  /\ IF a \in STRING THEN Assert(FALSE, "Test 1 failed")
+                           ELSE Print("Test 1 OK", TRUE)
+        /\ IF a = {1, 2}   THEN Assert(FALSE, "Test 2 failed")
+                           ELSE Print("Test 2 OK", TRUE)
+        /\ IF a \in {i \in Nat : i > 7} 
+             THEN Assert(FALSE, "Test 3 failed")
+             ELSE Print("Test 3 OK", TRUE)
+        /\ IF a \in {b, c} THEN Assert(FALSE, "Test 4 failed")
+                           ELSE Print("Test 4 OK", TRUE)
+
+=========================================
diff --git a/tlatools/test-model/suite/test35.cfg b/tlatools/test-model/suite/test35.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test35.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test35.tla b/tlatools/test-model/suite/test35.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bae48a67643eb3f980d2c0880ad1ad91815c8e6f
--- /dev/null
+++ b/tlatools/test-model/suite/test35.tla
@@ -0,0 +1,96 @@
+------------------------------ MODULE test35 -----------------------------
+(* Test of standard Sequences module.    *)
+
+EXTENDS Sequences, Integers, TLC
+
+VARIABLES x
+
+Init == x = 0
+
+Next == UNCHANGED x
+
+Inv == 
+       /\ IF  <<1, 2, 3>> \in Seq(Nat)
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")
+
+       /\ IF  <<1, -2, 3>> \notin Seq(Nat)
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")        
+
+       /\ IF <<1, 2, 3>> \o <<4, 5>> = <<1, 2, 3, 4, 5>>
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF << >> \o << >> = << >>
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+       /\ IF << >> \o <<1, 2 >> = <<1, 2 >>
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5 Failed")
+
+       /\ IF Len(<< >>) = 0
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6 Failed")
+
+       /\ IF Len(<<1, 2, 3>>) = 3
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7 Failed")
+
+       /\ IF Append(<<1, 2, 3>>, 4) = <<1, 2, 3, 4>>
+            THEN Print("Test 8 OK", TRUE)
+            ELSE Assert(FALSE, "Test 8 Failed")
+
+       /\ IF Append(<< >>, "a") = <<"a">>
+            THEN Print("Test 9 OK", TRUE)
+            ELSE Assert(FALSE, "Test 9 Failed")
+
+       /\ IF Head(<< 1 >>) = 1
+            THEN Print("Test 10 OK", TRUE)
+            ELSE Assert(FALSE, "Test 10 Failed")
+
+       /\ IF Head(<<1, 2, 3>>) = 1
+            THEN Print("Test 11 OK", TRUE)
+            ELSE Assert(FALSE, "Test 11 Failed")
+
+       /\ IF Tail(<<1>>) = << >>
+            THEN Print("Test 12 OK", TRUE)
+            ELSE Assert(FALSE, "Test 12 Failed")
+
+       /\ IF Tail (<<1, 2, 3>>) = <<2, 3>>
+            THEN Print("Test 13 OK", TRUE)
+            ELSE Assert(FALSE, "Test 13 Failed")
+
+(*       /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 2, 4) = <<2, 3, 4>>
+            THEN Print("Test 14 OK", TRUE)
+            ELSE Assert(FALSE, "Test 14 Failed")  
+
+       /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 1, 1) = <<1>>
+            THEN Print("Test 15 OK", TRUE)
+            ELSE Assert(FALSE, "Test 15 Failed")   *)
+
+       /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 2, 1) = <<>>
+            THEN Print("Test 16 OK", TRUE)
+            ELSE Assert(FALSE, "Test 16 Failed")
+
+(*       /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 5, 5) = <<5>>
+            THEN Print("Test 17 OK", TRUE)
+            ELSE Assert(FALSE, "Test 17 Failed")   *)
+
+       /\ IF LET T(a) == a \in Nat
+             IN  SelectSeq(<<-1, 2, -3, 4, -5, 6>>, T) = <<2, 4, 6>>
+            THEN Print("Test 18 OK", TRUE)
+            ELSE Assert(FALSE, "Test 18 Failed")
+
+       /\ IF LET T(a) == a \in Nat
+             IN  SelectSeq(<<0, -1, 2, -3, 4, -5>>, T) = <<0, 2, 4>>
+            THEN Print("Test 19 OK", TRUE)
+            ELSE Assert(FALSE, "Test 19 Failed")
+
+       /\ IF LET T(a) == a \in Nat
+             IN  SelectSeq(<<-1, -3, -5>>, T) = << >>
+            THEN Print("Test 20 OK", TRUE)
+            ELSE Assert(FALSE, "Test 20 Failed")
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test36.cfg b/tlatools/test-model/suite/test36.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test36.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test36.tla b/tlatools/test-model/suite/test36.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fc4f689d0def508b0e72d6ec01ddccf30383e7bb
--- /dev/null
+++ b/tlatools/test-model/suite/test36.tla
@@ -0,0 +1,90 @@
+----------------------------- MODULE test36 --------------------------------
+
+(* Test of standard Bags module *)
+
+EXTENDS Naturals, TLC, Sequences, FiniteSets, Bags
+
+Bag1 == SetToBag({"a", "b"})
+Bag2 == Bag1 (+) SetToBag({"a"})
+
+VARIABLES x
+Init == x = 0
+Next == x'=x
+
+Inv ==
+       /\ IF IsABag( Bag1 )
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")
+
+       /\ IF BagToSet(Bag2) = {"a", "b"}
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")
+
+       /\ IF BagIn("a", Bag2)
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF ~BagIn("c", Bag2)
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+       /\ IF BagToSet(EmptyBag) = {}
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5 Failed")
+
+       /\ IF EmptyBag (+) Bag2 = Bag2
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6 Failed")
+
+       /\ IF Bag2 (-) SetToBag({"a"}) = Bag1
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7 Failed")             
+
+       /\ IF Bag1 (-) SetToBag({"a"}) = SetToBag({"b"})
+            THEN Print("Test 8 OK", TRUE)
+            ELSE Assert(FALSE, "Test 8 Failed")            
+
+       /\ IF BagUnion({Bag1, Bag2}) = Bag1 (+) Bag2
+            THEN Print("Test 9 OK", TRUE)
+            ELSE Assert(FALSE, "Test 9 Failed")         
+
+       /\ IF Bag1 \sqsubseteq Bag2
+            THEN Print("Test 10 OK", TRUE)
+            ELSE Assert(FALSE, "Test 10 Failed")   
+
+       /\ Print("TLC Fails Tests 11 & 12 because SubBag not implemented", TRUE)
+(*       /\ IF Bag1 \in SubBag(Bag2)
+            THEN Print("Test 11 OK", TRUE)
+            ELSE Assert(FALSE, "Test 11 Failed")
+
+       /\ IF Cardinality(SubBag(Bag2)) = 6
+            THEN Print("Test 12 OK", TRUE)
+            ELSE Assert(FALSE, "Test 12 Failed")  *)
+
+       /\ IF LET f(a) == <<"c", a>>
+             IN  CopiesIn(<<"c", "a">>, BagOfAll(f, Bag2)) = 2
+            THEN Print("Test 13 OK", TRUE)
+            ELSE Assert(FALSE, "Test 13 Failed")  
+
+       /\ IF BagCardinality(Bag2) = 3
+            THEN Print("Test 14 OK", TRUE)
+            ELSE Assert(FALSE, "Test 14 Failed")
+
+       /\ IF CopiesIn("a", Bag2) = 2
+            THEN Print("Test 15 OK", TRUE)
+            ELSE Assert(FALSE, "Test 15 Failed")
+
+       /\ IF CopiesIn("c", Bag2) = 0
+            THEN Print("Test 16 OK", TRUE)
+            ELSE Assert(FALSE, "Test 16 Failed")
+
+
+
+=============================================================================
+
+(* Last modified on Thu Aug 08 13:28:29 PT 2002 by lamport *)
+
+ 6 Apr 99 : Modified version for standard module set
+ 7 Dec 98 : Corrected error found by Stephan Merz.
+ 6 Dec 98 : Modified comments based on suggestions by Lyle Ramshaw.
+ 5 Dec 98 : Initial version.
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test37.cfg b/tlatools/test-model/suite/test37.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test37.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test37.tla b/tlatools/test-model/suite/test37.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bedc7c94e11d48293bf475f18968fec21770d45b
--- /dev/null
+++ b/tlatools/test-model/suite/test37.tla
@@ -0,0 +1,29 @@
+---------------------------- MODULE test37 -----------------------------
+
+(* Test of standard FiniteSets module *)
+
+EXTENDS FiniteSets, TLC
+
+VARIABLES x
+
+Init == x = 0
+Next == x'=x
+Inv  ==
+       /\ IF IsFiniteSet({"a", "b"})
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")
+
+       /\ IF IsFiniteSet({})
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")
+
+       /\ IF Cardinality({}) = 0
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF Cardinality({"a", "b"}) = 2
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test38.cfg b/tlatools/test-model/suite/test38.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..067eacc43100c3c5c3c09d818e2ed16510c3f573
--- /dev/null
+++ b/tlatools/test-model/suite/test38.cfg
@@ -0,0 +1,15 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+ CONSTANT c = c
+
+ INVARIANT Inv
+
+
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test38.tla b/tlatools/test-model/suite/test38.tla
new file mode 100644
index 0000000000000000000000000000000000000000..580c1ba6cfdf2bae4368c3e69a47d072d9063773
--- /dev/null
+++ b/tlatools/test-model/suite/test38.tla
@@ -0,0 +1,16 @@
+---------------------------- MODULE test38 -----------------------------
+
+(* Test of EXTENDS *)
+
+EXTENDS test38a, TLC
+
+VARIABLES x
+\* CONSTANT c
+
+Init == MInit(x)
+Next == x'=c
+Inv  == IF (x=c) THEN Print("Test OK", TRUE)
+                 ELSE Print("Test Failed", TRUE)
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test38a.tla b/tlatools/test-model/suite/test38a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..537c7c2415fba686c4fd29e3a4bc2ad6f6810923
--- /dev/null
+++ b/tlatools/test-model/suite/test38a.tla
@@ -0,0 +1,10 @@
+---------------------------- MODULE test38a -----------------------------
+
+(* Test of EXTENDS *)
+
+\* VARIABLES x    multiple declarations no longer allowed.
+ CONSTANT c
+
+MInit(x) == x = c
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test39.cfg b/tlatools/test-model/suite/test39.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0cc6c2124ed19c2b3250dc1dfa1ab569eca3c252
--- /dev/null
+++ b/tlatools/test-model/suite/test39.cfg
@@ -0,0 +1,13 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  CONSTRAINT Constraint
+
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test39.tla b/tlatools/test-model/suite/test39.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6e13785f161b10c66d29d5c8e1d8ca036d7ff516
--- /dev/null
+++ b/tlatools/test-model/suite/test39.tla
@@ -0,0 +1,36 @@
+--------------- MODULE test39 -------------
+
+(* Test priming and operator arguments *)
+
+EXTENDS TLC, Integers, Sequences, FiniteSets
+
+VARIABLE x, y
+
+Init == /\ x = [i \in {1,2} |-> i]
+        /\ y = 1
+
+Action1(c,d) == c' = [c EXCEPT ![1] = d']
+Action2(c,d) == c' = [c EXCEPT ![1] = d]
+
+
+Next ==  \/ /\ y = 1
+            /\ y' = 2
+            /\ Action1(x, y)
+            /\ IF x[1]' = 2
+                 THEN Print("Test1 OK", TRUE)
+                 ELSE Assert(FALSE, "Test1 Failed") 
+
+         \/ /\ y = 1
+            /\ y' = 2
+            /\ Action2(x, y)
+            /\ IF x[1]' = 1
+                 THEN Print("Test2 OK", TRUE)
+                 ELSE Assert(FALSE, "Test2 Failed")
+
+         \/ UNCHANGED <<x, y>>
+
+Inv ==  TRUE
+         
+
+Constraint == TRUE
+=========================================
diff --git a/tlatools/test-model/suite/test4.cfg b/tlatools/test-model/suite/test4.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test4.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test4.tla b/tlatools/test-model/suite/test4.tla
new file mode 100644
index 0000000000000000000000000000000000000000..74529b488e490a575ed3f055105325faa103a418
--- /dev/null
+++ b/tlatools/test-model/suite/test4.tla
@@ -0,0 +1,72 @@
+--------------- MODULE test4 -------------
+
+(* Test of fingerprinting of sets. *)
+
+EXTENDS Naturals, TLC, Sequences
+
+VARIABLE x, y, z, w
+
+Type == 
+  /\ x \in {{1, 2, 3}}
+  /\ y \in {{"a", "b", "c"}}
+  /\ z \in {[a : {1, 2, 3}, b : {1, 2, 3}, c : {1, 2, 3}]}
+  /\ w \in {[{1, 2, 3} -> {1, 2, 3}]}
+
+
+Init == 
+  /\ Print("Should find only one distinct state", TRUE)
+  /\ x = {1, 2, 3}
+  /\ y = {"a", "b", "c"}
+  /\ z = [a : {1, 2, 3}, b : {1, 2, 3}, c : {1, 2, 3}]
+  /\ w = [{1, 2, 3} -> {1, 2, 3}]
+
+Inv  == 
+  /\ TRUE
+  /\ x = {1, 2, 3}
+  /\ y = {"a", "b", "c"}
+  /\ z = [a : {1, 2, 3}, b : {1, 2, 3}, c : {1, 2, 3}]
+  /\ w = [{1, 2, 3} -> {1, 2, 3}]
+
+
+Next ==
+  \/ /\ x' = {3, 3, 2, 1}
+     /\ UNCHANGED <<y, z, w>>
+     /\ Print("Test 1", TRUE)
+
+  \/ /\ x' = 1..3
+     /\ UNCHANGED <<y, z, w>>
+     /\ Print("Test 2", TRUE)
+
+  \/ /\ x' = {i \in {5, 4, 3, 3, 2, 2, 1} : i \leq 3}
+     /\ UNCHANGED <<y, z, w>>
+     /\ Print("Test 2", TRUE)
+
+  \/ /\ x' = {i-3 : i \in 4..6}
+     /\ UNCHANGED <<y, z, w>>
+     /\ Print("Test 2", TRUE)
+
+  \/ /\ x' = {i-3 : i \in {6, 6, 5, 4, 4, 5, 5}}
+     /\ UNCHANGED <<y, z, w>>
+     /\ Print("Test 2", TRUE)
+
+  \/ /\ x' = { f[i] : i \in 1..3, f \in [{1,2,3} -> {1,2,3}] }
+     /\ UNCHANGED <<y, z, w>>
+     /\ Print("Test 3", TRUE)
+
+  \/ /\ y' = DOMAIN [a |-> 1, b |-> 2, c |-> 3]
+     /\ UNCHANGED <<x, z, w>>
+     /\ Print("Test 4", TRUE)
+  \/ /\ z' = [{"a", "b", "c"} -> {1, 2, 3}]
+     /\ UNCHANGED <<y, x, w>>
+     /\ Print("Test 5", TRUE)
+
+  \/ /\ w' = [{3,1, 2} -> {1, 3, 2}]
+     /\ UNCHANGED <<y, x, z>>
+     /\ Print("Test 6", TRUE)
+
+  \/ /\ w' = [{3,1, 3, 3, 3, 2} -> {2, 2, 1, 3, 2}]
+     /\ UNCHANGED <<y, x, z>>
+     /\ Print("Test 6", TRUE)
+
+
+============================================
diff --git a/tlatools/test-model/suite/test40.cfg b/tlatools/test-model/suite/test40.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test40.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test40.tla b/tlatools/test-model/suite/test40.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e4d631d9757c5abcbf85f142bfe9e4c7c31b2040
--- /dev/null
+++ b/tlatools/test-model/suite/test40.tla
@@ -0,0 +1,101 @@
+------------------------------ MODULE test40 -----------------------------
+(* Test of standard Naturals module.    *)
+
+EXTENDS Naturals, TLC
+
+VARIABLES x
+
+Init == x = 0
+
+Next == UNCHANGED x
+
+Inv == 
+       /\ IF  2^10 + 2^10 = 2^11
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")    
+
+       /\ IF 0^20 = 0
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")    
+
+       /\ IF 2^21 - 2^20 = 2^20
+            THEN Print("Test 2a OK", TRUE)
+            ELSE Assert(FALSE, "Test 2a Failed") 
+
+       /\ IF 2 - 3 \notin Nat
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF 123 * 345 = 42435
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+       /\ IF 123 < 124
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5 Failed")
+
+       /\ IF 12345 > 12344
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6 Failed")
+
+       /\ IF 123 \leq 123
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7 Failed")
+
+       /\ IF 12345 \geq 12345
+            THEN Print("Test 8 OK", TRUE)
+            ELSE Assert(FALSE, "Test 8 Failed")
+
+       /\ IF 123 \leq 124
+            THEN Print("Test 9 OK", TRUE)
+            ELSE Assert(FALSE, "Test 9 Failed")
+
+       /\ IF 12344 \geq 12333
+            THEN Print("Test 10 OK", TRUE)
+            ELSE Assert(FALSE, "Test 10 Failed")
+
+       /\ IF 145939 = 487 * (145939 \div 487) + (145939 % 487) 
+            THEN Print("Test 11 OK", TRUE)
+            ELSE Assert(FALSE, "Test 11 Failed")  
+
+       /\ IF 139982 \div 1 = 139982 
+            THEN Print("Test 12 OK", TRUE)
+            ELSE Assert(FALSE, "Test 12 Failed")
+
+       /\ IF 123099 % 1 = 0
+            THEN Print("Test 13 OK", TRUE)
+            ELSE Assert(FALSE, "Test 13 Failed")
+
+       /\ IF 0 % 345 = 0
+            THEN Print("Test 14 OK", TRUE)
+            ELSE Assert(FALSE, "Test 14 Failed")  
+
+       /\ IF 24 % 9 = 6
+            THEN Print("Test 15 OK", TRUE)
+            ELSE Assert(FALSE, "Test 15 Failed")   
+
+       /\ IF 4566799 = 423 * (4566799 \div 423) + (4566799 % 423)
+            THEN Print("Test 16 OK", TRUE)
+            ELSE Assert(FALSE, "Test 16 Failed")    
+
+       /\ IF 2222222 = 18 * (2222222 \div 18) + (2222222 % 18)
+            THEN Print("Test 17 OK", TRUE)
+            ELSE Assert(FALSE, "Test 17 Failed")   
+
+       /\ IF 3 .. 2 = {}
+            THEN Print("Test 18 OK", TRUE)
+            ELSE Assert(FALSE, "Test 18 Failed")
+
+       /\ IF 2..4 = {2, 3, 4}
+            THEN Print("Test 19 OK", TRUE)
+            ELSE Assert(FALSE, "Test 19 Failed")
+
+       /\ IF (0-1) * (0-3) = 3
+            THEN Print("Test 20 OK", TRUE)
+            ELSE Assert(FALSE, "Test 20 Failed")
+
+       /\ IF (0-1) % 3 = 2
+            THEN Print("Test 21 OK", TRUE)
+            ELSE Assert(FALSE, "Test 21 Failed")
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test41.cfg b/tlatools/test-model/suite/test41.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test41.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test41.tla b/tlatools/test-model/suite/test41.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3101ecb97f7f4712251af5cf238c5842afc4eae9
--- /dev/null
+++ b/tlatools/test-model/suite/test41.tla
@@ -0,0 +1,130 @@
+------------------------------ MODULE test41 -----------------------------
+(* Test of standard Integers module.    *)
+
+EXTENDS Integers, TLC
+
+VARIABLES x
+
+Init == x = 0
+
+Next == UNCHANGED x
+
+Inv == 
+       /\ IF  2^10 + 2^10 = 2^11
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")    
+
+       /\ IF 0^20 = 0
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")   
+
+       /\ IF 2 - 3 \in Int
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF (-123) * (-345) = 42435
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+       /\ IF -123 > -124
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5 Failed")
+
+       /\ IF -12345 < -12344
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6 Failed")
+
+       /\ IF -123 \leq -123
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7 Failed")
+
+       /\ IF -12345 \geq -12345
+            THEN Print("Test 8 OK", TRUE)
+            ELSE Assert(FALSE, "Test 8 Failed")
+
+       /\ IF -123 \geq -124
+            THEN Print("Test 9 OK", TRUE)
+            ELSE Assert(FALSE, "Test 9 Failed")
+
+       /\ IF -12344 \leq 12333
+            THEN Print("Test 10 OK", TRUE)
+            ELSE Assert(FALSE, "Test 10 Failed")
+
+       /\ IF -145939 = 487 * ((-145939) \div 487) + ((-145939) % 487) 
+            THEN Print("Test 11 OK", TRUE)
+            ELSE Assert(FALSE, "Test 11 Failed")    
+
+       /\ IF -139982 \div 1 = -139982 
+            THEN Print("Test 12 OK", TRUE)
+            ELSE Assert(FALSE, "Test 12 Failed")
+
+       /\ IF -123099 % 1 = 0
+            THEN Print("Test 13 OK", TRUE)
+            ELSE Assert(FALSE, "Test 13 Failed")
+
+       /\ IF (-1) % 345 = 344
+            THEN Print("Test 14 OK", TRUE)
+            ELSE Assert(FALSE, "Test 14 Failed")  
+
+       /\ IF (-24) % 9 = 3
+            THEN Print("Test 15 OK", TRUE)
+            ELSE Assert(FALSE, "Test 15 Failed")   
+
+       /\ IF -4566799 = 423 * ((-4566799) \div 423) + ((-4566799) % 423)
+            THEN Print("Test 16 OK", TRUE)
+            ELSE Assert(FALSE, "Test 16 Failed")    
+
+       /\ IF -2222222 = 18 * ((-2222222) \div 18) + (-2222222 % 18)
+            THEN Print("Test 17 OK", TRUE)
+            ELSE Assert(FALSE, "Test 17 Failed")   
+
+       /\ IF (-2) .. (-3) = {}
+            THEN Print("Test 18 OK", TRUE)
+            ELSE Assert(FALSE, "Test 18 Failed")
+
+       /\ IF (-4)..(-2) = {-2, -3, -4}
+            THEN Print("Test 19 OK", TRUE)
+            ELSE Assert(FALSE, "Test 19 Failed")
+
+       /\ IF 2*3+4 = 10
+            THEN Print("Test 20 OK", TRUE)
+            ELSE Assert(FALSE, "Test 20 Failed")
+
+       /\ IF 2^3*3 = 24
+            THEN Print("Test 21 OK", TRUE)
+            ELSE Assert(FALSE, "Test 21 Failed")
+
+
+       /\ IF 4+2*3 = 10
+            THEN Print("Test 22 OK", TRUE)
+            ELSE Assert(FALSE, "Test 22 Failed") 
+
+
+       /\ IF 3*2^3 = 24
+            THEN Print("Test 23 OK", TRUE)
+            ELSE Assert(FALSE, "Test 23 Failed")
+
+
+       /\ IF 1 + -2 = -1
+            THEN Print("Test 24 OK", TRUE)
+            ELSE Assert(FALSE, "Test 24 Failed")
+
+
+       /\ IF 5 + 5 \div 2 = 7
+            THEN Print("Test 25 OK", TRUE)
+            ELSE Assert(FALSE, "Test 25 Failed")
+
+       /\ IF  5 \div 2 + 2 = 4
+            THEN Print("Test 26 OK", TRUE)
+            ELSE Assert(FALSE, "Test 26 Failed") 
+
+       /\ IF 3 - 2 * 5 = -7
+            THEN Print("Test 27 OK", TRUE)
+            ELSE Assert(FALSE, "Test 27 Failed")
+
+       /\ IF 4 * 2 % 3 = 2
+            THEN Print("Test 28 OK", TRUE)
+            ELSE Assert(FALSE, "Test 28 Failed")
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test42.cfg b/tlatools/test-model/suite/test42.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test42.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test42.tla b/tlatools/test-model/suite/test42.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2768d3996b52860a848531b47edca7c6b9e69590
--- /dev/null
+++ b/tlatools/test-model/suite/test42.tla
@@ -0,0 +1,44 @@
+--------------- MODULE test42 -------------
+
+(* Test of operator arguments  *)
+
+EXTENDS TLC, Integers, Sequences
+
+VARIABLE x
+
+a ++ b == a + b
+
+Foo(a, _--_) == a -- 0
+
+
+Init == x = 1
+
+
+Next ==  x' = x
+         
+FooBar(a, Op(_,_)) == Op(a, 0)
+
+Plus(a, b) == a + b
+
+Inv ==  /\ IF FooBar(7, Plus) = 7
+             THEN Print("Test 1 OK", TRUE)
+             ELSE Assert(FALSE, "Test 1 Failed")  
+
+        /\ IF FooBar(7,+) = 7 
+             THEN Print("Test 2 OK", TRUE)
+             ELSE Assert(FALSE, "Test 2 Failed")  
+
+        /\ IF Foo(7, *) = 0 
+             THEN Print("Test 3 OK", TRUE)
+             ELSE Assert(FALSE, "Test 3 Failed")  
+
+        /\ IF Foo(7,+) = 7 
+             THEN Print("Test 4 OK", TRUE)
+             ELSE Assert(FALSE, "Test 4 Failed")  
+
+        /\ IF Foo(7,++) = 7 
+             THEN Print("Test 5 OK", TRUE)
+             ELSE Assert(FALSE, "Test 5 Failed")  
+
+Constraint == TRUE
+=========================================
diff --git a/tlatools/test-model/suite/test43.cfg b/tlatools/test-model/suite/test43.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test43.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test43.tla b/tlatools/test-model/suite/test43.tla
new file mode 100644
index 0000000000000000000000000000000000000000..65ed2fe94fc9b732312cf42bcc8cc21651f3f407
--- /dev/null
+++ b/tlatools/test-model/suite/test43.tla
@@ -0,0 +1,83 @@
+--------------- MODULE test43  -------------
+
+(* Test of TLC module *)
+
+EXTENDS TLC, Integers, Sequences
+
+VARIABLE x
+
+FApply(f, Op(_,_), Identity) ==
+  LET fa[S \in SUBSET DOMAIN f] ==
+        IF S = { } THEN Identity
+                   ELSE LET s == CHOOSE s \in S : TRUE
+                        IN  Op(f[s], fa[S \ {s}])
+                            
+  IN  fa[DOMAIN f]
+
+BoundedSeq(S, n) == UNION {[1..i -> S] : i \in 0..n}
+Plus(a, b) == a + b
+
+Times(a, b) == a * b
+
+Init == x = 1
+
+Next ==  x' = x
+
+Inv ==  
+
+        /\ IF FApply([i \in {"a", "b", "c"} |-> 3], Times, 1) = 27
+             THEN Print("Test 1 OK", TRUE)
+             ELSE Assert(FALSE, "Test 1 Failed")
+
+        /\ IF FApply(<<3, 4, 5, 6>> , Plus, 0) = 18
+             THEN Print("Test 2 OK", TRUE)
+             ELSE Assert(FALSE, "Test 2 Failed")
+        
+        /\ IF FApply(<<3>> , Plus, 0) = 3
+             THEN Print("Test 3 OK", TRUE)
+             ELSE Assert(FALSE, "Test 3 Failed")
+        
+        /\ IF FApply(<<>> , Plus, 7) = 7
+             THEN Print("Test 4 OK", TRUE)
+             ELSE Assert(FALSE, "Test 4 Failed")  
+
+        /\ IF <<1, 2>> \in BoundedSeq(1..3, 3)
+             THEN Print("Test 5 OK", TRUE)
+             ELSE Assert(FALSE, "Test 5 Failed")
+
+        /\ IF <<1, 2, 3>> \notin BoundedSeq(1..3, 2)
+             THEN Print("Test 6 OK", TRUE)
+             ELSE Assert(FALSE, "Test 6 Failed")         
+
+        /\ IF <<-1, 2>> \notin BoundedSeq(1..4, 6)
+             THEN Print("Test 7 OK", TRUE)
+             ELSE Assert(FALSE, "Test 7 Failed")
+
+        /\ IF << >> \in BoundedSeq({"a", "b"}, 2)
+             THEN Print("Test 8 OK", TRUE)
+             ELSE Assert(FALSE, "Test 8 Failed")
+
+        /\ IF \A s \in BoundedSeq({"a", "b", "c"}, 2) :
+                Len(s) \leq 2
+             THEN Print("Test 9 OK", TRUE)
+             ELSE Assert(FALSE, "Test 10 Failed")
+
+        /\ IF SortSeq(<<3, 2, 4, 2>>, \leq) = <<2, 2, 3, 4>>
+             THEN Print("Test 10 OK", TRUE)
+             ELSE Assert(FALSE, "Test 12 Failed")
+
+        /\ IF SortSeq(<<3, 2, 4, 2>>, <) = <<2, 2, 3, 4>>
+             THEN Print("Test 11 OK", TRUE)
+             ELSE Assert(FALSE, "Test 13 Failed")
+
+        /\ IF SortSeq(<<>>, >) = <<>>
+             THEN Print("Test 12 OK", TRUE)
+             ELSE Assert(FALSE, "Test 14 Failed")
+
+        /\ IF SortSeq(<<1>>, >) = <<1>>
+             THEN Print("Test 13 OK", TRUE)
+             ELSE Assert(FALSE, "Test 15 Failed")
+
+
+Constraint == TRUE
+=========================================
diff --git a/tlatools/test-model/suite/test44.cfg b/tlatools/test-model/suite/test44.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test44.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test44.tla b/tlatools/test-model/suite/test44.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5370ca0aa508575ac0fa71fe4c5bb12103e9469b
--- /dev/null
+++ b/tlatools/test-model/suite/test44.tla
@@ -0,0 +1,479 @@
+--------------- MODULE test44  -------------
+
+(* Test of operator precedence and associativity . *)
+
+EXTENDS TLC, Integers, Sequences
+
+VARIABLE x
+
+Init == x = 1
+
+
+Next ==  x' = x
+         
+
+a | b == <<a, b>>
+a || b == <<a, b>>
+a & b == <<a, b>>
+a && b == <<a, b>>
+a $ b == <<a, b>>
+a $$ b == <<a, b>>
+a ?? b == <<a, b>>
+a %% b == <<a, b>>
+a ## b == <<a, b>>
+a ++ b == <<a, b>>
+a -- b == <<a, b>>
+a ** b == <<a, b>>
+a (+) b == <<a, b>>
+a (-) b == <<a, b>>
+a (.) b == <<a, b>>
+
+a (\X) b == <<a, b>>         
+
+\* a \otimes b == <<a, b>> 
+a \uplus b == <<a, b>>
+a \sqcap b == <<a, b>>
+a \sqcup b == <<a, b>>
+a \star b == <<a, b>>
+a \bigcirc b == <<a, b>>
+a \bullet b == <<a, b>>
+
+a ... b == <<a, b>>
+a // b == <<a, b>>
+a ^^ b == <<a, b>>
+a !! b == <<a, b>>
+a |- b == <<a, b>>
+a |= b == <<a, b>>
+a -| b == <<a, b>>
+a =| b == <<a, b>>
+
+a <: b == <<a, b>> 
+
+
+a := b == <<a, b>>
+a ::= b == <<a, b>>
+a (/) b == <<a, b>>
+\* a \oslash b == <<a, b>> 
+a \wr b == <<a, b>>
+a \prec b == <<a, b>>
+a \succ b == <<a, b>>
+a \preceq b == <<a, b>>
+a \succeq b == <<a, b>>
+a \sim b == <<a, b>>
+a \simeq b == <<a, b>>
+a \ll b == <<a, b>>
+a \gg b == <<a, b>>
+a \asymp b == <<a, b>>
+a \subset b == <<a, b>>
+a \supset b == <<a, b>>
+a \supseteq b == <<a, b>>
+a \approx b == <<a, b>>
+a \cong b == <<a, b>>
+a \sqsubset b == <<a, b>>
+a \sqsupset b == <<a, b>>
+a \sqsubseteq b == <<a, b>>
+a \sqsupseteq b == <<a, b>>
+a \doteq b == <<a, b>>
+a \propto b == <<a, b>>
+
+
+Inv ==
+        /\ IF (3 - 2 - 1) = 0
+             THEN Print("Test 1 OK", TRUE)
+             ELSE Assert(FALSE, "Test 1 Failed")
+
+        /\ IF 1 | 2 | 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 2 OK", TRUE)
+             ELSE Assert(FALSE, "Test 2 Failed")
+
+        /\ IF 1 || 2 || 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 3 OK", TRUE)
+             ELSE Assert(FALSE, "Test 3 Failed")
+
+        /\ IF 1 & 2 & 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 4 OK", TRUE)
+             ELSE Assert(FALSE, "Test 4 Failed")
+
+        /\ IF 1 && 2 && 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 5 OK", TRUE)
+             ELSE Assert(FALSE, "Test 5 Failed")
+
+        /\ IF 1 $ 2 $ 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 6 OK", TRUE)
+             ELSE Assert(FALSE, "Test 6 Failed")
+
+        /\ IF 1 $$ 2 $$ 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 7 OK", TRUE)
+             ELSE Assert(FALSE, "Test 7 Failed")
+
+        /\ IF 1 ?? 2 ?? 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 8 OK", TRUE)
+             ELSE Assert(FALSE, "Test 8 Failed")
+
+        /\ IF TRUE
+             THEN Print("Test 9 OK", TRUE)
+             ELSE Assert(FALSE, "Test 9 Failed")
+
+        /\ IF 1 %% 2 %% 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 10 OK", TRUE)
+             ELSE Assert(FALSE, "Test 10 Failed")
+
+        /\ IF 1 ## 2 ## 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 11 OK", TRUE)
+             ELSE Assert(FALSE, "Test 11 Failed")
+
+        /\ IF 1 ++ 2 ++ 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 12 OK", TRUE)
+             ELSE Assert(FALSE, "Test 12 Failed")
+
+        /\ IF 1 -- 2 -- 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 13 OK", TRUE)
+             ELSE Assert(FALSE, "Test 13 Failed")
+
+        /\ IF 1 ** 2 ** 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 14 OK", TRUE)
+             ELSE Assert(FALSE, "Test 14 Failed")
+
+        /\ IF 1 (+) 2 (+) 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 15 OK", TRUE)
+             ELSE Assert(FALSE, "Test 15 Failed") 
+
+        /\ IF 1 (-) 2 (-) 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 16 OK", TRUE)
+             ELSE Assert(FALSE, "Test 16 Failed") 
+
+        /\ IF 1 (.) 2 (.) 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 17 OK", TRUE)
+             ELSE Assert(FALSE, "Test 17 Failed") 
+
+        /\ IF 1 (\X) 2 (\X) 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 18 OK", TRUE)
+             ELSE Assert(FALSE, "Test 18 Failed")
+
+        /\ IF 1 \oplus 2 \oplus 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 19 OK", TRUE)
+             ELSE Assert(FALSE, "Test 19 Failed")
+
+        /\ IF 1 \ominus 2 \ominus 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 20 OK", TRUE)
+             ELSE Assert(FALSE, "Test 20 Failed")
+
+        /\ IF 1 \odot 2 \odot 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 21 OK", TRUE)
+             ELSE Assert(FALSE, "Test 21 Failed")
+
+        /\ IF 1 \otimes 2 \otimes 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 22 OK", TRUE)
+             ELSE Assert(FALSE, "Test 22 Failed")
+
+        /\ IF 1 \uplus 2 \uplus 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 23 OK", TRUE)
+             ELSE Assert(FALSE, "Test 23 Failed")
+
+
+        /\ IF 1 \sqcap 2 \sqcap 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 24 OK", TRUE)
+             ELSE Assert(FALSE, "Test 24 Failed")
+
+        /\ IF 1 \sqcup 2 \sqcup 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 25 OK", TRUE)
+             ELSE Assert(FALSE, "Test 25 Failed")
+
+        /\ IF 1 \star 2 \star 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 26 OK", TRUE)
+             ELSE Assert(FALSE, "Test 26 Failed")
+
+        /\ IF <<1>> \o <<2>> \o <<3>> = <<1, 2, 3>>
+             THEN Print("Test 27 OK", TRUE)
+             ELSE Assert(FALSE, "Test 27 Failed")
+
+        /\ IF 1 \bigcirc 2 \bigcirc 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 28 OK", TRUE)
+             ELSE Assert(FALSE, "Test 28 Failed")
+
+        /\ IF 1 \bullet 2 \bullet 3 = <<<<1, 2>>, 3>>
+             THEN Print("Test 29 OK", TRUE)
+             ELSE Assert(FALSE, "Test 29 Failed")
+
+        /\ IF 1 ... 2 = <<1, 2>>
+             THEN Print("Test 30 OK", TRUE)
+             ELSE Assert(FALSE, "Test 30 Failed")
+
+        /\ IF 1 // 2 = <<1, 2>>
+             THEN Print("Test 31 OK", TRUE)
+             ELSE Assert(FALSE, "Test 31 Failed")
+
+        /\ IF 1 ^^ 2 = <<1, 2>>
+             THEN Print("Test 32 OK", TRUE)
+             ELSE Assert(FALSE, "Test 32 Failed")
+
+        /\ IF 1 !! 2 = <<1, 2>>
+             THEN Print("Test 33 OK", TRUE)
+             ELSE Assert(FALSE, "Test 33 Failed")
+
+        /\ IF (1 |- 2) = <<1, 2>>
+             THEN Print("Test 34 OK", TRUE)
+             ELSE Assert(FALSE, "Test 34 Failed")
+
+        /\ IF (1 |= 2) = <<1, 2>>
+             THEN Print("Test 35 OK", TRUE)
+             ELSE Assert(FALSE, "Test 35 Failed")
+
+        /\ IF (1 -| 2) = <<1, 2>>
+             THEN Print("Test 36 OK", TRUE)
+             ELSE Assert(FALSE, "Test 36 Failed")
+
+        /\ IF (1 =| 2) = <<1, 2>>
+             THEN Print("Test 37 OK", TRUE)
+             ELSE Assert(FALSE, "Test 37 Failed")
+
+        /\ IF 1 <: 2 = <<1, 2>>
+             THEN Print("Test 38 OK", TRUE)
+             ELSE Assert(FALSE, "Test 38 Failed")
+
+        /\ IF TRUE
+             THEN Print("Test 39 OK", TRUE)
+             ELSE Assert(FALSE, "Test 39 Failed")
+
+        /\ IF (1 := 2) = <<1, 2>>
+             THEN Print("Test 40 OK", TRUE)
+             ELSE Assert(FALSE, "Test 40 Failed")
+
+        /\ IF (1 ::= 2) = <<1, 2>>
+             THEN Print("Test 41 OK", TRUE)
+             ELSE Assert(FALSE, "Test 41 Failed")
+
+        /\ IF 1 (/) 2 = <<1, 2>>
+             THEN Print("Test 42 OK", TRUE)
+             ELSE Assert(FALSE, "Test 42 Failed") 
+
+        /\ IF 1 \oslash 2 = <<1, 2>> 
+             THEN Print("Test 43 OK", TRUE)
+             ELSE Assert(FALSE, "Test 43 Failed")
+
+        /\ IF 1 \wr 2 = <<1, 2>>
+             THEN Print("Test 44 OK", TRUE)
+             ELSE Assert(FALSE, "Test 44 Failed")
+
+        /\ IF (1 \prec 2) = <<1, 2>>
+             THEN Print("Test 45 OK", TRUE)
+             ELSE Assert(FALSE, "Test 45 Failed")
+
+        /\ IF (1 \succ 2) = <<1, 2>>
+             THEN Print("Test 46 OK", TRUE)
+             ELSE Assert(FALSE, "Test 46 Failed")
+
+        /\ IF (1 \preceq 2) = <<1, 2>>
+             THEN Print("Test 47 OK", TRUE)
+             ELSE Assert(FALSE, "Test 47 Failed")
+
+        /\ IF (1 \succeq 2) = <<1, 2>>
+             THEN Print("Test 48 OK", TRUE)
+             ELSE Assert(FALSE, "Test 48 Failed")
+
+        /\ IF (1 \sim 2) = <<1, 2>>
+             THEN Print("Test 49 OK", TRUE)
+             ELSE Assert(FALSE, "Test 49 Failed")
+
+        /\ IF (1 \simeq 2) = <<1, 2>>
+             THEN Print("Test 50 OK", TRUE)
+             ELSE Assert(FALSE, "Test 50 Failed")
+
+        /\ IF (1 \ll 2) = <<1, 2>>
+             THEN Print("Test 51 OK", TRUE)
+             ELSE Assert(FALSE, "Test 51 Failed")
+
+        /\ IF (1 \gg 2) = <<1, 2>>
+             THEN Print("Test 52 OK", TRUE)
+             ELSE Assert(FALSE, "Test 52 Failed")
+
+        /\ IF (1 \asymp 2) = <<1, 2>>
+             THEN Print("Test 53 OK", TRUE)
+             ELSE Assert(FALSE, "Test 53 Failed")
+
+        /\ IF (1 \subset 2) = <<1, 2>>
+             THEN Print("Test 54 OK", TRUE)
+             ELSE Assert(FALSE, "Test 54 Failed")
+
+        /\ IF (1 \supset 2) = <<1, 2>>
+             THEN Print("Test 55 OK", TRUE)
+             ELSE Assert(FALSE, "Test 55 Failed")
+
+        /\ IF (1 \supseteq 2) = <<1, 2>>
+             THEN Print("Test 56 OK", TRUE)
+             ELSE Assert(FALSE, "Test 56 Failed")
+
+        /\ IF (1 \approx 2) = <<1, 2>>
+             THEN Print("Test 57 OK", TRUE)
+             ELSE Assert(FALSE, "Test 57 Failed")
+
+        /\ IF (1 \cong 2) = <<1, 2>>
+             THEN Print("Test 58 OK", TRUE)
+             ELSE Assert(FALSE, "Test 58 Failed")
+
+        /\ IF (1 \sqsubset 2) = <<1, 2>>
+             THEN Print("Test 59 OK", TRUE)
+             ELSE Assert(FALSE, "Test 59 Failed")
+
+        /\ IF (1 \sqsupset 2) = <<1, 2>>
+             THEN Print("Test 60 OK", TRUE)
+             ELSE Assert(FALSE, "Test 60 Failed")
+
+        /\ IF (1 \sqsubseteq 2) = <<1, 2>>
+             THEN Print("Test 61 OK", TRUE)
+             ELSE Assert(FALSE, "Test 61 Failed")
+
+        /\ IF (1 \sqsupseteq 2) = <<1, 2>>
+             THEN Print("Test 62 OK", TRUE)
+             ELSE Assert(FALSE, "Test 62 Failed")
+
+        /\ IF (1 \doteq 2) = <<1, 2>>
+             THEN Print("Test 63 OK", TRUE)
+             ELSE Assert(FALSE, "Test 63 Failed")
+
+        /\ IF (1 \propto 2) = <<1, 2>>
+             THEN Print("Test 64 OK", TRUE)
+             ELSE Assert(FALSE, "Test 64 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = 1 & 2 ... 3
+             THEN Print("Test 65 OK", TRUE)
+             ELSE Assert(FALSE, "Test 65 Failed")
+
+
+        /\ IF << <<1, 2>>, 3 >> = 1 && 2 ... 3
+             THEN Print("Test 66 OK", TRUE)
+             ELSE Assert(FALSE, "Test 66 Failed")
+
+
+        /\ IF << <<1, 2>>, 3 >> = 1 ** 2 ... 3
+             THEN Print("Test 67 OK", TRUE)
+             ELSE Assert(FALSE, "Test 67 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = 1 // 2 ... 3
+             THEN Print("Test 68 OK", TRUE)
+             ELSE Assert(FALSE, "Test 68 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = 1 ^^ 2 ... 3
+             THEN Print("Test 69 OK", TRUE)
+             ELSE Assert(FALSE, "Test 69 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = 1 (/) 2 ... 3
+             THEN Print("Test 70 OK", TRUE)
+             ELSE Assert(FALSE, "Test 70 Failed")  
+
+        /\ IF << <<1, 2>>, 3 >> =  1 (.) 2 ... 3
+             THEN Print("Test 71 OK", TRUE)
+             ELSE Assert(FALSE, "Test 71 Failed")  
+
+        /\ IF << <<1, 2>>, 3 >> = 1 \star 2 ... 3
+             THEN Print("Test 72 OK", TRUE)
+             ELSE Assert(FALSE, "Test 72 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> =  1 \bigcirc  2 ... 3
+             THEN Print("Test 73 OK", TRUE)
+             ELSE Assert(FALSE, "Test 73 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> =  1 \bullet 2 ... 3
+             THEN Print("Test 74 OK", TRUE)
+             ELSE Assert(FALSE, "Test 74 Failed")
+
+
+        /\ IF << <<1, 2>>, 3 >> = (1 $ 2 \sim 3)
+             THEN Print("Test 75 OK", TRUE)
+             ELSE Assert(FALSE, "Test 75 Failed") 
+
+
+        /\ IF << <<1, 2>>, 3 >> = (1 $$ 2 \simeq 3)
+             THEN Print("Test 76 OK", TRUE)
+             ELSE Assert(FALSE, "Test 76 Failed") 
+
+
+        /\ IF << <<1, 2>>, 3 >> = (1 $ 2 \gg 3)
+             THEN Print("Test 77 OK", TRUE)
+             ELSE Assert(FALSE, "Test 77 Failed") 
+
+        /\ IF << <<1, 2>>, 3 >> = (1 -- 2 ++ 3)
+             THEN Print("Test 78 OK", TRUE)
+             ELSE Assert(FALSE, "Test 78 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = (1 -- 2 <: 3)
+             THEN Print("Test 79 OK", TRUE)
+             ELSE Assert(FALSE, "Test 79 Failed") 
+
+
+        /\ IF << <<1, 2>>, 3 >> = (1 !! 2 \ll 3)
+             THEN Print("Test 80 OK", TRUE)
+             ELSE Assert(FALSE, "Test 80 Failed") 
+
+        /\ IF << <<1, 2>>, 3 >> =  1 (-) 2 (+) 3
+             THEN Print("Test 81 OK", TRUE)
+             ELSE Assert(FALSE, "Test 81 Failed") 
+
+        /\ IF << <<1, 2>>, 3 >> = (1 ++ 2 |- 3)
+             THEN Print("Test 82 OK", TRUE)
+             ELSE Assert(FALSE, "Test 82 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = 1 (.) 2 (-) 3
+             THEN Print("Test 83 OK", TRUE)
+             ELSE Assert(FALSE, "Test 83 Failed")  
+
+        /\ IF << <<1, 2>>, 3 >> = (1 \bullet 2 \prec 3)
+             THEN Print("Test 84 OK", TRUE)
+             ELSE Assert(FALSE, "Test 84 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = (1 \uplus 2 \approx 3)
+             THEN Print("Test 85 OK", TRUE)
+             ELSE Assert(FALSE, "Test 85 Failed")
+
+
+        /\ IF << <<1, 2>>, 3 >> = (1 ++ 2 \sim 3)
+             THEN Print("Test 86 OK", TRUE)
+             ELSE Assert(FALSE, "Test 86 Failed")
+
+        /\ IF << <<1, 2>>, 3 >> = (1 (/) 2 (-) 3)
+             THEN Print("Test 87 OK", TRUE)
+             ELSE Assert(FALSE, "Test 87 Failed")  
+
+
+
+Constraint == TRUE
+=========================================
+
+<< {"..."},         "Infix", <<P.dotdot,   P.dotdot>>,   "none" >>,
+<< {"//"},          "Infix", <<P.times,    P.times>>,    "none" >>,
+<< {"^^"},          "Infix", <<P.exponent, P.exponent>>, "none" >>,
+<< {"!!"},          "Infix", <<P.dotdot,   P.times>>,    "none" >>,
+<< {"|-"},          "Infix", <<P.equals,   P.equals>>,   "none" >>,
+<< {"|="},          "Infix", <<P.equals,   P.equals>>,   "none" >>,
+<< {"-|"},          "Infix", <<P.equals,   P.equals>>,   "none" >>,
+<< {"=|"},          "Infix", <<P.equals,   P.equals>>,   "none" >>,
+<< {"<:"},          "Infix", <<P.colongt,  P.colongt>>,  "none" >>,
+<< {":>"},          "Infix", <<P.colongt,  P.colongt>>,  "none" >>,
+<< {":="},          "Infix", <<P.equals,   P.equals>>,   "none" >>,
+<< {"::="},         "Infix", <<P.equals,   P.equals>>,   "none" >>,
+<< {"(/)", "\oslash"}, "Infix", <<P.times, P.times>>, "none"  >>,
+<< {"\wr"},             "Infix", <<P.dotdot, P.exponent>>, "none" >>,
+<< {"\prec"},           "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\succ"},           "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\preceq"},         "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\succeq"},         "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\sim"},            "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\simeq"},          "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\ll"},             "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\gg"},             "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\asymp"},          "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\subset"},         "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\supset"},         "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\supseteq"},       "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\approx"},         "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\cong"},           "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\sqsubset"},       "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\sqsupset"},       "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\sqsubseteq"},     "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\sqsupseteq"},     "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\doteq"},          "Infix", <<P.equals, P.equals>>,   "none" >>,
+<< {"\propto"},         "Infix", <<P.equals, P.equals>>,   "none" >>
+
+
+
+
diff --git a/tlatools/test-model/suite/test45.cfg b/tlatools/test-model/suite/test45.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test45.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test45.tla b/tlatools/test-model/suite/test45.tla
new file mode 100644
index 0000000000000000000000000000000000000000..66d29e9fe2cda1723e527fde5992e8a5e32e0cef
--- /dev/null
+++ b/tlatools/test-model/suite/test45.tla
@@ -0,0 +1,92 @@
+------------------------------ MODULE test45 -----------------------------
+(* Test of recursive function definitions. *)
+
+EXTENDS Naturals, TLC
+
+VARIABLES x
+
+Init == x = 2
+
+Next == UNCHANGED x
+
+f[i \in 0..5] == [j \in 0..5 |-> 
+                    IF j = 0 
+                      THEN [k \in 0..i |-> 
+                              IF k = 0 THEN i
+                                       ELSE f[i][1] + 1]
+                      ELSE IF i = 0
+                             THEN j
+                             ELSE f[i-1][j] + 1 ]
+
+g[i \in 0..5] == [j \in 0..i |-> 
+                   IF j = 0 
+                     THEN IF i = 0 THEN 0
+                                   ELSE g[i-1][i-1]
+                     ELSE g[i][j-1]+x]
+
+
+h[i \in 0..3, j \in 0..3] == [k \in 0..3 |->
+                               IF k = 0
+                                 THEN IF j = 0 
+                                        THEN IF i = 0 THEN 0
+                                                      ELSE h[i-1,j][k] + 1
+                                        ELSE h[i,j-1][k]+1
+                                 ELSE h[i,j][k-1] + 1]
+
+(* g[0][0] = 0  
+   g[0][j] = j * x
+   g[i][j] = j * x + g[i-1][i-1] 
+           = (j + i-1) * x + g[i-2][i-2]
+           = (j + i + i-1) * x  + g[i-3][i-2] 
+           = ... *)
+
+Inv == /\ IF  f[4][3] = 7
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")    
+
+       /\ IF f[4][0][2] = 6
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")    
+
+       /\ IF DOMAIN g[3] = 0..3
+            THEN Print("Test 3  OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF DOMAIN f[5][0] = 0..5
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+       /\ IF g[0][0] = 0
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5 Failed")
+
+       /\ IF g[3][3] = 12
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6 Failed")
+
+       /\ IF g[3][1] = 8
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7 Failed")
+
+       /\ IF g[2][0] = 2
+            THEN Print("Test 8 OK", TRUE)
+            ELSE Assert(FALSE, "Test 8 Failed")
+
+       /\ IF DOMAIN h[1,2] = 0..3
+            THEN Print("Test 9 OK", TRUE)
+            ELSE Assert(FALSE, "Test 9 Failed")
+
+       /\ IF DOMAIN h = (0..3) \X (0..3)
+            THEN Print("Test 10 OK", TRUE)
+            ELSE Assert(FALSE, "Test 10 Failed")
+
+       /\ IF h[2, 3] = [i \in 0..3 |-> i+5]
+            THEN Print("Test 11 OK", TRUE)
+            ELSE Assert(FALSE, "Test 11 Failed")
+
+       /\ IF h[1, 2][3] = 6
+            THEN Print("Test 12 OK", TRUE)
+            ELSE Assert(FALSE, "Test 12 Failed")
+
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test46.cfg b/tlatools/test-model/suite/test46.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..29941a19ca7c8e1d69ca3edf5999b370184f8bb6
--- /dev/null
+++ b/tlatools/test-model/suite/test46.cfg
@@ -0,0 +1,13 @@
+CONSTANTS
+  Reg = {r1}
+  Adr = {a1}
+  Val = {v1,v2}
+  Proc = {p1, p2}
+
+INIT Init
+
+NEXT Next
+
+CONSTRAINT Constraint
+
+INVARIANT TypeInvariant
diff --git a/tlatools/test-model/suite/test46.tla b/tlatools/test-model/suite/test46.tla
new file mode 100644
index 0000000000000000000000000000000000000000..1968254c3759950d0ff42992aea0633029c169b6
--- /dev/null
+++ b/tlatools/test-model/suite/test46.tla
@@ -0,0 +1,49 @@
+--------------------- MODULE test46 -----------------------
+
+(* This found a bug in Version 1.03 of 18 Oct 1999 *)
+
+EXTENDS Naturals, Sequences, FiniteSets, TLC
+CONSTANT Reg, Adr, Val, Proc
+VARIABLE reqQ, reqOrder
+
+Request    == [adr : Adr, val : Val, op : {"Rd", "Wr"}]
+FreeRegVal == [adr : Adr, val : Val, op : {"Free"}]
+
+reqId == UNION { [proc : {p}, idx : DOMAIN reqQ[p]] : p \in Proc }
+
+oi \prec oj == <<oi, oj>> \in reqOrder
+
+TypeInvariant == 
+  /\ reqQ \in [Proc -> Seq(Request \cup [reg : Reg])]
+  /\ reqOrder \in SUBSET (reqId \X reqId)
+
+Init == /\ reqQ = [p \in Proc |-> << >>]
+        /\ reqOrder = {}
+
+GoodReqOrder ==
+  /\ \A oi, oj, ok \in reqId : (oi \prec oj) /\ (oj \prec ok) => (oi \prec ok)
+  /\ \A oi \in reqId : ~(oi \prec oi)
+  /\ \A oi, oj \in reqId : 
+        (oi.proc = oj.proc) /\ (oi.idx < oj.idx) => (oi \prec oj)
+
+-----------------------------------------------------------------------------
+UpdateReqOrder == 
+  /\ reqOrder' \in SUBSET(reqId' \X reqId')
+  /\ reqOrder \subseteq reqOrder'
+  /\ GoodReqOrder'
+
+IssueRequest(proc, req, reg) ==
+  (*************************************************************************)
+  (* The action by which processor proc issues request req in register     *)
+  (* reg.                                                                  *)
+  (*************************************************************************)
+  /\ reqQ' = [reqQ EXCEPT ![proc] = Append(@, [reg |-> reg])]
+  /\ UpdateReqOrder
+
+
+Next == \E proc \in Proc, reg \in Reg :
+           \E req \in Request : IssueRequest(proc, req, reg)
+
+
+Constraint == \A p \in Proc : Len(reqQ[p]) < 2
+=============================================================================
diff --git a/tlatools/test-model/suite/test47.cfg b/tlatools/test-model/suite/test47.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test47.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test47.tla b/tlatools/test-model/suite/test47.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ba1931feae621a40e0d124b820b5217cf591e2ab
--- /dev/null
+++ b/tlatools/test-model/suite/test47.tla
@@ -0,0 +1,12 @@
+------- MODULE test47 -----
+
+\* Checks EXTENDing of both Naturals and Integers
+
+EXTENDS test47a , Integers 
+
+Init == x = -1
+
+Next == x'= IF x = -3 THEN x ELSE x - 1
+
+Inv == TRUE
+==========================
diff --git a/tlatools/test-model/suite/test47a.tla b/tlatools/test-model/suite/test47a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6fcfebb7d549774fc7f5f52eae4fbcc40256a492
--- /dev/null
+++ b/tlatools/test-model/suite/test47a.tla
@@ -0,0 +1,7 @@
+------------- MODULE test47a  --------------
+
+EXTENDS Naturals, TLC
+
+
+VARIABLE x
+=============================================
diff --git a/tlatools/test-model/suite/test48.cfg b/tlatools/test-model/suite/test48.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/test48.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/test48.tla b/tlatools/test-model/suite/test48.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b2841590f92154b4a528b147e8d47276e49ad883
--- /dev/null
+++ b/tlatools/test-model/suite/test48.tla
@@ -0,0 +1,148 @@
+--------------- MODULE test48 -------------
+
+(* test definability of all user-definable infix, prefix, and postfix  
+   operators *)
+
+EXTENDS TLC
+
+VARIABLE x
+
+
+z - y == z
+z + y == z
+z * y == z
+z / y == z
+z ^ y == z
+z < y == z
+z > y == z
+z .. y == z
+z ... y == z
+z | y == z
+z || y == z
+z & y == z
+z && y == z
+z $ y == z
+z $$ y == z
+z ?? y == z
+z %% y == z
+z ## y == z
+z ++ y == z
+z -- y == z
+z ** y == z
+z // y == z
+z ^^ y == z
+\* z @@ y == z  \* THIS IS DEFINED IN THE TLC MODULE
+z !! y == z
+z |- y == z
+z |= y == z
+z -| y == z
+z =| y == z
+z <: y == z  
+\* z :> y == z  \* THIS IS DEFINED IN THE TLC MODULE
+z := y == z
+z ::= y == z
+z \uplus y == z
+z \sqcap y == z
+z \sqcup y == z
+z \div y == z
+z \wr y == z
+z \star y == z
+z \bigcirc y == z
+z \bullet y == z
+z \prec y == z
+z \succ y == z
+z \preceq y == z
+z \succeq y == z
+z \sim y == z
+z \simeq y == z
+z \ll y == z
+z \gg y == z
+z \asymp y == z
+z \subset y == z
+z \supset y == z
+z \supseteq y == z
+z \approx y == z  
+z \cong y == z
+z \sqsubset y == z
+z \sqsupset y == z
+z \sqsubseteq y == z
+z \sqsupseteq y == z
+z \doteq y == z
+z \propto y == z
+
+z ^+ == z
+z ^* == z
+z ^# == z
+-. z == z
+
+
+Init == 
+ /\ x = 0
+
+Next ==
+/\ x = 0
+/\ 
+   \/ x' = x - 1   
+   \/ x' = x + 1
+   \/ x' = x * 1
+   \/ x' = x / 1
+   \/ x' = x ^ 1
+   \/ x' = (x < 1)  
+   \/ x' = (x > 1)  
+   \/ x' = x .. 1
+   \/ x' = x ... 1
+   \/ x' = x | 1
+   \/ x' = x || 1
+   \/ x' = x & 1
+   \/ x' = x && 1
+   \/ x' = x $ 1
+   \/ x' = x $$ 1
+   \/ x' = x ?? 1
+   \/ x' = x %% 1
+   \/ x' = x ## 1
+   \/ x' = x ++ 1
+   \/ x' = x -- 1
+   \/ x' = x ** 1
+   \/ x' = x // 1
+   \/ x' = x ^^ 1
+   \/ x' = x !! 1
+   \/ x' = (x |- 1)
+   \/ x' = (x |= 1)
+   \/ x' = (x -| 1)
+   \/ x' = (x =| 1)
+   \/ x' = (x <: 1)
+   \/ x' = (x := 1)
+   \/ x' = (x ::= 1)
+   \/ x' = (x \uplus 1)
+   \/ x' = (x \sqcap 1)
+   \/ x' = (x \sqcup 1)
+   \/ x' = x \div 1
+   \/ x' = x \wr 1
+   \/ x' = x \star 1
+   \/ x' = x \bigcirc 1
+   \/ x' = x \bullet 1
+   \/ x' = (x \prec 1 )
+   \/ x' = (x \succ 1)
+   \/ x' = (x \preceq 1)
+   \/ x' = (x \succeq 1)
+   \/ x' = (x \sim 1)
+   \/ x' = (x \simeq 1)
+   \/ x' = (x \ll 1)
+   \/ x' = (x \gg 1)
+   \/ x' = (x \asymp 1)
+   \/ x' = (x \subset 1)
+   \/ x' = (x \supset 1)
+   \/ x' = (x \supseteq 1)
+   \/ x' = (x \approx 1)
+   \/ x' = (x \cong 1)
+   \/ x' = (x \sqsubset 1)
+   \/ x' = (x \sqsupset 1)
+   \/ x' = (x \sqsubseteq 1)
+   \/ x' = (x \sqsupseteq 1)
+   \/ x' = (x \doteq 1)
+   \/ x' = (x \propto 1)
+   \/ x' = x ^+ 
+   \/ x' = x ^* 
+   \/ x' = x ^# 
+   \/ x' = -x  
+=============================================================================
diff --git a/tlatools/test-model/suite/test49.cfg b/tlatools/test-model/suite/test49.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..21d01a242b550020aa2f6bbd1102bc40ef14e051
--- /dev/null
+++ b/tlatools/test-model/suite/test49.cfg
@@ -0,0 +1,3 @@
+INIT Init
+NEXT Next
+
diff --git a/tlatools/test-model/suite/test49.tla b/tlatools/test-model/suite/test49.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b14cd37f18677ded82092280125ebbc9d1fc47ac
--- /dev/null
+++ b/tlatools/test-model/suite/test49.tla
@@ -0,0 +1,36 @@
+--------------- MODULE test49 -------------
+
+(* test definability of operators with aliases *)
+
+
+EXTENDS TLC
+
+VARIABLE x
+
+u (+) v == u
+u (-) v == u
+u (.) v == u
+u (/) v == u
+u (\X) v == u 
+u \o v == u
+u % v == u
+u \leq  v == u
+u \geq v == u
+
+
+Init == 
+ /\ x = 0
+
+Next ==
+   /\ x = 0
+   /\ 
+      \/ x' = x (+) 1    
+      \/ x' = x (-) 1    
+      \/ x' = x (.) 1    
+      \/ x' = x (/) 1    
+      \/ x' = x (\X) 1   
+      \/ x' = x \o 1 
+      \/ x' = x % 1 
+      \/ x' = (x \leq  1) 
+      \/ x' = (x \geq 1 ) 
+=======================================
diff --git a/tlatools/test-model/suite/test49a.cfg b/tlatools/test-model/suite/test49a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/test49a.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/test49a.tla b/tlatools/test-model/suite/test49a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..49ae3003ae37d4e6391b9302d7733ef7c9c3508c
--- /dev/null
+++ b/tlatools/test-model/suite/test49a.tla
@@ -0,0 +1,39 @@
+--------------- MODULE test49a -------------
+
+(* test definability of operators with aliases *)
+
+
+EXTENDS TLC
+
+VARIABLE x
+
+u (+) v == u
+u (-) v == u
+u (.) v == u
+u (/) v == u
+u (\X) v == u \* parser can't cope with this
+u \o v == u
+u % v == u
+u \leq  v == u
+u \geq v == u
+
+
+Init == 
+ /\ x = 0
+ /\ Print("******************************************************", TRUE)
+ /\ Print("Several tests have been commented out because of bugs.", TRUE)
+ /\ Print("******************************************************", TRUE)
+
+Next ==
+   /\ x = 0
+   /\ 
+      \/ x' = x (+) 1    \* caused TLC bug
+      \/ x' = x (-) 1    \* caused TLC bug
+      \/ x' = x (.) 1    \* caused TLC bug
+      \/ x' = x (/) 1    \* caused TLC bug
+      \/ x' = x (\X) 1   \* parser can't cope with this.
+      \/ x' = x \o 1 
+      \/ x' = x % 1 
+      \/ x' = (x \leq  1) 
+      \/ x' = (x \geq 1 ) 
+=======================================
diff --git a/tlatools/test-model/suite/test49b.cfg b/tlatools/test-model/suite/test49b.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/test49b.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/test49b.tla b/tlatools/test-model/suite/test49b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c8ba49fee511f4e27e6e180f246053bceaae400e
--- /dev/null
+++ b/tlatools/test-model/suite/test49b.tla
@@ -0,0 +1,39 @@
+--------------- MODULE test49b -------------
+
+(* test definability of operators with aliases *)
+
+
+EXTENDS TLC
+
+VARIABLE x
+
+u \oplus v == u
+u \ominus v == u
+u \odot v == u
+u \oslash v == u
+u \otimes v == u 
+u \circ v == u
+u \mod v == u
+u =<  v == u
+u >= v == u
+
+
+Init == 
+ /\ x = 0
+ /\ Print("******************************************************", TRUE)
+ /\ Print("Several tests have been commented out because of bugs.", TRUE)
+ /\ Print("******************************************************", TRUE)
+
+Next ==
+   /\ x = 0
+   /\ 
+      \/ x' = x \oplus 1  
+      \/ x' = x \ominus 1  
+      \/ x' = x \odot 1  
+      \/ x' = x \oslash 1  
+      \/ x' = x \otimes 1 
+\*      \/ x' = x \circ 1 
+\*      \/ x' = x \mod 1   \* causes TLC bug
+\*      \/ x' = (x =<  1)  \* causes TLC bug 
+\*      \/ x' = (x >= 1 )  \* causes TLC bug 
+=======================================
diff --git a/tlatools/test-model/suite/test49c.cfg b/tlatools/test-model/suite/test49c.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/test49c.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/test49c.tla b/tlatools/test-model/suite/test49c.tla
new file mode 100644
index 0000000000000000000000000000000000000000..828c88c2dfccb916a3e61ff20d367b49112f075e
--- /dev/null
+++ b/tlatools/test-model/suite/test49c.tla
@@ -0,0 +1,39 @@
+--------------- MODULE test49c -------------
+
+(* test definability of operators with aliases *)
+
+
+EXTENDS TLC
+
+VARIABLE x
+
+u \oplus v == u
+u \ominus v == u
+u \odot v == u
+u \oslash v == u
+u \otimes v == u 
+u \o v == u
+u \mod v == u
+u =<  v == u
+u >= v == u
+
+
+Init == 
+ /\ x = 0
+ /\ Print("*************************************************************", TRUE)
+ /\ Print("Almost all the tests have been commented out because of bugs.", TRUE)
+ /\ Print("*************************************************************", TRUE)
+
+Next ==
+   /\ x = 0
+   /\ 
+\*      \/ x' = x (+) 1     \* causes TLC bug
+\*      \/ x' = x (-) 1     \* causes TLC bug
+\*      \/ x' = x (.) 1     \* causes TLC bug
+\*      \/ x' = x (/) 1     \* causes TLC bug
+\*      \/ x' = x (\X) 1  \* parser can't handle this
+\*      \/ x' = x \circ 1   \* causes TLC bug
+      \/ x' = x % 1   
+\*      \/ x' = (x =<  1)  \* TLC bug : sets x' to TRUE
+\*      \/ x' = (x >= 1 )    \* TLC bug : sets x' to FALSE
+=======================================
diff --git a/tlatools/test-model/suite/test5.cfg b/tlatools/test-model/suite/test5.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test5.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test5.tla b/tlatools/test-model/suite/test5.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ec9791eadf878ba2080f061af7d9f48a0292e466
--- /dev/null
+++ b/tlatools/test-model/suite/test5.tla
@@ -0,0 +1,113 @@
+--------------- MODULE test5 -------------
+
+(* test of Cartesian Product. *)
+
+EXTENDS Integers, TLC
+
+VARIABLE x
+Type == x \in {1}
+Init == x = 1
+Next == UNCHANGED x
+
+Inv  == 
+  /\ IF {1, 2} \X {"a", "b"} # {<<1, "a">>, <<2, "a">>, <<2, "b">>, <<1, "b">>}
+      THEN Assert(FALSE, "Test 1 failed")
+      ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF [p \in Nat \X Int |-> p[1] - 2*p[2]][2, -3] # 8
+      THEN Assert(FALSE, "Test 2 failed")
+      ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF [p \in Int \X Int |-> p[1] - 2*p[2]][2, -3] # 8
+      THEN Assert(FALSE, "Test 3 failed")
+      ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF <<1, 3>> \notin Nat \X Nat
+      THEN Assert(FALSE, "Test 4 failed")
+      ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF <<1, 3>> \notin (Int \X Int)
+      THEN Assert(FALSE, "Test 5 failed")
+      ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF [1..2 -> {1, 2, 3}] \cap ({1} \X {2}) # {<<1,2>>}
+      THEN Assert(FALSE, "Test 6 failed")
+      ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF {<<1, 4>>, <<2,3>>} \notin SUBSET ({1,2} \X {3,4})
+      THEN Assert(FALSE, "Test 7 failed")
+      ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF <<1,3>> \notin {1,2,3} \X {2,3,4}
+      THEN Assert(FALSE, "Test 8 failed")
+      ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF {1, 2} \X {"a", "b"} \X {"c"} # 
+           {<<1, "a", "c">>, <<2, "a", "c">>, <<2, "b", "c">>, <<1, "b", "c">>}
+      THEN Assert(FALSE, "Test 9 failed")
+      ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF [p \in Nat \X Int \X Nat |-> p[1] - 2*p[2] + p[3]][2, -3, 4] # 12
+      THEN Assert(FALSE, "Test 10 failed")
+      ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF [p \in Int \X Int \X Int |-> p[1] - 2*p[2] - p[3]][2, -3, -4] # 12
+      THEN Assert(FALSE, "Test 11 failed")
+      ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF <<1, 2, 3, 4>> \notin Nat \X Nat \X Nat \X Nat
+      THEN Assert(FALSE, "Test 12 failed")
+      ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF <<1, 2, 3, 4>> \notin (Int \X Int \X Int \X Int)
+      THEN Assert(FALSE, "Test 13 failed")
+      ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF [1..2 -> {1, 2, 3}] \cap ({1} \X {2}) # {<<1,2>>}
+      THEN Assert(FALSE, "Test 14 failed")
+      ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF {<<1, 4, 2>>, <<2,3,3>>} \notin SUBSET ({1,2} \X {3,4} \X {2, 3})
+      THEN Assert(FALSE, "Test 15 failed")
+      ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF <<1,3, 5>> \notin {1,2,3} \X {2,3,4} \X {5,6}
+      THEN Assert(FALSE, "Test 16 failed")
+      ELSE Print("Test 16 OK", TRUE)
+  /\ \* Test 17 added 4 June 2014
+     IF \/ {} \X {1,2} # {2,3,4} \X {}
+        \/ {1, 2} \X {} \X {3,4} # {2} \X {4,4} \X {}
+        \/ {1} \X {2} \X {} # {} \X {3,3}
+      THEN Assert(FALSE, "Test 17 failed")
+      ELSE Print("Test 17 OK", TRUE)
+  /\ \* Test 18 added 12 Jun 2014
+     IF \/ [a: {12}, b : {}] # [c: {22}, d: {xyz \in {1,2,3} : xyz < 0}]
+        \/ [a: {12}, b : {}] # [c: {22}, d: 2..1]
+        \/ [a: {12}, b : {}] # [c: {22}, d: {1,2,3} \cap 4..6]
+        \/ [a: {12}, b : {}] # [c: {22}, d: {} \cup {xz \in {1,2,3} : xz < 0}]
+        \/ [a: {12}, b : {}] # [c: {22}, d: {2,3,4} \X {}]
+        \/ [a: {12}, b : {}] # [c: {22}, d: [a: {}, b : {1}]]
+        \/ [a: {12}, b : {}] = [c: {22}, d: SUBSET {}]
+        \/ [a: {12}, b : {}] # [c: {22}, d: UNION {}]
+        \/ [a: {12}, b : {}] # [c: {22}, d: {2,3,4} \ 1..5]
+        \/ [a: {12}, b : {}] # [c: {22}, d: [{1,2,3} -> {}]]
+        \/ [a: {12}, b : {}] # [c: {22}, d: {}]
+        \/ [a: {12}, b : {}] # [c: {22}, d: {xyz+1 : xyz \in 2..1}]
+      THEN Assert(FALSE, "Test 18 failed")
+      ELSE Print("Test 18 OK", TRUE)
+  /\ \* Test 19 added 12 Jun 2014
+     IF \/ {1} \X {2} \X {} # {22} \X  {xyz \in {1,2,3} : xyz < 0}
+        \/ {1} \X {2} \X {} # {22} \X  (2..1)
+        \/ {1} \X {2} \X {} # {22} \X  ({1,2,3} \cap (4..6))
+        \/ {1} \X {2} \X {} # {22} \X ({} \cup {xz \in {1,2,3} : xz < 0})
+        \/ {1} \X {2} \X {} # {22} \X ({2,3,4} \X {})
+        \/ {1} \X {2} \X {} # {22} \X ([a: {}, b : {1}])
+        \/ {1} \X {2} \X {} = {22} \X (SUBSET {})
+        \/ {1} \X {2} \X {} # {22} \X (UNION {})
+        \/ {1} \X {2} \X {} # {22} \X ({2,3,4} \ (1..5))
+        \/ {1} \X {2} \X {} # {22} \X ([{1,2,3} -> {}])
+        \/ {1} \X {2} \X {} # {22} \X ({})
+        \/ {1} \X {2} \X {} # {22} \X ({xyz+1 : xyz \in 2..1})
+      THEN Assert(FALSE, "Test 19 failed")
+      ELSE Print("Test 19 OK", TRUE)
+=========================================
diff --git a/tlatools/test-model/suite/test50.cfg b/tlatools/test-model/suite/test50.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test50.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test50.tla b/tlatools/test-model/suite/test50.tla
new file mode 100644
index 0000000000000000000000000000000000000000..15eecda99a8928eba06fbe67bf6a3f835ea7bc11
--- /dev/null
+++ b/tlatools/test-model/suite/test50.tla
@@ -0,0 +1,53 @@
+--------------- MODULE test50 -------------
+
+(* test usability of the following aliases with the standard modules:
+   <=  and >=  of Naturals
+   \circ of Sequences
+   (+) and (-) of Bags 
+
+*)
+
+
+EXTENDS Naturals, Sequences, Bags, TLC
+
+Bag1 == SetToBag({"a", "b"})
+Bag2 == Bag1 \oplus  SetToBag({"a"}) \* causes TLC bug
+Bag3 == Bag2 \ominus Bag1
+Bag2a == Bag1 (+) SetToBag({"a"})
+Bag3a == Bag2 (-) Bag1
+
+
+VARIABLES x
+Init == x = 0
+Next == x'=x
+
+Inv == 
+       /\ IF Bag2 = Bag2a
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed") 
+
+       /\ IF Bag3 = Bag3a
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed") 
+
+       /\ IF <<"a">> \o <<"b">> = <<"a">> \circ <<"b">>
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")  
+    
+       /\ IF 1 \leq 2
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4  Failed") 
+
+       /\ IF 1 <= 2
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5  Failed") 
+    
+       /\ IF 2 \geq 1 
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6  Failed") 
+
+       /\ IF 2 >= 1 
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7  Failed") 
+
+=======================================
diff --git a/tlatools/test-model/suite/test51.cfg b/tlatools/test-model/suite/test51.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f
--- /dev/null
+++ b/tlatools/test-model/suite/test51.cfg
@@ -0,0 +1 @@
+ 
diff --git a/tlatools/test-model/suite/test51.tla b/tlatools/test-model/suite/test51.tla
new file mode 100644
index 0000000000000000000000000000000000000000..1c8976bc7e53c74505bc38652e13bbed4a9a708c
--- /dev/null
+++ b/tlatools/test-model/suite/test51.tla
@@ -0,0 +1,495 @@
+----------------------   MODULE test51 -----------------------
+\* Test of handling of infix operator symbols
+
+EXTENDS TLC
+
+\* Test parser on action/temporal infix operators
+
+FooA == INSTANCE test51a
+FooB == INSTANCE test51b
+
+TempTest(_+_) == TRUE + FALSE
+Foo1 == TempTest(-+->)
+Foo2 == TempTest(~>)
+Foo3 == TempTest(\cdot)
+
+\* Definining all undefined infix operators 
+
+a + b == {a, b}
+a - b == {a, b}
+a * b == {a, b}
+a / b == {a, b}
+a ..b == {a, b}
+a < b == {a, b}
+a > b == {a, b}
+a \leq b == {a, b}
+a \geq b == {a, b}
+a % b == {a, b}
+a ^b == {a, b}
+a \o b == {a, b}
+a ++ b == {a, b}
+a \div b == {a, b}
+a ...b == {a, b}
+a -- b == {a, b}
+a (+)b == {a, b}
+a (-)b == {a, b}
+a (\X)b == {a, b}
+a (/)b == {a, b}
+a (.)b == {a, b}
+a ** b == {a, b}
+a \sqcap b == {a, b}
+a // b == {a, b}
+a \prec b == {a, b}
+a \succ b == {a, b}
+a \preceq b == {a, b}
+a \succeq b == {a, b}
+a \sqcup b == {a, b}
+a ^^b == {a, b}
+a \ll b == {a, b}
+a \gg b == {a, b}
+a <: b == {a, b}
+a & b == {a, b}
+a && b == {a, b}
+a \sqsubset b == {a, b}
+a \sqsupset b == {a, b}
+a \sqsubseteq b == {a, b}
+a \sqsupseteq b == {a, b}
+a | b == {a, b}
+a || b == {a, b}
+a \subset b == {a, b}
+a \supset b == {a, b}
+a \supseteq b == {a, b}
+a \star b == {a, b}
+a %% b == {a, b}
+a |- b == {a, b}
+a -|b == {a, b}
+a |= b == {a, b}
+a =|b == {a, b}
+a \bullet b == {a, b}
+a ##b == {a, b}
+a \sim b == {a, b}
+a \simeq b == {a, b}
+a \approx b == {a, b}
+a \cong b == {a, b}
+a $b == {a, b}
+a $$b == {a, b}
+a := b == {a, b}
+a ::= b == {a, b}
+a \asymp b == {a, b}
+a \doteq b == {a, b}
+a ?? b == {a, b}
+a !! b == {a, b}
+a \propto b == {a, b}
+a \wr b == {a, b}
+a \uplus b == {a, b}
+a \bigcirc b == {a, b}
+
+TestOpArg(Foo(_,_)) == Foo("a", "b")
+
+TestTLCOps(colongt(_,_), atat(_,_)) ==
+   atat( colongt(1, "ab"), colongt(2, "cd"))
+ \* 1 :> "ab" @@ 2 :> "cd"
+
+TestDefinedSetOp(Foo(_,_)) == Foo({"a", "b"}, {"b", "c"})
+TestDefinedBooleanOp(Foo(_,_)) == Foo(TRUE,FALSE)
+TestIn(Foo(_,_)) == Foo("a", {"b", "c"})
+
+ASSUME
+  /\ IF TestOpArg(+ ) = {"a", "b"}
+       THEN Print("Test 1 OK", TRUE)
+       ELSE Assert(FALSE, "Test 1 Failed")
+  /\ IF TestOpArg( - ) = {"a", "b"}
+       THEN Print("Test 2 OK", TRUE)
+       ELSE Assert(FALSE, "Test 2 Failed")
+  /\ IF TestOpArg( * ) = {"a", "b"}
+       THEN Print("Test 3 OK", TRUE)
+       ELSE Assert(FALSE, "Test 3 Failed")
+  /\ IF TestOpArg(/ ) = {"a", "b"}
+       THEN Print("Test 4 OK", TRUE)
+       ELSE Assert(FALSE, "Test 4 Failed")
+  /\ IF TestOpArg(..) = {"a", "b"}
+       THEN Print("Test 5 OK", TRUE)
+       ELSE Assert(FALSE, "Test 5 Failed")
+  /\ IF TestOpArg(< ) = {"a", "b"}
+       THEN Print("Test 6 OK", TRUE)
+       ELSE Assert(FALSE, "Test 6 Failed")
+  /\ IF TestOpArg(> ) = {"a", "b"}
+       THEN Print("Test 7 OK", TRUE)
+       ELSE Assert(FALSE, "Test 7 Failed")
+  /\ IF TestOpArg(\leq ) = {"a", "b"}
+       THEN Print("Test 8 OK", TRUE)
+       ELSE Assert(FALSE, "Test 8 Failed")
+  /\ IF TestOpArg(=<) = {"a", "b"}
+       THEN Print("Test 9 OK", TRUE)
+       ELSE Assert(FALSE, "Test 9 Failed")
+  /\ IF TestOpArg(<=) = {"a", "b"}
+       THEN Print("Test 10 OK", TRUE)
+       ELSE Assert(FALSE, "Test 10 Failed")
+  /\ IF TestOpArg(\geq ) = {"a", "b"}
+       THEN Print("Test 11 OK", TRUE)
+       ELSE Assert(FALSE, "Test 11 Failed")
+  /\ IF TestOpArg(>=) = {"a", "b"}
+       THEN Print("Test 12 OK", TRUE)
+       ELSE Assert(FALSE, "Test 12 Failed")
+  /\ IF TestOpArg(% ) = {"a", "b"}
+       THEN Print("Test 13 OK", TRUE)
+       ELSE Assert(FALSE, "Test 13 Failed")
+  /\ IF TestOpArg(^) = {"a", "b"}
+       THEN Print("Test 14 OK", TRUE)
+       ELSE Assert(FALSE, "Test 14 Failed")
+  /\ IF TestOpArg(\circ ) = {"a", "b"}
+       THEN Print("Test 15 OK", TRUE)
+       ELSE Assert(FALSE, "Test 15 Failed")
+  /\ IF TestOpArg(\o) = {"a", "b"}
+       THEN Print("Test 16 OK", TRUE)
+       ELSE Assert(FALSE, "Test 16 Failed")
+  /\ IF TestOpArg(++ ) = {"a", "b"}
+       THEN Print("Test 17 OK", TRUE)
+       ELSE Assert(FALSE, "Test 17 Failed")
+  /\ IF TestOpArg(\div ) = {"a", "b"}
+       THEN Print("Test 18 OK", TRUE)
+       ELSE Assert(FALSE, "Test 18 Failed")
+  /\ IF TestOpArg(...) = {"a", "b"}
+       THEN Print("Test 19 OK", TRUE)
+       ELSE Assert(FALSE, "Test 19 Failed")
+  /\ IF TestOpArg(-- ) = {"a", "b"}
+       THEN Print("Test 20 OK", TRUE)
+       ELSE Assert(FALSE, "Test 20 Failed")
+  /\ IF TestOpArg(\oplus ) = {"a", "b"}
+       THEN Print("Test 21 OK", TRUE)
+       ELSE Assert(FALSE, "Test 21 Failed")
+  /\ IF TestOpArg((+)) = {"a", "b"}
+       THEN Print("Test 22 OK", TRUE)
+       ELSE Assert(FALSE, "Test 22 Failed")
+  /\ IF TestOpArg(\ominus ) = {"a", "b"}
+       THEN Print("Test 23 OK", TRUE)
+       ELSE Assert(FALSE, "Test 23 Failed")
+  /\ IF TestOpArg((-)) = {"a", "b"}
+       THEN Print("Test 24 OK", TRUE)
+       ELSE Assert(FALSE, "Test 24 Failed")
+  /\ IF TestOpArg(\otimes ) = {"a", "b"}
+       THEN Print("Test 25 OK", TRUE)
+       ELSE Assert(FALSE, "Test 25 Failed")
+  /\ IF TestOpArg((\X)) = {"a", "b"}
+       THEN Print("Test 26 OK", TRUE)
+       ELSE Assert(FALSE, "Test 26 Failed")
+  /\ IF TestOpArg(\oslash ) = {"a", "b"}
+       THEN Print("Test 27 OK", TRUE)
+       ELSE Assert(FALSE, "Test 27 Failed")
+  /\ IF TestOpArg((/)) = {"a", "b"}
+       THEN Print("Test 28 OK", TRUE)
+       ELSE Assert(FALSE, "Test 28 Failed")
+  /\ IF TestOpArg(\odot ) = {"a", "b"}
+       THEN Print("Test 29 OK", TRUE)
+       ELSE Assert(FALSE, "Test 29 Failed")
+  /\ IF TestOpArg((.)) = {"a", "b"}
+       THEN Print("Test 30 OK", TRUE)
+       ELSE Assert(FALSE, "Test 30 Failed")
+  /\ IF TestOpArg( ** ) = {"a", "b"}
+       THEN Print("Test 31 OK", TRUE)
+       ELSE Assert(FALSE, "Test 31 Failed")
+  /\ IF TestOpArg(\sqcap ) = {"a", "b"}
+       THEN Print("Test 32 OK", TRUE)
+       ELSE Assert(FALSE, "Test 32 Failed")
+  /\ IF TestOpArg(// ) = {"a", "b"}
+       THEN Print("Test 33 OK", TRUE)
+       ELSE Assert(FALSE, "Test 33 Failed")
+  /\ IF TestOpArg(\prec ) = {"a", "b"}
+       THEN Print("Test 34 OK", TRUE)
+       ELSE Assert(FALSE, "Test 34 Failed")
+  /\ IF TestOpArg(\succ ) = {"a", "b"}
+       THEN Print("Test 35 OK", TRUE)
+       ELSE Assert(FALSE, "Test 35 Failed")
+  /\ IF TestOpArg(\preceq ) = {"a", "b"}
+       THEN Print("Test 36 OK", TRUE)
+       ELSE Assert(FALSE, "Test 36 Failed")
+  /\ IF TestOpArg(\succeq ) = {"a", "b"}
+       THEN Print("Test 37 OK", TRUE)
+       ELSE Assert(FALSE, "Test 37 Failed")
+  /\ IF TestOpArg(\sqcup ) = {"a", "b"}
+       THEN Print("Test 38 OK", TRUE)
+       ELSE Assert(FALSE, "Test 38 Failed")
+  /\ IF TestOpArg(^^) = {"a", "b"}
+       THEN Print("Test 39 OK", TRUE)
+       ELSE Assert(FALSE, "Test 39 Failed")
+  /\ IF TestOpArg(\ll ) = {"a", "b"}
+       THEN Print("Test 40 OK", TRUE)
+       ELSE Assert(FALSE, "Test 40 Failed")
+  /\ IF TestOpArg(\gg ) = {"a", "b"}
+       THEN Print("Test 41 OK", TRUE)
+       ELSE Assert(FALSE, "Test 41 Failed")
+  /\ IF TestOpArg(<: ) = {"a", "b"}
+       THEN Print("Test 42 OK", TRUE)
+       ELSE Assert(FALSE, "Test 42 Failed")
+  /\ IF TestOpArg(& ) = {"a", "b"}
+       THEN Print("Test 43 OK", TRUE)
+       ELSE Assert(FALSE, "Test 43 Failed")
+  /\ IF TestOpArg(&& ) = {"a", "b"}
+       THEN Print("Test 44 OK", TRUE)
+       ELSE Assert(FALSE, "Test 44 Failed")
+  /\ IF TestOpArg(\sqsubset ) = {"a", "b"}
+       THEN Print("Test 45 OK", TRUE)
+       ELSE Assert(FALSE, "Test 45 Failed")
+  /\ IF TestOpArg(\sqsupset ) = {"a", "b"}
+       THEN Print("Test 46 OK", TRUE)
+       ELSE Assert(FALSE, "Test 46 Failed")
+  /\ IF TestOpArg(\sqsubseteq ) = {"a", "b"}
+       THEN Print("Test 47 OK", TRUE)
+       ELSE Assert(FALSE, "Test 47 Failed")
+  /\ IF TestOpArg(\sqsupseteq ) = {"a", "b"}
+       THEN Print("Test 48 OK", TRUE)
+       ELSE Assert(FALSE, "Test 48 Failed")
+  /\ IF TestOpArg(| ) = {"a", "b"}
+       THEN Print("Test 49 OK", TRUE)
+       ELSE Assert(FALSE, "Test 49 Failed")
+  /\ IF TestOpArg(|| ) = {"a", "b"}
+       THEN Print("Test 50 OK", TRUE)
+       ELSE Assert(FALSE, "Test 50 Failed")
+  /\ IF TestOpArg(\subset ) = {"a", "b"}
+       THEN Print("Test 51 OK", TRUE)
+       ELSE Assert(FALSE, "Test 51 Failed")
+  /\ IF TestOpArg(\supset ) = {"a", "b"}
+       THEN Print("Test 52 OK", TRUE)
+       ELSE Assert(FALSE, "Test 52 Failed")
+  /\ IF TestOpArg(\supseteq ) = {"a", "b"}
+       THEN Print("Test 53 OK", TRUE)
+       ELSE Assert(FALSE, "Test 53 Failed")
+  /\ IF TestOpArg(\star ) = {"a", "b"}
+       THEN Print("Test 54 OK", TRUE)
+       ELSE Assert(FALSE, "Test 54 Failed")
+  /\ IF TestOpArg(%% ) = {"a", "b"}
+       THEN Print("Test 55 OK", TRUE)
+       ELSE Assert(FALSE, "Test 55 Failed")
+  /\ IF TestOpArg(|- ) = {"a", "b"}
+       THEN Print("Test 56 OK", TRUE)
+       ELSE Assert(FALSE, "Test 56 Failed")
+  /\ IF TestOpArg(-|) = {"a", "b"}
+       THEN Print("Test 57 OK", TRUE)
+       ELSE Assert(FALSE, "Test 57 Failed")
+  /\ IF TestOpArg(|= ) = {"a", "b"}
+       THEN Print("Test 58 OK", TRUE)
+       ELSE Assert(FALSE, "Test 58 Failed")
+  /\ IF TestOpArg(=|) = {"a", "b"}
+       THEN Print("Test 59 OK", TRUE)
+       ELSE Assert(FALSE, "Test 59 Failed")
+  /\ IF TestOpArg(\bullet ) = {"a", "b"}
+       THEN Print("Test 60 OK", TRUE)
+       ELSE Assert(FALSE, "Test 60 Failed")
+  /\ IF TestOpArg(##) = {"a", "b"}
+       THEN Print("Test 61 OK", TRUE)
+       ELSE Assert(FALSE, "Test 61 Failed")
+  /\ IF TestOpArg(\sim ) = {"a", "b"}
+       THEN Print("Test 62 OK", TRUE)
+       ELSE Assert(FALSE, "Test 62 Failed")
+  /\ IF TestOpArg(\simeq ) = {"a", "b"}
+       THEN Print("Test 63 OK", TRUE)
+       ELSE Assert(FALSE, "Test 63 Failed")
+  /\ IF TestOpArg(\approx ) = {"a", "b"}
+       THEN Print("Test 64 OK", TRUE)
+       ELSE Assert(FALSE, "Test 64 Failed")
+  /\ IF TestOpArg(\cong ) = {"a", "b"}
+       THEN Print("Test 65 OK", TRUE)
+       ELSE Assert(FALSE, "Test 65 Failed")
+  /\ IF TestOpArg($) = {"a", "b"}
+       THEN Print("Test 66 OK", TRUE)
+       ELSE Assert(FALSE, "Test 66 Failed")
+  /\ IF TestOpArg($$) = {"a", "b"}
+       THEN Print("Test 67 OK", TRUE)
+       ELSE Assert(FALSE, "Test 67 Failed")
+  /\ IF TestOpArg(:= ) = {"a", "b"}
+       THEN Print("Test 68 OK", TRUE)
+       ELSE Assert(FALSE, "Test 68 Failed")
+  /\ IF TestOpArg(::= ) = {"a", "b"}
+       THEN Print("Test 69 OK", TRUE)
+       ELSE Assert(FALSE, "Test 69 Failed")
+  /\ IF TestOpArg(\asymp ) = {"a", "b"}
+       THEN Print("Test 70 OK", TRUE)
+       ELSE Assert(FALSE, "Test 70 Failed")
+  /\ IF TestOpArg(\doteq ) = {"a", "b"}
+       THEN Print("Test 71 OK", TRUE)
+       ELSE Assert(FALSE, "Test 71 Failed")
+  /\ IF TestOpArg(?? ) = {"a", "b"}
+       THEN Print("Test 72 OK", TRUE)
+       ELSE Assert(FALSE, "Test 72 Failed")
+  /\ IF TestOpArg(!! ) = {"a", "b"}
+       THEN Print("Test 73 OK", TRUE)
+       ELSE Assert(FALSE, "Test 73 Failed")
+  /\ IF TestOpArg(\propto ) = {"a", "b"}
+       THEN Print("Test 74 OK", TRUE)
+       ELSE Assert(FALSE, "Test 74 Failed")
+  /\ IF TestOpArg(\wr ) = {"a", "b"}
+       THEN Print("Test 75 OK", TRUE)
+       ELSE Assert(FALSE, "Test 75 Failed")
+  /\ IF TestOpArg(\uplus ) = {"a", "b"}
+       THEN Print("Test 76 OK", TRUE)
+       ELSE Assert(FALSE, "Test 76 Failed")
+  /\ IF TestOpArg(\bigcirc ) = {"a", "b"}
+       THEN Print("Test 77 OK", TRUE)
+       ELSE Assert(FALSE, "Test 77 Failed")
+  /\ IF TestTLCOps(:>, @@) =  1 :> "ab" @@ 2 :> "cd"
+       THEN Print("Test 78 OK", TRUE)
+       ELSE Assert(FALSE, "Test 78 Failed")
+  /\ IF TestDefinedBooleanOp(=>) = (TRUE => FALSE)
+       THEN Print("Test 79 OK", TRUE)
+       ELSE Assert(FALSE, "Test 79 Failed")
+  /\ IF TestDefinedBooleanOp(\equiv) = (TRUE \equiv FALSE)
+       THEN Print("Test 80 OK", TRUE)
+       ELSE Assert(FALSE, "Test 80 Failed")
+  /\ IF TestDefinedBooleanOp(<=>) = (TRUE <=> FALSE)
+       THEN Print("Test 81 OK", TRUE)
+       ELSE Assert(FALSE, "Test 81 Failed")
+\*  /\ Print("Test 82 fails because of parser bug", TRUE)
+  /\ IF TestDefinedBooleanOp(/\) = (TRUE /\ FALSE)
+       THEN Print("Test 82 OK", TRUE)
+       ELSE Assert(FALSE, "Test 82 Failed")
+  /\ IF TestDefinedBooleanOp(\land) = (TRUE \land FALSE)
+       THEN Print("Test 83 OK", TRUE)
+       ELSE Assert(FALSE, "Test 83 Failed")
+\*  /\ Print("Test 84 fails because of parser bug", TRUE)
+  /\ IF TestDefinedBooleanOp(\/) = (TRUE \/ FALSE)
+       THEN Print("Test 84 OK", TRUE)
+       ELSE Assert(FALSE, "Test 84 Failed")
+  /\ IF TestDefinedBooleanOp(\lor) = (TRUE \lor FALSE)
+       THEN Print("Test 85 OK", TRUE)
+       ELSE Assert(FALSE, "Test 85 Failed")
+  /\ IF TestDefinedSetOp(#) = ({"a", "b"} # {"b", "c"})
+       THEN Print("Test 86 OK", TRUE)
+       ELSE Assert(FALSE, "Test 86 Failed")
+  /\ IF TestDefinedSetOp(/=) = ({"a", "b"} /= {"b", "c"})
+       THEN Print("Test 87 OK", TRUE)
+       ELSE Assert(FALSE, "Test 87 Failed")
+  /\ IF TestIn(\in) = ("a"\in {"b", "c"})
+       THEN Print("Test 88 OK", TRUE)
+       ELSE Assert(FALSE, "Test 88 Failed")
+  /\ IF TestIn(\notin) = ("a" \notin {"b", "c"})
+       THEN Print("Test 89 OK", TRUE)
+       ELSE Assert(FALSE, "Test 89 Failed")
+  /\ IF TestDefinedSetOp(\subseteq) = ({"a", "b"} \subseteq {"b", "c"})
+       THEN Print("Test 90 OK", TRUE)
+       ELSE Assert(FALSE, "Test 90 Failed")
+  /\ IF TestDefinedSetOp(\) = ({"a", "b"} \ {"b", "c"})
+       THEN Print("Test 91 OK", TRUE)
+       ELSE Assert(FALSE, "Test 91 Failed")
+  /\ IF TestDefinedSetOp(\cap) = ({"a", "b"} \cap {"b", "c"})
+       THEN Print("Test 92 OK", TRUE)
+       ELSE Assert(FALSE, "Test 92 Failed")
+  /\ IF TestDefinedSetOp(\intersect) = ({"a", "b"} \intersect {"b", "c"})
+       THEN Print("Test 93 OK", TRUE)
+       ELSE Assert(FALSE, "Test 93 Failed")
+  /\ IF TestDefinedSetOp(\cup) = ({"a", "b"} \cup {"b", "c"})
+       THEN Print("Test 94 OK", TRUE)
+       ELSE Assert(FALSE, "Test 94 Failed")
+  /\ IF TestDefinedSetOp(\union) = ({"a", "b"} \union {"b", "c"})
+       THEN Print("Test 95 OK", TRUE)
+       ELSE Assert(FALSE, "Test 95 Failed")
+
+
+==============================================================
+78
+ALL UNDEFINED INFIX OPERATORS
+
++ 
+- 
+* 
+/ 
+..
+< 
+> 
+\leq 
+=<
+<=
+\geq 
+>=
+% 
+^
+\circ 
+\o
+++ 
+\div 
+...
+-- 
+\oplus 
+(+)
+\ominus 
+(-)
+\otimes 
+(\X)
+\oslash 
+(/)
+\odot 
+(.)
+** 
+\sqcap 
+// 
+\prec 
+\succ 
+\preceq 
+\succeq 
+\sqcup 
+^^
+\ll 
+\gg 
+<: 
+& 
+&& 
+\sqsubset 
+\sqsupset 
+\sqsubseteq 
+\sqsupseteq 
+| 
+|| 
+\subset 
+\supset 
+\supseteq 
+\star 
+%% 
+|- 
+-|
+|= 
+=|
+\bullet 
+##
+\sim 
+\simeq 
+\approx 
+\cong 
+$
+$$
+:= 
+::= 
+\asymp 
+\doteq 
+?? 
+!! 
+\propto 
+\wr 
+\uplus 
+\bigcirc 
+
+
+BUILT-IN BOOLEANS
+=>
+\equiv
+<=>
+/\
+\land
+\/
+\lor
+
+BUILT-IN SET OPERATORS
+#
+/=
+\in
+\notin
+\subseteq
+\
+\cap
+\intersect
+\cup
+\union
+
+OTHER
+
+-+->
+~>
+\cdot
diff --git a/tlatools/test-model/suite/test51a.tla b/tlatools/test-model/suite/test51a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..60ad0e2ab62f2eacf5e53750e7756146a60ea403
--- /dev/null
+++ b/tlatools/test-model/suite/test51a.tla
@@ -0,0 +1,13 @@
+------------------- MODULE test51a -------------------------
+
+\* Test definability of some aliases
+
+a =< b == {a, b}
+a >=b == {a, b}
+a \circ b == {a, b}
+a \oplus b == {a, b}
+a \ominus b == {a, b}
+a \otimes b == {a, b}
+a \oslash b == {a, b}
+a \odot b == {a, b}
+=============================================================================
diff --git a/tlatools/test-model/suite/test51b.tla b/tlatools/test-model/suite/test51b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2c21874d170bd255904e7f7e66218df301c8c19b
--- /dev/null
+++ b/tlatools/test-model/suite/test51b.tla
@@ -0,0 +1,4 @@
+---------------- MODULE test51b -----------------
+a <= b == {a, b}
+
+==================================================================
diff --git a/tlatools/test-model/suite/test52.cfg b/tlatools/test-model/suite/test52.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..942df65a55fdabbd31d3495a5c5124ef762101af
--- /dev/null
+++ b/tlatools/test-model/suite/test52.cfg
@@ -0,0 +1,6 @@
+SPECIFICATION Spec
+PROPERTY Property
+\* The Invariant is commented because of a known bug in TLC. 
+\* If this bug is ever addressed, Invariant can be re-enabled
+\* and the test test52enabled be deleted. 
+\*INVARIANT Invariant
diff --git a/tlatools/test-model/test52.tla b/tlatools/test-model/suite/test52.tla
similarity index 93%
rename from tlatools/test-model/test52.tla
rename to tlatools/test-model/suite/test52.tla
index 10b5c5b0d6eb3cb35cdb085398ea3c01400659a3..0cf62f56535b99a564eee848238b31b57ea68d98 100644
--- a/tlatools/test-model/test52.tla
+++ b/tlatools/test-model/suite/test52.tla
@@ -1,71 +1,71 @@
-----------------------   MODULE test52 -----------------------
-
-\* Test of prefix and postfix operators as arguments
-
-EXTENDS TLC
-
-VARIABLES x, y
-
-BoxTest(-._) == -(x=0)
-Foo1 == BoxTest([])
-Foo2 == BoxTest(<>)
-
--. a == {{"a"}, {"b"}} \ a
-a ^+  == {{"a"}, {"b"}} \ a
-a ^*  == {{"a"}, {"b"}} \ a
-a ^#  == {{"a"}, {"b"}} \ a
-
-SetOpTest(Foo(_)) == Foo({{"b"}, {"c"}})
-DomainTest(Foo(_)) == Foo([i\in {"a", "b"} |-> i])
-
-ASSUME
-  /\ Print("Test1 Begun", TRUE)
-  /\ SetOpTest(-.) =  - {{"b"}, {"c"}}
-  /\ SetOpTest(^+) =   {{"b"}, {"c"}} ^+
-  /\ SetOpTest(^*) =   {{"b"}, {"c"}} ^*
-  /\ SetOpTest(^#) =   {{"b"}, {"c"}} ^#
-  /\ SetOpTest(SUBSET) = SUBSET {{"b"}, {"c"}}
-  /\ SetOpTest(UNION) = UNION {{"b"}, {"c"}}
-  /\ DomainTest(DOMAIN) = DOMAIN [i\in {"a", "b"} |-> i]
-  /\ Print("Test1 Finished", TRUE)
-
-
-Init == /\ x = {"a"}
-        /\ y = {"a"}
-Next == /\ x'= x \cup {"a"}
-        /\ y'= y \cup {"b"}
-Spec == Init /\ [][Next]_x
-
-EnabledTest(Foo(_)) == Foo(x'=x)
-UnchangedTest(Foo(_)) == Foo(x)
-PrimeTest(Foo(_)) == Foo(x) = x
-
-Invariant ==
- /\ EnabledTest(ENABLED)
-
-ImpliedAction ==
- /\ UnchangedTest(UNCHANGED)
- /\ PrimeTest(')
-
-Property == [][ImpliedAction]_<<x, y>>
-
-==================================================================
-\* Prefix Operators
-~
-ENABLED
-UNCHANGED
-[]
-<>
-SUBSET
-UNION
-DOMAIN
--
-
-\* Postfix Operators
-\* Predefined
-'
-
-\* Undefined
-^+
-^*
-^#
+----------------------   MODULE test52 -----------------------
+
+\* Test of prefix and postfix operators as arguments
+
+EXTENDS TLC
+
+VARIABLES x, y
+
+BoxTest(-._) == -(x=0)
+Foo1 == BoxTest([])
+Foo2 == BoxTest(<>)
+
+-. a == {{"a"}, {"b"}} \ a
+a ^+  == {{"a"}, {"b"}} \ a
+a ^*  == {{"a"}, {"b"}} \ a
+a ^#  == {{"a"}, {"b"}} \ a
+
+SetOpTest(Foo(_)) == Foo({{"b"}, {"c"}})
+DomainTest(Foo(_)) == Foo([i\in {"a", "b"} |-> i])
+
+ASSUME
+  /\ Print("Test1 Begun", TRUE)
+  /\ SetOpTest(-.) =  - {{"b"}, {"c"}}
+  /\ SetOpTest(^+) =   {{"b"}, {"c"}} ^+
+  /\ SetOpTest(^*) =   {{"b"}, {"c"}} ^*
+  /\ SetOpTest(^#) =   {{"b"}, {"c"}} ^#
+  /\ SetOpTest(SUBSET) = SUBSET {{"b"}, {"c"}}
+  /\ SetOpTest(UNION) = UNION {{"b"}, {"c"}}
+  /\ DomainTest(DOMAIN) = DOMAIN [i\in {"a", "b"} |-> i]
+  /\ Print("Test1 Finished", TRUE)
+
+
+Init == /\ x = {"a"}
+        /\ y = {"a"}
+Next == /\ x'= x \cup {"a"}
+        /\ y'= y \cup {"b"}
+Spec == Init /\ [][Next]_<<x, y>>
+
+EnabledTest(Foo(_)) == Foo(x'=x)
+UnchangedTest(Foo(_)) == Foo(x)
+PrimeTest(Foo(_)) == Foo(x) = x
+
+Invariant ==
+ /\ EnabledTest(ENABLED)
+
+ImpliedAction ==
+ /\ UnchangedTest(UNCHANGED)
+ /\ PrimeTest(')
+
+Property == [][ImpliedAction]_<<x, y>>
+
+==================================================================
+\* Prefix Operators
+~
+ENABLED
+UNCHANGED
+[]
+<>
+SUBSET
+UNION
+DOMAIN
+-
+
+\* Postfix Operators
+\* Predefined
+'
+
+\* Undefined
+^+
+^*
+^#
diff --git a/tlatools/test-model/suite/test53.cfg b/tlatools/test-model/suite/test53.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/test53.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/test53.tla b/tlatools/test-model/suite/test53.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a395492b848682f035d04f7db5a95832f09854ac
--- /dev/null
+++ b/tlatools/test-model/suite/test53.tla
@@ -0,0 +1,36 @@
+--------------- MODULE test53 -------------
+
+(* test definability of operators with aliases *)
+
+
+EXTENDS TLC
+
+VARIABLE x
+
+u \oplus v == u
+u \ominus v == u
+u \odot v == u
+u \oslash v == u
+u \otimes v == u 
+u \circ v == u
+u % v == u
+u =<  v == u
+u >= v == u
+
+
+Init == 
+ /\ x = 0
+
+Next ==
+   /\ x = 0
+   /\ 
+      \/ x' = x \oplus 1  
+      \/ x' = x \ominus 1  
+      \/ x' = x \odot 1  
+      \/ x' = x \oslash 1  
+      \/ x' = x \otimes 1 
+      \/ x' = x \circ 1 
+      \/ x' = x % 1   \* causes TLC bug
+      \/ x' = (x =<  1)  \* causes TLC bug 
+      \/ x' = (x >= 1 )  \* causes TLC bug 
+=======================================
diff --git a/tlatools/test-model/suite/test54.cfg b/tlatools/test-model/suite/test54.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c
--- /dev/null
+++ b/tlatools/test-model/suite/test54.cfg
@@ -0,0 +1,2 @@
+INIT Init
+NEXT Next
diff --git a/tlatools/test-model/suite/test54.tla b/tlatools/test-model/suite/test54.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ae7c1670ea8e68ee6685af8c45d3696d661fa36b
--- /dev/null
+++ b/tlatools/test-model/suite/test54.tla
@@ -0,0 +1,36 @@
+--------------- MODULE test54 -------------
+
+(* test definability of operators with aliases *)
+
+
+EXTENDS TLC
+
+VARIABLE x
+
+u \oplus v == u
+u \ominus v == u
+u \odot v == u
+u \oslash v == u
+u \otimes v == u 
+u \o v == u
+u % v == u
+u =<  v == u
+u >= v == u
+
+
+Init == 
+ /\ x = 0
+
+Next ==
+   /\ x = 0
+   /\ 
+      \/ x' = x (+) 1     \* causes TLC bug
+      \/ x' = x (-) 1     \* causes TLC bug
+      \/ x' = x (.) 1     \* causes TLC bug
+      \/ x' = x (/) 1     \* causes TLC bug
+      \/ x' = x (\X) 1  \* parser can't handle this
+      \/ x' = x \circ 1   \* causes TLC bug
+      \/ x' = x % 1   
+      \/ x' = (x =<  1)  \* TLC bug : sets x' to TRUE
+      \/ x' = (x >= 1 )    \* TLC bug : sets x' to FALSE
+=======================================
diff --git a/tlatools/test-model/test55.cfg b/tlatools/test-model/suite/test55.cfg
similarity index 95%
rename from tlatools/test-model/test55.cfg
rename to tlatools/test-model/suite/test55.cfg
index fe7d864f88046c4367c05c018a558362c39fe97c..305c3a68fbd27fb38d68161f05488ca00c1b8202 100644
--- a/tlatools/test-model/test55.cfg
+++ b/tlatools/test-model/suite/test55.cfg
@@ -1,3 +1,3 @@
-SPECIFICATION Spec
-INVARIANT Invariant
-PROPERTY InitProperty
+SPECIFICATION Spec
+INVARIANT Invariant
+PROPERTY InitProperty
diff --git a/tlatools/test-model/test55.tla b/tlatools/test-model/suite/test55.tla
similarity index 95%
rename from tlatools/test-model/test55.tla
rename to tlatools/test-model/suite/test55.tla
index fd95511e3caad8f32b2dd3be3e3fe8cfa02a4f21..3b94b2d51d54bd4283f59c7737a8e369b478416e 100644
--- a/tlatools/test-model/test55.tla
+++ b/tlatools/test-model/suite/test55.tla
@@ -1,22 +1,22 @@
-------------------------------- MODULE test55 -------------------------------
-\* Test of ENABLED with INSTANCE semantics
-EXTENDS Naturals
-
-VARIABLE z
-
-I == INSTANCE test55a WITH x <- z, y <- z
-
-Init == z = 0
-Next == z' = 1 - z
-
-Spec == Init /\ [][Next]_z /\ WF_z(Next)
-Invariant == /\ I!F
-             /\ ~I!G
-
-InitProperty == I!Init 
-SafeProperty == I!SafeSpec
-LiveProperty == I!LiveSpec
-
-=============================================================================
-
-
+------------------------------- MODULE test55 -------------------------------
+\* Test of ENABLED with INSTANCE semantics
+EXTENDS Naturals
+
+VARIABLE z
+
+I == INSTANCE test55a WITH x <- z, y <- z
+
+Init == z = 0
+Next == z' = 1 - z
+
+Spec == Init /\ [][Next]_z /\ WF_z(Next)
+Invariant == /\ I!F
+             /\ ~I!G
+
+InitProperty == I!Init 
+SafeProperty == I!SafeSpec
+LiveProperty == I!LiveSpec
+
+=============================================================================
+
+
diff --git a/tlatools/test-model/test55a.tla b/tlatools/test-model/suite/test55a.tla
similarity index 96%
rename from tlatools/test-model/test55a.tla
rename to tlatools/test-model/suite/test55a.tla
index bd17db17c8ca799dc92596fd99f1300a7bc8b231..ad6aee45a926630145f1336ac71eb2fccdeb2ff2 100644
--- a/tlatools/test-model/test55a.tla
+++ b/tlatools/test-model/suite/test55a.tla
@@ -1,14 +1,14 @@
-------------------------------- MODULE test55a ------------------------------
-EXTENDS Naturals
-VARIABLES x, y
-
-F == ENABLED (x'=0 /\ y'=1)
-G == ENABLED ((x # y) /\ (x'=0) /\ (y'=1))
-
-Init == (x = 0) /\ (y = 0)
-Next == /\ x' = 1 - x
-        /\ y' = y
-
-SafeSpec == Init /\ [][Next]_<<x,y>>
-LiveSpec == SafeSpec /\ WF_<<x,y>>(Next)
-=============================================================================
+------------------------------- MODULE test55a ------------------------------
+EXTENDS Naturals
+VARIABLES x, y
+
+F == ENABLED (x'=0 /\ y'=1)
+G == ENABLED ((x # y) /\ (x'=0) /\ (y'=1))
+
+Init == (x = 0) /\ (y = 0)
+Next == /\ x' = 1 - x
+        /\ y' = y
+
+SafeSpec == Init /\ [][Next]_<<x,y>>
+LiveSpec == SafeSpec /\ WF_<<x,y>>(Next)
+=============================================================================
diff --git a/tlatools/test-model/test56.cfg b/tlatools/test-model/suite/test56.cfg
similarity index 97%
rename from tlatools/test-model/test56.cfg
rename to tlatools/test-model/suite/test56.cfg
index 59df66b1045c7bbcce67167852fd154f3d81c41e..8a93632b965223198ef42bd956c09e80e1a9dfb5 100644
--- a/tlatools/test-model/test56.cfg
+++ b/tlatools/test-model/suite/test56.cfg
@@ -1,2 +1,2 @@
-SPECIFICATION Spec
+SPECIFICATION Spec
 PROPERTY Property
\ No newline at end of file
diff --git a/tlatools/test-model/test56.tla b/tlatools/test-model/suite/test56.tla
similarity index 96%
rename from tlatools/test-model/test56.tla
rename to tlatools/test-model/suite/test56.tla
index 4f204445c4a3d8157babd576089f7b0e5cbf62f1..e44515da94c7d77852bfd7b3994f8e0ceac11bbb 100644
--- a/tlatools/test-model/test56.tla
+++ b/tlatools/test-model/suite/test56.tla
@@ -1,18 +1,18 @@
-------------------------------- MODULE test56 -------------------------------
-\* Test of subtle INSTANCE semantics
-EXTENDS Naturals
-VARIABLE x
-
-I == INSTANCE test56a WITH u <- x
-
-NotIBangH == (x'=x) /\ I!G(x, x'=x+1)
-
-Init == \E i \in 1..3 : x = i
-Next == x' = 9-x
-Spec == Init /\ [][Next]_x
-
-Property == [][I!H = NotIBangH]_x
-
-=============================================================================
-I!H = (x'=x) /\ ENABLED (u'=x+1 \/ (u'=x /\ u'=x) \/ (u'=x /\ u'=x))
-NotIBangH = (x'=x) /\ ENABLED (x'=x+1 \/ (u'=x /\ x'=x) \/ (u'=x /\ x'=u))
+------------------------------- MODULE test56 -------------------------------
+\* Test of subtle INSTANCE semantics
+EXTENDS Naturals
+VARIABLE x
+
+I == INSTANCE test56a WITH u <- x
+
+NotIBangH == (x'=x) /\ I!G(x, x'=x+1)
+
+Init == \E i \in 1..3 : x = i
+Next == x' = 9-x
+Spec == Init /\ [][Next]_x
+
+Property == [][I!H = NotIBangH]_x
+
+=============================================================================
+I!H = (x'=x) /\ ENABLED (u'=x+1 \/ (u'=x /\ u'=x) \/ (u'=x /\ u'=x))
+NotIBangH = (x'=x) /\ ENABLED (x'=x+1 \/ (u'=x /\ x'=x) \/ (u'=x /\ x'=u))
diff --git a/tlatools/test-model/test56a.tla b/tlatools/test-model/suite/test56a.tla
similarity index 98%
rename from tlatools/test-model/test56a.tla
rename to tlatools/test-model/suite/test56a.tla
index 521cf1046b8d7dc9b71f0117916beb05f98b665e..449dd447ff88465b7a710a7eb77853c563fce942 100644
--- a/tlatools/test-model/test56a.tla
+++ b/tlatools/test-model/suite/test56a.tla
@@ -1,7 +1,7 @@
-------------------------------- MODULE test56a ------------------------------
-\* Test of subtle INSTANCE semantics
-EXTENDS Naturals
-VARIABLE u
-G(v, A) == ENABLED (A \/ (u'=u /\ v'=v) \/ (v'=u /\ u'=v))
-H == (u'=u) /\ G(u, u' = u + 1)
+------------------------------- MODULE test56a ------------------------------
+\* Test of subtle INSTANCE semantics
+EXTENDS Naturals
+VARIABLE u
+G(v, A) == ENABLED (A \/ (u'=u /\ v'=v) \/ (v'=u /\ u'=v))
+H == (u'=u) /\ G(u, u' = u + 1)
 =============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test57.cfg b/tlatools/test-model/suite/test57.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e9294d0442019c6b2d506c2a5118da6ffb19b55a
--- /dev/null
+++ b/tlatools/test-model/suite/test57.cfg
@@ -0,0 +1,3 @@
+SPECIFICATION Spec
+INVARIANT Invariant0 Invariant2 Invariant3 Invariant1  
+PROPERTY Property
\ No newline at end of file
diff --git a/tlatools/test-model/test57.tla b/tlatools/test-model/suite/test57.tla
similarity index 96%
rename from tlatools/test-model/test57.tla
rename to tlatools/test-model/suite/test57.tla
index 18539eb3e576a32528123ddd14aea25dc3a0c219..08adb6d0e2ac94935105328a0935bc037cbec538 100644
--- a/tlatools/test-model/test57.tla
+++ b/tlatools/test-model/suite/test57.tla
@@ -1,19 +1,20 @@
-------------------------------- MODULE test57 -------------------------------
-\* Test of subtle INSTANCE semantics
-EXTENDS Naturals
-VARIABLE x
-
-I == INSTANCE test57a WITH u <- x, v <- x
-
-Init == \E i \in 1..3 : x = i
-Next == x' = 9-x
-Spec == Init /\ [][Next]_x
-
-Invariant0 == I!B(Next)
-Invariant1 == I!C
-Invariant2 == ~ I!B(I!A)
-Property == [][~I!A]_x
-=============================================================================
-I!C = ENABLED((u'=x) /\ (v' = x+1))
-I!B(I!A) = ENABLED ((x'=x) /\ (x'=x+1))
-
+------------------------------- MODULE test57 -------------------------------
+\* Test of subtle INSTANCE semantics
+EXTENDS Naturals
+VARIABLE x
+
+I == INSTANCE test57a WITH u <- x, v <- x
+
+Init == \E i \in 1..3 : x = i
+Next == x' = 9-x
+Spec == Init /\ [][Next]_x
+
+Invariant0 == I!B(Next)
+Invariant1 == I!C
+Invariant2 == ~ I!B(I!A)
+Invariant3 == I!D
+Property == [][~I!A]_x
+=============================================================================
+I!C = ENABLED((u'=x) /\ (v' = x+1))
+I!B(I!A) = ENABLED ((x'=x) /\ (x'=x+1))
+
diff --git a/tlatools/test-model/test57a.tla b/tlatools/test-model/suite/test57a.tla
similarity index 94%
rename from tlatools/test-model/test57a.tla
rename to tlatools/test-model/suite/test57a.tla
index d90b00c1be9dcd095ff9389681ef82062ee665cf..4fde946c021c6d5e2ed2267ef65fa1fdb6376616 100644
--- a/tlatools/test-model/test57a.tla
+++ b/tlatools/test-model/suite/test57a.tla
@@ -1,8 +1,9 @@
-------------------------------- MODULE test57a ------------------------------
-\* Test of subtle INSTANCE semantics
-EXTENDS Naturals
-VARIABLES u, v
-A == (u'=u) /\ (v'=v+1)
-B(d) == ENABLED d
-C == B(A)
+------------------------------- MODULE test57a ------------------------------
+\* Test of subtle INSTANCE semantics
+EXTENDS Naturals
+VARIABLES u, v
+A == (u'=u) /\ (v'=v+1)
+B(d) == ENABLED d
+C == B(A)
+D == ENABLED A
 =============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test58.cfg b/tlatools/test-model/suite/test58.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f
--- /dev/null
+++ b/tlatools/test-model/suite/test58.cfg
@@ -0,0 +1 @@
+ 
diff --git a/tlatools/test-model/suite/test58.tla b/tlatools/test-model/suite/test58.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8fff7e580b1e748907f8d3de6e2a3554031c506f
--- /dev/null
+++ b/tlatools/test-model/suite/test58.tla
@@ -0,0 +1,16 @@
+------------------- MODULE test58 --------------------
+\* Test of binary, octal, and hexadecimal numbers
+
+EXTENDS Naturals
+
+ASSUME
+  /\ 63 = \b111111
+  /\ 63 = \o77
+  /\ 63 = \h3F
+  /\ 63 = \B111111
+  /\ 63 = \O77
+  /\ 63 = \H3f
+  /\ 63 = \H3F
+  /\ 63 = \h3f
+
+=======================================================
diff --git a/tlatools/test-model/test59.cfg b/tlatools/test-model/suite/test59.cfg
similarity index 94%
rename from tlatools/test-model/test59.cfg
rename to tlatools/test-model/suite/test59.cfg
index 9816c74f9b53e2266134e30e7da07fa5392c5384..8e570acdc96c3b5f8e28b4ecccd0273858365db2 100644
--- a/tlatools/test-model/test59.cfg
+++ b/tlatools/test-model/suite/test59.cfg
@@ -1,2 +1,2 @@
-SPECIFICATION Spec
-PROPERTY Liveness
+SPECIFICATION Spec
+PROPERTY Liveness
diff --git a/tlatools/test-model/test59.tla b/tlatools/test-model/suite/test59.tla
similarity index 96%
rename from tlatools/test-model/test59.tla
rename to tlatools/test-model/suite/test59.tla
index 8a1b24ddf2904342cb22317bb7122f775a6de463..f1e1d2dff5279079471423bfa94b06a8450547d1 100644
--- a/tlatools/test-model/test59.tla
+++ b/tlatools/test-model/suite/test59.tla
@@ -1,18 +1,18 @@
-------------------------------- MODULE test59 ------------------------------- 
-EXTENDS Naturals
-\* Test of INSTANCE inside LET
-
-VARIABLE y
-
-Next == LET x == y+1
-            M == INSTANCE test59a
-        IN  y' = (M!xplus1 - 1) % 5
-
-Init == y = 0
-
-Spec == Init /\ [][Next]_y /\ WF_y(Next)
-
-Invariant == y \in 0..4
-Liveness == []<>(y = 4)
-
+------------------------------- MODULE test59 ------------------------------- 
+EXTENDS Naturals
+\* Test of INSTANCE inside LET
+
+VARIABLE y
+
+Next == LET x == y+1
+            M == INSTANCE test59a
+        IN  y' = (M!xplus1 - 1) % 5
+
+Init == y = 0
+
+Spec == Init /\ [][Next]_y /\ WF_y(Next)
+
+Invariant == y \in 0..4
+Liveness == []<>(y = 4)
+
 =============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/test59a.tla b/tlatools/test-model/suite/test59a.tla
similarity index 96%
rename from tlatools/test-model/test59a.tla
rename to tlatools/test-model/suite/test59a.tla
index 2963511f41478b31003db2ae4ff96c17207f1f9d..a5c42c5d2c8769bffa23a0d871a100c4d0799fda 100644
--- a/tlatools/test-model/test59a.tla
+++ b/tlatools/test-model/suite/test59a.tla
@@ -1,9 +1,9 @@
-------------------------------- MODULE test59a ------------------------------- 
-EXTENDS Naturals
-\* Test of INSTANCE inside LET
-
-VARIABLE x
-
-xplus1 == x+1
-
+------------------------------- MODULE test59a ------------------------------- 
+EXTENDS Naturals
+\* Test of INSTANCE inside LET
+
+VARIABLE x
+
+xplus1 == x+1
+
 =============================================================================
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test6.cfg b/tlatools/test-model/suite/test6.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test6.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test6.tla b/tlatools/test-model/suite/test6.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2665292fa01e629dede7c238430f0597b8ff27e6
--- /dev/null
+++ b/tlatools/test-model/suite/test6.tla
@@ -0,0 +1,132 @@
+--------------- MODULE test6 -------------
+
+(* test of Propositional Logic. *)
+
+EXTENDS Integers, TLC
+
+VARIABLE x, y, z, w
+Type == /\ x \in BOOLEAN
+        /\ y \in BOOLEAN
+        /\ z \in BOOLEAN
+        /\ w = 0
+Init == /\ x = TRUE
+        /\ y = TRUE
+        /\ z = FALSE
+        /\ w = 0
+Next ==  /\ \/ (w = 1) /\ Assert(FALSE, "This is a bug")
+            \/ (w=0)
+         /\ (w=0) \* \/ Assert(FALSE, "This is a bug, too")
+                  \* Commented out because I now don't think it's a bug.
+         /\ UNCHANGED <<w, x, y, z>>
+
+Inv  ==  
+
+  /\ IF ~(x /\ y /\ ~z)
+      THEN Assert(FALSE, "Test 1 failed")
+      ELSE Print("Test 1 OK", TRUE)
+ 
+  /\ IF ~(~z /\ y)
+      THEN Assert(FALSE, "Test 2 failed")
+      ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF ~(x \/ ~y \/ z)
+      THEN Assert(FALSE, "Test 3 failed")
+      ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF ~(z \/ ~y \/ x)
+      THEN Assert(FALSE, "Test 4 failed")
+      ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF ~(x /\ y)
+      THEN Assert(FALSE, "Test 5 failed")
+      ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF (x /\ z)
+      THEN Assert(FALSE, "Test 6 failed")
+      ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF (z /\ x)
+      THEN Assert(FALSE, "Test 7 failed")
+      ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF (z /\ ~x)
+      THEN Assert(FALSE, "Test 8 failed")
+      ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF (z /\ (x=1))
+      THEN Assert(FALSE, "Test 9 failed")
+      ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF ~(x \/ (z=1))
+      THEN Assert(FALSE, "Test 10 failed")
+      ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF ~(x => y)
+      THEN Assert(FALSE, "Test 11 failed")
+      ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF ~(~x => y)
+      THEN Assert(FALSE, "Test 12 failed")
+      ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF ~(~x => z)
+      THEN Assert(FALSE, "Test 13 failed")
+      ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF (x => z)
+      THEN Assert(FALSE, "Test 14 failed")
+      ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF ~x
+      THEN Assert(FALSE, "Test 15 failed")
+      ELSE Print("Test 15 OK", TRUE)
+
+  /\ IF ~~z
+      THEN Assert(FALSE, "Test 16 failed")
+      ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF ~(x <=> y)
+      THEN Assert(FALSE, "Test 17 failed")
+      ELSE Print("Test 17 OK", TRUE)
+
+  /\ IF ~(~x <=> z)
+      THEN Assert(FALSE, "Test 18 failed")
+      ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF (x <=> z)
+      THEN Assert(FALSE, "Test 19 failed")
+      ELSE Print("Test 19 OK", TRUE)
+
+  /\ IF (z <=> x)
+      THEN Assert(FALSE, "Test 20 failed")
+      ELSE Print("Test 20 OK", TRUE)
+
+  /\ IF ~(x \equiv y)
+      THEN Assert(FALSE, "Test 21 failed")
+      ELSE Print("Test 21 OK", TRUE)
+
+  /\ IF ~(~x \equiv z)
+      THEN Assert(FALSE, "Test 22 failed")
+      ELSE Print("Test 22 OK", TRUE)
+
+  /\ IF (x \equiv z)
+      THEN Assert(FALSE, "Test 23 failed")
+      ELSE Print("Test 23 OK", TRUE)
+
+  /\ IF (z \equiv x)
+      THEN Assert(FALSE, "Test 24 failed")
+      ELSE Print("Test 24 OK", TRUE)
+
+  /\ IF TRUE \notin BOOLEAN
+      THEN Assert(FALSE, "Test 25 failed")
+      ELSE Print("Test 25 OK", TRUE)
+
+  /\ IF FALSE \notin BOOLEAN
+      THEN Assert(FALSE, "Test 26 failed")
+      ELSE Print("Test 26 OK", TRUE)
+
+  /\ IF {TRUE, FALSE} # BOOLEAN
+      THEN Assert(FALSE, "Test 27 failed")
+      ELSE Print("Test 27 OK", TRUE)
+
+=========================================
diff --git a/tlatools/test-model/suite/test60.cfg b/tlatools/test-model/suite/test60.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f
--- /dev/null
+++ b/tlatools/test-model/suite/test60.cfg
@@ -0,0 +1 @@
+ 
diff --git a/tlatools/test-model/suite/test60.tla b/tlatools/test-model/suite/test60.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0fef36a2cd81fa358d639907cb7dee87e547659f
--- /dev/null
+++ b/tlatools/test-model/suite/test60.tla
@@ -0,0 +1,14 @@
+-------------------- MODULE test60 --------------------
+(***************************************************************************)
+(* Test of handling strings as sequences.                                  *)
+(***************************************************************************)
+
+EXTENDS Sequences, TLC
+
+ASSUME "abc" \o "def" = "abcdef"
+ASSUME Len("abcdef") = 6
+ASSUME "a\\b\"c\nd" = "a\\b\"" \o "c\nd" 
+ASSUME Len("a\\b\"c\nd") = 7
+
+========================================
+
diff --git a/tlatools/test-model/suite/test61.cfg b/tlatools/test-model/suite/test61.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d2c069de399c1fe1495c4a2ca92e0320fd53347d
--- /dev/null
+++ b/tlatools/test-model/suite/test61.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION ISpec
+INVARIANT Invariant
diff --git a/tlatools/test-model/suite/test61.tla b/tlatools/test-model/suite/test61.tla
new file mode 100644
index 0000000000000000000000000000000000000000..bb8de9ead6edc804da3c2325d4a7f3db36a63b9d
--- /dev/null
+++ b/tlatools/test-model/suite/test61.tla
@@ -0,0 +1,21 @@
+-------------------- MODULE test61 --------------------
+(***************************************************************************)
+(* Test of parametrized INSTANCE                                            *)
+(***************************************************************************)
+\* Because of bug, it Handles JSpec but not ISpec
+
+EXTENDS Naturals
+
+VARIABLE y
+
+IM(x) == INSTANCE test61a
+
+ISpec == IM(y)!Spec
+
+Invariant == y \in 0..4
+
+JM == INSTANCE test61a WITH x <- y
+JSpec == JM!Spec
+
+========================================
+
diff --git a/tlatools/test-model/suite/test61a.tla b/tlatools/test-model/suite/test61a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c7f83a41bb788675f361125b200224a40b167f73
--- /dev/null
+++ b/tlatools/test-model/suite/test61a.tla
@@ -0,0 +1,11 @@
+-------------------- MODULE test61a --------------------
+(***************************************************************************)
+(* Test of parametrized INSTANCE                                            *)
+(***************************************************************************)
+
+EXTENDS Naturals
+
+VARIABLE x
+
+Spec == (x = 0) /\ [][x'=(x+1)%5]_x
+========================================
diff --git a/tlatools/test-model/suite/test62.cfg b/tlatools/test-model/suite/test62.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/tlatools/test-model/suite/test62.cfg
@@ -0,0 +1 @@
+
diff --git a/tlatools/test-model/suite/test62.tla b/tlatools/test-model/suite/test62.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ccff29bbedd611998e86e8be370b1917fd446e04
--- /dev/null
+++ b/tlatools/test-model/suite/test62.tla
@@ -0,0 +1,5 @@
+----------------------- MODULE test62 --------------------------
+EXTENDS TLC
+ASSUME Print("Should print this", TRUE)
+ASSUME PrintT("And this")
+=================================================================
diff --git a/tlatools/test-model/test63.cfg b/tlatools/test-model/suite/test63.cfg
similarity index 100%
rename from tlatools/test-model/test63.cfg
rename to tlatools/test-model/suite/test63.cfg
diff --git a/tlatools/test-model/test63.tla b/tlatools/test-model/suite/test63.tla
similarity index 100%
rename from tlatools/test-model/test63.tla
rename to tlatools/test-model/suite/test63.tla
diff --git a/tlatools/test-model/suite/test63a.cfg b/tlatools/test-model/suite/test63a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9608bf7492bdbbbdf9966ffa01eb5eac559a8584
--- /dev/null
+++ b/tlatools/test-model/suite/test63a.cfg
@@ -0,0 +1,2 @@
+SPECIFICATION HC
+
diff --git a/tlatools/test-model/test63a.tla b/tlatools/test-model/suite/test63a.tla
similarity index 100%
rename from tlatools/test-model/test63a.tla
rename to tlatools/test-model/suite/test63a.tla
diff --git a/tlatools/test-model/suite/test64.cfg b/tlatools/test-model/suite/test64.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d
--- /dev/null
+++ b/tlatools/test-model/suite/test64.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
diff --git a/tlatools/test-model/suite/test64.tla b/tlatools/test-model/suite/test64.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5e66d028eafb7af9c2594c394f3cc4a10f874203
--- /dev/null
+++ b/tlatools/test-model/suite/test64.tla
@@ -0,0 +1,8 @@
+-------------------------------- MODULE test64 --------------------------------
+VARIABLE y   
+ 
+I == INSTANCE test64a WITH x <- y  
+  \* Changed from Test64a on 12 June 2014 because that's now illegal
+
+Spec == I!Spec
+=======================================================================
diff --git a/tlatools/test-model/suite/test64a.cfg b/tlatools/test-model/suite/test64a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d
--- /dev/null
+++ b/tlatools/test-model/suite/test64a.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
diff --git a/tlatools/test-model/suite/test64a.tla b/tlatools/test-model/suite/test64a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c8a0994d90665a3bb9c5e1fb80d527b77fa61f6f
--- /dev/null
+++ b/tlatools/test-model/suite/test64a.tla
@@ -0,0 +1,8 @@
+----------------------- MODULE test64a -----------------------
+VARIABLE x
+
+ASet == {r \in  [{0} -> {0}]  : x}
+
+Spec == x = TRUE /\ [][x' = (ASet = {})]_x
+=============================================================
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test65.cfg b/tlatools/test-model/suite/test65.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..1446df75569d1fca6a285c43b13fb3d99699e1f8
--- /dev/null
+++ b/tlatools/test-model/suite/test65.cfg
@@ -0,0 +1 @@
+CONSTANT c = "a"
diff --git a/tlatools/test-model/suite/test65.tla b/tlatools/test-model/suite/test65.tla
new file mode 100644
index 0000000000000000000000000000000000000000..ea1615d4a5ebee5d42b25f3db547d961ebcfad95
--- /dev/null
+++ b/tlatools/test-model/suite/test65.tla
@@ -0,0 +1,8 @@
+-------------------------------- MODULE test65 --------------------------------
+EXTENDS test65a
+ 
+I == INSTANCE test65a WITH c <- 42
+J == INSTANCE test65a WITH c <- 44
+ASSUME /\ <<I!foo, J!foo>>  = <<42, 44>>
+       /\ foo = c
+=======================================================================
diff --git a/tlatools/test-model/suite/test65a.cfg b/tlatools/test-model/suite/test65a.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..1446df75569d1fca6a285c43b13fb3d99699e1f8
--- /dev/null
+++ b/tlatools/test-model/suite/test65a.cfg
@@ -0,0 +1 @@
+CONSTANT c = "a"
diff --git a/tlatools/test-model/suite/test65a.tla b/tlatools/test-model/suite/test65a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c82bd7513ae6ec612280ac37a5063262cbec4369
--- /dev/null
+++ b/tlatools/test-model/suite/test65a.tla
@@ -0,0 +1,7 @@
+----------------------- MODULE test65a -----------------------
+CONSTANT c
+
+bar == c
+foo == bar
+=============================================================
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test7.cfg b/tlatools/test-model/suite/test7.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test7.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test7.tla b/tlatools/test-model/suite/test7.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b4468cd6736c8d1024be6b6860821c3416d53b46
--- /dev/null
+++ b/tlatools/test-model/suite/test7.tla
@@ -0,0 +1,100 @@
+--------------- MODULE test7 -------------
+
+(* test of Predicate Logic and CHOOSE *)
+
+EXTENDS Integers, TLC
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+Inv  ==  
+
+  /\ IF ~ \E i \in {1,2,3} : i=2
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF ~ \exists i \in {1,2,3} : i=2
+       THEN Assert(FALSE, "Test 1a Failed")
+       ELSE Print("Test 1a OK", TRUE)
+
+  /\ IF \A i \in {3,3,3,3,2,1} : i=3
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF \forall i \in {3,3,3,3,2,1} : i=3
+       THEN Assert(FALSE, "Test 2a Failed")
+       ELSE Print("Test 2a OK", TRUE)
+
+  /\ IF \E i \in {1,2,3} : FALSE
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF ~\A i \in {1,2,3} : TRUE
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF \E i \in {} : TRUE
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF \exists i \in {} : TRUE
+       THEN Assert(FALSE, "Test 5a Failed")
+       ELSE Print("Test 5a OK", TRUE)
+
+  /\ IF ~\A i \in {} : FALSE
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF ~\forall i \in {} : FALSE
+       THEN Assert(FALSE, "Test 6a Failed")
+       ELSE Print("Test 6a OK", TRUE)
+
+  /\ IF ~\E <<i, j>> \in {1,2} \X {3,4}, 
+             k, l \in {4, 5}, m \in {6,7} : i + j + k + l + m = 23
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF ~\A <<i, j>> \in {1,2} \X {3,4}, 
+        k, l \in {4, 5}, m \in {6,7} : i + j + k + l + m > 17
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF (CHOOSE i \in {1,2,3} : i > 2) # 3
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF (CHOOSE i \in {1,2,3, 4} : i > 2) \leq 2
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF <<1, 3>> # CHOOSE <<i, j>> \in {1,2} \X {3,4} : (i < 2) /\ (j = i+2)
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF ~(\A i \in {1,2}, <<j, k>> \in {3,4} \X {5, 6}, m, n \in {7,8} :
+            i+j+k+m+n \geq 23)
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF \E i \in {1,2}, <<j, k>> \in {3,4} \X {5, 6}, m, n \in {7,8} :
+            i+j+k+m+n < 23
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+
+  /\ IF \A i \in CHOOSE j \in {{Nat}, {Int}} : \E k \in j : -1 \in k :
+           -4 \in i
+       THEN Print("Test 14 OK", TRUE)
+       ELSE Assert(FALSE, "Test 14 Failed")
+=========================================
+
+The following test is not expected to work because
+TLC does not preserve the semantics of CHOOSE
+
+  /\ IF (CHOOSE i \in {1,2,3, 4} : i > 2) 
+           # (CHOOSE i \in {4, 1, 3 , 2} : i > 2) 
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
diff --git a/tlatools/test-model/suite/test8.cfg b/tlatools/test-model/suite/test8.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test8.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test8.tla b/tlatools/test-model/suite/test8.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fe958975938226c2b001df313438f5d7368f01d0
--- /dev/null
+++ b/tlatools/test-model/suite/test8.tla
@@ -0,0 +1,102 @@
+--------------- MODULE test8 -------------
+
+(* Test of set operators \cup, \cap, \subseteq, \ *)
+
+EXTENDS Integers, TLC, Sequences
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+Inv  ==  
+
+  /\ IF {1, 2, 3} \cup {3, 4, 5} # 1..5
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF {1, 2, 3} \cap {3, 4, 5} # {3}
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF -5 \notin Int \cup Nat 
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF -5 \notin Nat \cup Int
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF {1, 2, 3, 4} \cap {i \in Int : i > 2} # {3, 4}
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF {1, 2, 3} \cap {} # {}
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF {} \cap {1, 2, 3} # {}
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF {1, 2, 3, 4} \ {i \in Int : i > 2} # {2, 1}
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF {1, 2, 3} \ {} # {3,1,2}
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF {} \ {1, 2, 3} # {}
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF -5 \in {-5} \ Int
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF <<1, 2>> \in Seq({1,2,3}) \ Seq({1,2})
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF <<1, 2>> \notin Seq({1,2,3}) 
+       THEN Assert(FALSE, "Test 12a Failed")
+       ELSE Print("Test 12a OK", TRUE)
+
+  /\ IF <<1, 2>> \notin Seq({1,2,3}) \cap  Seq({0,1,2})
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF <<1, 2>> \notin Seq({0,1}) \cup  Seq({1,2,3}) 
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 14 OK", TRUE)
+
+  /\ IF <<1, 2>> \in [1..2 ->{1,2,3}] \ [1..2 ->{1,2}]
+       THEN Assert(FALSE, "Test 16 Failed")
+       ELSE Print("Test 16 OK", TRUE)
+
+  /\ IF <<1, 2>> \notin [1..2 ->{1,2,3}] 
+       THEN Assert(FALSE, "Test 17 Failed")
+       ELSE Print("Test 17 OK", TRUE)
+
+  /\ IF <<1, 2>> \notin [1..2 ->{1,2,3}] \cap  [1..2 ->{0,1,2}]
+       THEN Assert(FALSE, "Test 18 Failed")
+       ELSE Print("Test 18 OK", TRUE)
+
+  /\ IF <<1, 2>> \notin [1..2 ->{0,1}] \cup [1..2 ->{1,2,3}] 
+       THEN Assert(FALSE, "Test 19 Failed")
+       ELSE Print("Test 19 OK", TRUE)
+
+  /\ IF ~({1,2,3} \subseteq Int)
+       THEN Assert(FALSE, "Test 20 Failed")
+       ELSE Print("Test 20 OK", TRUE)
+
+  /\ IF {1,2,3} \subseteq {i \in Int : i < 3}
+       THEN Assert(FALSE, "Test 21 Failed")
+       ELSE Print("Test 21 OK", TRUE)
+
+  /\ IF <<1,2>> \in ({1,2} \X Nat) \cup {<<3,4>>, <<5,6>>}
+       THEN Print("Test 22 OK", TRUE)
+       ELSE Assert(FALSE, "Test 22 Failed")
+
+=========================================
diff --git a/tlatools/test-model/suite/test9.cfg b/tlatools/test-model/suite/test9.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947
--- /dev/null
+++ b/tlatools/test-model/suite/test9.cfg
@@ -0,0 +1,8 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
diff --git a/tlatools/test-model/suite/test9.tla b/tlatools/test-model/suite/test9.tla
new file mode 100644
index 0000000000000000000000000000000000000000..baac310c2b3fc7bd5b29fe68216a884c2a1f2ca3
--- /dev/null
+++ b/tlatools/test-model/suite/test9.tla
@@ -0,0 +1,73 @@
+--------------- MODULE test9 -------------
+
+(* Test of set constructors {x \in S : P(x)} and {e(x) : x \in S} *)
+
+EXTENDS Integers, TLC, FiniteSets
+
+VARIABLE x
+Type == x \in BOOLEAN
+Init == x = TRUE
+Next == UNCHANGED x
+
+Inv  ==  
+
+  /\ IF {i \in {1,3,3,3,4} : i > 1} # {4,3}
+       THEN Assert(FALSE, "Test 1 Failed")
+       ELSE Print("Test 1 OK", TRUE)
+
+  /\ IF {<<i, j>> \in {1,2} \X {2,3} : j > i} # {<<1,2>>, <<1,3>>, <<2,3>>}
+       THEN Assert(FALSE, "Test 2 Failed")
+       ELSE Print("Test 2 OK", TRUE)
+
+  /\ IF 2 \notin {i \in Int : i > 1}
+       THEN Assert(FALSE, "Test 3 Failed")
+       ELSE Print("Test 3 OK", TRUE)
+
+  /\ IF <<3,4>> \notin {<<i,j>> \in Int \X Int : j > i}
+       THEN Assert(FALSE, "Test 4 Failed")
+       ELSE Print("Test 4 OK", TRUE)
+
+  /\ IF {i+j+k+l+m  : i, j \in {1, 2, 3}, 
+                      <<k, l>> \in {4,5} \X {6,7}, m \in {8}} # 20..26
+       THEN Assert(FALSE, "Test 5 Failed")
+       ELSE Print("Test 5 OK", TRUE)
+
+  /\ IF 24 \notin {i+j+k+l+m  : i, j \in {1, 2, 3}, 
+                                <<k, l>> \in {4,5} \X {6,7}, m \in {8}} 
+       THEN Assert(FALSE, "Test 6 Failed")
+       ELSE Print("Test 6 OK", TRUE)
+
+  /\ IF {i+j : i \in {1,2,3}, j \in {}} # {}
+       THEN Assert(FALSE, "Test 7 Failed")
+       ELSE Print("Test 7 OK", TRUE)
+
+  /\ IF {i \in {} : i > 2} # {}
+       THEN Assert(FALSE, "Test 8 Failed")
+       ELSE Print("Test 8 OK", TRUE)
+
+  /\ IF {<<i, j>>  \in {1,3} \X {} : i > 2} # {}
+       THEN Assert(FALSE, "Test 9 Failed")
+       ELSE Print("Test 9 OK", TRUE)
+
+  /\ IF <<1,2>> \notin {f \in UNION {[S -> {1,2,3}] : 
+                          S \in SUBSET {1,2,3}} : f[2] = 2}
+       THEN Assert(FALSE, "Test 10 Failed")
+       ELSE Print("Test 10 OK", TRUE)
+
+  /\ IF Cardinality({f \in [{1,2,3} -> {1,2,3}] : f[2] > 1}) # 18
+       THEN Assert(FALSE, "Test 11 Failed")
+       ELSE Print("Test 11 OK", TRUE)
+
+  /\ IF Cardinality([{1,2,3} -> {1,2,3,4}]) # 64
+       THEN Assert(FALSE, "Test 12 Failed")
+       ELSE Print("Test 12 OK", TRUE)
+
+  /\ IF Cardinality([ {5, 5, 5, 3, 5, 2} -> {1, 3, 3, 3} ]) # 8
+       THEN Assert(FALSE, "Test 13 Failed")
+       ELSE Print("Test 13 OK", TRUE)
+
+  /\ IF [ {5, 5, 5, 3, 5, 2} -> {1, 3, 3, 3} ] # [{2,3,5} -> {1,3}]
+       THEN Assert(FALSE, "Test 14 Failed")
+       ELSE Print("Test 14 OK", TRUE)
+
+=========================================
diff --git a/tlatools/test-model/suite/test99.cfg b/tlatools/test-model/suite/test99.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/test99.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test99.tla b/tlatools/test-model/suite/test99.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b020e7eaae3d0e0d844dd2704ef4d4eac81c68cd
--- /dev/null
+++ b/tlatools/test-model/suite/test99.tla
@@ -0,0 +1,89 @@
+------------------------------ MODULE test99 -----------------------------
+(* Test of standard Naturals module.    *)
+
+EXTENDS Integers, TLC
+
+VARIABLES x
+
+Init == x = 0
+
+Next == UNCHANGED x
+
+Inv == 
+(*       /\ IF  2^10 + 2^10 = 2^11
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")    
+
+       /\ IF 0^20 = 0
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")    *)    
+
+       /\ IF 2 - 3 \notin Nat
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF 123 * 345 = 42435
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+       /\ IF 123 < 124
+            THEN Print("Test 5 OK", TRUE)
+            ELSE Assert(FALSE, "Test 5 Failed")
+
+       /\ IF 12345 > 12344
+            THEN Print("Test 6 OK", TRUE)
+            ELSE Assert(FALSE, "Test 6 Failed")
+
+       /\ IF 123 \leq 123
+            THEN Print("Test 7 OK", TRUE)
+            ELSE Assert(FALSE, "Test 7 Failed")
+
+       /\ IF 12345 \geq 12345
+            THEN Print("Test 8 OK", TRUE)
+            ELSE Assert(FALSE, "Test 8 Failed")
+
+       /\ IF 123 \leq 124
+            THEN Print("Test 9 OK", TRUE)
+            ELSE Assert(FALSE, "Test 9 Failed")
+
+       /\ IF 12344 \geq 12333
+            THEN Print("Test 10 OK", TRUE)
+            ELSE Assert(FALSE, "Test 10 Failed")
+
+       /\ IF 145939 = 487 * (145939 \div 487) + (145939 % 487) 
+            THEN Print("Test 11 OK", TRUE)
+            ELSE Assert(FALSE, "Test 11 Failed")  
+
+       /\ IF 139982 \div 1 = 139982 
+            THEN Print("Test 12 OK", TRUE)
+            ELSE Assert(FALSE, "Test 12 Failed")
+
+       /\ IF 123099 % 1 = 0
+            THEN Print("Test 13 OK", TRUE)
+            ELSE Assert(FALSE, "Test 13 Failed")
+
+       /\ IF 0 % 345 = 0
+            THEN Print("Test 14 OK", TRUE)
+            ELSE Assert(FALSE, "Test 14 Failed")  
+
+       /\ IF 24 % 9 = 6
+            THEN Print("Test 15 OK", TRUE)
+            ELSE Assert(FALSE, "Test 15 Failed")   
+
+       /\ IF 4566799 = 423 * (4566799 \div 423) + (4566799 % 423)
+            THEN Print("Test 16 OK", TRUE)
+            ELSE Assert(FALSE, "Test 16 Failed")    
+
+       /\ IF 2222222 = 18 * (2222222 \div 18) + (2222222 % 18)
+            THEN Print("Test 17 OK", TRUE)
+            ELSE Assert(FALSE, "Test 17 Failed")   
+
+       /\ IF 3 .. 2 = {}
+            THEN Print("Test 18 OK", TRUE)
+            ELSE Assert(FALSE, "Test 18 Failed")
+
+       /\ IF 2..4 = {2, 3, 4}
+            THEN Print("Test 19 OK", TRUE)
+            ELSE Assert(FALSE, "Test 19 Failed")
+
+=============================================================================
diff --git a/tlatools/test-model/suite/test999.cfg b/tlatools/test-model/suite/test999.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..eecec9a4f071796a4236f4bf1bf5a1a1e115560e
--- /dev/null
+++ b/tlatools/test-model/suite/test999.cfg
@@ -0,0 +1,15 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+  CONSTANT 
+     P    <- PRep
+     PLen <- Len
+     Seq  <- MCSeq
+     ++   <- PlusPlus
+     Plus <- + 
+     \o   <- +
\ No newline at end of file
diff --git a/tlatools/test-model/suite/test999.tla b/tlatools/test-model/suite/test999.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d28a8963809b6279c5d49487d512fcd9a7c15f4d
--- /dev/null
+++ b/tlatools/test-model/suite/test999.tla
@@ -0,0 +1,47 @@
+--------------- MODULE test999  -------------
+
+(* Test replacement of and by infix operators and overridden operators. *)
+
+EXTENDS TLC, Naturals, Sequences
+
+VARIABLE x
+
+CONSTANT P(_,_), _++_ , Plus(_, _), PLen(_)
+
+PlusPlus(a, b) == <<a, b>>
+
+PRep(a, b) == {a, b}
+
+MCSeq(a) == {a}
+
+MCCat(a, b) == a + b
+
+Init == /\ x = 1
+        /\ IF P(2, x+3) = {2, 4}
+             THEN Print("Test 1 OK", TRUE)
+             ELSE Assert(FALSE, "Test 1 Failed")
+        /\ IF (2++(x+3)) = <<2, 4>>
+             THEN Print("Test 2 OK", TRUE)
+             ELSE Assert(FALSE, "Test 2 Failed")
+        /\ IF PLen(<<1, 2, 3>>) = 3
+             THEN Print("Test 3 OK", TRUE)
+             ELSE Assert(FALSE, "Test 3 Failed")
+        /\ IF Plus(2, x+3) = 6
+             THEN Print("Test 4 OK", TRUE)
+             ELSE Assert(FALSE, "Test 4 Failed")
+        /\ IF Seq(22) = {22}
+             THEN Print("Test 5 OK", TRUE)
+             ELSE Assert(FALSE, "Test 5 Failed")
+        /\ IF 1 \o 2 = 3                          (* Huh???? *)
+             THEN Print("Test 6 OK", TRUE)
+             ELSE Assert(FALSE, "Test 6 Failed")
+        /\ LET a \prec b == a < b
+           IN  IF 1 \prec 2
+                 THEN Print("Test 7 OK", TRUE)
+                 ELSE Assert(FALSE, "Test 7 Failed")
+
+Next == UNCHANGED x
+
+Inv ==  TRUE
+
+=========================================
diff --git a/tlatools/test-model/suite/testa.cfg b/tlatools/test-model/suite/testa.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c
--- /dev/null
+++ b/tlatools/test-model/suite/testa.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+  INVARIANT Inv
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/testa.tla b/tlatools/test-model/suite/testa.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a535e319b8e45f214181388511f29114c626b2f0
--- /dev/null
+++ b/tlatools/test-model/suite/testa.tla
@@ -0,0 +1,5 @@
+------- MODULE testa -----
+
+EXTENDS test, Integers
+
+==========================
diff --git a/tlatools/test-model/suite/testfoo.cfg b/tlatools/test-model/suite/testfoo.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..2a209c125237fe03cfb6a69cde7bd574d490c9ca
--- /dev/null
+++ b/tlatools/test-model/suite/testfoo.cfg
@@ -0,0 +1,11 @@
+  INIT
+    Init
+  
+  NEXT
+    Next
+  
+
+
+
+
+ 
\ No newline at end of file
diff --git a/tlatools/test-model/suite/testfoo.tla b/tlatools/test-model/suite/testfoo.tla
new file mode 100644
index 0000000000000000000000000000000000000000..19ee2927f791114501c75d8bcfe2a00b47930110
--- /dev/null
+++ b/tlatools/test-model/suite/testfoo.tla
@@ -0,0 +1,9 @@
+------------- MODULE test  --------------
+EXTENDS Naturals
+
+VARIABLES x
+
+Init == x \in SUBSET (1..32)
+        
+Next == x' = 1
+=============================================
diff --git a/tlatools/test-model/suite/testt37.tla b/tlatools/test-model/suite/testt37.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f1f6fbbaf012b6ad9b450c2b4f30a940fbcedf27
--- /dev/null
+++ b/tlatools/test-model/suite/testt37.tla
@@ -0,0 +1,26 @@
+---------------------------- MODULE test37 -----------------------------
+EXTENDS FiniteSets, TLC
+
+VARIABLES x
+
+Init == x = 0
+Next == x'=x
+Inv  ==
+       /\ IF IsFiniteSet({"a", "b"})
+            THEN Print("Test 1 OK", TRUE)
+            ELSE Assert(FALSE, "Test 1 Failed")
+
+       /\ IF IsFiniteSet({})
+            THEN Print("Test 2 OK", TRUE)
+            ELSE Assert(FALSE, "Test 2 Failed")
+
+       /\ IF Cardinality({}) = 0
+            THEN Print("Test 3 OK", TRUE)
+            ELSE Assert(FALSE, "Test 3 Failed")
+
+       /\ IF Cardinality({"a", "b"}) = 2
+            THEN Print("Test 4 OK", TRUE)
+            ELSE Assert(FALSE, "Test 4 Failed")
+
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April20a.tla b/tlatools/test-model/symmetry/April20a.tla
new file mode 100644
index 0000000000000000000000000000000000000000..07c10a69a59e08ee76dd3699026556ffca70a101
--- /dev/null
+++ b/tlatools/test-model/symmetry/April20a.tla
@@ -0,0 +1,24 @@
+----------------------------- MODULE April20a -----------------------------
+EXTENDS Integers
+
+CONSTANT S
+
+VARIABLE x, y
+vars == <<x, y>>
+
+Init == (x = 0) /\ (y=0)
+
+Next == \/ /\ y=0
+           /\ y'=1
+           /\ x' \in S
+        \/ /\ y=1
+           /\ y'=2
+           /\ x' \in S \ {x} 
+        \/ /\ y = 2
+           /\ y'=0
+           /\ x' = 0
+
+Spec == Init /\ [][Next]_vars
+
+Live == ~ \A i \in S : \E j \in S \ {i} : []<><<x'=i /\ x=j>>_x
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April20aMC.cfg b/tlatools/test-model/symmetry/April20aMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f
--- /dev/null
+++ b/tlatools/test-model/symmetry/April20aMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+m1 = m1
+m2 = m2
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146115895642519000
+\* SYMMETRY definition
+SYMMETRY symm_146115895643520000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146115895644521000
+\* PROPERTY definition
+PROPERTY
+prop_146115895645522000
diff --git a/tlatools/test-model/symmetry/April20aMC.tla b/tlatools/test-model/symmetry/April20aMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..278a73f775e200b7fb275a6576dd83d52a6bcf35
--- /dev/null
+++ b/tlatools/test-model/symmetry/April20aMC.tla
@@ -0,0 +1,27 @@
+---- MODULE April20aMC ----
+EXTENDS April20a, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+m1, m2
+----
+
+\* MV CONSTANT definitions S
+const_146115895642519000 == 
+{m1, m2}
+----
+
+\* SYMMETRY definition
+symm_146115895643520000 == 
+Permutations(const_146115895642519000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146115895644521000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146115895645522000 ==
+Live
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April20b.tla b/tlatools/test-model/symmetry/April20b.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c0d1e888f6932bdbb22fafc7637cac6fc84241a3
--- /dev/null
+++ b/tlatools/test-model/symmetry/April20b.tla
@@ -0,0 +1,24 @@
+----------------------------- MODULE April20b -----------------------------
+EXTENDS Integers, Sequences
+
+CONSTANT S
+
+VARIABLE x, y
+vars == <<x, y>>
+
+Init == (x = << >>) /\ (y=0)
+
+Next == \/ /\ y=0
+           /\ y'=1
+           /\ \E s \in S : x' = <<s>>
+        \/ /\ y=1
+           /\ y'=2
+           /\ \E s \in S \ {x[1]} : x' = Append(x, s) 
+        \/ /\ y = 2
+           /\ y'=0
+           /\ x' = << >>
+
+Spec == Init /\ [][Next]_vars
+
+Live == ~ \A i \in S : \E j \in S \ {i} : []<>(x = <<i, j>>)
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April20bMC.cfg b/tlatools/test-model/symmetry/April20bMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..850ed89ce61152d41aff311467b5eaf1f40668c2
--- /dev/null
+++ b/tlatools/test-model/symmetry/April20bMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+m1 = m1
+m2 = m2
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146115906599338000
+\* SYMMETRY definition
+SYMMETRY symm_146115906600339000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146115906601340000
+\* PROPERTY definition
+PROPERTY
+prop_146115906602341000
diff --git a/tlatools/test-model/symmetry/April20bMC.tla b/tlatools/test-model/symmetry/April20bMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b2b4059482829ba9fa897613d9fd3424c68d0e36
--- /dev/null
+++ b/tlatools/test-model/symmetry/April20bMC.tla
@@ -0,0 +1,27 @@
+---- MODULE April20bMC ----
+EXTENDS April20b, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+m1, m2
+----
+
+\* MV CONSTANT definitions S
+const_146115906599338000 == 
+{m1, m2}
+----
+
+\* SYMMETRY definition
+symm_146115906600339000 == 
+Permutations(const_146115906599338000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146115906601340000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146115906602341000 ==
+Live
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April21.tla b/tlatools/test-model/symmetry/April21.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e161d36f1abcac6bdf2040b46b3d6f63302a056c
--- /dev/null
+++ b/tlatools/test-model/symmetry/April21.tla
@@ -0,0 +1,15 @@
+----------------------------- MODULE April21 -----------------------------
+EXTENDS Integers
+
+CONSTANT S
+
+VARIABLE x
+
+Init == x \in S
+
+Next == x' \in S
+
+Spec == Init /\ [][Next]_x
+
+Live == \E i \in S : <>[][x # i]_x
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April21MC.cfg b/tlatools/test-model/symmetry/April21MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f
--- /dev/null
+++ b/tlatools/test-model/symmetry/April21MC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+m1 = m1
+m2 = m2
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146115895642519000
+\* SYMMETRY definition
+SYMMETRY symm_146115895643520000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146115895644521000
+\* PROPERTY definition
+PROPERTY
+prop_146115895645522000
diff --git a/tlatools/test-model/symmetry/April21MC.tla b/tlatools/test-model/symmetry/April21MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a3005a9817fa1b7f7a6aa0d940bb3c70d29d6b17
--- /dev/null
+++ b/tlatools/test-model/symmetry/April21MC.tla
@@ -0,0 +1,27 @@
+---- MODULE April21MC ----
+EXTENDS April21, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+m1, m2
+----
+
+\* MV CONSTANT definitions S
+const_146115895642519000 == 
+{m1, m2}
+----
+
+\* SYMMETRY definition
+symm_146115895643520000 == 
+Permutations(const_146115895642519000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146115895644521000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146115895645522000 ==
+Live
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April22.tla b/tlatools/test-model/symmetry/April22.tla
new file mode 100644
index 0000000000000000000000000000000000000000..33bcdfd724e834db56863c4a030277e1e63b8956
--- /dev/null
+++ b/tlatools/test-model/symmetry/April22.tla
@@ -0,0 +1,15 @@
+----------------------------- MODULE April22 -----------------------------
+EXTENDS Integers
+
+CONSTANT S
+
+VARIABLE x
+
+Init == x \in S
+
+Next == x' \in S
+
+Spec == Init /\ [][Next]_x /\ WF_x(Next)
+
+Live == \A i \in S : []<>(x=i)
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April22MC.cfg b/tlatools/test-model/symmetry/April22MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f
--- /dev/null
+++ b/tlatools/test-model/symmetry/April22MC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+m1 = m1
+m2 = m2
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146115895642519000
+\* SYMMETRY definition
+SYMMETRY symm_146115895643520000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146115895644521000
+\* PROPERTY definition
+PROPERTY
+prop_146115895645522000
diff --git a/tlatools/test-model/symmetry/April22MC.tla b/tlatools/test-model/symmetry/April22MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f6536924153fa30d97cabc30fccb41e74b278056
--- /dev/null
+++ b/tlatools/test-model/symmetry/April22MC.tla
@@ -0,0 +1,27 @@
+---- MODULE April22MC ----
+EXTENDS April22, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+m1, m2
+----
+
+\* MV CONSTANT definitions S
+const_146115895642519000 == 
+{m1, m2}
+----
+
+\* SYMMETRY definition
+symm_146115895643520000 == 
+Permutations(const_146115895642519000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146115895644521000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146115895645522000 ==
+Live
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April25.tla b/tlatools/test-model/symmetry/April25.tla
new file mode 100644
index 0000000000000000000000000000000000000000..9dc40bc60ffe1e58b1caf9dd71c34ddd8447bb7e
--- /dev/null
+++ b/tlatools/test-model/symmetry/April25.tla
@@ -0,0 +1,77 @@
+----------------------------- MODULE April25 -----------------------------
+EXTENDS Integers
+
+CONSTANT S
+
+VARIABLE x
+
+Init == x \in S
+
+Next == x' \in S
+
+Spec == Init /\ [][Next]_x
+
+(* 
+   There exists (at least) an element e in S for which variable x in all
+   suffixes of all behaviors is never assigned e. Prefixes can be anything.
+   There can be other elements in S which show a different behavior
+   (even though this is not be the case when the spec/model declares S
+   to be symmetric).
+   
+   A counterexample - to such a liveness claim - is an infinite suffix path
+   with the property that x gets assigned e. This assignment can occur 1 to
+   N times within the infinite suffix path. Any SCC is an infinite suffix
+   path.
+   
+   Assume S={a,b}:
+   
+   This behavior satisfies Live with e bound to b (in Omega notation)
+   ... > (x=a)^w
+   
+   Conversely, this behavior satisfies Live with e bound to a 
+   ... > (x=b)^w
+   
+   Thus, the only infinite path that violates Live is where x is assigned
+   both elements of the set S. This behavior violates Live for both 
+   values a and b.
+   ... > (x=a > x=b)^w
+   
+   Under no symmetry and S consisting out of two elements, the corresponding
+   behavior graph - which shows the above violation - has two nodes. The two
+   nodes are strongly connected. TLC searches the behavior graph for an SCC
+   whose path dissatisfies all action predicates (arc labels) - indicating
+   whether x' # x for each of the values of S evaluates to true.
+   See April25NoSymmetry.dot for an illustration. The illustration omits
+   self loops as they are irrelevant in this context.
+   
+   TLC generates two initial states (x=a and x=b) and for each, explores
+   its successor states. The successors for x=a are {x=a, x=b} and for x=b
+   {x=b, x=a}. Thus, the action predicate x'=e is evaluated for element
+   a *and* b.
+   
+   Gen_init = {x=a, x=b}
+   Gen_succ(x=a) = {x=a, x=b}
+   Gen_succ(x=b) = {x=a, x=b}
+   Arcs_sat = {x=a -> x=b, x=b -> x=a}
+   
+   
+   With symmetry declared on S, the behavior graph is reduced to a single
+   node. This node is logically equivalent to all states of the symmetry
+   set (which is indicated by the label in April25WithSymmetry.dot).
+   Consequently, TLC only generates successor states for a single element
+   of the symmetry set. Thus, it evaluates the action predicate for the
+   chosen element of S only and just adds its corresponding arcs to the
+   behavior graph.
+   
+   Let the initial state for which successor states are generated be x=a.
+   Its successors are x=a and x=b. TLC therefore only adds the arc
+   x=a -> x=b to the behavior graph which satisfies the action predicate
+   for (-[x#a]_x).
+   
+   Gen_init = {x=a, x=b}
+   Gen_succ(x=a) = {x=a, x=b}
+   Gen_succ(x=b) never explored due to symmetry of x=a and x=b.
+   Arcs_sat = {x=a -> x=b}
+*)
+Live == \E e \in S : <>[][x # e]_x
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April25MC.cfg b/tlatools/test-model/symmetry/April25MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f
--- /dev/null
+++ b/tlatools/test-model/symmetry/April25MC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+m1 = m1
+m2 = m2
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146115895642519000
+\* SYMMETRY definition
+SYMMETRY symm_146115895643520000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146115895644521000
+\* PROPERTY definition
+PROPERTY
+prop_146115895645522000
diff --git a/tlatools/test-model/symmetry/April25MC.tla b/tlatools/test-model/symmetry/April25MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..afb14df4b32ecba252df13fe5e791bcf0767da4a
--- /dev/null
+++ b/tlatools/test-model/symmetry/April25MC.tla
@@ -0,0 +1,27 @@
+---- MODULE April25MC ----
+EXTENDS April25, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+m1, m2
+----
+
+\* MV CONSTANT definitions S
+const_146115895642519000 == 
+{m1, m2}
+----
+
+\* SYMMETRY definition
+symm_146115895643520000 == 
+Permutations(const_146115895642519000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146115895644521000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146115895645522000 ==
+Live
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April25NoSymmetry.dot b/tlatools/test-model/symmetry/April25NoSymmetry.dot
new file mode 100644
index 0000000000000000000000000000000000000000..8fe72c150a6b9677c1585a10ad63b57bee89b77a
--- /dev/null
+++ b/tlatools/test-model/symmetry/April25NoSymmetry.dot
@@ -0,0 +1,6 @@
+digraph DiskGraph {
+3247490433077925545 -> 5068397444161466873 [label="[ft]"];
+5068397444161466873 -> 3247490433077925545 [label="[tf]"];
+3247490433077925545 [label="x = m1"];
+5068397444161466873 [label="x = m2"];
+}
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/April25WithSymmetry.dot b/tlatools/test-model/symmetry/April25WithSymmetry.dot
new file mode 100644
index 0000000000000000000000000000000000000000..4942746ec112ea1f5a41a7cec744dbb702e3b990
--- /dev/null
+++ b/tlatools/test-model/symmetry/April25WithSymmetry.dot
@@ -0,0 +1,4 @@
+digraph DiskGraph {
+3247490433077925545 [label="x = m1 /\\ x = m2"];
+3247490433077925545 -> 3247490433077925545 [label="[ft]"];
+}
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/April29.tla b/tlatools/test-model/symmetry/April29.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f7e0cea8a3bc9f11c02cb73256c89822fc1adaae
--- /dev/null
+++ b/tlatools/test-model/symmetry/April29.tla
@@ -0,0 +1,23 @@
+--------------------------- MODULE April29 ---------------------------
+CONSTANT S
+
+VARIABLES x,y
+vars == <<x,y>>
+
+Init == x \in S /\ y = 0
+
+NextA == /\ y = 0
+         /\ y' = 1
+         /\ x' = x
+
+NextB == /\ y = 1
+         /\ y' = 1
+         /\ x' = x
+Spec == Init /\ [][NextA \/ NextB]_vars /\ WF_vars(NextA \/ NextB)
+
+NextC == /\ y = 1
+         /\ y' = 1
+         /\ \E i \in (S \ {x}): x' = i
+SpecD == Init /\ [][NextA \/ NextB \/ NextC]_vars /\ WF_vars(NextA \/ NextB \/ NextC)
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April29MC.cfg b/tlatools/test-model/symmetry/April29MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fa4c00c2199b3370c2b04f8053176aeb78be16bc
--- /dev/null
+++ b/tlatools/test-model/symmetry/April29MC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146183936175119000
+\* SYMMETRY definition
+SYMMETRY symm_146183936176120000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146183936177121000
+\* PROPERTY definition
+PROPERTY
+prop_146183936178122000
diff --git a/tlatools/test-model/symmetry/April29MC.tla b/tlatools/test-model/symmetry/April29MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..7000424b8ffc7d234eaaffc50e0f3fba44b3c856
--- /dev/null
+++ b/tlatools/test-model/symmetry/April29MC.tla
@@ -0,0 +1,27 @@
+---- MODULE April29MC ----
+EXTENDS April29, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_146183936175119000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_146183936176120000 == 
+Permutations(const_146183936175119000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146183936177121000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146183936178122000 ==
+[]<>(x=a) /\ []<>(x=b)
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/April29dMC.cfg b/tlatools/test-model/symmetry/April29dMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c4252e0458359333615cd48b7973a8b1a05d8e21
--- /dev/null
+++ b/tlatools/test-model/symmetry/April29dMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146183938495726000
+\* SYMMETRY definition
+SYMMETRY symm_146183938496727000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146183938497728000
+\* PROPERTY definition
+PROPERTY
+prop_146183938498729000
diff --git a/tlatools/test-model/symmetry/April29dMC.tla b/tlatools/test-model/symmetry/April29dMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cf342a8b9f24c4a985004e1c7cafff69f628e099
--- /dev/null
+++ b/tlatools/test-model/symmetry/April29dMC.tla
@@ -0,0 +1,27 @@
+---- MODULE April29dMC ----
+EXTENDS April29, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_146183938495726000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_146183938496727000 == 
+Permutations(const_146183938495726000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146183938497728000 ==
+SpecD
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146183938498729000 ==
+[]<>(x=a) /\ []<>(x=b)
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetry.tla b/tlatools/test-model/symmetry/ChooseTableauSymmetry.tla
new file mode 100644
index 0000000000000000000000000000000000000000..14ae2fe6835294f1df023f4a019bef612cc9351f
--- /dev/null
+++ b/tlatools/test-model/symmetry/ChooseTableauSymmetry.tla
@@ -0,0 +1,29 @@
+-------------------------- MODULE ChooseTableauSymmetry --------------------------
+CONSTANT Val
+VARIABLE arr
+
+Init == arr = [v \in Val |-> "ready"]
+
+Ready(v) == /\ arr[v] = "ready"
+       	    /\ arr' = [arr EXCEPT ![v]= "busy"]
+
+Busy(v) == /\ arr[v] = "busy"
+       	   /\ arr' = [arr EXCEPT ![v]= "done"]
+
+Done(v) == /\ arr[v] = "done"
+       	   /\ arr' = [arr EXCEPT ![v]= "ready"]
+
+Next == \E v \in Val : Ready(v) \/ Busy(v) \/ Done(v)
+
+Spec == Init /\[][Next]_<<arr>> /\ WF_<<arr>>(Next)
+
+Some     == CHOOSE e \in Val: TRUE
+Other(v) == CHOOSE e \in Val \ {v}: TRUE
+
+Liveness  == LET v == Some
+		     IN (arr[v] = "busy") ~> (arr[v] = "ready")
+
+LivenessO == LET v == Other(Some)
+		     IN (arr[v] = "busy") ~> (arr[v] = "ready")
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.cfg b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..823aee7b2c78dafe6e5cc779c7a2de726802bbba
--- /dev/null
+++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+Val <- const_144481269486926000
+\* SYMMETRY definition
+SYMMETRY symm_144481269487927000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144481269488928000
+\* PROPERTY definition
+PROPERTY
+prop_144481269489929000
diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.tla b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..36b43db6bfdb214fcdf67d5a716386dccea6eff4
--- /dev/null
+++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.tla
@@ -0,0 +1,27 @@
+---- MODULE ChooseTableauSymmetryMC ----
+EXTENDS ChooseTableauSymmetry, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions Val
+const_144481269486926000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_144481269487927000 == 
+Permutations(const_144481269486926000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144481269488928000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144481269489929000 ==
+Liveness
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.cfg b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..823aee7b2c78dafe6e5cc779c7a2de726802bbba
--- /dev/null
+++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+Val <- const_144481269486926000
+\* SYMMETRY definition
+SYMMETRY symm_144481269487927000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144481269488928000
+\* PROPERTY definition
+PROPERTY
+prop_144481269489929000
diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.tla b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.tla
new file mode 100644
index 0000000000000000000000000000000000000000..5f807d01b9677db2b1351565608f6bbeb74dbc86
--- /dev/null
+++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.tla
@@ -0,0 +1,27 @@
+---- MODULE ChooseTableauSymmetryMCa ----
+EXTENDS ChooseTableauSymmetry, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions Val
+const_144481269486926000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_144481269487927000 == 
+Permutations(const_144481269486926000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144481269488928000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144481269489929000 ==
+LivenessO
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/ErrorTraceConstruction.tla b/tlatools/test-model/symmetry/ErrorTraceConstruction.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a07f60b96cb6b4cdf21efb21b1e6b0406fd30124
--- /dev/null
+++ b/tlatools/test-model/symmetry/ErrorTraceConstruction.tla
@@ -0,0 +1,49 @@
+------------------------- MODULE ErrorTraceConstruction -------------------------
+EXTENDS Integers, TLC
+
+S == {0,1}
+Other(n) == CHOOSE x \in S \ {n} : TRUE
+
+VARIABLES x, y
+
+Init == x = 0 /\ y = 0
+
+N0 == /\ y = 0
+	  /\ y' = y+1
+	  /\ x' = x
+N1 == /\ y = 1
+	  /\ y' = y+1
+	  /\ x' = x
+N2 == /\ y = 2
+	  /\ y' = y+1
+	  /\ x' = x
+N3 == /\ y = 3
+	  /\ y' = y+1
+	  /\ x' = x
+N4 == /\ y = 4
+	  /\ y' = y+1
+	  /\ x' = Other(x) \* flip x to violate Prop1
+N5 == /\ y = 5
+	  /\ y' = y+1
+	  /\ x' = Other(x) \* flip x back to reduce the number of overall states
+N6 == /\ y = 6
+	  /\ y' = y+1
+	  /\ x' = x
+N7 == /\ y = 7
+	  /\ y' = 3 \* loop to N3
+	  /\ x' = x
+
+Next == \/ N0
+		\/ N1
+		\/ N2
+		\/ N3
+		\/ N4
+		\/ N5
+		\/ N6
+		\/ N7
+           
+Spec == Init /\ [][Next]_<<x, y>> /\ WF_<<x,y>>(Next)
+
+Prop == <>[][x'=x]_<<x, y>>
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/ErrorTraceConstructionMC.cfg b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8a9daaec38db0a8f607f72ea76dad6e58afc573a
--- /dev/null
+++ b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.cfg
@@ -0,0 +1,6 @@
+\* SPECIFICATION definition
+SPECIFICATION
+spec_14411937652404000
+\* PROPERTY definition
+PROPERTY
+prop_14411937652505000
diff --git a/tlatools/test-model/symmetry/ErrorTraceConstructionMC.tla b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..17c7bd11b547e285884077de5a4bf8ea6beeb271
--- /dev/null
+++ b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.tla
@@ -0,0 +1,12 @@
+---- MODULE ErrorTraceConstructionMC ----
+EXTENDS ErrorTraceConstruction, TLC
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_14411937652404000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_14411937652505000 ==
+Prop
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png b/tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a87f95c1080372fea4548fa09851cdb997b9303
Binary files /dev/null and b/tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png differ
diff --git a/tlatools/test-model/symmetry/LongMC.cfg b/tlatools/test-model/symmetry/LongMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..edacc0bc0474c6c8a2fe290ac875c9641fed7e4c
--- /dev/null
+++ b/tlatools/test-model/symmetry/LongMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_143263724205431000
+\* SYMMETRY definition
+SYMMETRY symm_143263724206532000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_143263724207533000
+\* PROPERTY definition
+PROPERTY
+prop_143263724208534000
diff --git a/tlatools/test-model/symmetry/LongMC.tla b/tlatools/test-model/symmetry/LongMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d606b420e83bc9e9b8049cfa86388162b90d1620
--- /dev/null
+++ b/tlatools/test-model/symmetry/LongMC.tla
@@ -0,0 +1,27 @@
+---- MODULE LongMC ----
+EXTENDS SymmetryLivenessLong, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_143263724205431000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_143263724206532000 == 
+Permutations(const_143263724205431000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_143263724207533000 ==
+Spec2
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_143263724208534000 ==
+Prop1
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/LongMCa.cfg b/tlatools/test-model/symmetry/LongMCa.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..edacc0bc0474c6c8a2fe290ac875c9641fed7e4c
--- /dev/null
+++ b/tlatools/test-model/symmetry/LongMCa.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_143263724205431000
+\* SYMMETRY definition
+SYMMETRY symm_143263724206532000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_143263724207533000
+\* PROPERTY definition
+PROPERTY
+prop_143263724208534000
diff --git a/tlatools/test-model/symmetry/LongMCa.tla b/tlatools/test-model/symmetry/LongMCa.tla
new file mode 100644
index 0000000000000000000000000000000000000000..dadfdfbb68daff61b295a9146aafc17171114beb
--- /dev/null
+++ b/tlatools/test-model/symmetry/LongMCa.tla
@@ -0,0 +1,27 @@
+---- MODULE LongMCa ----
+EXTENDS SymmetryLivenessLong, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_143263724205431000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_143263724206532000 == 
+Permutations(const_143263724205431000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_143263724207533000 ==
+Spec1
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_143263724208534000 ==
+Prop1
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/MC.cfg b/tlatools/test-model/symmetry/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ed287f880ec454f7d7e3019a83302a49af39c8fd
--- /dev/null
+++ b/tlatools/test-model/symmetry/MC.cfg
@@ -0,0 +1,16 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_143263724205431000
+\* SYMMETRY definition
+SYMMETRY symm_143263724206532000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_143263724207533000
+\* PROPERTY definition
+PROPERTY
+prop_143263724208534000
+\* Generated on Tue May 26 12:47:22 CEST 2015
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/MC.tla b/tlatools/test-model/symmetry/MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b98e4a2db2d1f62ecc6558a4ee461562d3b4f7fc
--- /dev/null
+++ b/tlatools/test-model/symmetry/MC.tla
@@ -0,0 +1,29 @@
+---- MODULE MC ----
+EXTENDS SymmetryLiveness3, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_143263724205431000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_143263724206532000 == 
+Permutations(const_143263724205431000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_143263724207533000 ==
+Spec2
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_143263724208534000 ==
+Prop1
+----
+=============================================================================
+\* Modification History
+\* Created Tue May 26 12:47:22 CEST 2015 by markus
diff --git a/tlatools/test-model/symmetry/MC_Graph.png b/tlatools/test-model/symmetry/MC_Graph.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca330f79ea241615b55cca76c3b6ab3c3acd8ead
Binary files /dev/null and b/tlatools/test-model/symmetry/MC_Graph.png differ
diff --git a/tlatools/test-model/symmetry/MCa.cfg b/tlatools/test-model/symmetry/MCa.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ed287f880ec454f7d7e3019a83302a49af39c8fd
--- /dev/null
+++ b/tlatools/test-model/symmetry/MCa.cfg
@@ -0,0 +1,16 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_143263724205431000
+\* SYMMETRY definition
+SYMMETRY symm_143263724206532000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_143263724207533000
+\* PROPERTY definition
+PROPERTY
+prop_143263724208534000
+\* Generated on Tue May 26 12:47:22 CEST 2015
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/MCa.tla b/tlatools/test-model/symmetry/MCa.tla
new file mode 100644
index 0000000000000000000000000000000000000000..0520dcaadc07e35e01de4649b9a747e5efa81e83
--- /dev/null
+++ b/tlatools/test-model/symmetry/MCa.tla
@@ -0,0 +1,29 @@
+---- MODULE MCa ----
+EXTENDS SymmetryLiveness3, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_143263724205431000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_143263724206532000 == 
+Permutations(const_143263724205431000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_143263724207533000 ==
+Spec1
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_143263724208534000 ==
+Prop1
+----
+=============================================================================
+\* Modification History
+\* Created Tue May 26 12:47:22 CEST 2015 by markus
diff --git a/tlatools/test-model/symmetry/MCa_Graph.png b/tlatools/test-model/symmetry/MCa_Graph.png
new file mode 100644
index 0000000000000000000000000000000000000000..c4944d2074755519584364eef815feca0f8d6c7a
Binary files /dev/null and b/tlatools/test-model/symmetry/MCa_Graph.png differ
diff --git a/tlatools/test-model/symmetry/May09.tla b/tlatools/test-model/symmetry/May09.tla
new file mode 100644
index 0000000000000000000000000000000000000000..600ec7b10a4247105b3309763f922925b0b3ac03
--- /dev/null
+++ b/tlatools/test-model/symmetry/May09.tla
@@ -0,0 +1,29 @@
+------------------------- MODULE May09 -------------------------
+EXTENDS Integers
+CONSTANT S
+
+VARIABLES x,y
+vars == <<x,y>>
+
+Init == x \in S /\ y = 0
+
+NextA == /\ y = 0
+         /\ y' = 1
+         /\ x' = x
+
+NextB == /\ y >= 1
+         /\ y < 5
+         /\ y' = y + 1
+         /\ x' = x
+         
+NextC == /\ y = 5
+         /\ y' = 1
+         /\ x' = x
+Spec == Init /\ [][NextA \/ NextB \/ NextC]_vars /\ WF_vars(NextA \/ NextB \/ NextC)
+
+NextD == /\ y = 5
+         /\ y' = 1
+         /\ \E i \in (S \ {x}) : x' = i
+SpecD == Init /\ [][NextA \/ NextB \/ NextD]_vars /\ WF_vars(NextA \/ NextB \/ NextD)
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/May09MC.cfg b/tlatools/test-model/symmetry/May09MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fa4c00c2199b3370c2b04f8053176aeb78be16bc
--- /dev/null
+++ b/tlatools/test-model/symmetry/May09MC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146183936175119000
+\* SYMMETRY definition
+SYMMETRY symm_146183936176120000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146183936177121000
+\* PROPERTY definition
+PROPERTY
+prop_146183936178122000
diff --git a/tlatools/test-model/symmetry/May09MC.tla b/tlatools/test-model/symmetry/May09MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..31fc76203af855b7aaf0a50da3a18ae0c28bcdd5
--- /dev/null
+++ b/tlatools/test-model/symmetry/May09MC.tla
@@ -0,0 +1,27 @@
+---- MODULE May09MC ----
+EXTENDS May09, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_146183936175119000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_146183936176120000 == 
+Permutations(const_146183936175119000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146183936177121000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146183936178122000 ==
+[]<>(x=a) /\ []<>(x=b)
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/May09dMC.cfg b/tlatools/test-model/symmetry/May09dMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fa4c00c2199b3370c2b04f8053176aeb78be16bc
--- /dev/null
+++ b/tlatools/test-model/symmetry/May09dMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_146183936175119000
+\* SYMMETRY definition
+SYMMETRY symm_146183936176120000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146183936177121000
+\* PROPERTY definition
+PROPERTY
+prop_146183936178122000
diff --git a/tlatools/test-model/symmetry/May09dMC.tla b/tlatools/test-model/symmetry/May09dMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f829b215add2d9f174db5b93a76a33828a463672
--- /dev/null
+++ b/tlatools/test-model/symmetry/May09dMC.tla
@@ -0,0 +1,27 @@
+---- MODULE May09dMC ----
+EXTENDS May09, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_146183936175119000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_146183936176120000 == 
+Permutations(const_146183936175119000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146183936177121000 ==
+SpecD
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146183936178122000 ==
+[]<>(x=a) /\ []<>(x=b)
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/NQ/MC.cfg b/tlatools/test-model/symmetry/NQ/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6c49e8e3722e6db56fb627cc0d6e35119a30e55a
--- /dev/null
+++ b/tlatools/test-model/symmetry/NQ/MC.cfg
@@ -0,0 +1,49 @@
+\* MV CONSTANT declarations
+CONSTANTS
+e1 = e1
+e2 = e2
+\* MV CONSTANT declarations
+CONSTANTS
+i1 = i1
+i2 = i2
+\* MV CONSTANT declarations
+CONSTANTS
+d1 = d1
+\* MV CONSTANT declarations
+CONSTANTS
+v1 = v1
+v2 = v2
+\* MV CONSTANT definitions
+CONSTANT
+EnQers <- const_14467714432321108000
+\* MV CONSTANT definitions
+CONSTANT
+Ids <- const_14467714432421109000
+\* MV CONSTANT definitions
+CONSTANT
+DeQers <- const_14467714432521110000
+\* MV CONSTANT definitions
+CONSTANT
+Data <- const_14467714432621111000
+\* SYMMETRY definition
+SYMMETRY symm_14467714432721112000
+\* CONSTANT definitions
+CONSTANT
+InitData <- const_14467714432821113000
+\* CONSTANT definition
+CONSTANT
+Done = Done
+Busy = Busy
+NotAnId = NotAnId
+NotData = NotData
+NotAnElement = NotAnElement
+\* SPECIFICATION definition
+SPECIFICATION
+spec_14467714433421119000
+\* INVARIANT definition
+INVARIANT
+inv_14467714433521120000
+\* PROPERTY definition
+PROPERTY
+prop_14467714433621121000
+\* Generated on Thu Nov 05 16:57:23 PST 2015
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/NQ/MC.tla b/tlatools/test-model/symmetry/NQ/MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..78bbe6b0fee0b7560146e68c33eaae4b0f0a675f
--- /dev/null
+++ b/tlatools/test-model/symmetry/NQ/MC.tla
@@ -0,0 +1,68 @@
+---- MODULE MC ----
+EXTENDS NQSpec, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+e1, e2
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+i1, i2
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+d1
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+v1, v2
+----
+
+\* MV CONSTANT definitions EnQers
+const_14467714432321108000 == 
+{e1, e2}
+----
+
+\* MV CONSTANT definitions Ids
+const_14467714432421109000 == 
+{i1, i2}
+----
+
+\* MV CONSTANT definitions DeQers
+const_14467714432521110000 == 
+{d1}
+----
+
+\* MV CONSTANT definitions Data
+const_14467714432621111000 == 
+{v1, v2}
+----
+
+\* SYMMETRY definition
+symm_14467714432721112000 == 
+Permutations(const_14467714432321108000) \union Permutations(const_14467714432521110000)
+----
+
+\* CONSTANT definitions @modelParameterConstants:4InitData
+const_14467714432821113000 == 
+v1
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_14467714433421119000 ==
+Spec
+----
+\* INVARIANT definition @modelCorrectnessInvariants:0
+inv_14467714433521120000 ==
+TypeOK
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_14467714433621121000 ==
+(\E e \in elts : e.data = v2) ~> (\E d \in DeQers : deq[d] = v2)
+----
+=============================================================================
+\* Modification History
+\* Created Thu Nov 05 16:57:23 PST 2015 by lamport
diff --git a/tlatools/test-model/symmetry/NQ/MCa.cfg b/tlatools/test-model/symmetry/NQ/MCa.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b27f9c68e77d42d8c8a70f4e8b5d7170c5ddf283
--- /dev/null
+++ b/tlatools/test-model/symmetry/NQ/MCa.cfg
@@ -0,0 +1,49 @@
+\* MV CONSTANT declarations
+CONSTANTS
+e1 = e1
+e2 = e2
+\* MV CONSTANT declarations
+CONSTANTS
+i1 = i1
+i2 = i2
+\* MV CONSTANT declarations
+CONSTANTS
+d1 = d1
+\* MV CONSTANT declarations
+CONSTANTS
+v1 = v1
+v2 = v2
+\* MV CONSTANT definitions
+CONSTANT
+EnQers <- const_14467710209401026000
+\* MV CONSTANT definitions
+CONSTANT
+Ids <- const_14467710209501027000
+\* MV CONSTANT definitions
+CONSTANT
+DeQers <- const_14467710209601028000
+\* MV CONSTANT definitions
+CONSTANT
+Data <- const_14467710209701029000
+\* SYMMETRY definition
+SYMMETRY symm_14467710209801030000
+\* CONSTANT definitions
+CONSTANT
+InitData <- const_14467710209901031000
+\* CONSTANT definition
+CONSTANT
+Done = Done
+Busy = Busy
+NotAnId = NotAnId
+NotData = NotData
+NotAnElement = NotAnElement
+\* SPECIFICATION definition
+SPECIFICATION
+spec_14467710210501037000
+\* INVARIANT definition
+INVARIANT
+inv_14467710210601038000
+\* PROPERTY definition
+PROPERTY
+prop_14467710210701039000
+\* Generated on Thu Nov 05 16:50:21 PST 2015
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/NQ/MCa.tla b/tlatools/test-model/symmetry/NQ/MCa.tla
new file mode 100644
index 0000000000000000000000000000000000000000..76e9f8de9d9e3def7841e3fcee240631ea6972fd
--- /dev/null
+++ b/tlatools/test-model/symmetry/NQ/MCa.tla
@@ -0,0 +1,68 @@
+---- MODULE MCa ----
+EXTENDS NQSpec, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+e1, e2
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+i1, i2
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+d1
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+v1, v2
+----
+
+\* MV CONSTANT definitions EnQers
+const_14467710209401026000 == 
+{e1, e2}
+----
+
+\* MV CONSTANT definitions Ids
+const_14467710209501027000 == 
+{i1, i2}
+----
+
+\* MV CONSTANT definitions DeQers
+const_14467710209601028000 == 
+{d1}
+----
+
+\* MV CONSTANT definitions Data
+const_14467710209701029000 == 
+{v1, v2}
+----
+
+\* SYMMETRY definition
+symm_14467710209801030000 == 
+Permutations(const_14467710209401026000) \union Permutations(const_14467710209601028000)
+----
+
+\* CONSTANT definitions @modelParameterConstants:4InitData
+const_14467710209901031000 == 
+v1
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_14467710210501037000 ==
+Spec
+----
+\* INVARIANT definition @modelCorrectnessInvariants:0
+inv_14467710210601038000 ==
+TypeOK
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_14467710210701039000 ==
+(\E e \in EnQers : enq[e] = v2) ~> (\E d \in DeQers : deq[d] = v2)
+----
+=============================================================================
+\* Modification History
+\* Created Thu Nov 05 16:50:21 PST 2015 by lamport
diff --git a/tlatools/test-model/symmetry/NQ/NQSpec.tla b/tlatools/test-model/symmetry/NQ/NQSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..09774ed31418f2d0b6d970ac2b57a317332c2557
--- /dev/null
+++ b/tlatools/test-model/symmetry/NQ/NQSpec.tla
@@ -0,0 +1,84 @@
+------------------------------- MODULE NQSpec -------------------------------
+EXTENDS Integers, Sequences
+
+CONSTANTS EnQers, DeQers, Data, InitData, Ids
+
+ASSUME InitData \in Data
+
+Done == CHOOSE D : D \notin Data
+Busy == CHOOSE D : D \notin Data
+NotAnId == CHOOSE i : i \notin Ids
+NotData == CHOOSE D : D \notin Data
+Elements == [data : Data, id : Ids]
+NotAnElement == CHOOSE E : E \notin Elements
+
+VARIABLES enq, deq, elts, after, adding
+vars == <<enq, deq, elts, after, adding>>
+
+
+
+TypeOK == /\ enq \in [EnQers -> Data \cup {Done}]
+          /\ deq \in [DeQers -> Data \cup {Busy}]
+          /\ elts \in SUBSET Elements
+          /\ after \in [elts -> SUBSET elts]
+          /\ adding \in [EnQers -> Elements \cup {NotAnElement}]
+
+Init == /\ enq = [e \in EnQers |-> Done]
+        /\ deq = [d \in DeQers |-> InitData]
+        /\ elts = {}
+        /\ after = << >>
+        /\ adding = [e \in EnQers |-> NotAnElement]
+-----------------------------------------------------------------------------
+
+Assign(var, idx, val) == var' = [var EXCEPT ![idx] = val]
+
+UnusedIds == Ids \ {el.id : el \in elts}
+
+AddElt(el) == 
+  /\ elts' = elts \cup {el}
+  /\ after' = [x \in elts' |-> 
+                 IF x = el THEN elts \ {adding[e] : e \in EnQers}
+                           ELSE after[x] ]
+                           
+RemoveElt(el) == 
+  /\ elts' = elts \ {el}
+  /\ after' = [x \in elts' |-> after[x] \ {el}]
+
+minimalElts == {el \in elts : after[el] = {}}
+      
+BeginEnq(e) == /\ enq[e] = Done 
+               /\ \E D \in Data, id \in UnusedIds : 
+                     LET el == [data |-> D, id |-> id]
+                     IN  /\ Assign(enq, e, D)
+                         /\ AddElt(el)
+                         /\ Assign(adding, e, el)
+               /\ UNCHANGED deq
+                           
+EndEnq(e) == /\ enq[e] # Done
+             /\ Assign(enq, e, Done)
+             /\ Assign(adding, e, NotAnElement)
+             /\ UNCHANGED <<deq, elts, after>>
+
+\*  enq, deq, elts, after, adding
+BeginDeq(d) == /\ deq[d] # Busy
+               /\ Assign(deq, d, Busy)
+               /\ UNCHANGED <<enq, elts, after, adding>>
+               
+EndDeq(d) == /\ deq[d] = Busy
+             /\ \E el \in minimalElts :
+                  /\ RemoveElt(el)
+                  /\ Assign(deq, d, el.data)
+             /\ UNCHANGED <<enq, adding>>
+             
+Next == \/ \E e \in EnQers : BeginEnq(e)  \/ EndEnq(e)
+        \/ \E d \in DeQers : BeginDeq(d)  \/ EndDeq(d)
+
+Liveness == /\ \A e \in EnQers : WF_vars(BeginEnq(e)  \/ EndEnq(e))
+            /\ \A d \in DeQers :  WF_vars(BeginDeq(d)  \/ EndDeq(d))
+            
+Spec == Init /\ [][Next]_vars /\ Liveness
+
+=============================================================================
+\* Modification History
+\* Last modified Thu Nov 05 16:16:15 PST 2015 by lamport
+\* Created Thu Nov 05 15:07:25 PST 2015 by lamport
diff --git a/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.cfg b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..95c4dce4083eb2edfa412ba1459dfb5803ab183a
--- /dev/null
+++ b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.cfg
@@ -0,0 +1,31 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT declarations
+CONSTANTS
+c = c
+d = d
+\* MV CONSTANT declarations
+CONSTANTS
+e = e
+f = f
+\* MV CONSTANT definitions
+CONSTANT
+Val <- const_144172196716899000
+\* MV CONSTANT definitions
+CONSTANT
+Proc <- const_1441721967178100000
+\* MV CONSTANT definitions
+CONSTANT
+Adr <- const_1441721967189101000
+\* CONSTANT definition
+CONSTANT
+NoVal = NoVal
+\* SPECIFICATION definition
+SPECIFICATION
+spec_1441721967209103000
+\* PROPERTY definition
+PROPERTY
+prop_1441721967219104000
+\* Generated on Tue Sep 08 16:19:27 CEST 2015
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.tla b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..8427d29d68cd968144d758e9daa70911fa658344
--- /dev/null
+++ b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.tla
@@ -0,0 +1,42 @@
+---- MODULE NoSymmetryLivenessTableauMC ----
+EXTENDS SymmetryLivenessTableau, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+c, d
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+e, f
+----
+
+\* MV CONSTANT definitions Val
+const_144172196716899000 == 
+{a, b}
+----
+
+\* MV CONSTANT definitions Proc
+const_1441721967178100000 == 
+{c, d}
+----
+
+\* MV CONSTANT definitions Adr
+const_1441721967189101000 == 
+{e, f}
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_1441721967209103000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_1441721967219104000 ==
+Liveness
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutex.tla b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutex.tla
new file mode 100644
index 0000000000000000000000000000000000000000..242889c543ef70f26f7dbdffe82132c8983176dc
--- /dev/null
+++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutex.tla
@@ -0,0 +1,141 @@
+---------------------------- MODULE OneBitMutex ----------------------------
+EXTENDS Integers
+CONSTANT Procs
+
+(***************************************************************************
+--algorithm OneBitMutex
+	{ 
+		variable x = [i \in Procs |-> FALSE];
+		fair process (p \in Procs)
+			variables unchecked = {};
+			other \in Procs ;
+			{ 
+				ncs:- while (TRUE)
+				{
+				e1: x[self] := TRUE ;
+					unchecked := Procs \ {self};
+				e2: while (unchecked # {})
+					    {
+						with (i \in unchecked) { other := i } ;
+						unchecked := unchecked \ {other};
+					e3: if (x[other])
+						{ 
+						  	if (TRUE) \* Changed from "self > other" to "TRUE"
+							{ 
+							e4: x[self] := FALSE;
+							e5: await ~x[other];
+								goto e1;
+							}
+						    else
+						    { 
+							e6: await ~x[other];
+							}
+						};
+					} ;
+				cs: skip;
+				f: x[self] := FALSE
+			}
+	}
+}
+ ***************************************************************************)
+
+\* BEGIN TRANSLATION
+VARIABLES x, pc, unchecked, other
+
+vars == << x, pc, unchecked, other >>
+
+ProcSet == (Procs)
+
+Init == (* Global variables *)
+        /\ x = [i \in Procs |-> FALSE]
+        (* Process p *)
+        /\ unchecked = [self \in Procs |-> {}]
+        /\ other \in [Procs -> Procs]
+        /\ pc = [self \in ProcSet |-> "ncs"]
+
+ncs(self) == /\ pc[self] = "ncs"
+             /\ pc' = [pc EXCEPT ![self] = "e1"]
+             /\ UNCHANGED << x, unchecked, other >>
+
+e1(self) == /\ pc[self] = "e1"
+            /\ x' = [x EXCEPT ![self] = TRUE]
+            /\ unchecked' = [unchecked EXCEPT ![self] = Procs \ {self}]
+            /\ pc' = [pc EXCEPT ![self] = "e2"]
+            /\ other' = other
+
+e2(self) == /\ pc[self] = "e2"
+            /\ IF unchecked[self] # {}
+                  THEN /\ \E i \in unchecked[self]:
+                            other' = [other EXCEPT ![self] = i]
+                       /\ unchecked' = [unchecked EXCEPT ![self] = unchecked[self] \ {other'[self]}]
+                       /\ pc' = [pc EXCEPT ![self] = "e3"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "cs"]
+                       /\ UNCHANGED << unchecked, other >>
+            /\ x' = x
+
+e3(self) == /\ pc[self] = "e3"
+            /\ IF x[other[self]]
+                  THEN /\ IF TRUE
+                             THEN /\ pc' = [pc EXCEPT ![self] = "e4"]
+                             ELSE /\ pc' = [pc EXCEPT ![self] = "e6"]
+                  ELSE /\ pc' = [pc EXCEPT ![self] = "e2"]
+            /\ UNCHANGED << x, unchecked, other >>
+
+e4(self) == /\ pc[self] = "e4"
+            /\ x' = [x EXCEPT ![self] = FALSE]
+            /\ pc' = [pc EXCEPT ![self] = "e5"]
+            /\ UNCHANGED << unchecked, other >>
+
+e5(self) == /\ pc[self] = "e5"
+            /\ ~x[other[self]]
+            /\ pc' = [pc EXCEPT ![self] = "e1"]
+            /\ UNCHANGED << x, unchecked, other >>
+
+e6(self) == /\ pc[self] = "e6"
+            /\ ~x[other[self]]
+            /\ pc' = [pc EXCEPT ![self] = "e2"]
+            /\ UNCHANGED << x, unchecked, other >>
+
+cs(self) == /\ pc[self] = "cs"
+            /\ TRUE
+            /\ pc' = [pc EXCEPT ![self] = "f"]
+            /\ UNCHANGED << x, unchecked, other >>
+
+f(self) == /\ pc[self] = "f"
+           /\ x' = [x EXCEPT ![self] = FALSE]
+           /\ pc' = [pc EXCEPT ![self] = "ncs"]
+           /\ UNCHANGED << unchecked, other >>
+
+p(self) == ncs(self) \/ e1(self) \/ e2(self) \/ e3(self) \/ e4(self)
+              \/ e5(self) \/ e6(self) \/ cs(self) \/ f(self)
+
+Next == (\E self \in Procs: p(self))
+
+Spec == /\ Init /\ [][Next]_vars
+        /\ \A self \in Procs : WF_vars((pc[self] # "ncs") /\ p(self))
+
+\* END TRANSLATION
+
+-----------------------------------------------------------------------------
+TypeOK == /\ pc \in [{0,1} -> {"r", "e1", "e2", "cs"}]
+		  /\ x \in [{0,1} -> BOOLEAN]
+
+Past(i,j) == \/ ((pc[i] = "e2") /\ (j \notin unchecked[i]))
+			 \/ /\ pc[i] \in {"e3", "e6"}
+			    /\ j \notin unchecked[i] \cup {other[i]}
+			 \/ pc[i] = "cs"
+
+Inv == /\ TypeOK
+	   /\ \A i \in Procs :
+	   		/\ (pc[i] \in {"e2", "e3", "e6", "cs"}) => x[i]
+       		/\ \A j \in Procs \ {i} :
+						Past(i, j) => ~Past(j, i) /\ x[i]
+
+
+(***************************************************************************
+ N-process One-Bit mutual exclusion is _not_ starvation free!
+ ***************************************************************************)
+StarvationFreedom == \A e \in Procs : pc[e] = "e2" ~> pc[e] = "cs"
+
+ 
+=============================================================================
diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.cfg b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..5b31cea12d04a107fda0619f30b270e06e33179b
--- /dev/null
+++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+A = A
+B = B
+\* MV CONSTANT definitions
+CONSTANT
+Procs <- const_144491423291817000
+\* SYMMETRY definition
+SYMMETRY symm_144481269487927000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144491423292818000
+\* PROPERTY definition
+PROPERTY
+prop_144491423293819000
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.tla b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e81b50c63ee43ca0d7bd806cfc0f49c0b16ca962
--- /dev/null
+++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.tla
@@ -0,0 +1,27 @@
+---- MODULE OneBitMutexMC ----
+EXTENDS OneBitMutex, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+A, B
+----
+
+\* MV CONSTANT definitions Procs
+const_144491423291817000 == 
+{A, B}
+----
+
+\* SYMMETRY definition
+symm_144481269487927000 == 
+Permutations(const_144491423291817000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144491423292818000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144491423293819000 ==
+StarvationFreedom
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.cfg b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..aaaa39d3089a0410098ef62bec4ba63d040d030e
--- /dev/null
+++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.cfg
@@ -0,0 +1,13 @@
+\* MV CONSTANT declarations
+CONSTANTS
+A = A
+B = B
+\* MV CONSTANT definitions
+CONSTANT
+Procs <- const_144491423291817000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144491423292818000
+\* PROPERTY definition
+PROPERTY
+prop_144491423293819000
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.tla b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e5d1f68dd4e98e537e246aaca1089504e6b5750a
--- /dev/null
+++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.tla
@@ -0,0 +1,22 @@
+---- MODULE OneBitMutexNoSymmetryMC ----
+EXTENDS OneBitMutex, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+A, B
+----
+
+\* MV CONSTANT definitions Procs
+const_144491423291817000 == 
+{A, B}
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144491423292818000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144491423293819000 ==
+StarvationFreedom
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/SymmetryLiveness3.tla b/tlatools/test-model/symmetry/SymmetryLiveness3.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2e0cb3f2576ce119eccb5f0fab427ab1352e35b1
--- /dev/null
+++ b/tlatools/test-model/symmetry/SymmetryLiveness3.tla
@@ -0,0 +1,34 @@
+------------------------- MODULE SymmetryLiveness3 -------------------------
+EXTENDS Integers
+CONSTANT S
+Other(n) == CHOOSE x \in S \ {n} : TRUE
+
+VARIABLES x, y
+
+Init == x \in S /\ y = 0
+
+\* MCa (SymmetryModelCheckerTest3a)
+N1 ==   \/ /\ y = 0
+           /\ y' = 1
+           /\ x' = x
+        \/ /\ y = 1
+           /\ y' = 0
+           /\ x' = Other(x)
+Spec1 == Init /\ [][N1]_<<x, y>> /\ WF_<<x,y>>(N1) \* Violates Prop1, but TLC produces bogus trace
+
+\* MC (SymmetryModelCheckerTest3)
+\* Contrary to N1, x' either stays unchanged OR assumes Other(x)
+N2 ==   \/ /\ y = 0
+           /\ y' = 1
+           /\ x' = x
+        \/ /\ y = 1
+           /\ y' = 0
+           /\ x' = x
+        \/ /\ y = 1
+           /\ y' = 0
+           /\ x' = Other(x)
+Spec2 == Init /\ [][N2]_<<x, y>> /\ WF_<<x,y>>(N2) \* Fails to find error in Prop1
+
+
+Prop1 == <>[][x'=x]_<<x, y>>
+=============================================================================
diff --git a/tlatools/test-model/symmetry/SymmetryLivenessLong.tla b/tlatools/test-model/symmetry/SymmetryLivenessLong.tla
new file mode 100644
index 0000000000000000000000000000000000000000..3606cf58c05ab61b9938dbbe9fc5818edb82dcc8
--- /dev/null
+++ b/tlatools/test-model/symmetry/SymmetryLivenessLong.tla
@@ -0,0 +1,54 @@
+------------------------- MODULE SymmetryLivenessLong -------------------------
+EXTENDS Integers
+CONSTANT S
+Other(n) == CHOOSE x \in S \ {n} : TRUE
+
+VARIABLES x, y
+
+Init == x \in S /\ y = 0
+
+\* Contrary to SymmetryLiveness3, this produces a long trace which results in LiveWorker#bfsPostFix finding a longer path.
+
+\* MCa (SymmetryModelCheckerTestLonga)
+N1 ==   \/ /\ y = 0
+           /\ y' = 1
+           /\ x' = x
+        \/ /\ y = 1
+           /\ y' = 2
+           /\ x' = x
+        \/ /\ y = 2
+           /\ y' = 3
+           /\ x' = x
+        \/ /\ y = 3
+           /\ y' = 4
+           /\ x' = x
+        \/ /\ y = 4
+           /\ y' = 0
+           /\ x' = Other(x)
+Spec1 == Init /\ [][N1]_<<x, y>> /\ WF_<<x,y>>(N1) \* Violates Prop1, but TLC produces bogus trace
+
+\* MC (SymmetryModelCheckerTestLong)
+\* Contrary to N1, x' either stays unchanged OR assumes Other(x)
+N2 ==   \/ /\ y = 0
+           /\ y' = 1
+           /\ x' = x
+        \/ /\ y = 1
+           /\ y' = 2
+           /\ x' = x
+        \/ /\ y = 2
+           /\ y' = 3
+           /\ x' = x
+        \/ /\ y = 3
+           /\ y' = 4
+           /\ x' = x
+        \/ /\ y = 4
+           /\ y' = 0
+           /\ x' = x
+        \/ /\ y = 4
+           /\ y' = 0
+           /\ x' = Other(x)
+Spec2 == Init /\ [][N2]_<<x, y>> /\ WF_<<x,y>>(N2) \* Fails to find error in Prop1
+
+
+Prop1 == <>[][x'=x]_<<x, y>>
+=============================================================================
diff --git a/tlatools/test-model/symmetry/SymmetryLivenessTableau.tla b/tlatools/test-model/symmetry/SymmetryLivenessTableau.tla
new file mode 100644
index 0000000000000000000000000000000000000000..a1cc65b0d45bad2696ca299e10fe4b2a6f575bc9
--- /dev/null
+++ b/tlatools/test-model/symmetry/SymmetryLivenessTableau.tla
@@ -0,0 +1,66 @@
+---------------------- MODULE SymmetryLivenessTableau ----------------------
+VARIABLES mem, ctl, buf
+VARIABLE memInt
+CONSTANTS  Proc,  
+           Adr,  
+           Val
+
+NoVal == CHOOSE x : x \notin Val 
+InitMemInt == {<<p, NoVal>> : p \in Proc}
+Send(p, d, oldMemInt, newMemInt)  ==  newMemInt = <<p, d>>
+Reply(p, d, oldMemInt, newMemInt) ==  newMemInt = <<p, d>>
+(***************************************************************************)
+(* We comment out the assumption because TLC cannot handle unbounded       *)
+(* quantifiers.                                                            *)
+(***************************************************************************)
+\* ASSUME \A p, d, miOld, miNew : 
+\*         /\ Send(p,d,miOld,miNew)  \in BOOLEAN
+\*         /\ Reply(p,d,miOld,miNew) \in BOOLEAN  
+
+-----------------------------------------------------------------------------
+MReq == [op : {"Rd"}, adr: Adr] 
+          \cup [op : {"Wr"}, adr: Adr, val : Val]
+
+--------------------------------------------------------------
+IInit == /\ mem \in [Adr->Val]
+         /\ ctl = [p \in Proc |-> "rdy"] 
+         /\ buf = [p \in Proc |-> NoVal] 
+         /\ memInt \in InitMemInt
+
+TypeInvariant == 
+  /\ mem \in [Adr->Val]
+  /\ ctl \in [Proc -> {"rdy", "busy","done"}] 
+  /\ buf \in [Proc -> MReq \cup Val \cup {NoVal}]
+
+Req(p) == /\ ctl[p] = "rdy" 
+          /\ \E req \in  MReq :
+                /\ Send(p, req, memInt, memInt')
+                /\ buf' = [buf EXCEPT ![p] = req]
+                /\ ctl' = [ctl EXCEPT ![p] = "busy"]
+          /\ UNCHANGED mem 
+
+Do(p) == 
+  /\ ctl[p] = "busy" 
+  /\ mem' = IF buf[p].op = "Wr"
+              THEN [mem EXCEPT ![buf[p].adr] = buf[p].val] 
+              ELSE mem 
+  /\ buf' = [buf EXCEPT ![p] = IF buf[p].op = "Wr"
+                                  THEN NoVal
+                                  ELSE mem[buf[p].adr]]
+  /\ ctl' = [ctl EXCEPT ![p] = "done"] 
+  /\ UNCHANGED memInt 
+
+Rsp(p) == /\ ctl[p] = "done"
+          /\ Reply(p, buf[p], memInt, memInt')
+          /\ ctl' = [ctl EXCEPT ![p]= "rdy"]
+          /\ UNCHANGED <<mem, buf>> 
+
+INext == \E p \in Proc: Req(p) \/ Do(p) \/ Rsp(p) 
+
+Spec == IInit  /\  [][INext]_<<memInt, mem, ctl, buf>>
+             /\ \A p \in Proc : WF_<<memInt, mem, ctl, buf>>(Do(p) \/ Rsp(p))
+ 
+Liveness == \A p \in Proc : (ctl[p] = "busy") ~> (ctl[p] = "rdy")
+--------------------------------------------------------------
+THEOREM Spec => []TypeInvariant
+==============================================================
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.cfg b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a6a36db92f92499a81c85156f9f49481cf55c8c1
--- /dev/null
+++ b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.cfg
@@ -0,0 +1,33 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT declarations
+CONSTANTS
+c = c
+d = d
+\* MV CONSTANT declarations
+CONSTANTS
+e = e
+f = f
+\* MV CONSTANT definitions
+CONSTANT
+Val <- const_144172195407986000
+\* MV CONSTANT definitions
+CONSTANT
+Proc <- const_144172195408987000
+\* MV CONSTANT definitions
+CONSTANT
+Adr <- const_144172195409988000
+\* SYMMETRY definition
+SYMMETRY symm_144172195410989000
+\* CONSTANT definition
+CONSTANT
+NoVal = NoVal
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144172195413091000
+\* PROPERTY definition
+PROPERTY
+prop_144172195414092000
+\* Generated on Tue Sep 08 16:19:14 CEST 2015
\ No newline at end of file
diff --git a/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.tla b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..d3fb95c9829c6a4464620a2fabe3b4624756aef7
--- /dev/null
+++ b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.tla
@@ -0,0 +1,47 @@
+---- MODULE SymmetryLivenessTableauMC ----
+EXTENDS SymmetryLivenessTableau, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+c, d
+----
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+e, f
+----
+
+\* MV CONSTANT definitions Val
+const_144172195407986000 == 
+{a, b}
+----
+
+\* MV CONSTANT definitions Proc
+const_144172195408987000 == 
+{c, d}
+----
+
+\* MV CONSTANT definitions Adr
+const_144172195409988000 == 
+{e, f}
+----
+
+\* SYMMETRY definition
+symm_144172195410989000 == 
+Permutations(const_144172195407986000) \union Permutations(const_144172195408987000) \union Permutations(const_144172195409988000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144172195413091000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144172195414092000 ==
+Liveness
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/TableauSymmetry.tla b/tlatools/test-model/symmetry/TableauSymmetry.tla
new file mode 100644
index 0000000000000000000000000000000000000000..829677122cabe09df53e3e21e4b139adfc303902
--- /dev/null
+++ b/tlatools/test-model/symmetry/TableauSymmetry.tla
@@ -0,0 +1,22 @@
+-------------------------- MODULE TableauSymmetry --------------------------
+CONSTANT Val
+VARIABLE arr
+
+Init == arr = [v \in Val |-> "ready"]
+
+Ready(v) == /\ arr[v] = "ready"
+       	    /\ arr' = [arr EXCEPT ![v]= "busy"]
+
+Busy(v) == /\ arr[v] = "busy"
+       	   /\ arr' = [arr EXCEPT ![v]= "done"]
+
+Done(v) == /\ arr[v] = "done"
+       	   /\ arr' = [arr EXCEPT ![v]= "ready"]
+
+Next == \E v \in Val : Ready(v) \/ Busy(v) \/ Done(v)
+
+Spec == Init /\[][Next]_<<arr>> /\ WF_<<arr>>(Next)
+
+Liveness == \A v \in Val : (arr[v] = "busy") ~> (arr[v] = "ready")
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/TableauSymmetryDisabled.png b/tlatools/test-model/symmetry/TableauSymmetryDisabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea8343cbd6d4ad9494a0f0b456f6bb5faf58360e
Binary files /dev/null and b/tlatools/test-model/symmetry/TableauSymmetryDisabled.png differ
diff --git a/tlatools/test-model/symmetry/TableauSymmetryMC.cfg b/tlatools/test-model/symmetry/TableauSymmetryMC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..823aee7b2c78dafe6e5cc779c7a2de726802bbba
--- /dev/null
+++ b/tlatools/test-model/symmetry/TableauSymmetryMC.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+Val <- const_144481269486926000
+\* SYMMETRY definition
+SYMMETRY symm_144481269487927000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_144481269488928000
+\* PROPERTY definition
+PROPERTY
+prop_144481269489929000
diff --git a/tlatools/test-model/symmetry/TableauSymmetryMC.tla b/tlatools/test-model/symmetry/TableauSymmetryMC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e44b77ec5c7b59cffa99815735c739c58efd1180
--- /dev/null
+++ b/tlatools/test-model/symmetry/TableauSymmetryMC.tla
@@ -0,0 +1,27 @@
+---- MODULE TableauSymmetryMC ----
+EXTENDS TableauSymmetry, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions Val
+const_144481269486926000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_144481269487927000 == 
+Permutations(const_144481269486926000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_144481269488928000 ==
+Spec
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_144481269489929000 ==
+Liveness
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/DistributedCommit.tla b/tlatools/test-model/symmetry/TwoPhaseCommit/DistributedCommit.tla
new file mode 100644
index 0000000000000000000000000000000000000000..89afbcea5c077f6c0c82e1c0427959004d22ec6b
--- /dev/null
+++ b/tlatools/test-model/symmetry/TwoPhaseCommit/DistributedCommit.tla
@@ -0,0 +1,66 @@
+------------------------- MODULE DistributedCommit -------------------------
+(***************************************************************************)
+(* This is a high-level TLA+ specification of a distributed commit         *)
+(* protocol. A set of nodes individually decide whether they wish to       *)
+(* commit or abort a transaction. Second, the transaction will be          *)
+(* committed iff every node wishes to commit.                              *)
+(***************************************************************************)
+CONSTANT Participant  \* set of participating nodes
+
+VARIABLE pState  \* state of each participant
+
+(***************************************************************************)
+(* Possible states of non-coordinator participants.                        *)
+(***************************************************************************)
+PState == {"preparing", "readyCommit", "readyAbort", "committed", "aborted"}
+
+(***************************************************************************)
+(* Initially, every participant is preparing the transaction.              *)
+(***************************************************************************)
+Init == pState = [p \in Participant |-> "preparing"]
+
+(***************************************************************************)
+(* A participant decides whether she wishes to commit or abort.            *)
+(***************************************************************************)
+Decide(p) ==
+  /\ pState[p] = "preparing"
+  /\ \E dec \in {"readyCommit", "readyAbort"} : 
+        pState' = [pState EXCEPT ![p] = dec]
+
+(***************************************************************************)
+(* A participant may definitely commit only if all participants wish       *)
+(* to do so.                                                               *)
+(***************************************************************************)
+Commit(p) ==
+  /\ \A q \in Participant : pState[q] \in {"readyCommit", "committed"}
+  /\ pState' = [pState EXCEPT ![p] = "committed"]
+
+(***************************************************************************)
+(* A participant aborts if some participant wishes to do so.               *)
+(***************************************************************************)
+Abort(p) ==
+  /\ \E q \in Participant : pState[q] \in {"readyAbort", "aborted"}
+  /\ pState' = [pState EXCEPT ![p] = "aborted"]
+
+(***************************************************************************)
+(* The next-state relation is the disjunction of the above actions.        *)
+(***************************************************************************)
+Next ==
+  \E p \in Participant : Decide(p) \/ Commit(p) \/ Abort(p)
+
+(***************************************************************************)
+(* Overall specification.                                                  *)
+(***************************************************************************)
+Spec == Init /\ [][Next]_pState /\ WF_pState(Next)
+
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* Main safety property: participants may definitely commit only if        *)
+(* all participants agree.                                                 *)
+(***************************************************************************)
+Safety == 
+  \A p \in Participant : pState[p] = "committed" =>
+     \A q \in Participant : pState[q] \in {"readyCommit", "committed"}
+
+=============================================================================
diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/MC.cfg b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..46bf85e8552506cb7dd0e250d429b4a174b52e64
--- /dev/null
+++ b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.cfg
@@ -0,0 +1,23 @@
+\* MV CONSTANT declarations
+CONSTANTS
+anna = anna
+berta = berta
+charlie = charlie
+dimitry = dimitry
+eugene = eugene
+\* MV CONSTANT definitions
+CONSTANT
+Participant <- const_146894168642461000
+\* SYMMETRY definition
+SYMMETRY symm_146894168643562000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_146894168644663000
+\* INVARIANT definition
+INVARIANT
+inv_146894168645764000
+inv_146894168646965000
+inv_146894168647966000
+\* PROPERTY definition
+PROPERTY
+prop_146894168649067000
diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/MC.tla b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c195e717456b6454a7aa6297a0b8ba984ffc1c9c
--- /dev/null
+++ b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.tla
@@ -0,0 +1,39 @@
+---- MODULE MC ----
+EXTENDS TwoPhaseCommit, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+anna, berta, charlie, dimitry, eugene
+----
+
+\* MV CONSTANT definitions Participant
+const_146894168642461000 == 
+{anna, berta, charlie, dimitry, eugene}
+----
+
+\* SYMMETRY definition
+symm_146894168643562000 == 
+Permutations(const_146894168642461000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_146894168644663000 ==
+Spec
+----
+\* INVARIANT definition @modelCorrectnessInvariants:0
+inv_146894168645764000 ==
+TypeOK
+----
+\* INVARIANT definition @modelCorrectnessInvariants:1
+inv_146894168646965000 ==
+CommitOrAbort
+----
+\* INVARIANT definition @modelCorrectnessInvariants:2
+inv_146894168647966000 ==
+AbortWins
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_146894168649067000 ==
+Liveness
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/TwoPhaseCommit.tla b/tlatools/test-model/symmetry/TwoPhaseCommit/TwoPhaseCommit.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2e7833d68a5b7ce65167282b6cd79201ce44eae3
--- /dev/null
+++ b/tlatools/test-model/symmetry/TwoPhaseCommit/TwoPhaseCommit.tla
@@ -0,0 +1,150 @@
+--------------------------- MODULE TwoPhaseCommit ---------------------------
+(***************************************************************************)
+(* This is a TLA+ specification of the two-phase commit protocol used for  *)
+(* distributed data bases. It models only one instance of the protocol,    *)
+(* i.e. for a single transaction.                                          *)
+(*                                                                         *)
+(* We specify only the safety part of the specification: what may occur    *)
+(* during an execution. This would be complemented by a specification of   *)
+(* what must eventually occur, specified by suitable fairness conditions.  *)
+(***************************************************************************)
+
+CONSTANT Participant  \* set of nodes other than the coordinator
+
+VARIABLES
+  cState,     \* the state of the coordinator
+  pState,     \* the state of the non-coordinator participants
+  committed,  \* participants that the coordinator knows are OK for committing
+  msgs        \* messages sent during the protocol
+
+vars == <<cState, pState, committed, msgs>>
+
+(***************************************************************************)
+(* Possible states of coordinator.                                         *)
+(***************************************************************************)
+CState == {"preparing", "committed", "aborted"}
+
+(***************************************************************************)
+(* Possible states of non-coordinator participants.                        *)
+(***************************************************************************)
+PState == {"preparing", "readyCommit", "readyAbort", "committed", "aborted"}
+
+(***************************************************************************)
+(* Messages sent during the protocol.                                      *)
+(***************************************************************************)
+Messages ==
+  \* participant informs coordinator about its decision
+  [kind : {"commit", "abort"}, part : Participant] \cup
+  \* coordinator tells participants whether to commit or abort
+  [kind : {"doCommit", "doAbort"}]
+
+commit(p) == [kind |-> "commit", part |-> p]
+abort(p) == [kind |-> "abort", part |-> p]
+doCommit == [kind |-> "doCommit"]
+doAbort == [kind |-> "doAbort"]
+
+(***************************************************************************)
+(* The following predicate specifies what values the variables can take    *)
+(* during an execution of the protocol.                                    *)
+(***************************************************************************)
+TypeOK ==
+  /\ cState \in CState
+  /\ pState \in [Participant -> PState]
+  /\ committed \subseteq Participant
+  /\ msgs \subseteq Messages
+  
+(***************************************************************************)
+(* The initial state of the protocol.                                      *)
+(***************************************************************************)
+Init ==
+  /\ cState = "preparing"
+  /\ pState = [p \in Participant |-> "preparing"]
+  /\ committed = {}
+  /\ msgs = {}
+
+(***************************************************************************)
+(* The following action formulas describe the possible transitions of      *)
+(* the nodes.                                                              *)
+(***************************************************************************)
+
+(***************************************************************************)
+(* A participant decides and informs the coordinator of its decision.      *)
+(***************************************************************************)
+Decide(p) ==
+  /\ pState[p] = "preparing"
+  /\ \/ pState' = [pState EXCEPT ![p] = "readyCommit"] /\ msgs' = msgs \cup {commit(p)}
+     \/ pState' = [pState EXCEPT ![p] = "readyAbort"] /\ msgs' = msgs \cup {abort(p)}
+  /\ UNCHANGED <<cState, committed>>
+
+(***************************************************************************)
+(* A participant receives a commit or abort order from the coordinator.    *)
+(***************************************************************************)
+Execute(p) ==
+  /\ \/ doCommit \in msgs /\ pState' = [pState EXCEPT ![p] = "committed"]
+     \/ doAbort \in msgs /\ pState' = [pState EXCEPT ![p] = "aborted"]
+  /\ UNCHANGED <<cState, committed, msgs>>
+
+(***************************************************************************)
+(* The coordinator receives a new commit decision for some participant.    *)
+(* If all participants wish to commit, it sends an order to commit.        *)
+(***************************************************************************)
+RcvCommit == \E p \in Participant \ committed :
+  /\ commit(p) \in msgs
+  /\ committed' = committed \cup {p}
+  /\ IF committed' = Participant
+     THEN /\ cState' = "committed"
+          /\ msgs' = msgs \cup {doCommit}
+     ELSE UNCHANGED <<cState, msgs>> 
+  /\ pState' = pState
+
+(***************************************************************************)
+(* The coordinator receives an abort decision and sends an order to abort. *)
+(***************************************************************************)
+RcvAbort == \E p \in Participant :
+  /\ abort(p) \in msgs
+  /\ cState' = "aborted"
+  /\ msgs' = msgs \cup {doAbort}
+  /\ UNCHANGED <<pState, committed>>
+
+(***************************************************************************)
+(* The overall next-state relation is the disjunction of the action        *)
+(* formulas defined previously.                                            *)
+(***************************************************************************)
+Next ==
+  \/ \E p \in Participant : Decide(p) \/ Execute(p)
+  \/ RcvCommit
+  \/ RcvAbort
+
+Spec == Init /\ [][Next]_vars /\ WF_vars(Next)
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* Correctness properties.                                                 *)
+(***************************************************************************)
+
+(***************************************************************************)
+(* The coordinator never sends both a doCommit and a doAbort message.      *)
+(***************************************************************************)
+CommitOrAbort == ~(doCommit \in msgs /\ doAbort \in msgs)
+
+(***************************************************************************)
+(* The coordinator may commit only if all participants wish to commit and  *)
+(* no participant wishes to abort.                                         *)
+(***************************************************************************)
+AbortWins == 
+  doCommit \in msgs => 
+    \A p \in Participant : 
+       /\ commit(p) \in msgs /\ pState[p] \in {"readyCommit", "committed"}
+       /\ abort(p) \notin msgs
+
+Liveness ==
+  \A p \in Participant : <>(pState[p] \in {"committed", "aborted"})
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* Two-phase commitment implements distributed commitment.                 *)
+(***************************************************************************)
+DC == INSTANCE DistributedCommit
+
+THEOREM Spec => DC!Spec
+=============================================================================
diff --git a/tlatools/test-model/symmetry/Unsymmetric.tla b/tlatools/test-model/symmetry/Unsymmetric.tla
new file mode 100644
index 0000000000000000000000000000000000000000..26b1c5f8b3a63f1234160ef8f1119ab332d37376
--- /dev/null
+++ b/tlatools/test-model/symmetry/Unsymmetric.tla
@@ -0,0 +1,45 @@
+--------------------------- MODULE Unsymmetric ---------------------------
+CONSTANT S
+VARIABLE x
+
+Init ==  x \in S
+
+Back == \/ /\ x \in {1,2}
+           /\ x' \in S
+
+\* A:
+\*
+\* A honors S being symmetric. TLC finds and
+\* prints the correct counter-example.
+
+tA == CHOOSE a \in S : TRUE
+
+NextA == \/ /\ x \in S
+            /\ IF x = tA THEN x' = 1
+                         ELSE x' = 2
+SpecA == Init /\ [][NextA \/ Back]_x /\ WF_x(NextA \/ Back)
+
+\* B:
+\*
+\* 'tb' breaks symmetry because it explicitly
+\* requires an element in S that is different to
+\* another S element. This is exactly opposite
+\* to what symmetry represents. Hence, the spec
+\* is _not_ symmetric and thus TLC fails to find a 
+\* violation/counter-example (TLC finds the 
+\* counter-example when S is not declared symmetric.
+
+tB == LET t1 == CHOOSE a \in S : TRUE
+      IN  CHOOSE a \in S \ {t1} : TRUE
+
+
+NextB == \/ /\ x \in S
+            /\ IF x = tB THEN x' = 1
+                         ELSE x' = 2
+SpecB == Init /\ [][NextB \/ Back]_x /\ WF_x(NextB \/ Back)
+
+\* Liveness
+
+Prop == []<>(x = 2)
+============================================================================
+
diff --git a/tlatools/test-model/symmetry/UnsymmetricAWithSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricAWithSymmetry.png
new file mode 100644
index 0000000000000000000000000000000000000000..dceb2092db2b2ec7a633f63a20f3e3ac4d7ff2ea
Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricAWithSymmetry.png differ
diff --git a/tlatools/test-model/symmetry/UnsymmetricAWithoutSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricAWithoutSymmetry.png
new file mode 100644
index 0000000000000000000000000000000000000000..47bb11b146cb7a64522bb3df45c769e73d71107a
Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricAWithoutSymmetry.png differ
diff --git a/tlatools/test-model/symmetry/UnsymmetricBWithSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricBWithSymmetry.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c8b7da1242badefaf7eaab33930c4e537727d83
Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricBWithSymmetry.png differ
diff --git a/tlatools/test-model/symmetry/UnsymmetricBWithoutSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricBWithoutSymmetry.png
new file mode 100644
index 0000000000000000000000000000000000000000..47242297c74fd5ad0abba3e90e3ff1b9996e13f1
Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricBWithoutSymmetry.png differ
diff --git a/tlatools/test-model/symmetry/UnsymmetricMCA.cfg b/tlatools/test-model/symmetry/UnsymmetricMCA.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bec8fd9c0fe0a39d9571a24cc1d5070bb5373876
--- /dev/null
+++ b/tlatools/test-model/symmetry/UnsymmetricMCA.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_1444366015085101000
+\* SYMMETRY definition
+SYMMETRY symm_1444366015096102000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_1444366015106103000
+\* PROPERTY definition
+PROPERTY
+prop_1444366015116104000
diff --git a/tlatools/test-model/symmetry/UnsymmetricMCA.tla b/tlatools/test-model/symmetry/UnsymmetricMCA.tla
new file mode 100644
index 0000000000000000000000000000000000000000..fd772c596d3d202a98694d08942efc73968ed18c
--- /dev/null
+++ b/tlatools/test-model/symmetry/UnsymmetricMCA.tla
@@ -0,0 +1,27 @@
+---- MODULE UnsymmetricMCA ----
+EXTENDS Unsymmetric, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_1444366015085101000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_1444366015096102000 == 
+Permutations(const_1444366015085101000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_1444366015106103000 ==
+SpecA
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_1444366015116104000 ==
+Prop
+----
+=============================================================================
diff --git a/tlatools/test-model/symmetry/UnsymmetricMCB.cfg b/tlatools/test-model/symmetry/UnsymmetricMCB.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bec8fd9c0fe0a39d9571a24cc1d5070bb5373876
--- /dev/null
+++ b/tlatools/test-model/symmetry/UnsymmetricMCB.cfg
@@ -0,0 +1,15 @@
+\* MV CONSTANT declarations
+CONSTANTS
+a = a
+b = b
+\* MV CONSTANT definitions
+CONSTANT
+S <- const_1444366015085101000
+\* SYMMETRY definition
+SYMMETRY symm_1444366015096102000
+\* SPECIFICATION definition
+SPECIFICATION
+spec_1444366015106103000
+\* PROPERTY definition
+PROPERTY
+prop_1444366015116104000
diff --git a/tlatools/test-model/symmetry/UnsymmetricMCB.tla b/tlatools/test-model/symmetry/UnsymmetricMCB.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b6d8a0fec9d6d3926b50fa55db4ef68673c841a7
--- /dev/null
+++ b/tlatools/test-model/symmetry/UnsymmetricMCB.tla
@@ -0,0 +1,27 @@
+---- MODULE UnsymmetricMCB ----
+EXTENDS Unsymmetric, TLC
+
+\* MV CONSTANT declarations@modelParameterConstants
+CONSTANTS
+a, b
+----
+
+\* MV CONSTANT definitions S
+const_1444366015085101000 == 
+{a, b}
+----
+
+\* SYMMETRY definition
+symm_1444366015096102000 == 
+Permutations(const_1444366015085101000)
+----
+
+\* SPECIFICATION definition @modelBehaviorSpec:0
+spec_1444366015106103000 ==
+SpecB
+----
+\* PROPERTY definition @modelCorrectnessProperties:0
+prop_1444366015116104000 ==
+Prop
+----
+=============================================================================
diff --git a/tlatools/test-model/test57.cfg b/tlatools/test-model/test57.cfg
deleted file mode 100644
index 2e385425054877319618031959b1771ec5b65ca3..0000000000000000000000000000000000000000
--- a/tlatools/test-model/test57.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-SPECIFICATION Spec
-INVARIANT Invariant0 Invariant1 Invariant2  
-PROPERTY Property
\ No newline at end of file
diff --git a/tlatools/test-model/test52.cfg b/tlatools/test-model/testinvalidinvariant.cfg
similarity index 95%
rename from tlatools/test-model/test52.cfg
rename to tlatools/test-model/testinvalidinvariant.cfg
index f0e860231ad0256721aa61b69aec5db318681a6c..da219465bfd7a82d092808d0c6661eed71f7d66a 100644
--- a/tlatools/test-model/test52.cfg
+++ b/tlatools/test-model/testinvalidinvariant.cfg
@@ -1,3 +1,3 @@
-SPECIFICATION Spec
-PROPERTY Property
-INVARIANT Invariant
+SPECIFICATION Spec
+PROPERTY Property
+INVARIANT Invariant
diff --git a/tlatools/test-model/testinvalidinvariant.tla b/tlatools/test-model/testinvalidinvariant.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e00c9bd9158a664ad6639c0094f6e0d3f014fff7
--- /dev/null
+++ b/tlatools/test-model/testinvalidinvariant.tla
@@ -0,0 +1,12 @@
+------------------ MODULE testinvalidinvariant --------------------
+EXTENDS Integers
+
+VARIABLES x
+Init == /\ x = 1
+Next == /\ x'= x + 1 
+Spec == Init /\ [][Next]_<<x>>
+
+\* Primed variable is invalid.
+Invariant == x' \in Nat
+
+==================================================================
diff --git a/tlatools/test-model/tlc2/overrides/TLCOverrides.class b/tlatools/test-model/tlc2/overrides/TLCOverrides.class
new file mode 100644
index 0000000000000000000000000000000000000000..fada9ec400c899edf0c7e43d29d448a39216a5fd
Binary files /dev/null and b/tlatools/test-model/tlc2/overrides/TLCOverrides.class differ
diff --git a/tlatools/test-model/tlc2/overrides/TLCOverrides.java b/tlatools/test-model/tlc2/overrides/TLCOverrides.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dff9fd00a406e7952aecf8e39bb19ee11d763f1
--- /dev/null
+++ b/tlatools/test-model/tlc2/overrides/TLCOverrides.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.overrides;
+
+import tlc2.tool.EvaluatingValueTest;
+
+public class TLCOverrides implements ITLCOverrides {
+
+	/* (non-Javadoc)
+	 * @see tlc2.overrides.ITLCOverrides#get()
+	 */
+	@Override
+	public Class[] get() {
+		return new Class[] { EvaluatingValueTest.class, UserModuleOverrideAnnotationImpl.class };
+	}
+
+}
diff --git a/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.class b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.class
new file mode 100644
index 0000000000000000000000000000000000000000..dc2d251fc94396f72481e5f609fae6b6307aaf64
Binary files /dev/null and b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.class differ
diff --git a/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.java b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..33048fa6d4d42e59e72e98bbbbdb33ef1025a843
--- /dev/null
+++ b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.overrides;
+
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.Value;
+
+public class UserModuleOverrideAnnotationImpl {
+
+	@TLAPlusOperator(identifier="Get", module="UserModuleOverrideAnnotation")
+	public static Value getNumberOne() {
+		return BoolValue.ValTrue;
+	}
+	
+	@TLAPlusOperator(identifier="Get2", module="UserModuleOverrideAnnotation")
+	public static Value Get2() {
+		return BoolValue.ValTrue;
+	}
+	
+	//************ The ones below will cause warnings because they don't match ************//
+	
+	@TLAPlusOperator(identifier="Get2", module="UserModuleOverrideAnnotation")
+	public static Value Get2(Value v1) {
+		return BoolValue.ValFalse;
+	}
+
+	@TLAPlusOperator(identifier="NoSuchIdentifier", module="UserModuleOverrideAnnotation")
+	public static Value noSuchIdentifier() {
+		return BoolValue.ValFalse;
+	}
+	
+	@TLAPlusOperator(identifier="Get", module="NoSuchModule")
+	public static Value noSuchModule() {
+		return BoolValue.ValFalse;
+	}
+}
diff --git a/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java b/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ec0a2225b03b7003b5d6697af8d79e8870e596c
--- /dev/null
+++ b/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+// This class verifies a core part of tlc2.tool.fp.OffHeapDiskFPSet. It is - more or less - a verbatim copy.
+public class OffHeapDiskFPSetJPFTest extends TestJPF {
+	private static final int PROBE_LIMIT = 2;
+
+	@Test
+	public void test() throws InterruptedException, BrokenBarrierException {
+		if (verifyNoPropertyViolation("+listener=.listener.AssertionProperty,.listener.ErrorTraceGenerator")) {
+			final int size = 3;
+			final int max = 3;
+			final DummyOffHeapDiskFPSet fpSet = new DummyOffHeapDiskFPSet(size, max);
+
+			// Two concurrent writers.
+			for (int i = 0; i < 2; i++) {
+				Thread worker = new Thread(new Runnable() {
+					public void run() {
+						for (long i = 1; i <= max; i++) {
+							fpSet.memInsert(i);
+						}
+					}
+				}, "Worker");
+				worker.start();
+			}
+			assert fpSet.checkInvariant() : "FPSet violates its invariant: " + Arrays.toString(fpSet.array.array);
+		}
+	}
+	
+	private static class DummyOffHeapDiskFPSet {
+
+		private static final int EMPTY = 0;
+		
+		private final OffHeapDiskFPSet.Indexer indexer;
+		private final DummyLongArray array;
+		// AtomicInteger should be LongAdder but JPF fails with what seems to be an
+		// internal bug.
+		private final AtomicInteger tblCnt;
+
+		public DummyOffHeapDiskFPSet(int positions, long max) {
+			this.tblCnt = new AtomicInteger(0);
+			this.array = new DummyLongArray(positions);
+			this.indexer = new OffHeapDiskFPSet.Indexer(positions, 1, max);
+		}
+
+		// This is what we want to verify.
+		public boolean memInsert(final long fp) {
+			for (int i = 0; i < PROBE_LIMIT; i++) {
+				final int position = (int) indexer.getIdx(fp, i);
+				final long expected = array.get(position);
+				if (expected == EMPTY) {
+					if (array.trySet(position, expected, fp)) {
+						tblCnt.incrementAndGet();
+						return false;
+					} else {
+						i = i - 1;
+						continue;
+					}
+				}
+				if (expected == fp) {
+					return true;
+				}
+			}
+			return false;
+		}
+		
+		// Heap-variant of LongArray with synchronized instead of CAS. JPF does
+		// not seem to support sun.misc.Unsafe.
+		private static class DummyLongArray {
+			private final long[] array;
+			
+			public DummyLongArray(int positions) {
+				this.array = new long[positions];
+			}
+
+			public long get(int position) {
+				return this.array[position];
+			}
+
+			public synchronized boolean trySet(int position, long expected, long l) {
+				if (this.array[position] == expected) {
+					this.array[position] = l;
+					return true;
+				}
+				return false;
+			}
+		}
+
+		//**** Assertion Helper ****//
+		
+		public synchronized boolean checkInvariant() {
+			// No duplicates.
+			int cnt = 0;
+			final Set<Long> s = new HashSet<Long>(array.array.length);
+			for (int i = 0; i < array.array.length; i++) {
+				if (array.array[i] > 0) {
+					s.add(array.array[i]);
+					cnt++;
+				}
+			}
+			return cnt == s.size();
+		}
+	}
+}
diff --git a/tlatools/test-verify/tlc2/tool/queue/StateQueueJFP.launch b/tlatools/test-verify/tlc2/tool/queue/StateQueueJFP.launch
new file mode 100644
index 0000000000000000000000000000000000000000..f36bb231b11e3069caeaabd4020d4f7398e19976
--- /dev/null
+++ b/tlatools/test-verify/tlc2/tool/queue/StateQueueJFP.launch
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/tlatools/lib/jpf.jar"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;tlatools&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;tlatools&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/tlatools/lib/jpf/jgraphx.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/tlatools/lib/jpf/jpf-shell.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/tlatools/lib/jpf/jpf-visual.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="gov.nasa.jpf.tool.RunJPF"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="${project_loc:tlatools}/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf -show"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tlatools"/>
+</launchConfiguration>
diff --git a/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf
new file mode 100644
index 0000000000000000000000000000000000000000..ce498466fcdff73f1cf6e367c7d270ff13285f83
--- /dev/null
+++ b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf
@@ -0,0 +1,22 @@
+##
+## Adjust for current system!!! Run in Eclispe with .launch file.
+##
+basepath=/home/markus/src/TLA/tla/tlatools
+
+
+target=tlc2.tool.queue.StateQueueJPFTest
+jpf-visual.sourcepath+=${basepath}/test-verify;${basepath}/src
+
+# register console errorTracePrinter as a publisher
+report.publisher+=,errorTracePrinter
+report.errorTracePrinter.class=ErrorTracePrinter
+
+# print trace when property is violated
+report.errorTracePrinter.property_violation=trace
+
+#turn on the shell
+shell=.shell.basicshell.BasicShell
+
+#turn on the new panel
+shell.panels+=,errorTrace
+shell.panels.errorTrace=ErrorTracePanel
diff --git a/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java
index ea4bc80a8bf128715696af493ac21d00eedd1312..f531a1111f9cf0487caab2370d37795d8ba20ab3 100644
--- a/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java
+++ b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java
@@ -35,6 +35,10 @@ import tlc2.tool.TLCState;
 
 public class StateQueueJPFTest extends TestJPF {
 
+	public static void main(String[] args) {
+		new StateQueueJPFTest().test();
+	}
+	
 	@Test
 	public void test() {
 		if (verifyNoPropertyViolation()) {
@@ -50,21 +54,23 @@ public class StateQueueJPFTest extends TestJPF {
 				}
 			}, "Main");
 			main.start();
-			
-			Thread worker = new Thread(new Runnable() {
-				public void run() {
-					for (int i = 0; i < 10; i++) {
-						TLCState state = queue.dequeue();
-						if (state == null) {
-							queue.finishAll();
-							return;
+
+			for (int i = 0; i < 3; i++) {
+				Thread worker = new Thread(new Runnable() {
+					public void run() {
+						for (int i = 0; i < 3; i++) {
+							TLCState state = queue.dequeue();
+							if (state == null) {
+								queue.finishAll();
+								return;
+							}
+							queue.enqueue(tlcState);
 						}
-						queue.enqueue(tlcState);
+						queue.finishAll();
 					}
-					queue.finishAll();
-				}
-			}, "Worker");
-			worker.start();
+				}, "Worker" + i);
+				worker.start();
+			}
 		}
 	}
 	
@@ -85,6 +91,10 @@ public class StateQueueJPFTest extends TestJPF {
 			return state;
 		}
 
+		TLCState peekInner() {
+			return state;
+		}
+
 		public void beginChkpt() throws IOException {
 			// checkpointing not being verified
 		}
diff --git a/tlatools/test/pcal/AssignmentToUndeclaredVariableTest.java b/tlatools/test/pcal/AssignmentToUndeclaredVariableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..988f67fa208a34a3dbe7c3a135c6646f55dafe57
--- /dev/null
+++ b/tlatools/test/pcal/AssignmentToUndeclaredVariableTest.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import util.ToolIO;
+
+public class AssignmentToUndeclaredVariableTest extends PCalTest {
+	@Test
+	public void procedure() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo {\n" + 
+				"  variables v, w;\n" + 
+				"    procedure Proc1() \n" + 
+				"      {p1 : v := 23;\n" + 
+				"            c := 42 }\n" + 
+				" {\n" +
+				"  i: call Proc1();\n" + // Assignment to constant
+				" }\n" + 
+				"}*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.toString(ToolIO.getAllMessages()),
+				Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable c\n"
+					+ "    at line 8, column 13.\n"));
+	}
+	
+	@Test
+	public void process() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo {\n" + 
+				"  variables v, w;\n" + 
+				"  process (proc \\in {1,2})\n" + 
+				"    variable loc\n" + 
+				" {\n" +
+				"   lbl1: loc := 42;\n" +
+				"   lbl2: v := 23;\n" +
+				"   lbl3: w := 174;\n" +
+				"   lbl4: c := \"fail\";\n" + // Assignment to constant
+				" }\n" + 
+				"}*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.toString(ToolIO.getAllMessages()),
+				Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable c\n"
+					+ "    at line 12, column 10.\n"));
+	}
+	
+	@Test
+	public void multiAssignment() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo {\n" + 
+				"  variables v, w;\n" + 
+				" {\n" +
+				"  v := 42 || w := 23;\n" +
+				"  v := 42 || c := 23;\n" + // Assignment to constant
+				" }\n" + 
+				"}*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.toString(ToolIO.getAllMessages()),
+				Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable c\n"
+					+ "    at line 8, column 11.\n"));
+	}
+
+	@Test
+	public void macro() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo {\n" + 
+				"  variables v;\n" + 
+				"  macro Mac() { v := \"pmac\";\n c := 42; }\n" + 
+				" {\n" +
+				"  Mac();\n" + // Assignment to constant
+				" }\n" + 
+				"}*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.toString(ToolIO.getAllMessages()),
+				Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable c\n"
+					+ "    at line 7, column 2 of macro called at line 9, column 3.\n"));
+	}
+	
+	@Test
+	public void macroParam() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo {\n" + 
+				"  variables v;\n" + 
+				"  macro Mac2(p) { p := \"pmac\"}\n" + 
+				" {\n" +
+				"  lbl1: Mac2(v);\n" +
+				"  lbl2: Mac2(c);\n" + // Assignment to constant
+				" }\n" + 
+				"}*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable c\n"
+					+ "    at line 6, column 19 of macro called at line 9, column 9.\n"));
+	}
+	
+	@Test
+	public void boundIdentifier() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo\n" + 
+				"  variables v;\n" + 
+				"begin\n" +
+				"   with n \\in {1,2,3} do\n" +
+				"      v := n;\n" + 
+				"      n := 42;\n" + // Assignment to bound identifier!
+				"   end with;" +
+				"end algorithm\n" + 
+				" *)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable n\n"
+					+ "    at line 9, column 7.\n"));
+	}
+	
+	@Test
+	public void constant() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+			writeTempFile("AssignmentToUndeclaredVariableTest", 
+				"---- MODULE algo ----\n" + 
+				"CONSTANT c\n" + 
+				"(*\n" + 
+				"--algorithm algo\n" + 
+				"  variables v;\n" + 
+				"begin\n" + 
+				"   v := 23;\n" + 
+				"   c := 42;\n" + // Assignment to constant! 
+				"end algorithm\n" + 
+				" *)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Assignment to undeclared variable c\n"
+					+ "    at line 8, column 4.\n"));
+	}
+}
diff --git a/tlatools/test/pcal/BakeryTest.java b/tlatools/test/pcal/BakeryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1735eb9b5dac6c4eb09480e7ed78470491d08233
--- /dev/null
+++ b/tlatools/test/pcal/BakeryTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class BakeryTest extends PCalModelCheckerTestCase {
+
+	public BakeryTest() {
+		super("Bakery", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1183", "668", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "41"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/Test059.java b/tlatools/test/pcal/Bug051003Test.java
similarity index 79%
rename from tlatools/test/tlc2/tool/liveness/Test059.java
rename to tlatools/test/pcal/Bug051003Test.java
index dc9f162db9e30a5d6cf43ce352309d4f3c6b501f..3f783025f816e2b52ddd6a5a355e1f2c86716594 100644
--- a/tlatools/test/tlc2/tool/liveness/Test059.java
+++ b/tlatools/test/pcal/Bug051003Test.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
  *
  * The MIT License (MIT)
  * 
@@ -23,22 +23,29 @@
  * Contributors:
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
+package pcal;
 
-package tlc2.tool.liveness;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import tlc2.output.EC;
 
-public class Test059 extends ModelCheckerTestCase {
+public class Bug051003Test extends PCalModelCheckerTestCase {
 
-	public Test059() {
-		super("test59");
+	public Bug051003Test() {
+		super("bug_05_10_03", "pcal");
 	}
-	
+
+	@Test
 	public void testSpec() {
-		// ModelChecker has finished and generated the expected amount of states
-		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "5"));
 		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "5", "0"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+		
+		assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/pcal/Bug051210aTest.java b/tlatools/test/pcal/Bug051210aTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ada455e80a9e3418194f5b4f70144e779ec390b9
--- /dev/null
+++ b/tlatools/test/pcal/Bug051210aTest.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Bug051210aTest extends PCalModelCheckerTestCase {
+
+	public Bug051210aTest() {
+		super("bug_05_12_10a", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15", "14", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14"));
+		
+		assertUncovered("  line 134, col 25 to line 137, col 42 of module bug_05_12_10a: 0\n" + 
+				"  line 138, col 25 to line 138, col 43 of module bug_05_12_10a: 0\n" + 
+				"  line 139, col 22 to line 139, col 32 of module bug_05_12_10a: 0\n" + 
+				"  line 140, col 22 to line 140, col 29 of module bug_05_12_10a: 0\n" + 
+				"  line 147, col 12 to line 147, col 21 of module bug_05_12_10a: 0\n" + 
+				"  line 148, col 12 to line 148, col 22 of module bug_05_12_10a: 0\n" + 
+				"  line 149, col 12 to line 149, col 74 of module bug_05_12_10a: 0\n" + 
+				"  line 153, col 25 to line 153, col 44 of module bug_05_12_10a: 0\n" + 
+				"  line 154, col 25 to line 157, col 42 of module bug_05_12_10a: 0\n" + 
+				"  line 158, col 22 to line 158, col 32 of module bug_05_12_10a: 0\n" + 
+				"  line 159, col 22 to line 159, col 29 of module bug_05_12_10a: 0\n" + 
+				"  line 167, col 33 to line 167, col 50 of module bug_05_12_10a: 0\n" + 
+				"  line 168, col 33 to line 168, col 43 of module bug_05_12_10a: 0\n" + 
+				"  line 173, col 12 to line 173, col 21 of module bug_05_12_10a: 0\n" + 
+				"  line 174, col 12 to line 174, col 22 of module bug_05_12_10a: 0\n" + 
+				"  line 175, col 12 to line 175, col 74 of module bug_05_12_10a: 0\n" + 
+				"  line 196, col 25 to line 196, col 43 of module bug_05_12_10a: 0\n" + 
+				"  line 197, col 25 to line 200, col 42 of module bug_05_12_10a: 0\n" + 
+				"  line 201, col 22 to line 201, col 33 of module bug_05_12_10a: 0\n" + 
+				"  line 202, col 22 to line 202, col 32 of module bug_05_12_10a: 0\n" + 
+				"  line 203, col 22 to line 203, col 48 of module bug_05_12_10a: 0\n" + 
+				"  line 204, col 11 to line 204, col 64 of module bug_05_12_10a: 0\n" + 
+				"  line 207, col 12 to line 207, col 21 of module bug_05_12_10a: 0\n" + 
+				"  line 208, col 12 to line 208, col 22 of module bug_05_12_10a: 0\n" + 
+				"  line 209, col 12 to line 209, col 74 of module bug_05_12_10a: 0\n" + 
+				"  line 232, col 22 to line 232, col 33 of module bug_05_12_10a: 0\n" + 
+				"  line 233, col 22 to line 233, col 50 of module bug_05_12_10a: 0\n" + 
+				"  line 241, col 12 to line 241, col 19 of module bug_05_12_10a: 0\n" + 
+				"  line 242, col 12 to line 242, col 22 of module bug_05_12_10a: 0\n" + 
+				"  line 243, col 12 to line 243, col 75 of module bug_05_12_10a: 0\n" + 
+				"  line 248, col 11 to line 248, col 30 of module bug_05_12_10a: 0\n" + 
+				"  line 249, col 11 to line 249, col 34 of module bug_05_12_10a: 0\n" + 
+				"  line 250, col 11 to line 250, col 30 of module bug_05_12_10a: 0\n" + 
+				"  line 251, col 11 to line 251, col 64 of module bug_05_12_10a: 0\n" + 
+				"  line 256, col 11 to line 256, col 30 of module bug_05_12_10a: 0\n" + 
+				"  line 257, col 11 to line 257, col 36 of module bug_05_12_10a: 0\n" + 
+				"  line 258, col 11 to line 258, col 30 of module bug_05_12_10a: 0\n" + 
+				"  line 259, col 11 to line 259, col 63 of module bug_05_12_10a: 0\n" + 
+				"  line 272, col 12 to line 272, col 31 of module bug_05_12_10a: 0\n" + 
+				"  line 273, col 12 to line 273, col 35 of module bug_05_12_10a: 0\n" + 
+				"  line 274, col 12 to line 274, col 31 of module bug_05_12_10a: 0\n" + 
+				"  line 275, col 12 to line 275, col 65 of module bug_05_12_10a: 0");
+	}
+}
diff --git a/tlatools/test/pcal/Bug051216bTest.java b/tlatools/test/pcal/Bug051216bTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2de30905f074678c0ec5eb4fbf0f4456c89024d0
--- /dev/null
+++ b/tlatools/test/pcal/Bug051216bTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Bug051216bTest extends PCalModelCheckerTestCase {
+
+	public Bug051216bTest() {
+		super("bug_05_12_16b", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "146", "64", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "10"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Bug051231Test.java b/tlatools/test/pcal/Bug051231Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8995126394988c6b6ce8a031d2bf99777739dc9
--- /dev/null
+++ b/tlatools/test/pcal/Bug051231Test.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Bug051231Test extends PCalModelCheckerTestCase {
+
+	public Bug051231Test() {
+		super("bug_05_12_31", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "3", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Bug060125Test.java b/tlatools/test/pcal/Bug060125Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..725c05b6250335a9fd8581e490db25007804c020
--- /dev/null
+++ b/tlatools/test/pcal/Bug060125Test.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Bug060125Test extends PCalModelCheckerTestCase {
+
+	public Bug060125Test() {
+		super("bug_06_01_25", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "130", "64", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "15"));
+		
+		assertUncovered("line 138, col 27 to line 138, col 53 of module bug_06_01_25: 0\n" + 
+				"line 139, col 27 to line 139, col 53 of module bug_06_01_25: 0\n" + 
+				"line 140, col 27 to line 140, col 53 of module bug_06_01_25: 0\n" + 
+				"line 162, col 27 to line 162, col 53 of module bug_06_01_25: 0\n" + 
+				"line 163, col 27 to line 163, col 53 of module bug_06_01_25: 0\n" + 
+				"line 164, col 27 to line 164, col 53 of module bug_06_01_25: 0\n" + 
+				"line 165, col 27 to line 167, col 75 of module bug_06_01_25: 0\n" + 
+				"line 168, col 27 to line 168, col 58 of module bug_06_01_25: 0");
+	}
+}
diff --git a/tlatools/test/pcal/CBakeryTest.java b/tlatools/test/pcal/CBakeryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..31b4ad2dbd6b22f82d16df947adbeabc10b0f84a
--- /dev/null
+++ b/tlatools/test/pcal/CBakeryTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CBakeryTest extends PCalModelCheckerTestCase {
+
+	public CBakeryTest() {
+		super("CBakery", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "867", "486", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "31"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/CCallReturn1Test.java b/tlatools/test/pcal/CCallReturn1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..4285b5862412f57f1686b1b2df8d137b2d2c5a14
--- /dev/null
+++ b/tlatools/test/pcal/CCallReturn1Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CCallReturn1Test extends PCalModelCheckerTestCase {
+
+	public CCallReturn1Test() {
+		super("CCallReturn1", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/CDiningPhilosophersTest.java b/tlatools/test/pcal/CDiningPhilosophersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2435f924a0ccd3bfacc94fd034c6489a33a8fa5d
--- /dev/null
+++ b/tlatools/test/pcal/CDiningPhilosophersTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CDiningPhilosophersTest extends PCalModelCheckerTestCase {
+
+	public CDiningPhilosophersTest() {
+		super("CDiningPhilosophers", "pcal", new String[] {"-sf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "301", "118", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/CEither1Test.java b/tlatools/test/pcal/CEither1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..270087db26b95f3fb7cd0c3ddb8ec0569cf34938
--- /dev/null
+++ b/tlatools/test/pcal/CEither1Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CEither1Test extends PCalModelCheckerTestCase {
+
+	public CEither1Test() {
+		super("CEither1", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "7", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/CMultiprocDefineTest.java b/tlatools/test/pcal/CMultiprocDefineTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1df2feee8d3cd0a4d944432916a95ab7a2ac70f
--- /dev/null
+++ b/tlatools/test/pcal/CMultiprocDefineTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CMultiprocDefineTest extends PCalModelCheckerTestCase {
+
+	public CMultiprocDefineTest() {
+		super("CMultiprocDefine", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "8", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/CallGotoUnlabeledTest.java b/tlatools/test/pcal/CallGotoUnlabeledTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2a535996eecdc771d31f58c92c4bf41c487d657
--- /dev/null
+++ b/tlatools/test/pcal/CallGotoUnlabeledTest.java
@@ -0,0 +1,39 @@
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import tlc2.tool.CommonTestCase;
+import util.ToolIO;
+
+public class CallGotoUnlabeledTest extends PCalTest {
+	
+	// https://groups.google.com/forum/#!topic/tlaplus/6M1oFOtN-5k/discussion
+	
+	@Test
+	public void test() {
+		ToolIO.setMode(ToolIO.TOOL);
+		
+		final String fileName = "CallGotoUnlabeledTest.tla";
+		
+		assertEquals(0, trans.runMe(new String[] {"-nocfg", "-unixEOL", "-reportLabels", CommonTestCase.BASE_PATH + fileName}));
+		final TLAtoPCalMapping mapping = PcalParams.tlaPcalMapping;
+		assertNotNull(mapping);
+		
+		final String[] messages = ToolIO.getAllMessages();
+		assertTrue(Arrays.toString(messages), messages.length == 6);
+		
+        assertEquals("The following labels were added:", messages[0]);
+        assertEquals("  Lbl_1 at line 10, column 3", messages[1]);
+        assertEquals("  Lbl_2 at line 19, column 3", messages[2]);
+		assertEquals("Parsing completed.", messages[3]);
+		assertEquals("Translation completed.", messages[4]);
+		// Ignore last line "New file ...." because it depends on from where the test is executed.
+//		assertEquals("New file test-model/" + fileName + " written.", messages[5]);
+	}
+}
diff --git a/tlatools/test/pcal/CallReturn1Test.java b/tlatools/test/pcal/CallReturn1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..40b0b20d386ab06e9410102977f90771b4777a04
--- /dev/null
+++ b/tlatools/test/pcal/CallReturn1Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CallReturn1Test extends PCalModelCheckerTestCase {
+
+	public CallReturn1Test() {
+		super("CallReturn1", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/CallReturn2Test.java b/tlatools/test/pcal/CallReturn2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3be012b0aeec3235a4b28cd6de70318446c71e1
--- /dev/null
+++ b/tlatools/test/pcal/CallReturn2Test.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CallReturn2Test extends PCalModelCheckerTestCase {
+
+	public CallReturn2Test() {
+		super("CallReturn2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "11"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "12", "11", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11"));
+		
+		assertUncovered("  line 129, col 10 to line 129, col 15 of module CallReturn2: 0\n" + 
+				"  line 130, col 10 to line 130, col 19 of module CallReturn2: 0\n" + 
+				"  line 131, col 10 to line 131, col 65 of module CallReturn2: 0\n" + 
+				"  line 134, col 13 to line 134, col 18 of module CallReturn2: 0\n" + 
+				"  line 135, col 13 to line 138, col 36 of module CallReturn2: 0\n" + 
+				"  line 139, col 13 to line 139, col 30 of module CallReturn2: 0\n" + 
+				"  line 140, col 10 to line 140, col 19 of module CallReturn2: 0\n" + 
+				"  line 141, col 10 to line 141, col 55 of module CallReturn2: 0\n" + 
+				"  line 147, col 10 to line 147, col 29 of module CallReturn2: 0\n" + 
+				"  line 148, col 10 to line 148, col 27 of module CallReturn2: 0\n" + 
+				"  line 149, col 10 to line 149, col 29 of module CallReturn2: 0\n" + 
+				"  line 150, col 10 to line 150, col 58 of module CallReturn2: 0");
+	}
+}
diff --git a/tlatools/test/pcal/DetlefSpecTest.java b/tlatools/test/pcal/DetlefSpecTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdbb095f712f61624bc1fce46d61f0ff294ab5ae
--- /dev/null
+++ b/tlatools/test/pcal/DetlefSpecTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class DetlefSpecTest extends PCalModelCheckerTestCase {
+
+	public DetlefSpecTest() {
+		super("DetlefSpec", "pcal", new String[] {"-wf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "31", "15", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/DetlefsTest.java b/tlatools/test/pcal/DetlefsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d23fbf8dec62a95f5da9452442ebe84316eeba5
--- /dev/null
+++ b/tlatools/test/pcal/DetlefsTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class DetlefsTest extends PCalModelCheckerTestCase {
+
+	public DetlefsTest() {
+		super("Detlefs", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2127012", "952912", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "82"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Dijkstra1Test.java b/tlatools/test/pcal/Dijkstra1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..7be46c0a75c086a374858d8f5f751f1fec6770c6
--- /dev/null
+++ b/tlatools/test/pcal/Dijkstra1Test.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Dijkstra1Test extends PCalModelCheckerTestCase {
+
+	public Dijkstra1Test() {
+		super("Dijkstra1", "pcal", new String[] {"-wf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "625"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "5510"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "16775", "5510", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "10"));
+		
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Dijkstra1.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file Dijkstra1.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module FiniteSets
+Semantic processing of module Dijkstra1
+Starting... (2012-08-10 17:39:54)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 625 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 3.4E-12
+  based on the actual fingerprints:  val = 5.3E-14
+16775 states generated, 5510 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 10.
+Finished. (2012-08-10 17:39:55)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/DiningPhilosophersTest.java b/tlatools/test/pcal/DiningPhilosophersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..57349d69ab7811ca7b458fc9ea9a731abb851c90
--- /dev/null
+++ b/tlatools/test/pcal/DiningPhilosophersTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class DiningPhilosophersTest extends PCalModelCheckerTestCase {
+
+	public DiningPhilosophersTest() {
+		super("DiningPhilosophers", "pcal", new String[] {"-sf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "118"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "301", "118", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/DivergenceTest.java b/tlatools/test/pcal/DivergenceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6db76aebaafc8ba75f24406a93fcb53dfb75a97
--- /dev/null
+++ b/tlatools/test/pcal/DivergenceTest.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tla2sany.drivers.SANY;
+import util.TestPrintStream;
+import util.ToolIO;
+
+// Plz can haz https://openjdk.java.net/jeps/355 ?
+public class DivergenceTest extends PCalTest {
+	@Test
+	public void divergenceTest00() throws IOException {
+		final String filename = "divergenceTest001" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  skip;\n" +
+				"end algorithm; *)\n" +
+				"\\* BEGIN TRANSLATION\n" +
+				"VARIABLE pc\n" +
+				"\n" +
+				"vars == << pc >>\n" +
+				"\n" +
+				"Init == /\\ pc = \"Lbl_1\"\n" +
+				"\n" +
+				"Lbl_1 == /\\ pc = \"Lbl_1\"\n" +
+				"         /\\ TRUE\n" +
+				"         /\\ pc' = \"Done\"\n" +
+				"\n" +
+				"(* Allow infinite stuttering to prevent deadlock on termination. *)\n" +
+				"Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" +
+				"\n" +
+				"Next == Lbl_1\n" +
+				"           \\/ Terminating\n" +
+				"\n" +
+				"Spec == Init /\\ [][Next]_vars\n" +
+				"\n" +
+				"Termination == <>(pc = \"Done\")\n" +
+				"\n" +
+				"\\* END TRANSLATION\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertNoSubstring("!! WARNING " + filename);
+	}
+
+	@Test
+	public void divergenceTest01() throws IOException {
+		final String filename = "divergenceTest01" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  skip;\n" +
+				"end algorithm; *)\n" +
+				"\\* BEGIN TRANSLATION (chksum(PCal) \\in STRING /\\ chksum(TLA+) \\in STRING)\n" +
+				"VARIABLE pc\n" +
+				"\n" +
+				"vars == << pc >>\n" +
+				"\n" +
+				"Init == /\\ pc = \"Lbl_1\"\n" +
+				"\n" +
+				"Lbl_1 == /\\ pc = \"Lbl_1\"\n" +
+				"         /\\ TRUE\n" +
+				"         /\\ pc' = \"Done\"\n" +
+				"\n" +
+				"(* Allow infinite stuttering to prevent deadlock on termination. *)\n" +
+				"Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" +
+				"\n" +
+				"Next == Lbl_1\n" +
+				"           \\/ Terminating\n" +
+				"\n" +
+				"Spec == Init /\\ [][Next]_vars\n" +
+				"\n" +
+				"Termination == <>(pc = \"Done\")\n" +
+				"\n" +
+				"\\* END TRANSLATION\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertNoSubstring("!! WARNING " + filename);
+	}
+
+	@Test
+	public void divergenceTest02() throws IOException {
+		final String filename = "divergenceTest02" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  print \"msg\";\n" + // PlusCal diverged
+				"end algorithm; *)\n" +
+				"\\* BEGIN TRANSLATION (chksum(PCal) = \"4860ac97\" /\\ chksum(TLA+) = \"af3d9146\")\n" +
+				"VARIABLE pc\n" +
+				"\n" +
+				"vars == << pc >>\n" +
+				"\n" +
+				"Init == /\\ pc = \"Lbl_1\"\n" +
+				"\n" +
+				"Lbl_1 == /\\ pc = \"Lbl_1\"\n" +
+				"         /\\ TRUE\n" +
+				"         /\\ pc' = \"Done\"\n" +
+				"\n" +
+				"(* Allow infinite stuttering to prevent deadlock on termination. *)\n" +
+				"Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" +
+				"\n" +
+				"Next == Lbl_1\n" +
+				"           \\/ Terminating\n" +
+				"\n" +
+				"Spec == Init /\\ [][Next]_vars\n" +
+				"\n" +
+				"Termination == <>(pc = \"Done\")\n" +
+				"\n" +
+				"\\* END TRANSLATION\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertSubstring(String.format(
+				"!! WARNING: The PlusCal algorithm in module %s has changed since its last translation.",
+				filename));
+	}
+
+	@Test
+	public void divergenceTest03() throws IOException {
+		final String filename = "divergenceTest03" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  skip;\n" +
+				"end algorithm; *)\n" +
+				"\\* BEGIN TRANSLATION (checksum(PlusCal) = \"4860ac97\" /\\ ChkSum(tla+) = \"af3d9146\")\n" +
+				"VARIABLE pc\n" +
+				"\n" +
+				"vars == << pc >>\n" +
+				"\n" +
+				"Init == /\\ pc = \"Lbl_1\"\n" +
+				"\n" +
+				"Lbl_1 == /\\ pc = \"Lbl_1\"\n" +
+				"         /\\ TRUE\n" +
+				"         /\\ pc' = \"Done\"\n" +
+				"\n" +
+				"(* Allow infinite stuttering to prevent deadlock on termination. *)\n" +
+				"Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" +
+				"\n" +
+				"Next == Lbl_1\n" + // TLA+ diverged.
+				"\n" +
+				"Spec == Init /\\ [][Next]_vars\n" +
+				"\n" +
+				"Termination == <>(pc = \"Done\")\n" +
+				"\n" +
+				"\\* END TRANSLATION\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertSubstring(String.format(
+				"!! WARNING: The TLA+ translation in module %s has changed since its last translation.",
+				filename));
+	}
+
+	@Test
+	public void divergenceTest04() throws IOException {
+		final String filename = "divergenceTest04" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  print \"msg\";\n" + // PlusCal diverged
+				"end algorithm; *)\n" +
+				"\\* BEGIN TRANSLATION   (checksum(PlusCal) = \"4860ac97\" /\\ ChkSum(tla+) = \"af3d9146\")  \n" +
+				"VARIABLE pc\n" +
+				"\n" +
+				"vars == << pc >>\n" +
+				"\n" +
+				"Init == /\\ pc = \"Lbl_1\"\n" +
+				"\n" +
+				"Lbl_1 == /\\ pc = \"Lbl_1\"\n" +
+				"         /\\ TRUE\n" +
+				"         /\\ pc' = \"Done\"\n" +
+				"\n" +
+				"(* Allow infinite stuttering to prevent deadlock on termination. *)\n" +
+				"Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" +
+				"\n" +
+				"Next == Lbl_1\n" + // TLA+ diverged.
+				"\n" +
+				"Spec == Init /\\ [][Next]_vars\n" +
+				"\n" +
+				"Termination == <>(pc = \"Done\")\n" +
+				"\n" +
+				"\\* END TRANSLATION\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertSubstring(String.format(
+				"!! WARNING: The PlusCal algorithm and its TLA+ translation in module %s filename since the last translation.",
+				filename));
+	}
+	@Test
+	public void divergenceTest05() throws IOException {
+		final String filename = "divergenceTest04" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  print \"msg\";\n" + // PlusCal diverged
+				"end algorithm; *)\n" +
+				"\\* BEGIN TRANSLATION   (checksum(PlusCal) \\in  STRING /\\ ChkSum(tla+) \\in STRING)  \n" +
+				"VARIABLE pc\n" +
+				"\n" +
+				"vars == << pc >>\n" +
+				"\n" +
+				"Init == /\\ pc = \"Lbl_1\"\n" +
+				"\n" +
+				"Lbl_1 == /\\ pc = \"Lbl_1\"\n" +
+				"         /\\ TRUE\n" +
+				"         /\\ pc' = \"Done\"\n" +
+				"\n" +
+				"(* Allow infinite stuttering to prevent deadlock on termination. *)\n" +
+				"Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" +
+				"\n" +
+				"Next == Lbl_1\n" + // TLA+ diverged.
+				"\n" +
+				"Spec == Init /\\ [][Next]_vars\n" +
+				"\n" +
+				"Termination == <>(pc = \"Done\")\n" +
+				"\n" +
+				"\\* END TRANSLATION\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertNoSubstring(String.format(
+				"!! WARNING:",
+				filename));
+	}
+
+	@Test
+	public void divergenceTest06() throws IOException {
+		final String filename = "divergenceTest05" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" +
+				"\n" +
+				"(*\n" +
+				"--algorithm a\n" +
+				"begin\n" +
+				"  skip;\n" +
+				"end algorithm; *)\n" +
+				"\n=========================");
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+		testPrintStream.assertNoSubstring("!! WARNING " + filename);
+	}
+}
diff --git a/tlatools/test/pcal/Either1Test.java b/tlatools/test/pcal/Either1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..47558d6d0d9b30d4fb36a0e12034ce21e1245eeb
--- /dev/null
+++ b/tlatools/test/pcal/Either1Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Either1Test extends PCalModelCheckerTestCase {
+
+	public Either1Test() {
+		super("Either1", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "7", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Either2Test.java b/tlatools/test/pcal/Either2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..72809ff31b202d0617108231798953201efa7cbc
--- /dev/null
+++ b/tlatools/test/pcal/Either2Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Either2Test extends PCalModelCheckerTestCase {
+
+	public Either2Test() {
+		super("Either2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "7", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Either3Test.java b/tlatools/test/pcal/Either3Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..37494778c877542f53fbc5ab4831e574ccbd50da
--- /dev/null
+++ b/tlatools/test/pcal/Either3Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Either3Test extends PCalModelCheckerTestCase {
+
+	public Either3Test() {
+		super("Either3", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "12", "9", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Either4Test.java b/tlatools/test/pcal/Either4Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d2eabd7bd20b36e5f736f976d81e2fc376502d6
--- /dev/null
+++ b/tlatools/test/pcal/Either4Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Either4Test extends PCalModelCheckerTestCase {
+
+	public Either4Test() {
+		super("Either4", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "81"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "154", "81", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "7"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Either5Test.java b/tlatools/test/pcal/Either5Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb283cade8439d6fef412cb4314942ff2bb08828
--- /dev/null
+++ b/tlatools/test/pcal/Either5Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Either5Test extends PCalModelCheckerTestCase {
+
+	public Either5Test() {
+		super("Either5", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10", "7", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Euclid2Test.java b/tlatools/test/pcal/Euclid2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..d44252a5aa71d5f279f8b01b3960ba0a6567292a
--- /dev/null
+++ b/tlatools/test/pcal/Euclid2Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Euclid2Test extends PCalModelCheckerTestCase {
+
+	public Euclid2Test() {
+		super("Euclid2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "500"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "18852"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "19352", "18852", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "90"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Euclid3Test.java b/tlatools/test/pcal/Euclid3Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e41f3cc2da343165e01a9159ef4ae506f363d5a
--- /dev/null
+++ b/tlatools/test/pcal/Euclid3Test.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Euclid3Test extends PCalModelCheckerTestCase {
+
+	public Euclid3Test() {
+		super("Euclid3", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "3"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "94"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "97", "94", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "50"));
+
+		assertUncovered("line 38, col 35 to line 38, col 40 of module Euclid3: 0\n" + 
+				"line 39, col 35 to line 39, col 40 of module Euclid3: 0");
+	}
+}
diff --git a/tlatools/test/pcal/EuclidTest.java b/tlatools/test/pcal/EuclidTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f865501ea24ae96939cd5613880a86f33291f49a
--- /dev/null
+++ b/tlatools/test/pcal/EuclidTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class EuclidTest extends PCalModelCheckerTestCase {
+
+	public EuclidTest() {
+		super("Euclid", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "400"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6352"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6752", "6352", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "42"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/EvenOddBadTest.java b/tlatools/test/pcal/EvenOddBadTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f8e2d371cf0623db81fe21c980633a443d01ef7
--- /dev/null
+++ b/tlatools/test/pcal/EvenOddBadTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class EvenOddBadTest extends PCalModelCheckerTestCase {
+
+	public EvenOddBadTest() {
+		super("EvenOddBad", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13"));
+		assertTrue(recorder.recorded(EC.TLC_CHECKING_TEMPORAL_PROPS_END));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15", "13", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9"));
+
+		assertUncovered("line 66, col 23 to line 66, col 37 of module EvenOddBad: 0\n" + 
+				"line 67, col 23 to line 67, col 32 of module EvenOddBad: 0\n" + 
+				"line 68, col 23 to line 68, col 50 of module EvenOddBad: 0");
+	}
+}
diff --git a/tlatools/test/pcal/EvenOddTest.java b/tlatools/test/pcal/EvenOddTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..be0e4fbee2de067f04c035b8fcf28414b48801a9
--- /dev/null
+++ b/tlatools/test/pcal/EvenOddTest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class EvenOddTest extends PCalModelCheckerTestCase {
+
+	public EvenOddTest() {
+		super("EvenOdd", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "13", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13"));
+
+		assertUncovered("line 67, col 23 to line 67, col 37 of module EvenOdd: 0\n" + 
+				"line 68, col 23 to line 68, col 34 of module EvenOdd: 0\n" + 
+				"line 69, col 23 to line 69, col 50 of module EvenOdd: 0");
+	}
+}
diff --git a/tlatools/test/pcal/Factorial2Test.java b/tlatools/test/pcal/Factorial2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..516a50e7c89923fecd875b5e897e62515609470d
--- /dev/null
+++ b/tlatools/test/pcal/Factorial2Test.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Factorial2Test extends PCalModelCheckerTestCase {
+
+	public Factorial2Test() {
+		super("Factorial2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12"));
+
+		assertUncovered("line 53, col 21 to line 53, col 40 of module Factorial2: 0\n" + 
+				"line 54, col 21 to line 54, col 38 of module Factorial2: 0\n" + 
+				"line 55, col 21 to line 55, col 44 of module Factorial2: 0\n" + 
+				"line 56, col 21 to line 56, col 40 of module Factorial2: 0\n" + 
+				"line 57, col 21 to line 57, col 52 of module Factorial2: 0");
+	}
+}
diff --git a/tlatools/test/pcal/FactorialTest.java b/tlatools/test/pcal/FactorialTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..870caefb161bc6da17b0accd2bc2d817395f31e1
--- /dev/null
+++ b/tlatools/test/pcal/FactorialTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FactorialTest extends PCalModelCheckerTestCase {
+
+	public FactorialTest() {
+		super("Factorial", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10", "9", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FairSeq2Test.java b/tlatools/test/pcal/FairSeq2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..a41d78c1c8fb99ebe387396cb0de5e823d0ec907
--- /dev/null
+++ b/tlatools/test/pcal/FairSeq2Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FairSeq2Test extends PCalModelCheckerTestCase {
+
+	public FairSeq2Test() {
+		super("FairSeq2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FairSeqTest.java b/tlatools/test/pcal/FairSeqTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..af5d65c106045f162c9465935cebe52c961d89d2
--- /dev/null
+++ b/tlatools/test/pcal/FairSeqTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FairSeqTest extends PCalModelCheckerTestCase {
+
+	public FairSeqTest() {
+		super("FairSeq", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "24"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FastMutex2Test.java b/tlatools/test/pcal/FastMutex2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..a75d6009d5c6a5aab8450b418f0bc8c811772e19
--- /dev/null
+++ b/tlatools/test/pcal/FastMutex2Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FastMutex2Test extends PCalModelCheckerTestCase {
+
+	public FastMutex2Test() {
+		super("FastMutex2", "pcal", new String[] {"-wf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "60"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FastMutex3Test.java b/tlatools/test/pcal/FastMutex3Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a2c4b21593ee85245e4b8322e6b66ba0a8c4034
--- /dev/null
+++ b/tlatools/test/pcal/FastMutex3Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FastMutex3Test extends PCalModelCheckerTestCase {
+
+	public FastMutex3Test() {
+		super("FastMutex3", "pcal", new String[] {"-wf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "60"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FastMutexTest.java b/tlatools/test/pcal/FastMutexTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..75a2a9ae2f5d01525935e5500cd814137b4912c0
--- /dev/null
+++ b/tlatools/test/pcal/FastMutexTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FastMutexTest extends PCalModelCheckerTestCase {
+
+	public FastMutexTest() {
+		super("FastMutex", "pcal", new String[] {"-wf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "60"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FastMutexWithGoto2Test.java b/tlatools/test/pcal/FastMutexWithGoto2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a14a4430ddbacc3c154c6f063c5ac6445618488
--- /dev/null
+++ b/tlatools/test/pcal/FastMutexWithGoto2Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FastMutexWithGoto2Test extends PCalModelCheckerTestCase {
+
+	public FastMutexWithGoto2Test() {
+		super("FastMutexWithGoto2", "pcal", new String[] {"-wfNext"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "15900"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "42277", "15900", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "47"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FastMutexWithGotoTest.java b/tlatools/test/pcal/FastMutexWithGotoTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a46bb0059f00927b927b0d5d45a282bc46b3ef66
--- /dev/null
+++ b/tlatools/test/pcal/FastMutexWithGotoTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FastMutexWithGotoTest extends PCalModelCheckerTestCase {
+
+	public FastMutexWithGotoTest() {
+		super("FastMutexWithGoto", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "58"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/FischerTest.java b/tlatools/test/pcal/FischerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..37c3e49e0c1ffa8bf4d66b574dc9510dee69f547
--- /dev/null
+++ b/tlatools/test/pcal/FischerTest.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FischerTest extends PCalModelCheckerTestCase {
+
+	public FischerTest() {
+		super("Fischer", "pcal", new String[] {"-wf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1002"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2487", "1002", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "24"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Fischer.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file Fischer.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module Fischer
+Starting... (2012-08-10 17:38:12)
+Implied-temporal checking--satisfiability problem has 1 branches.
+"Testing Fischer's Mutual Exclusion Algorithm"  TRUE
+<<" Number of processes = ", 3>>  TRUE
+<<" Delta   = ", 2>>  TRUE
+<<" Epsilon = ", 3>>  TRUE
+"Should find a bug if N > 1 and Delta >= Epsilon"  TRUE
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 8.1E-14
+  based on the actual fingerprints:  val = 8.0E-15
+2487 states generated, 1002 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 24.
+Finished. (2012-08-10 17:38:13)
+InnerLabeledIf.tla
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/Github358.java b/tlatools/test/pcal/Github358.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7851681e2c19132e359b4d1a64518eacdd5d602
--- /dev/null
+++ b/tlatools/test/pcal/Github358.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.tool.CommonTestCase;
+import util.ToolIO;
+
+public class Github358 extends PCalTest {
+	
+	@Test
+	public void test() {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS,
+				trans.runMe(new String[] {"-nocfg", CommonTestCase.BASE_PATH + "Github358.tla"}));
+		
+		final String[] messages = ToolIO.getAllMessages();
+		assertTrue(messages.length == 1);
+		
+		final String msg = messages[0];
+		assertEquals("Unrecoverable error:\n" + 
+				" -- Expected \":=\" but found \"skip\"\n" + 
+				"    line 5, column 7.", msg.trim());
+	}
+}
diff --git a/tlatools/test/pcal/InnerLabeledIfTest.java b/tlatools/test/pcal/InnerLabeledIfTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f97510f664c2eb732f017220835e9bd18d79b6a5
--- /dev/null
+++ b/tlatools/test/pcal/InnerLabeledIfTest.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class InnerLabeledIfTest extends PCalModelCheckerTestCase {
+
+	public InnerLabeledIfTest() {
+		super("InnerLabeledIf", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "16"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "20", "16", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup InnerLabeledIf.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file InnerLabeledIf.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module InnerLabeledIf
+Starting... (2012-08-10 17:38:14)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 4 distinct states generated.
+"made it to end"
+"made it to end"
+"made it to end"
+"made it to end"
+"made it to end"
+"made it to end"
+"made it to end"
+"made it to end"
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 3.5E-18
+  based on the actual fingerprints:  val = 1.7E-17
+20 states generated, 16 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 4.
+Finished. (2012-08-10 17:38:14)
+*/
diff --git a/tlatools/test/pcal/MPFactorial2Test.java b/tlatools/test/pcal/MPFactorial2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..5acbd064fac3b161549780de5840083a9a1de986
--- /dev/null
+++ b/tlatools/test/pcal/MPFactorial2Test.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MPFactorial2Test extends PCalModelCheckerTestCase {
+
+	public MPFactorial2Test() {
+		super("MPFactorial2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1728"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4754", "1728", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "34"));
+
+		assertUncovered("line 61, col 27 to line 61, col 74 of module MPFactorial2: 0\n" + 
+				"line 62, col 27 to line 62, col 71 of module MPFactorial2: 0\n" + 
+				"line 63, col 27 to line 63, col 80 of module MPFactorial2: 0\n" + 
+				"line 64, col 27 to line 64, col 77 of module MPFactorial2: 0\n" + 
+				"line 65, col 27 to line 65, col 58 of module MPFactorial2: 0");
+	}
+}
diff --git a/tlatools/test/pcal/MPFactorialTest.java b/tlatools/test/pcal/MPFactorialTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0abcbfc1eb5696db7d1c97d263b4d175006913c
--- /dev/null
+++ b/tlatools/test/pcal/MPFactorialTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MPFactorialTest extends PCalModelCheckerTestCase {
+
+	public MPFactorialTest() {
+		super("MPFactorial", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "729"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1946", "729", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "25"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/MPNoParamsTest.java b/tlatools/test/pcal/MPNoParamsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..587d42992c0ffe1ed2ed3928cb5252d5d7d51e5c
--- /dev/null
+++ b/tlatools/test/pcal/MPNoParamsTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MPNoParamsTest extends PCalModelCheckerTestCase {
+
+	public MPNoParamsTest() {
+		super("MPNoParams", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "96"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "250", "96", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/MacroQuicksortTest.java b/tlatools/test/pcal/MacroQuicksortTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f132b260dc32947a199089e453e1cec267c0b388
--- /dev/null
+++ b/tlatools/test/pcal/MacroQuicksortTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MacroQuicksortTest extends PCalModelCheckerTestCase {
+
+	public MacroQuicksortTest() {
+		super("MacroQuicksort", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "256"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "31092"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "38084", "31092", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "23"));
+	}
+}
diff --git a/tlatools/test/pcal/MacroRealQuicksortTest.java b/tlatools/test/pcal/MacroRealQuicksortTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..34b9f96c75c266b23dd092b5782464c25c5502aa
--- /dev/null
+++ b/tlatools/test/pcal/MacroRealQuicksortTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MacroRealQuicksortTest extends PCalModelCheckerTestCase {
+
+	public MacroRealQuicksortTest() {
+		super("MacroRealQuicksort", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "256"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "27336"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "64072", "27336", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9"));
+	}
+}
diff --git a/tlatools/test/pcal/MergeSortTest.java b/tlatools/test/pcal/MergeSortTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..994be4e7b6c3c2b14f64b81ba3d5928b11fc3b8d
--- /dev/null
+++ b/tlatools/test/pcal/MergeSortTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MergeSortTest extends PCalModelCheckerTestCase {
+
+	public MergeSortTest() {
+		super("MergeSort", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "6"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "80", ""));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "86", "80", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "18"));
+		assertUncovered("line 162, col 32 to line 162, col 61 of module MergeSort: 0\n" + 
+				"line 178, col 32 to line 178, col 61 of module MergeSort: 0\n" + 
+				"line 179, col 32 to line 179, col 37 of module MergeSort: 0");
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MergeSort.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file MergeSort.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module MergeSort
+Starting... (2012-08-10 17:38:17)
+Implied-temporal checking--satisfiability problem has 1 branches.
+<<"Testing Mergesort on all arrays of length <= ", 2>>  TRUE
+Computing initial states...
+Finished computing initial states: 6 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 2.6E-17
+  based on the actual fingerprints:  val = 1.6E-16
+86 states generated, 80 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 18.
+Finished. (2012-08-10 17:38:17)
+*/
diff --git a/tlatools/test/pcal/MissingBodyInWhileTest.java b/tlatools/test/pcal/MissingBodyInWhileTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..898c952e8d0de0a34f866022a2a8bf5114cc868d
--- /dev/null
+++ b/tlatools/test/pcal/MissingBodyInWhileTest.java
@@ -0,0 +1,26 @@
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.tool.CommonTestCase;
+import util.ToolIO;
+
+public class MissingBodyInWhileTest extends PCalTest {
+	
+	@Test
+	public void test() {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS,
+				trans.runMe(new String[] {"-nocfg", CommonTestCase.BASE_PATH + "MissingBodyInWhile.tla"}));
+		
+		final String[] messages = ToolIO.getAllMessages();
+		assertTrue(messages.length == 1);
+		
+		final String msg = messages[0];
+		assertEquals("Unrecoverable error:\n" + 
+				" -- Missing body of while statement at\n" + 
+				"    line 6, column 14.", msg.trim());
+	}
+}
diff --git a/tlatools/test/pcal/MissingBodyInWithTest.java b/tlatools/test/pcal/MissingBodyInWithTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a57b20270cc55b7b9fdd5c5ab284c2a1471e4013
--- /dev/null
+++ b/tlatools/test/pcal/MissingBodyInWithTest.java
@@ -0,0 +1,26 @@
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.tool.CommonTestCase;
+import util.ToolIO;
+
+public class MissingBodyInWithTest extends PCalTest {
+	
+	@Test
+	public void test() {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS,
+				trans.runMe(new String[] { "-nocfg", CommonTestCase.BASE_PATH + "MissingBodyInWith.tla" }));
+		
+		final String[] messages = ToolIO.getAllMessages();
+		assertTrue(messages.length == 1);
+		
+		final String msg = messages[0];
+		assertEquals("Unrecoverable error:\n" + 
+				" -- Missing body of with statement\n" + 
+				"    at line 5, column 5.", msg.trim());
+	}
+}
diff --git a/tlatools/test/pcal/MultiAssignmentTest.java b/tlatools/test/pcal/MultiAssignmentTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..296ae0ae6eddf95ee587df78437ef9690dbeceb9
--- /dev/null
+++ b/tlatools/test/pcal/MultiAssignmentTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MultiAssignmentTest extends PCalModelCheckerTestCase {
+
+	public MultiAssignmentTest() {
+		super("MultiAssignment", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "27"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "56", "27", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "7"));
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MultiAssignment.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file MultiAssignment.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module MultiAssignment
+Starting... (2012-08-10 17:38:18)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 4.2E-17
+  based on the actual fingerprints:  val = 1.4E-17
+56 states generated, 27 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 7.
+Finished. (2012-08-10 17:38:18)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/MultiProc2Test.java b/tlatools/test/pcal/MultiProc2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..72d0af22c56cb3558d5bd197c4ec3843ebbca601
--- /dev/null
+++ b/tlatools/test/pcal/MultiProc2Test.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MultiProc2Test extends PCalModelCheckerTestCase {
+
+	public MultiProc2Test() {
+		super("MultiProc2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "504"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1212", "504", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14"));
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MultiProc2.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file MultiProc2.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module MultiProc2
+Starting... (2012-08-10 17:38:19)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 4 distinct states generated.
+13  TRUE
+13  TRUE
+14  TRUE
+14  TRUE
+14  TRUE
+14  TRUE
+15  TRUE
+15  TRUE
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 1.9E-14
+  based on the actual fingerprints:  val = 3.7E-13
+1212 states generated, 504 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 14.
+Finished. (2012-08-10 17:38:20)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/MultiprocDefineTest.java b/tlatools/test/pcal/MultiprocDefineTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..227303870a8cfb3fbb65a2673cb9c101a93490f3
--- /dev/null
+++ b/tlatools/test/pcal/MultiprocDefineTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class MultiprocDefineTest extends PCalModelCheckerTestCase {
+
+	public MultiprocDefineTest() {
+		super("MultiprocDefine", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "8", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MultiprocDefine.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file MultiprocDefine.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module MultiprocDefine
+Starting... (2012-08-10 17:40:00)
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 2.6E-18
+  based on the actual fingerprints:  val = 4.8E-19
+14 states generated, 8 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 4.
+Finished. (2012-08-10 17:40:00)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/NestedMacrosTest.java b/tlatools/test/pcal/NestedMacrosTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8b202d927bcd8e2d9a3ff6b2fbe40b1085d0ce7
--- /dev/null
+++ b/tlatools/test/pcal/NestedMacrosTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NestedMacrosTest extends PCalModelCheckerTestCase {
+
+	public NestedMacrosTest() {
+		super("NestedMacros", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "9", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "5"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/NoLoop2Test.java b/tlatools/test/pcal/NoLoop2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..62e5cd8e5a40eedc32d5a3127ddf2fbd4a7e6386
--- /dev/null
+++ b/tlatools/test/pcal/NoLoop2Test.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NoLoop2Test extends PCalModelCheckerTestCase {
+
+	public NoLoop2Test() {
+		super("NoLoop2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "11", "9", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "6"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NoLoop2.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file NoLoop2.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module NoLoop2
+Starting... (2012-08-10 17:38:22)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+7  TRUE
+7  TRUE
+10  TRUE
+10  TRUE
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 9.8E-19
+  based on the actual fingerprints:  val = 1.6E-17
+11 states generated, 9 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 6.
+Finished. (2012-08-10 17:38:22)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/NoLoopTest.java b/tlatools/test/pcal/NoLoopTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fa96e5109e5f5a1d13eafbf4e5a412449ef1586
--- /dev/null
+++ b/tlatools/test/pcal/NoLoopTest.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NoLoopTest extends PCalModelCheckerTestCase {
+
+	public NoLoopTest() {
+		super("NoLoop", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "8", "6", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NoLoop.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file NoLoop.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module NoLoop
+Starting... (2012-08-10 17:38:21)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+5  TRUE
+5  TRUE
+7  TRUE
+7  TRUE
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 6.5E-19
+  based on the actual fingerprints:  val = 1.5E-18
+8 states generated, 6 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 4.
+Finished. (2012-08-10 17:38:21)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/NoParamsTest.java b/tlatools/test/pcal/NoParamsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d84a809071b4894a20a59c3283fd428dde03ff0f
--- /dev/null
+++ b/tlatools/test/pcal/NoParamsTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NoParamsTest extends PCalModelCheckerTestCase {
+
+	public NoParamsTest() {
+		super("NoParams", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "6", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "6"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NoParams.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file NoParams.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module NoParams
+Starting... (2012-08-10 17:38:23)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+2  TRUE
+2  TRUE
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 3.3E-19
+  based on the actual fingerprints:  val = 1.7E-18
+7 states generated, 6 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 6.
+Finished. (2012-08-10 17:38:23)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/NotSoSimpleLoopTest.java b/tlatools/test/pcal/NotSoSimpleLoopTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4df7ab3718a5b5588e60c453a13f6962ab6de4aa
--- /dev/null
+++ b/tlatools/test/pcal/NotSoSimpleLoopTest.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NotSoSimpleLoopTest extends PCalModelCheckerTestCase {
+
+	public NotSoSimpleLoopTest() {
+		super("NotSoSimpleLoop", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "13", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NotSoSimpleLoop.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file NotSoSimpleLoop.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module NotSoSimpleLoop
+Starting... (2012-08-10 17:38:24)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 7.0E-19
+  based on the actual fingerprints:  val = 3.3E-18
+14 states generated, 13 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 13.
+Finished. (2012-08-10 17:38:25)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/OptionalSemicolonTest.java b/tlatools/test/pcal/OptionalSemicolonTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1385820d82cdc1ce8721df17c45b5de54dc3748
--- /dev/null
+++ b/tlatools/test/pcal/OptionalSemicolonTest.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import tla2sany.drivers.SANY;
+import util.TestPrintStream;
+import util.ToolIO;
+
+public class OptionalSemicolonTest extends PCalTest {
+	
+	/*
+	 * A PlusCal User's Manual P-Syntax:
+	 * "[...] the final semicolon [or comma] is optional in the p-syntax."
+	 */
+	
+	@Test
+	public void noOptionalSemiColonVariableList1() throws IOException {
+		// Translate from PCal to TLA+
+		final String filename = "MissingSemiColonVariableListTest1" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" + 
+				"(*\n" + 
+				"--algorithm algo\n" + 
+				"variables foo = 0" + // no optional semicolon
+				"\n" + 
+				"fair process bug = 0\n" + // notice the "fair" statement 
+				"begin\n" + 
+				"L:\n" + 
+				"    skip\n" + 
+				"end process\n" + 
+				"\n" + 
+				"end algorithm *)\n" + 
+				"===="
+				);
+		test(filename, absolutePath);
+	}
+	
+	@Test
+	public void noOptionalSemiColonVariableList2() throws IOException {
+		// Translate from PCal to TLA+
+		final String filename = "MissingSemiColonVariableListTest2" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" + 
+				"(*\n" + 
+				"--algorithm algo\n" + 
+				"variables foo = 0 ;" + // optional semicolon
+				"\n" + 
+				"fair process bug = 0\n" + // notice the "fair" statement 
+				"begin\n" + 
+				"L:\n" + 
+				"    skip\n" + 
+				"end process\n" + 
+				"\n" + 
+				"end algorithm *)\n" + 
+				"===="
+				);
+		test(filename, absolutePath);
+	}
+	
+	@Test
+	public void noOptionalSemiColonVariableList3() throws IOException {
+		// Translate from PCal to TLA+
+		final String filename = "MissingSemiColonVariableListTest3" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" + 
+				"(*\n" + 
+				"--algorithm algo\n" + 
+				"variables foo = 0 ;" + // optional semicolon
+				"\n" + 
+				"process bug = 0\n" + // notice the "fair" statement 
+				"begin\n" + 
+				"L:\n" + 
+				"    skip\n" + 
+				"end process\n" + 
+				"\n" + 
+				"end algorithm *)\n" + 
+				"===="
+				);
+		test(filename, absolutePath);
+	}
+	
+	@Test
+	public void noOptionalSemiColonVariableList4() throws IOException {
+		// Translate from PCal to TLA+
+		final String filename = "MissingSemiColonVariableListTest4" + System.currentTimeMillis();
+		final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename,
+				"---- MODULE " + filename + " ----\n" + 
+				"(*\n" + 
+				"--algorithm algo\n" + 
+				"variables foo = 0" + // no optional semicolon
+				"\n" + 
+				"process bug = 0\n" + // no "fair" statement 
+				"begin\n" + 
+				"L:\n" + 
+				"    skip\n" + 
+				"end process\n" + 
+				"\n" + 
+				"end algorithm *)\n" + 
+				"===="
+				);
+		test(filename, absolutePath);
+	}
+
+	private void test(final String filename, final String absolutePath) {
+		assertEquals(trans.STATUS_EXIT_WITHOUT_ERROR, trans.runMe(new String[] { "-nocfg", absolutePath }));
+
+		// Parse with SANY and check for errors (collects parse errors into ToolIO.out)
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		SANY.SANYmain(new String[] { absolutePath });
+		testPrintStream.assertSubstring("Semantic processing of module " + filename);
+	}
+}
diff --git a/tlatools/test/pcal/PCalModelCheckerTestCase.java b/tlatools/test/pcal/PCalModelCheckerTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..d683d6576dbca9324c86251556047a26c122f387
--- /dev/null
+++ b/tlatools/test/pcal/PCalModelCheckerTestCase.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+
+import tlc2.output.EC;
+import tlc2.tool.CommonTestCase;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.TLAConstants;
+import util.ToolIO;
+
+public abstract class PCalModelCheckerTestCase extends ModelCheckerTestCase {
+
+	private final List<String> pcalArgs = new ArrayList<String>();
+
+	public PCalModelCheckerTestCase(final String spec, final String path) {
+		this(spec, path, EC.ExitStatus.SUCCESS);
+	}
+	
+	public PCalModelCheckerTestCase(final String spec, final String path, final String[] extraPcalArgs) {
+		this(spec, path, EC.ExitStatus.SUCCESS);
+		this.pcalArgs.addAll(Arrays.asList(extraPcalArgs));
+	}
+	
+	public PCalModelCheckerTestCase(final String spec, final String path, final int exitStatus) {
+		super(spec, path, exitStatus);
+		this.pcalArgs.add("-unixEOL");
+	}
+
+	@Before
+	@Override
+	public void setUp() {
+		// Make tool capture the output written to ToolIO.out. Otherwise,
+		// ToolIO#getAllMessages returns an empty array.
+		ToolIO.setMode(ToolIO.TOOL);
+		
+		// Reset ToolIO for each test case. Otherwise, a test case sees the output of
+		// the previous tests.
+		ToolIO.reset();
+		
+		this.pcalArgs.add(CommonTestCase.BASE_PATH + File.separator + path + File.separator + spec
+				+ TLAConstants.Files.TLA_EXTENSION);
+		
+		// Run PCal translator
+		assertEquals(0, trans.runMe(pcalArgs.toArray(new String[pcalArgs.size()])));
+		assertNotNull(PcalParams.tlaPcalMapping); // successfully translated PCal to TLA+
+		
+		final String[] messages = ToolIO.getAllMessages();
+		assertTrue(Arrays.toString(messages), messages.length == 4 || messages.length == 5);
+
+		// Run TLC
+		super.setUp();
+	}
+}
diff --git a/tlatools/test/pcal/PCalTest.java b/tlatools/test/pcal/PCalTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8db29ff4c37eea12dcbbbd5e22b46c2d4206ce59
--- /dev/null
+++ b/tlatools/test/pcal/PCalTest.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Before;
+
+import util.TLAConstants;
+import util.ToolIO;
+
+public abstract class PCalTest {
+	
+	@Before
+	public void setup() {
+		// Make tool capture the output written to ToolIO.out. Otherwise,
+		// ToolIO#getAllMessages returns an empty array.
+		ToolIO.setMode(ToolIO.TOOL);
+		
+		// Reset ToolIO for each test case. Otherwise, a test case sees the output of
+		// the previous tests.
+		ToolIO.reset();
+	}
+
+	protected static String writeFile(String filename, String content) throws IOException {
+		final Path path = Files.createFile(Paths.get(filename + TLAConstants.Files.TLA_EXTENSION));
+		Files.write(path, content.getBytes());
+		
+		final File file = path.toFile();
+		file.deleteOnExit();
+		return file.getAbsolutePath();
+	}
+	
+	protected static String writeTempFile(String filename, String content) throws IOException {
+		final Path path = Files.createTempFile(filename, TLAConstants.Files.TLA_EXTENSION);
+		Files.write(path, content.getBytes());
+		
+		final File file = path.toFile();
+		file.deleteOnExit();
+		return file.getAbsolutePath();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/Test052.java b/tlatools/test/pcal/PcalPaxosTest.java
similarity index 78%
rename from tlatools/test/tlc2/tool/liveness/Test052.java
rename to tlatools/test/pcal/PcalPaxosTest.java
index 8ed0bcf4251bb6f9d5985d00b808c1a80955e626..a8526a2e004d6d5c89afb22ce5f707c65093055e 100644
--- a/tlatools/test/tlc2/tool/liveness/Test052.java
+++ b/tlatools/test/pcal/PcalPaxosTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
  *
  * The MIT License (MIT)
  * 
@@ -23,24 +23,27 @@
  * Contributors:
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
+package pcal;
 
-package tlc2.tool.liveness;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import tlc2.output.EC;
 
-public class Test052 extends ModelCheckerTestCase {
+public class PcalPaxosTest extends PCalModelCheckerTestCase {
 
-	public Test052() {
-		super("test52");
+	public PcalPaxosTest() {
+		super("PcalPaxos", "pcal");
 	}
 
+	@Test
 	public void testSpec() {
-		// ModelChecker has finished and generated the expected amount of states
-		assertTrue(recorder.recorded(EC.TLC_MODE_MC));
-		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
 		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "", "", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, ""));
 	}
 }
diff --git a/tlatools/test/pcal/PetersonTest.java b/tlatools/test/pcal/PetersonTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6928eb13bcd43aa9a38f1e8eef85a0085a99af47
--- /dev/null
+++ b/tlatools/test/pcal/PetersonTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class PetersonTest extends PCalModelCheckerTestCase {
+
+	public PetersonTest() {
+		super("Peterson", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "85", "42", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/Quicksort2ProcsTest.java b/tlatools/test/pcal/Quicksort2ProcsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f3b17ac4c23650783d1e08e0a730f2a75db0a18
--- /dev/null
+++ b/tlatools/test/pcal/Quicksort2ProcsTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Quicksort2ProcsTest extends PCalModelCheckerTestCase {
+
+	public Quicksort2ProcsTest() {
+		super("Quicksort2Procs", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "361"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "445", "361", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "15"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Quicksort2Procs.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file Quicksort2Procs.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module Quicksort2Procs
+Starting... (2012-08-10 17:38:29)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 27 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 1.6E-15
+  based on the actual fingerprints:  val = 1.1E-14
+445 states generated, 361 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 15.
+Finished. (2012-08-10 17:38:29)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/QuicksortMacroTest.java b/tlatools/test/pcal/QuicksortMacroTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..19876442cab166a454bbd210ecf71fc9836da533
--- /dev/null
+++ b/tlatools/test/pcal/QuicksortMacroTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class QuicksortMacroTest extends PCalModelCheckerTestCase {
+
+	public QuicksortMacroTest() {
+		super("QuicksortMacro", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "306"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "390", "306", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup QuicksortMacro.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file QuicksortMacro.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module QuicksortMacro
+Starting... (2012-08-10 17:38:27)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 27 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 1.4E-15
+  based on the actual fingerprints:  val = 4.6E-15
+390 states generated, 306 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 13.
+Finished. (2012-08-10 17:38:28)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/QuicksortTest.java b/tlatools/test/pcal/QuicksortTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc8a6bb67e257c743723346e35dc0d99f3fb7db4
--- /dev/null
+++ b/tlatools/test/pcal/QuicksortTest.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class QuicksortTest extends PCalModelCheckerTestCase {
+
+	public QuicksortTest() {
+		super("Quicksort", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "933"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1017", "933", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "16"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Quicksort.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file Quicksort.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module Quicksort
+Starting... (2012-08-10 17:38:26)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 27 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 4.2E-15
+  based on the actual fingerprints:  val = 2.3E-14
+1017 states generated, 933 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 16.
+Finished. (2012-08-10 17:38:26)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/RABTest.java b/tlatools/test/pcal/RABTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3099d1f12eaaf2365c6ddb6be7c6a5d666e74f3
--- /dev/null
+++ b/tlatools/test/pcal/RABTest.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class RABTest extends PCalModelCheckerTestCase {
+
+	public RABTest() {
+		super("RAB", "pcal", EC.ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "551", "350", "130"));
+		assertEquals(7, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"A\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Loop\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> FALSE, value |-> FALSE] ]");
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"A\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"FetchFlags\" @@ p1 :> \"Loop\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> FALSE, value |-> FALSE] ]");
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"A\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"Loop\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> FALSE, value |-> FALSE] ]");
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"FetchFlags\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> FALSE, value |-> FALSE] ]");
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"StoreFlags\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> FALSE, value |-> FALSE] ]");
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"ReadFlags\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> TRUE, value |-> TRUE] ]");
+		expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + 
+				"/\\ temp = ( p0 :>\n" + 
+				"      [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + 
+				"  p1 :>\n" + 
+				"      [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + 
+				"        B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + 
+				"/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + 
+				"/\\ pc = (p0 :> \"ReadFlags\" @@ p1 :> \"ReadFlags\")\n" + 
+				"/\\ flags = [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + 
+				"  B |-> [valid |-> FALSE, value |-> FALSE] ]");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup RAB.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file RAB.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module RAB
+Starting... (2012-08-10 17:38:30)
+Computing initial states...
+Finished computing initial states: 4 distinct states generated.
+Error: Invariant Consistency is violated.
+Error: The behavior up to this point is:
+State 1: <Initial predicate>
+/\ myattr = (p0 :> "A" @@ p1 :> "A")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "Loop" @@ p1 :> "Loop")
+/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],
+  B |-> [valid |-> FALSE, value |-> FALSE] ]
+
+State 2: <Action line 163, col 15 to line 175, col 44 of module RAB>
+/\ myattr = (p0 :> "A" @@ p1 :> "A")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> TRUE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "FetchFlags" @@ p1 :> "Loop")
+/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],
+  B |-> [valid |-> FALSE, value |-> FALSE] ]
+
+State 3: <Action line 182, col 21 to line 185, col 58 of module RAB>
+/\ myattr = (p0 :> "A" @@ p1 :> "A")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> TRUE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "StoreFlags" @@ p1 :> "Loop")
+/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],
+  B |-> [valid |-> FALSE, value |-> FALSE] ]
+
+State 4: <Action line 163, col 15 to line 175, col 44 of module RAB>
+/\ myattr = (p0 :> "A" @@ p1 :> "B")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> TRUE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> TRUE, value |-> TRUE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "StoreFlags" @@ p1 :> "FetchFlags")
+/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],
+  B |-> [valid |-> FALSE, value |-> FALSE] ]
+
+State 5: <Action line 182, col 21 to line 185, col 58 of module RAB>
+/\ myattr = (p0 :> "A" @@ p1 :> "B")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> TRUE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> TRUE, value |-> TRUE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "StoreFlags" @@ p1 :> "StoreFlags")
+/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],
+  B |-> [valid |-> FALSE, value |-> FALSE] ]
+
+State 6: <Action line 187, col 21 to line 190, col 57 of module RAB>
+/\ myattr = (p0 :> "A" @@ p1 :> "B")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> TRUE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> TRUE, value |-> TRUE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "StoreFlags" @@ p1 :> "ReadFlags")
+/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],
+  B |-> [valid |-> TRUE, value |-> TRUE] ]
+
+State 7: <Action line 187, col 21 to line 190, col 57 of module RAB>
+/\ myattr = (p0 :> "A" @@ p1 :> "B")
+/\ temp = ( p0 :>
+      [ A |-> [valid |-> TRUE, value |-> FALSE],
+        B |-> [valid |-> FALSE, value |-> FALSE] ] @@
+  p1 :>
+      [ A |-> [valid |-> FALSE, value |-> FALSE],
+        B |-> [valid |-> TRUE, value |-> TRUE] ] )
+/\ calc = [A |-> FALSE, B |-> TRUE]
+/\ pc = (p0 :> "ReadFlags" @@ p1 :> "ReadFlags")
+/\ flags = [ A |-> [valid |-> TRUE, value |-> FALSE],
+  B |-> [valid |-> FALSE, value |-> FALSE] ]
+
+551 states generated, 350 distinct states found, 131 states left on queue.
+The depth of the complete state graph search is 7.
+Finished. (2012-08-10 17:38:30)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/RealQuicksort2Test.java b/tlatools/test/pcal/RealQuicksort2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea808b8e35609fc52d7ac42321572d515bda2e42
--- /dev/null
+++ b/tlatools/test/pcal/RealQuicksort2Test.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class RealQuicksort2Test extends PCalModelCheckerTestCase {
+
+	public RealQuicksort2Test() {
+		super("RealQuicksort2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "33"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "612"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "853", "612", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup RealQuicksort2.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file RealQuicksort2.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module FiniteSets
+Semantic processing of module RealQuicksort2
+Starting... (2012-08-10 17:38:33)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 33 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 8.0E-15
+  based on the actual fingerprints:  val = 2.2E-14
+853 states generated, 612 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 11.
+Finished. (2012-08-10 17:38:33)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/RealQuicksortTest.java b/tlatools/test/pcal/RealQuicksortTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7d0548c4dffd809d214b431802a93dc25d5f1c3
--- /dev/null
+++ b/tlatools/test/pcal/RealQuicksortTest.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class RealQuicksortTest extends PCalModelCheckerTestCase {
+
+	public RealQuicksortTest() {
+		super("RealQuicksort", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "33"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "495"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "706", "495", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup RealQuicksort.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file RealQuicksort.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module FiniteSets
+Semantic processing of module RealQuicksort
+Starting... (2012-08-10 17:38:31)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 33 distinct states generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 5.7E-15
+  based on the actual fingerprints:  val = 2.7E-15
+706 states generated, 495 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 14.
+Finished. (2012-08-10 17:38:32)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/ReallySimpleMultiProcTest.java b/tlatools/test/pcal/ReallySimpleMultiProcTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c2ed58b2f4cfb07b427cb5d97fab2d8f44e5a43
--- /dev/null
+++ b/tlatools/test/pcal/ReallySimpleMultiProcTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ReallySimpleMultiProcTest extends PCalModelCheckerTestCase {
+
+	public ReallySimpleMultiProcTest() {
+		super("ReallySimpleMultiProc", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "76"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "144", "76", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "7"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/SBBTest.java b/tlatools/test/pcal/SBBTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9c4074439642a999e0bf7aa127e9cfb9ca4a78b
--- /dev/null
+++ b/tlatools/test/pcal/SBBTest.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class SBBTest extends PCalModelCheckerTestCase {
+
+	public SBBTest() {
+		super("SBB", "pcal", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3126", "1617", "328"));
+		assertEquals(15, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> NoBuf @@ p1 :> NoBuf)\n" + 
+				"/\\ op = (p0 :> {} @@ p1 :> {})\n" + 
+				"/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Loop\")");
+		expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> NoBuf)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> {})\n" + 
+				"/\\ pc = (p0 :> \"Publish1\" @@ p1 :> \"Loop\")");
+		expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> NoBuf)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> {})\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Loop\")");
+		expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b0)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify1\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b0, owner |-> p1]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify3\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> p1]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Loop\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> p1]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify1\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> p1]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish3\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {b0}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {b0}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish1\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {b0}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {b0}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Publish3\" @@ p1 :> \"Modify2\")");
+		expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + 
+				"/\\ publishedbuffers = {b0, b1}\n" + 
+				"/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + 
+				"/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + 
+				"/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + 
+				"/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Modify2\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/SemaphoreMutexTest.java b/tlatools/test/pcal/SemaphoreMutexTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bab0bb2def3a75de5850115ebb55ea8d55cf95c3
--- /dev/null
+++ b/tlatools/test/pcal/SemaphoreMutexTest.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SemaphoreMutexTest extends PCalModelCheckerTestCase {
+
+	public SemaphoreMutexTest() {
+		super("SemaphoreMutex", "pcal", new String[] {"-sf"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "32"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "73", "32", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "6"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SemaphoreMutex.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file SemaphoreMutex.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Semantic processing of module Naturals
+Semantic processing of module SemaphoreMutex
+Starting... (2012-08-10 17:38:36)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 7.1E-17
+  based on the actual fingerprints:  val = 2.9E-15
+73 states generated, 32 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 6.
+Finished. (2012-08-10 17:38:36)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/SimpleLoopTest.java b/tlatools/test/pcal/SimpleLoopTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..81e655475b07724106015170b4d7add0bd0f217e
--- /dev/null
+++ b/tlatools/test/pcal/SimpleLoopTest.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SimpleLoopTest extends PCalModelCheckerTestCase {
+
+	public SimpleLoopTest() {
+		super("SimpleLoop", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SimpleLoop.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file SimpleLoop.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module SimpleLoop
+Starting... (2012-08-10 17:38:37)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 6.5E-19
+  based on the actual fingerprints:  val = 2.1E-18
+13 states generated, 12 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 12.
+Finished. (2012-08-10 17:38:37)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/SimpleLoopWithProcedureTest.java b/tlatools/test/pcal/SimpleLoopWithProcedureTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d03a43224c5eb7484d02fa39dd0ea93d8e83aa5
--- /dev/null
+++ b/tlatools/test/pcal/SimpleLoopWithProcedureTest.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SimpleLoopWithProcedureTest extends PCalModelCheckerTestCase {
+
+	public SimpleLoopWithProcedureTest() {
+		super("SimpleLoopWithProcedure", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "64"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "66", "64", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "32"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SimpleLoopWithProcedure.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file SimpleLoopWithProcedure.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module SimpleLoopWithProcedure
+Starting... (2012-08-10 17:38:38)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 2 distinct states generated.
+0  TRUE
+0  TRUE
+0  TRUE
+0  TRUE
+0  TRUE
+0  TRUE
+0  TRUE
+0  TRUE
+3  TRUE
+3  TRUE
+3  TRUE
+3  TRUE
+4  TRUE
+4  TRUE
+4  TRUE
+4  TRUE
+6  TRUE
+6  TRUE
+6  TRUE
+6  TRUE
+8  TRUE
+8  TRUE
+8  TRUE
+8  TRUE
+9  TRUE
+9  TRUE
+9  TRUE
+9  TRUE
+12  TRUE
+12  TRUE
+12  TRUE
+12  TRUE
+12  TRUE
+12  TRUE
+12  TRUE
+12  TRUE
+16  TRUE
+16  TRUE
+16  TRUE
+16  TRUE
+15  TRUE
+15  TRUE
+15  TRUE
+15  TRUE
+20  TRUE
+20  TRUE
+20  TRUE
+20  TRUE
+18  TRUE
+18  TRUE
+18  TRUE
+18  TRUE
+24  TRUE
+24  TRUE
+24  TRUE
+24  TRUE
+21  TRUE
+21  TRUE
+21  TRUE
+21  TRUE
+28  TRUE
+28  TRUE
+28  TRUE
+28  TRUE
+24  TRUE
+24  TRUE
+24  TRUE
+24  TRUE
+32  TRUE
+32  TRUE
+32  TRUE
+32  TRUE
+27  TRUE
+27  TRUE
+27  TRUE
+27  TRUE
+36  TRUE
+36  TRUE
+36  TRUE
+36  TRUE
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 6.9E-18
+  based on the actual fingerprints:  val = 8.3E-16
+66 states generated, 64 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 32.
+Finished. (2012-08-10 17:38:39)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/SimpleMultiProcTest.java b/tlatools/test/pcal/SimpleMultiProcTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..597ed04223c45cc6dfb1b3952c57b23850505524
--- /dev/null
+++ b/tlatools/test/pcal/SimpleMultiProcTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SimpleMultiProcTest extends PCalModelCheckerTestCase {
+
+	public SimpleMultiProcTest() {
+		super("SimpleMultiProc", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "16"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "103944"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "369680", "103944", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17"));
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SimpleMultiProc.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file SimpleMultiProc.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module SimpleMultiProc
+Starting... (2012-08-10 17:38:40)
+Implied-temporal checking--satisfiability problem has 1 branches.
+Computing initial states...
+Finished computing initial states: 16 distinct states generated.
+Progress(7) at 2012-08-10 17:38:43: 27922 states generated (27922 s/min), 12586 distinct states found (12586 ds/min), 6768 states left on queue.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 1.5E-9
+  based on the actual fingerprints:  val = 4.7E-10
+369680 states generated, 103944 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 17.
+Finished. (2012-08-10 17:39:23)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/StackTestTest.java b/tlatools/test/pcal/StackTestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..752db590030723b19edf95465c80dd5af1161e41
--- /dev/null
+++ b/tlatools/test/pcal/StackTestTest.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class StackTestTest extends PCalModelCheckerTestCase {
+
+	public StackTestTest() {
+		super("StackTest", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
+/*
+TLC2 Version 2.12 of 29 January 2018
+Running breadth-first search Model-Checking with 1 worker on 4 cores with 5303MB heap and 64MB offheap memory (Linux 4.15.0-22-generic amd64, Oracle Corporation 1.8.0_171 x86_64).
+Parsing file /home/markus/Desktop/LesliePCalTest/alltests/StackTest.tla
+Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Sequences.tla
+Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Naturals.tla
+Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module StackTest
+Starting... (2018-05-23 12:30:31)
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Warning: The EXCEPT was applied to non-existing fields of the value at
+line 36, col 26 to line 36, col 30 of module StackTest
+(Use the -nowarning option to disable this warning.)
+Error: TLC threw an unexpected exception.
+This was probably caused by an error in the spec or model.
+See the User Output or TLC Console for clues to what happened.
+The exception was a tlc2.tool.EvalException
+: Attempted to apply the operator overridden by the Java method
+public static tlc2.value.Value tlc2.module.TLC.Assert(tlc2.value.Value,tlc2.value.Value),
+but it produced the following error:
+The first argument of Assert evaluated to FALSE; the second argument was:
+"Failure of assertion at line 13, column 17."
+Error: The behavior up to this point is:
+State 1: <Initial predicate>
+/\ stack = (0 :> <<>> @@ 1 :> <<>> @@ 2 :> <<>>)
+/\ a = (0 :> 42 @@ 1 :> 42 @@ 2 :> 42)
+/\ pc = (0 :> "Q1" @@ 1 :> "Q1" @@ 2 :> "Q1")
+
+State 2: <Q1 line 51, col 13 to line 59, col 47 of module StackTest>
+/\ stack = ( 0 :> <<[pc |-> "Done", a |-> 42, procedure |-> "P"]>> @@
+  1 :> <<>> @@
+  2 :> <<>> )
+/\ a = (0 :> 22 @@ 1 :> 42 @@ 2 :> 42)
+/\ pc = (0 :> "P1" @@ 1 :> "Q1" @@ 2 :> "Q1")
+
+Error: The error occurred when TLC was evaluating the nested
+expressions at the following positions:
+0. Line 35, column 13 to line 42, column 21 in StackTest
+1. Line 35, column 16 to line 35, column 30 in StackTest
+2. Line 36, column 16 to line 36, column 80 in StackTest
+3. Line 37, column 16 to line 38, column 68 in StackTest
+
+
+4 states generated, 4 distinct states found, 2 states left on queue.
+The depth of the complete state graph search is 2.
+The average outdegree of the complete state graph is 3 (minimum is 3, the maximum 3 and the 95th percentile is 3).
+Finished in 00s at (2018-05-23 12:30:31)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/StarkMutexTest.java b/tlatools/test/pcal/StarkMutexTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e7f49fd994922090e50ece44a34444bb561a2e2
--- /dev/null
+++ b/tlatools/test/pcal/StarkMutexTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class StarkMutexTest extends PCalModelCheckerTestCase {
+
+	public StarkMutexTest() {
+		super("StarkMutex", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "69504", "3 branches of "));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "54809", "23168", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "92"));
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup StarkMutex.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file StarkMutex.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module StarkMutex
+Starting... (2012-08-10 17:39:24)
+Implied-temporal checking--satisfiability problem has 3 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Progress(34) at 2012-08-10 17:39:27: 6899 states generated (6899 s/min), 3423 distinct states found (3423 ds/min), 265 states left on queue.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 4.0E-11
+  based on the actual fingerprints:  val = 1.6E-10
+54809 states generated, 23168 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 92.
+Finished. (2012-08-10 17:39:49)
+*/
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/liveness/Test055.java b/tlatools/test/pcal/SubSubTest.java
similarity index 77%
rename from tlatools/test/tlc2/tool/liveness/Test055.java
rename to tlatools/test/pcal/SubSubTest.java
index 4a35ec0c1b282e0a8d30cc14a02b830cc6c134ec..dedd8655e10c8d52b4aeff0f3530be41bcb05c96 100644
--- a/tlatools/test/tlc2/tool/liveness/Test055.java
+++ b/tlatools/test/pcal/SubSubTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
  *
  * The MIT License (MIT)
  * 
@@ -23,24 +23,29 @@
  * Contributors:
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
+package pcal;
 
-package tlc2.tool.liveness;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import tlc2.output.EC;
 
-public class Test055 extends ModelCheckerTestCase {
+public class SubSubTest extends PCalModelCheckerTestCase {
 
-	public Test055() {
-		super("test55");
+	public SubSubTest() {
+		super("SubSub", "pcal");
 	}
 
+	@Test
 	public void testSpec() {
-		// ModelChecker has finished and generated the expected amount of states
-		assertTrue(recorder.recorded(EC.TLC_MODE_MC));
-		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
 		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "8", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/pcal/SyncConsTest.java b/tlatools/test/pcal/SyncConsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..43b92e126ba523f65ee5116d0d95e74b270c251c
--- /dev/null
+++ b/tlatools/test/pcal/SyncConsTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SyncConsTest extends PCalModelCheckerTestCase {
+
+	public SyncConsTest() {
+		super("SyncCons", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "22580", "8754", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "48"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SyncCons.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file SyncCons.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module FiniteSets
+Semantic processing of module TLC
+Semantic processing of module SyncCons
+Starting... (2012-08-10 17:39:56)
+Computing initial states...
+Finished computing initial states: 2 distinct states generated.
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 6.6E-12
+  based on the actual fingerprints:  val = 4.4E-12
+22580 states generated, 8754 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 48.
+Finished. (2012-08-10 17:39:57)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/TestPCandStackTest.java b/tlatools/test/pcal/TestPCandStackTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..239597e7744cdfe42aa229decbc76e13fb526eff
--- /dev/null
+++ b/tlatools/test/pcal/TestPCandStackTest.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class TestPCandStackTest extends PCalModelCheckerTestCase {
+
+	public TestPCandStackTest() {
+		super("TestPCandStack", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
+/*
+TLC2 Version 2.12 of 29 January 2018
+Running breadth-first search Model-Checking with 1 worker on 4 cores with 5303MB heap and 64MB offheap memory (Linux 4.15.0-22-generic amd64, Oracle Corporation 1.8.0_171 x86_64).
+Parsing file /home/markus/Desktop/LesliePCalTest/alltests/TestPCandStack.tla
+Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Naturals.tla
+Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Sequences.tla
+Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module TestPCandStack
+Starting... (2018-05-23 12:30:30)
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Warning: The EXCEPT was applied to non-existing fields of the value at
+line 46, col 26 to line 46, col 30 of module TestPCandStack
+(Use the -nowarning option to disable this warning.)
+Error: TLC threw an unexpected exception.
+This was probably caused by an error in the spec or model.
+See the User Output or TLC Console for clues to what happened.
+The exception was a tlc2.tool.EvalException
+: Attempted to apply the operator overridden by the Java method
+public static tlc2.value.Value tlc2.module.TLC.Assert(tlc2.value.Value,tlc2.value.Value),
+but it produced the following error:
+The first argument of Assert evaluated to FALSE; the second argument was:
+"Failure of assertion at line 16, column 26."
+Error: The behavior up to this point is:
+State 1: <Initial predicate>
+/\ stack = <<<<>>, <<>>, <<>>>>
+/\ pc = <<"M1", "M1", "M1">>
+
+State 2: <M1 line 43, col 13 to line 47, col 47 of module TestPCandStack>
+/\ stack = <<<<>>, <<>>, <<>>>>
+/\ pc = <<"M2", "M1", "M1">>
+
+Error: The error occurred when TLC was evaluating the nested
+expressions at the following positions:
+0. Line 49, column 13 to line 56, column 29 in TestPCandStack
+1. Line 49, column 16 to line 49, column 30 in TestPCandStack
+2. Line 50, column 16 to line 54, column 79 in TestPCandStack
+3. Line 51, column 24 to line 52, column 79 in TestPCandStack
+4. Line 51, column 27 to line 52, column 79 in TestPCandStack
+
+
+4 states generated, 4 distinct states found, 2 states left on queue.
+The depth of the complete state graph search is 2.
+The average outdegree of the complete state graph is 3 (minimum is 3, the maximum 3 and the 95th percentile is 3).
+Finished in 00s at (2018-05-23 12:30:30)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/TestReplaceTest.java b/tlatools/test/pcal/TestReplaceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d498528eb300167cbe4c0197ea05d64079741ec
--- /dev/null
+++ b/tlatools/test/pcal/TestReplaceTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class TestReplaceTest extends PCalModelCheckerTestCase {
+
+	public TestReplaceTest() {
+		super("TestReplace", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "17", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17"));
+
+		assertUncovered("line 124, col 32 to line 124, col 41 of module TestReplace: 0\n"
+				+ "line 175, col 33 to line 175, col 41 of module TestReplace: 0");
+	}
+}
diff --git a/tlatools/test/pcal/TestTabsTest.java b/tlatools/test/pcal/TestTabsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ceb4f580356c4e591874fa11552cf94ec13a77dd
--- /dev/null
+++ b/tlatools/test/pcal/TestTabsTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class TestTabsTest extends PCalModelCheckerTestCase {
+
+	public TestTabsTest() {
+		super("TestTabs", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup TestTabs.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file TestTabs.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module TestTabs
+Starting... (2012-08-10 17:40:01)
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 1.1E-19
+  based on the actual fingerprints:  val = 1.1E-19
+3 states generated, 2 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 2.
+Finished. (2012-08-10 17:40:01)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/TestTest.java b/tlatools/test/pcal/TestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3385dd16518e10f3274ca807df8eed09ec630166
--- /dev/null
+++ b/tlatools/test/pcal/TestTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class TestTest extends PCalModelCheckerTestCase {
+
+	public TestTest() {
+		super("Test", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "4"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		assertUncovered("line 47, col 31 to line 47, col 37 of module Test: 0\n"
+				+ "line 48, col 31 to line 48, col 37 of module Test: 0\n"
+				+ "line 51, col 24 to line 51, col 30 of module Test: 0\n"
+				+ "line 52, col 9 to line 52, col 28 of module Test: 0\n"
+				+ "line 53, col 9 to line 53, col 28 of module Test: 0\n"
+				+ "line 54, col 9 to line 54, col 14 of module Test: 0\n"
+				+ "line 68, col 43 to line 68, col 49 of module Test: 0\n"
+				+ "line 69, col 43 to line 69, col 49 of module Test: 0\n"
+				+ "line 72, col 36 to line 72, col 42 of module Test: 0\n"
+				+ "line 73, col 21 to line 73, col 26 of module Test: 0\n"
+				+ "line 84, col 32 to line 84, col 38 of module Test: 0\n"
+				+ "line 87, col 25 to line 87, col 31 of module Test: 0");
+	}
+}
diff --git a/tlatools/test/pcal/TreeBarrierTest.java b/tlatools/test/pcal/TreeBarrierTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8822ae03f635310f0d1504afe54292ac280b7a84
--- /dev/null
+++ b/tlatools/test/pcal/TreeBarrierTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class TreeBarrierTest extends PCalModelCheckerTestCase {
+
+	public TreeBarrierTest() {
+		super("TreeBarrier", "pcal", new String[] {"-wfNext"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12570", "6 branches of ")); // 6 * 2095 = 12570 
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5414", "2095", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "106"));
+
+		assertUncovered("line 120, col 19 to line 120, col 32 of module TreeBarrier: 0");
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup TreeBarrier.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file TreeBarrier.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TreeBarrier
+Starting... (2012-08-10 17:39:50)
+Implied-temporal checking--satisfiability problem has 6 branches.
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Checking temporal properties for the complete state space...
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 3.8E-13
+  based on the actual fingerprints:  val = 6.4E-14
+5414 states generated, 2095 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 106.
+Finished. (2012-08-10 17:39:53)
+*/
\ No newline at end of file
diff --git a/tlatools/test/pcal/ULCallReturn1Test.java b/tlatools/test/pcal/ULCallReturn1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..89275814ba9f7e0567180f8eb7bc07251354e74e
--- /dev/null
+++ b/tlatools/test/pcal/ULCallReturn1Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ULCallReturn1Test extends PCalModelCheckerTestCase {
+
+	public ULCallReturn1Test() {
+		super("ULCallReturn1", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/ULEuclidTest.java b/tlatools/test/pcal/ULEuclidTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6a144210ca0a39eda9a94c95a9edacadfc4d569
--- /dev/null
+++ b/tlatools/test/pcal/ULEuclidTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ULEuclidTest extends PCalModelCheckerTestCase {
+
+	public ULEuclidTest() {
+		super("ULEuclid", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "400"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6352"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6752", "6352", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "42"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/ULEvenOddTest.java b/tlatools/test/pcal/ULEvenOddTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..534f8af2c4001b1faca9e048b0f254469ee364a4
--- /dev/null
+++ b/tlatools/test/pcal/ULEvenOddTest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ULEvenOddTest extends PCalModelCheckerTestCase {
+
+	public ULEvenOddTest() {
+		super("ULEvenOdd", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "13", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13"));
+
+		assertUncovered("line 67, col 24 to line 67, col 38 of module ULEvenOdd: 0\n"
+				+ "line 68, col 24 to line 68, col 36 of module ULEvenOdd: 0\n"
+				+ "line 69, col 24 to line 69, col 51 of module ULEvenOdd: 0");
+	}
+}
diff --git a/tlatools/test/pcal/ULFactorial2Test.java b/tlatools/test/pcal/ULFactorial2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..facd2dd7b9170b111acc7856ab3f8aadea9db34e
--- /dev/null
+++ b/tlatools/test/pcal/ULFactorial2Test.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ULFactorial2Test extends PCalModelCheckerTestCase {
+
+	public ULFactorial2Test() {
+		super("ULFactorial2", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10", "9", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9"));
+
+		assertUncovered("line 53, col 24 to line 53, col 43 of module ULFactorial2: 0\n"
+				+ "line 54, col 24 to line 54, col 41 of module ULFactorial2: 0\n"
+				+ "line 55, col 24 to line 55, col 47 of module ULFactorial2: 0\n"
+				+ "line 56, col 24 to line 56, col 43 of module ULFactorial2: 0\n"
+				+ "line 57, col 24 to line 57, col 55 of module ULFactorial2: 0");
+	}
+}
diff --git a/tlatools/test/pcal/ULQuicksortMacroTest.java b/tlatools/test/pcal/ULQuicksortMacroTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d256c3c1085c8457c67a791b957828e494ef4b7
--- /dev/null
+++ b/tlatools/test/pcal/ULQuicksortMacroTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ULQuicksortMacroTest extends PCalModelCheckerTestCase {
+
+	public ULQuicksortMacroTest() {
+		super("ULQuicksortMacro", "pcal", new String[] {"-wf", "-termination"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "258"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "342", "258", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/pcal/UnhandledInvalidSyntaxTest.java b/tlatools/test/pcal/UnhandledInvalidSyntaxTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..32442af1b6c8bd84890e3948f16cd72e66145bbf
--- /dev/null
+++ b/tlatools/test/pcal/UnhandledInvalidSyntaxTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import util.ToolIO;
+
+public class UnhandledInvalidSyntaxTest extends PCalTest {
+
+	@Test
+	public void test1() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("MissingSemicolonTest1", 
+				"---- MODULE algo ----\n" + 
+				"(*\n" + 
+				" --algorithm algo\n" + 
+				" begin\n" + 
+				" await;\n" +
+				" end algorithm;\n" +
+				"*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.toString(ToolIO.getAllMessages()),
+				Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Unknown error at or before\n"
+					+ "    line 5, column 2.\n"));
+	}
+	
+	@Test
+	public void test2() throws IOException {
+		assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", 
+							writeTempFile("MissingSemicolonTest2", 
+				"---- MODULE algo ----\n" + 
+				"(*\n" + 
+				" --algorithm algo\n" + 
+				" begin\n" + 
+				" if TRUE then\n" + // missing semicolon
+				" end if;\n" +
+				" end algorithm;\n" +
+				"*)\n" + 
+				"===="
+			)}));
+		
+		assertTrue(Arrays.toString(ToolIO.getAllMessages()),
+				Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n"
+					+ " -- Unknown error at or before\n"
+					+ "    line 6, column 8.\n"));
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/pcal/UniprocDefineTest.java b/tlatools/test/pcal/UniprocDefineTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb85e1c2689b8e4a2ec3c75e6ef327a8bd324e3b
--- /dev/null
+++ b/tlatools/test/pcal/UniprocDefineTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package pcal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class UniprocDefineTest extends PCalModelCheckerTestCase {
+
+	public UniprocDefineTest() {
+		super("UniprocDefine", "pcal");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		assertZeroUncovered();
+	}
+}
+/*
+C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup UniprocDefine.tla         
+TLC2 Version 2.05 of 18 May 2012
+Running in Model-Checking mode.
+Parsing file UniprocDefine.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla
+Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla
+Semantic processing of module Naturals
+Semantic processing of module Sequences
+Semantic processing of module TLC
+Semantic processing of module UniprocDefine
+Starting... (2012-08-10 17:39:58)
+Computing initial states...
+Finished computing initial states: 1 distinct state generated.
+Model checking completed. No error has been found.
+  Estimates of the probability that TLC did not check all reachable states
+  because two distinct states had the same fingerprint:
+  calculated (optimistic):  val = 2.2E-19
+  based on the actual fingerprints:  val = 6.0E-19
+5 states generated, 4 distinct states found, 0 states left on queue.
+The depth of the complete state graph search is 4.
+Finished. (2012-08-10 17:39:59)
+*/
\ No newline at end of file
diff --git a/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java b/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java
index 7b396d3817216425315992910904bbea80d3e2a8..02f935a051e68e4f3e00fae06ce8e5ca6bb0517e 100644
--- a/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java
+++ b/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java
@@ -1,21 +1,24 @@
 package tla2sany.drivers;
 
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+
 import tla2sany.modanalyzer.SpecObj;
 import tla2sany.parser.ParseException;
 import util.SimpleFilenameToStream;
 import util.ToolIO;
 
 /**
- * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=156
+ * @see Bug #156 in general/bugzilla/index.html
  */
-public class Bug156TEStackOverflowTest extends TestCase {
+public class Bug156TEStackOverflowTest {
 
 	private SpecObj moduleSpec;
 
 	/**
 	 * @throws java.lang.Exception
 	 */
+	@Before
 	public void setUp() throws Exception {
 		// create a model and initialize
 		moduleSpec = new SpecObj("test-model/Bug156/TE.tla", new SimpleFilenameToStream());
@@ -26,6 +29,7 @@ public class Bug156TEStackOverflowTest extends TestCase {
 	 * Test method for {@link tla2sany.drivers.SANY#frontEndParse(tla2sany.modanalyzer.SpecObj, java.io.PrintStream)}.
 	 * @throws ParseException 
 	 */
+	@Test
 	public void testFrontEndParse() throws ParseException {
 		// uncomment if bug 156 has been fixed
 //        try {
diff --git a/tlatools/test/tla2sany/drivers/Github429Test.java b/tlatools/test/tla2sany/drivers/Github429Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..11d70a05f04fa397c890d70709fb7ee827080aaf
--- /dev/null
+++ b/tlatools/test/tla2sany/drivers/Github429Test.java
@@ -0,0 +1,33 @@
+package tla2sany.drivers;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import tla2sany.modanalyzer.SpecObj;
+import tlc2.tool.CommonTestCase;
+import util.SimpleFilenameToStream;
+import util.ToolIO;
+
+public class Github429Test {
+
+	private SpecObj moduleSpec;
+
+	@Before
+	public void setUp() throws Exception {
+		// create a model and initialize
+		moduleSpec = new SpecObj(CommonTestCase.BASE_PATH + "Github429.tla", new SimpleFilenameToStream());
+		SANY.frontEndInitialize(moduleSpec, ToolIO.out);
+	}
+
+	@Test
+	public void testForFailedParse() {
+        try {
+			SANY.frontEndParse(moduleSpec, ToolIO.out);
+			SANY.frontEndSemanticAnalysis(moduleSpec, ToolIO.out, false);
+		} catch (final Exception e) {
+			Assert.fail("No exception should occur during parse. Instead encountered [" + e.getClass()
+								+ "] with message: " + e.getMessage());
+		}
+	}
+}
diff --git a/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java b/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cec33db7440ac436d9ded5a8760feac439187f3
--- /dev/null
+++ b/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tla2sany.drivers;
+
+import org.junit.Test;
+
+import tlc2.tool.CommonTestCase;
+import util.TestPrintStream;
+import util.ToolIO;
+
+public class IllegalOperatorTest  {
+
+	@Test
+	public void test() {
+		final TestPrintStream testPrintStream = new TestPrintStream();
+		ToolIO.out = testPrintStream;
+		
+		SANY.SANYmain(
+				new String[] { CommonTestCase.BASE_PATH + "IllegalOperatorTest" });
+		
+		testPrintStream.assertSubstring("*** Errors: 1\n" + 
+				"\n" + 
+				"line 3, col 8 to line 3, col 11 of module IllegalOperatorTest\n" + 
+				"\n" + 
+				"Argument number 1 to operator 'D' \n" + 
+				"should be a 1-parameter operator.");
+	}
+}
diff --git a/tlatools/test/tla2sany/st/LocationTest.java b/tlatools/test/tla2sany/st/LocationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..214250a399ff4b078885f644d24115e6a5347a20
--- /dev/null
+++ b/tlatools/test/tla2sany/st/LocationTest.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tla2sany.st;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.junit.Test;
+
+public class LocationTest {
+
+	@Test
+	public void testContains() {
+		assertTrue(new Location(0, 0, 10, 10).includes(new Location(0, 0, 10, 10)));
+		assertFalse(new Location(1, 0, 10, 9).includes(new Location(0, 0, 10, 10)));
+		
+		assertTrue(new Location(0, 0, 10, 10).includes(new Location(1, 0, 10, 9)));
+		assertFalse(new Location(1, 0, 10, 9).includes(new Location(0, 0, 10, 10)));
+		
+		
+		Location[] parsedLocations = Location
+				.getParsedLocations("line 781, col 31 to line 784, col 68 of module OpenAddressing\n"
+						+ "line 783, col 38 to line 784, col 68 of module OpenAddressing\n"
+						+ "line 784, col 41 to line 784, col 68 of module OpenAddressing\n"
+						
+						+ "line 786, col 30 to line 786, col 69 of module OpenAddressing\n"
+						+ "line 793, col 29 to line 793, col 63 of module OpenAddressing");
+		
+		assertTrue(parsedLocations[0].includes(parsedLocations[0]));
+		
+		assertTrue(parsedLocations[0].includes(parsedLocations[1]));
+		assertFalse(parsedLocations[1].includes(parsedLocations[0]));
+		
+		assertTrue(parsedLocations[0].includes(parsedLocations[2]));
+		assertFalse(parsedLocations[2].includes(parsedLocations[0]));
+
+		assertFalse(parsedLocations[0].includes(parsedLocations[3]));
+		assertFalse(parsedLocations[3].includes(parsedLocations[0]));
+		assertFalse(parsedLocations[4].includes(parsedLocations[0]));
+	}
+	
+	@Test
+	public void testComparator() {
+		final Location[] parsedLocations = Location.getParsedLocations(
+				  "line 15, col 9 to line 15, col 9 of module CostMetrics\n"
+				+ "line 15, col 9 to line 15, col 17 of module CostMetrics\n"
+				+ "line 8, col 11 to line 8, col 11 of module CostMetrics\n"
+				+ "line 8, col 13 to line 8, col 13 of module CostMetrics\n"
+				+ "line 8, col 9 to line 8, col 15 of module CostMetrics\n"
+				+ "line 14, col 15 to line 14, col 17 of module CostMetrics\n"
+				+ "line 15, col 15 to line 15, col 17 of module CostMetrics\n"
+				+ "line 16, col 34 to line 16, col 52 of module CostMetrics\n"
+				+ "line 8, col 9 to line 8, col 15 of module CostMetrics\n"
+				+ "line 16, col 42 to line 16, col 51 of module CostMetrics\n"
+				+ "line 16, col 42 to line 16, col 50 of module CostMetrics\n"
+				+ "line 16, col 46 to line 16, col 46 of module CostMetrics\n"
+				+ "line 16, col 46 to line 16, col 50 of module CostMetrics\n"
+				+ "line 16, col 46 to line 16, col 50 of module CostMetrics\n"
+				+ "line 23, col 6 to line 25, col 18 of module CostMetrics\n"
+				+ "line 18, col 9 to line 18, col 9 of module CostMetrics");
+		assertEquals(16, parsedLocations.length);
+		
+		final TreeSet<Location> locations = new TreeSet<>(Arrays.asList(parsedLocations));
+		assertEquals(14, locations.size());
+		
+		final Iterator<Location> iterator = locations.iterator();
+		Location l = iterator.next();
+		for (int i = 1; i < locations.size(); i++) {
+			final Location next = iterator.next();
+			assertTrue(l.bLine <= next.bLine);
+			if (l.bLine == next.bLine) {
+				assertTrue(l.bColumn <= next.bColumn);
+				if (l.bColumn == next.bColumn) {
+					assertTrue(l.eLine <= next.eLine);
+					if (l.eLine == next.eLine) {
+						assertTrue(l.eColumn < next.eColumn);
+					}
+				}
+			}
+			l = next;
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/TLCTest.java b/tlatools/test/tlc2/TLCTest.java
index ad8fc027cac3ccda08375a892a9d79e0d4358134..2cdc42cde9681233627b535c3491e95745d6fd76 100644
--- a/tlatools/test/tlc2/TLCTest.java
+++ b/tlatools/test/tlc2/TLCTest.java
@@ -1,96 +1,135 @@
 package tlc2;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Test;
+
+import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.fp.FPSetFactory;
+import util.TLAConstants;
 import util.TLCRuntime;
-import junit.framework.TestCase;
 
-public class TLCTest extends TestCase {
+public class TLCTest {
 
+	@Test
 	public void testHandleParametersAbsoluteInvalid() {
 		final TLC tlc = new TLC();
-		assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-1", "MC"}));
+		assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-1", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 	}
 	
+	@Test
 	public void testHandleParametersAbsoluteValid() {
 		final TLC tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-fpmem", "101", "MC"}));
+		assertTrue(tlc.handleParameters(new String[] {"-fpmem", "101", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 	}
 	
+	@Test
 	public void testHandleParametersFractionInvalid() {
 		final TLC tlc = new TLC();
-		assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-0.5", "MC"}));
+		assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-0.5", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 	}
 	
 	/**
 	 * Allocating to little should result in min default
 	 */
+	@Test
 	public void testHandleParametersAllocateLowerBound() {
 		final TLC tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-fpmem", "0", "MC"}));
+		assertTrue(tlc.handleParameters(new String[] {"-fpmem", "0", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
+		final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration();
+		assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation()));
 		assertEquals("Allocating to little should result in min default",
-				TLCRuntime.MinFpMemSize, tlc.getFPSetConfiguration()
+				TLCRuntime.MinFpMemSize, fpSetConfiguration
 						.getMemoryInBytes());
 	}
 	
 	/**
 	 * Overallocating should result in max default
 	 */
+	@Test
 	public void testHandleParametersAllocateUpperBound() {
 		final TLC tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-fpmem", Long.toString(Long.MAX_VALUE), "MC"}));
+		assertTrue(tlc.handleParameters(new String[] {"-fpmem", Long.toString(Long.MAX_VALUE),
+				TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
         final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.75d);
+        final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration();
+		assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation()));
 		assertEquals("Overallocating should result in max default (75%)",
-				maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes());
+				maxMemory, fpSetConfiguration.getMemoryInBytes());
 	}
 	
 	/**
 	 * .5 is valid
 	 */
+	@Test
 	public void testHandleParametersAllocateHalf() {
 		final TLC tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".5", "MC"}));
+		assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".5", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
         final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.50d);
+        final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration();
+		assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation()));
 		assertEquals("Overallocating should result in max default (50%)",
-				maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes());
+				maxMemory, fpSetConfiguration.getMemoryInBytes());
 	}
 	
 	/**
 	 * .99 is valid
 	 */
+	@Test
 	public void testHandleParametersAllocate90() {
 		final TLC tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".99", "MC"}));
+		assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".99", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
         final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.99d);
+		final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration();
+		assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation()));
 		assertEquals("Overallocating should result in max default (99%)",
-				maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes());
+				maxMemory, fpSetConfiguration.getMemoryInBytes());
 	}
 	
 	/**
 	 *  is valid
 	 */
+	@Test
 	public void testHandleParametersMaxSetSize() {
 		final int progDefault = TLCGlobals.setBound;
 		
 		TLC tlc = new TLC();
-		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "NaN", "MC"}));
+		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "NaN", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 		
 		tlc = new TLC();
-		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "0", "MC"}));
+		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "0", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 		tlc = new TLC();
-		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "-1", "MC"}));
+		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "-1", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 		tlc = new TLC();
-		assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", Integer.toString(Integer.MIN_VALUE), "MC"}));
+		assertFalse(tlc.handleParameters(new String[] { "-maxSetSize", Integer.toString(Integer.MIN_VALUE),
+				TLAConstants.Files.MODEL_CHECK_FILE_BASENAME }));
 		
 		tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", "1", "MC"}));
+		assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", "1", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME}));
 		assertTrue(TLCGlobals.setBound == 1);
 		
 
 		tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", Integer.toString(progDefault), "MC"}));
+		assertTrue(tlc.handleParameters(
+				new String[] { "-maxSetSize", Integer.toString(progDefault), TLAConstants.Files.MODEL_CHECK_FILE_BASENAME }));
 		assertTrue(TLCGlobals.setBound == progDefault);
 		
 		tlc = new TLC();
-		assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", Integer.toString(Integer.MAX_VALUE), "MC"}));
+		assertTrue(tlc.handleParameters(new String[] { "-maxSetSize", Integer.toString(Integer.MAX_VALUE),
+				TLAConstants.Files.MODEL_CHECK_FILE_BASENAME }));
 		assertTrue(TLCGlobals.setBound == Integer.MAX_VALUE);
 	}
+	
+	@Test
+	public void testRuntimeConversion() {
+		assertEquals("59s", TLC.convertRuntimeToHumanReadable(59000L));
+		assertEquals("59min 59s", TLC.convertRuntimeToHumanReadable(3599000L));
+		assertEquals("23h 59min", TLC.convertRuntimeToHumanReadable(86340000L));
+		assertEquals("1d 23h", TLC.convertRuntimeToHumanReadable(169200000L));
+		assertEquals("2d 23h", TLC.convertRuntimeToHumanReadable(255600000L));
+		assertEquals("99d 23h", TLC.convertRuntimeToHumanReadable(8636400000L));
+	}
 }
diff --git a/tlatools/test/tlc2/TestDriver.java b/tlatools/test/tlc2/TestDriver.java
index 2ec9ba768cc08a13572b099149c6ce7072d2f659..9bbd8bb4786c5d8a630d0b85e59f3c475e599993 100644
--- a/tlatools/test/tlc2/TestDriver.java
+++ b/tlatools/test/tlc2/TestDriver.java
@@ -8,7 +8,6 @@ public class TestDriver
     private static final int COUNT = 3;
 
     private static final long TIMEOUT = 1000 * 5;
-    private static final int STEP = 30;
 
     private static int reported;
     private static TLCThread tlcThread;
diff --git a/tlatools/test/tlc2/TestDriver2.java b/tlatools/test/tlc2/TestDriver2.java
deleted file mode 100644
index 6f28d608a860f4f80e0130089a7165c1f4732890..0000000000000000000000000000000000000000
--- a/tlatools/test/tlc2/TestDriver2.java
+++ /dev/null
@@ -1,232 +0,0 @@
-package tlc2;
-
-import java.io.File;
-
-import util.SimpleFilenameToStream;
-import util.ToolIO;
-
-public class TestDriver2
-{
-
-    /**
-     * @author Simon Zambrovski
-     * @version $Id: TLCJob.java 638 2009-04-10 04:08:14Z simonzam $
-     */
-    private static final long TIMEOUT = 1000 * 5;
-    private static final int REPEATS = 10;
-
-    private String rootModule;
-    private String cfgFile;
-    private String projectDir;
-
-    private TLCThread tlcThread;
-    private int workers = 2;
-
-    private int reported;
-
-    
-    public static void main(String[] args) 
-    {
-        if (args.length < 6 ) 
-        {
-            // -workers 2 -config AtomicBakery_MC_1.cfg -metadir C:\org.zambrovski\download\AtomicBakery_MC_1.toolbox AtomicBakery_MC_1
-            System.out.println("Call with: -workers <num> -config <name-of-config.cfg> -metadir <dir-to-put-states> <name-of-module>");
-        }
-        TestDriver2 testDriver2 = new TestDriver2(args[6], args[3], args[5]);
-        testDriver2.setWorkers(Integer.parseInt(args[1]));
-
-        for (int i = 0 ; i < REPEATS; i++)
-        {
-            testDriver2.reported = 0;
-            testDriver2.run();
-        }
-        System.exit(0);
-    }
-    /**
-     * @param name
-     */
-    public TestDriver2(String rootModule, String cfgFile, String projectDir)
-    {
-
-        this.rootModule = rootModule;
-        this.cfgFile = cfgFile;
-        this.projectDir = projectDir;
-
-        // initialize the progress reporting variable
-        this.reported = 0;
-
-    }
-
-    /**
-     * Sets the number of workers
-     * @param workers number of threads to be run in parallel
-     */
-    public void setWorkers(int workers)
-    {
-        this.workers = workers;
-    }
-
-    protected int run()
-    {
-        report("entering run");
-
-        // setup tool IO
-        // Reset the tool output messages.
-        ToolIO.reset();
-        ToolIO.setMode(ToolIO.TOOL);
-        ToolIO.setUserDir(new File(rootModule).getParent());
-
-        // create a TLC instance
-        TLC tlc = new TLC();
-        report("tlc created " + tlc.toString());
-        
-        // setup name resolver
-        // in RCP FS model use:
-        // tlc.setResolver(new RCPNameToFileIStream(null));
-
-        // for simple FS model:
-        tlc.setResolver(new SimpleFilenameToStream());
-
-        // setup SpecObj from parser
-        // SpecObj specObj = ToolboxHandle.getSpecObj();
-        // tlc.setSpecObject(specObj);
-
-        // handle parameters
-        String[] params = new String[] { 
-                "-config", cfgFile,
-                // "-coverage", "0.1",
-                "-workers", "" + workers, 
-                "-metadir", projectDir, 
-                rootModule };
-        boolean status = tlc.handleParameters(params);
-
-        // report errors in parameters
-        if (!status)
-        {
-            return -1;
-        }
-
-        // create thread for TLC running
-        tlcThread = new TLCThread(tlc);
-        tlcThread.setName("TLC Thread");
-        report("tlcthread created " + tlcThread.getId());
-
-        // Start the TLC thread
-        tlcThread.start();
-        report("tlcthread started");
-
-        while (this.checkAndSleep())
-        {
-            // report the messages created since last reporting
-            // check the cancellation status
-            if (isCancelled())
-            {
-                // cancel the TLC
-                tlc.setCanceledFlag(true);
-
-                // report the messages created since last reporting
-                reportProgress();
-
-                // abnormal termination
-                return -1;
-            }
-        }
-        // report progress
-        reportProgress();
-
-        // successful termination
-        return 1;
-    }
-
-    /**
-     * Method for external cancellation
-     */
-    private boolean isCancelled()
-    {
-        return false;
-    }
-    
-    
-    // check if TLC is ready and sleep
-    private boolean checkAndSleep()
-    {
-        report("entering checkAndSleep()");
-        try
-        {
-            report("Go sleep \t" + System.currentTimeMillis());
-            // go sleep
-            Thread.sleep(TIMEOUT);
-
-            report("Wake up \t" + System.currentTimeMillis());
-
-        } catch (InterruptedException e)
-        {
-            // nothing to do
-            e.printStackTrace();
-        }
-
-        // return true if the tlc is still calculating
-        boolean result = tlcThread.isRunning();
-        report("leaving checkAndSleep() with " + result);
-        return result;
-    }
-
-    /**
-     * Report progress to the monitor 
-     */
-    protected void reportProgress()
-    {
-        // report progress
-        String[] messages = ToolIO.getAllMessages();
-        for (; reported < messages.length; reported++)
-        {
-            System.out.println(messages[reported]);
-        }
-    }
-
-    /**
-     * Thread to run TLC in
-     */
-    class TLCThread extends Thread
-    {
-        private boolean isRunning = false;
-        private TLC tlc;
-
-        public TLCThread(TLC tlc)
-        {
-            this.tlc = tlc;
-        }
-
-        public void run()
-        {
-            synchronized (this)
-            {
-                report("TLC Thread: {STARTED}");
-                isRunning = true;
-            }
-            // start TLC
-            this.tlc.process();
-
-            synchronized (this)
-            {
-                isRunning = false;
-                report("TLC Thread: {FINISHED}");
-            }
-        }
-
-        /**
-         * 
-         * @return
-         */
-        public synchronized boolean isRunning()
-        {
-            return isRunning;
-        }
-    }
-
-    public void report(String message)
-    {
-        // System.err.println("" + Thread.currentThread().getId() + "\t" + message);
-    }
-
-}
diff --git a/tlatools/test/tlc2/TestMPRecorder.java b/tlatools/test/tlc2/TestMPRecorder.java
index a8061ea16380f7007513937179863828ebbde70a..54dc1c6be99d516b45b8cfac45eac2c0fcdbedb5 100644
--- a/tlatools/test/tlc2/TestMPRecorder.java
+++ b/tlatools/test/tlc2/TestMPRecorder.java
@@ -30,6 +30,10 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import tlc2.output.EC;
 
 public class TestMPRecorder extends tlc2.output.MPRecorder {
 	private final Map<Integer, List<Object>> records = new HashMap<Integer, List<Object>>();
@@ -48,7 +52,25 @@ public class TestMPRecorder extends tlc2.output.MPRecorder {
 	public List<Object> getRecords(int code) {
 		return records.get(code);
 	}
+	
+	private List<Object> getRecordsOrDefault(final int code, final List<Object> defaultValue) {
+		return records.getOrDefault(code, defaultValue);
+	}
+
+	public int getRecordAsInt(int code) {
+		return Integer.parseInt(((String[]) records.get(code).get(0))[0]);
+	}
 
+	public List<String[]> getRecordAsStringArray(int code) {
+		final List<Object> l = records.get(code);
+		
+		final List<String[]> strs = new ArrayList<>(l.size());
+		for (Object o : l) {
+			strs.add((String[]) o);
+		}
+		return strs;
+	}
+	
 	// This is a best effort implementation that only checks the first
 	// elements of the nested records and contained arrays
 	public boolean recordedWithStringValue(int code, String str) {
@@ -59,6 +81,30 @@ public class TestMPRecorder extends tlc2.output.MPRecorder {
 		}
 	}
 
+	public boolean recordedWithSubStringValue(int code, String substring) {
+		return recordedWithSubStringValue(code, substring, 0);
+	}
+	
+	public boolean recordedWithSubStringValue(int code, String substring, int idx) {
+		try {
+			Object object = records.get(code).get(0);
+			if (object instanceof String[]) {
+				String[] strs = (String[]) object;
+				for (String string : strs) {
+					if (string.contains(substring)) {
+						return true;
+					}
+				}
+				return false;
+			} else if (object instanceof String) {
+				return ((String) object).contains(substring);
+			}
+			return false;
+		} catch (Exception e) {
+			return false;
+		}
+	}
+
 	public boolean recordedWithStringValueAt(int code, String str, int idx) {
 		try {
 			Object object = records.get(code).get(0);
@@ -80,8 +126,167 @@ public class TestMPRecorder extends tlc2.output.MPRecorder {
 			if (!recordedWithStringValueAt(code, string, i++)) {
 				return false;
 			}
+		}
+		return true;
+	}
+
+	public String getCoverageRecords() {
+		final List<Object> coverages = getRecords(EC.TLC_COVERAGE_VALUE);
+		String out = "";
+		if (coverages == null) {
+			return out;
+		}
+		for (final Object o : coverages) {
+			final String[] coverage = (String[]) o;
+			out += coverage[0] + ": " + Integer.parseInt(coverage[1]) + "\n";
+		}
+		return out;
+	}
+
+	public List<Coverage> getActionCoverage() {
+		final List<Object> init = getRecordsOrDefault(EC.TLC_COVERAGE_INIT, new ArrayList<>(0));
+		final List<Object> next = getRecordsOrDefault(EC.TLC_COVERAGE_NEXT, new ArrayList<>(0));
+		final List<Object> prop = getRecordsOrDefault(EC.TLC_COVERAGE_PROPERTY, new ArrayList<>(0));
+		init.addAll(next);
+		init.addAll(prop);
+
+		return init.stream().map(o -> (String[]) o).map(a -> new Coverage(a)).filter(Coverage::isAction)
+				.collect(Collectors.toList());
+	}
+
+	public List<Coverage> getZeroCoverage() {
+		return getCoverage(EC.TLC_COVERAGE_VALUE, (Predicate<? super Coverage>) o -> o.isZero());
+	}
+	
+	public List<Coverage> getNonZeroCoverage() {
+		return getCoverage(EC.TLC_COVERAGE_VALUE, (Predicate<? super Coverage>) o -> !o.isZero());
+	}
+	
+	public List<Coverage> getCostCoverage() {
+		return getCoverage(EC.TLC_COVERAGE_VALUE_COST, (Predicate<? super Coverage>) o -> !o.isZero());
+	}
+
+	private List<Coverage> getCoverage(final int code, Predicate<? super Coverage> p) {
+		final List<Object> coverages = getRecordsOrDefault(code, new ArrayList<>(0));
+		return coverages.stream().map(o -> (String[]) o).map(a -> new Coverage(a)).filter(p)
+				.collect(Collectors.toList());
+	}
+
+	public static class Coverage {
+		private final String line;
+		private final long count;
+		private final long cost;
+		//TODO Take level into account in comparison!
+		private final int level;
+		private final boolean isAction;
+		
+		public Coverage(String[] line) {
+			this.isAction = line[0].startsWith("<");
+			this.line = line[0].replace("|", "").trim();
+			this.level = line[0].length() - this.line.length();
+			if (line.length == 1) {
+				this.count = -1;
+				this.cost = -1;
+			} else if (line.length == 2) {
+				this.count = Long.valueOf(line[1].trim());
+				this.cost = -1;
+			} else if (line.length == 3) {
+				this.count = Long.valueOf(line[1].trim());
+				this.cost = Long.valueOf(line[2].trim());
+			} else {
+				throw new IllegalArgumentException();
+			}
+		}
+		
+		public String getLine() {
+			return line;
+		}
+
+		public long getCount() {
+			return count;
+		}
+		
+		public int getLevel() {
+			return level;
+		}
+		
+		public boolean isZero() {
+			return count == 0L;
+		}
+		
+		public boolean isCoverage() {
+			return !isAction;
+		}
+		
+		public boolean isCost() {
+			return cost >= 0;
+		}
+		
+		public boolean isAction() {
+			return isAction;
+		}
+
+		@Override
+		public String toString() {
+			return "Coverage [line=" + line + ", count=" + count  + ", cost=" + cost + "]";
+		}
+
+		@Override
+		public int hashCode() {
+			final int prime = 31;
+			int result = 1;
+			result = prime * result + (int) (count ^ (count >>> 32));
+			result = prime * result + (int) (cost ^ (cost >>> 32));
+			result = prime * result + ((line == null) ? 0 : line.hashCode());
+			return result;
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Coverage other = (Coverage) obj;
+			if (count != other.count)
+				return false;
+			if (cost != other.cost)
+				return false;
+			if (line == null) {
+				if (other.line != null)
+					return false;
+			} else if (!line.equals(other.line))
+				return false;
 			return true;
 		}
-		return false;
+	}
+	
+	/* (non-Javadoc)
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		final StringBuffer buf = new StringBuffer(records.size());
+		for(Integer key : records.keySet()) {
+			final List<Object> list = records.get(key);
+			for (Object elem : list) {
+				if (elem instanceof String[]) {
+					String[] strs = (String[]) elem;
+					for (String s : strs) {
+						buf.append(key);
+						buf.append(" -> ");
+						buf.append(s);
+						buf.append("\n");
+					}
+				} else if (elem instanceof String) {
+					buf.append(key);
+					buf.append(" -> ");
+					buf.append(elem);
+					buf.append("\n");
+				}
+			}
+		}
+		return buf.toString();
 	}
 }
diff --git a/tlatools/test/tlc2/model/AssignmentTest.java b/tlatools/test/tlc2/model/AssignmentTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3918f4d40ca19a9fb665caf10d0bc6c8a3fed40
--- /dev/null
+++ b/tlatools/test/tlc2/model/AssignmentTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.model;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import tlc2.model.Assignment;
+
+public class AssignmentTest {
+
+	@Test
+	public void test() {
+		final Assignment a = new Assignment("X", new String[0], "X");
+		a.setModelValue(true);
+		assertEquals("X", a.prettyPrint());
+		
+		final Assignment b = new Assignment("Y", new String[0], "{a1, b1}");
+		b.setModelValue(true);
+		assertEquals("Y" + Assignment.ASSIGNMENT_SIGN + "{a1, b1}", b.prettyPrint());
+
+		final Assignment c = new Assignment("Z", new String[0], "{s1, s2}");
+		c.setModelValue(true);
+		c.setSymmetric(true);
+		assertEquals("Z" + Assignment.ASSIGNMENT_SIGN + "s{s1, s2}", c.prettyPrint());
+
+		final Assignment d = new Assignment("W", new String[0], "1");
+		assertEquals("W" + Assignment.ASSIGNMENT_SIGN + "1", d.prettyPrint());
+	}
+}
diff --git a/tlatools/test/tlc2/model/FormulaTest.java b/tlatools/test/tlc2/model/FormulaTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cf4d97f1394db69ae6e56b2bbe61ac9b19d7b38d
--- /dev/null
+++ b/tlatools/test/tlc2/model/FormulaTest.java
@@ -0,0 +1,52 @@
+package tlc2.model;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import tlc2.model.Formula;
+
+public class FormulaTest {
+
+	@Test
+	public void testUnnamed() {
+		Formula formula = new Formula("TRUE");
+		assertFalse(formula.isNamed());
+		assertEquals("TRUE", formula.getRightHandSide());
+
+		formula = new Formula("LET clock[i \\in 1..(__trace_var_state)] ==\n" + 
+				"   IF i = 1\n" + 
+				"   THEN [ p \\in DOMAIN pc |-> 0 ]\n" + 
+				"   ELSE clock[i - 1]\n" + 
+				"IN clock[__trace_var_state]");
+		assertFalse(formula.isNamed());
+		assertEquals("LET clock[i \\in 1..(__trace_var_state)] ==\n" + 
+				"   IF i = 1\n" + 
+				"   THEN [ p \\in DOMAIN pc |-> 0 ]\n" + 
+				"   ELSE clock[i - 1]\n" + 
+				"IN clock[__trace_var_state]", formula.getRightHandSide());
+	}
+
+	@Test
+	public void testNamed() {
+		Formula formula = new Formula("foo == TRUE");
+		assertEquals("foo", formula.getLeftHandSide());
+		assertEquals("TRUE", formula.getRightHandSide());
+		
+		formula = new Formula("foo == LET bar == TRUE IN bar");
+		assertEquals("foo", formula.getLeftHandSide());
+		assertEquals("LET bar == TRUE IN bar", formula.getRightHandSide());
+		
+		formula = new Formula("bar == LET clock[i \\in 1..(__trace_var_state)] ==\n" + 
+				"   IF i = 1\n" + 
+				"   THEN [ p \\in DOMAIN pc |-> 0 ]\n" + 
+				"   ELSE clock[i - 1]\n" + 
+				"IN clock[__trace_var_state]");
+		assertEquals("bar", formula.getLeftHandSide());
+		assertEquals("LET clock[i \\in 1..(__trace_var_state)] ==\n" + 
+				"   IF i = 1\n" + 
+				"   THEN [ p \\in DOMAIN pc |-> 0 ]\n" + 
+				"   ELSE clock[i - 1]\n" + 
+				"IN clock[__trace_var_state]", formula.getRightHandSide());
+	}
+}
diff --git a/tlatools/test/tlc2/model/TypedSetTest.java b/tlatools/test/tlc2/model/TypedSetTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2388d72e75e1de1c0f529036b0172c18a9c3061c
--- /dev/null
+++ b/tlatools/test/tlc2/model/TypedSetTest.java
@@ -0,0 +1,93 @@
+package tlc2.model;
+
+import junit.framework.TestCase;
+import tlc2.model.TypedSet;
+
+/**
+ * Test of the typed set factory
+ * @author Simon Zambrovski
+ * @version $Id$
+ */
+public class TypedSetTest extends TestCase
+{
+
+    /**
+     * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}.
+     */
+    public void testParseSet1()
+    {
+        
+        TypedSet reference = new TypedSet();
+        reference.setValues(new String[]{"a", "b", "c", "d", "dsfdf"});
+        assertEquals(reference, TypedSet.parseSet("a, b, c,     d,   dsfdf"));
+    }
+
+    /**
+     * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}.
+     */
+    public void testParseSet2()
+    {
+        TypedSet reference = new TypedSet();
+        reference.setValues(new String[]{"1", "2", "p", "h!@#$%^&*()_", "dsfdf"});
+        assertEquals(reference, TypedSet.parseSet("1, 2, p, h!@#$%^&*()_, dsfdf"));
+    }
+
+    /**
+     * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}.
+     */
+    public void testParseSet3()
+    {
+        // positive test
+        TypedSet reference = new TypedSet();
+        reference.setValues(new String[]{"1", "2", "3", "4", "5"});
+        reference.setType("p");
+        TypedSet sample = TypedSet.parseSet("p_1,     p_2,    p_3, \n p_4, p_5");
+        assertEquals(reference, sample);
+    }
+
+    
+    /**
+     * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}.
+     */
+    public void testParseSet4()
+    {
+        TypedSet reference = new TypedSet();
+        reference.setValues(new String[]{"p_1", "i_2", "p_3", "p_4", "p_5"});
+        TypedSet sample = TypedSet.parseSet("p_1, i_2, p_3, p_4, p_5");
+        assertEquals(reference, sample);
+    }
+
+    
+    /**
+     * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}.
+     */
+    public void testParseSet5()
+    {
+        TypedSet reference = new TypedSet();
+        reference.setValues(new String[]{"p_", "p_2", "p_3", "p_4", "p_5"});
+        TypedSet sample = TypedSet.parseSet("p_, p_2, p_3, p_4, p_5");
+        assertEquals(reference, sample);
+    }
+
+    /**
+     * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}.
+     */
+    public void testParseSet6()
+    {
+        // null set
+        TypedSet reference = new TypedSet();
+        TypedSet sample = TypedSet.parseSet("");
+        assertEquals(reference, sample);
+        
+        sample = TypedSet.parseSet(null);
+        assertEquals(reference, sample);
+
+        sample = TypedSet.parseSet(", , , , ");
+        assertEquals(reference, sample);
+
+        sample = TypedSet.parseSet("{, , , ,}");
+        assertEquals(reference, sample);
+
+    }
+
+}
diff --git a/tlatools/test/tlc2/module/RandomizationTest.java b/tlatools/test/tlc2/module/RandomizationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..75132a85135dcc9099a558fbf46cdfe7ca6f7f9c
--- /dev/null
+++ b/tlatools/test/tlc2/module/RandomizationTest.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 20178 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.module;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import tlc2.tool.EvalException;
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.Value;
+
+public class RandomizationTest {
+	
+	@BeforeClass
+	public static void setup() {
+		// Make test repeatable by setting random seed always to same value. 
+		RandomEnumerableValues.setSeed(15041980L);
+		
+		// Initialize FP64 to prevent NPE in hashCode (which relies on Value#fingerprint).
+		FP64.Init();
+	}
+
+	/* RandomSubsetSet */
+
+	@Test
+	public void testV1Valid() {
+		final Enumerable randomSubset = (Enumerable) Randomization.RandomSubsetSet(IntValue.gen(42), new StringValue("0.1"),
+				new IntervalValue(1, 42));
+
+		assertNotNull(randomSubset);
+		assertEquals(42, randomSubset.size());
+	}
+
+	@Test
+	public void testV2Larger1() {
+		try {
+			Randomization.RandomSubsetSet(IntValue.gen(23), new StringValue("1.1"), new IntervalValue(1, 42));
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains("1.1"));
+			return;
+		}
+		fail();
+	}
+		
+	/* RandomSetOfSubsets */
+
+	@Test
+	public void testV1Negative() {
+		final Value v1 = IntValue.gen(-42);
+		try {
+			Randomization.RandomSetOfSubsets(v1, IntValue.gen(42), new IntervalValue(1, 42));
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains("The first argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n-42"));
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testV1NoIntValue() {
+		final Value v1 = new StringValue("52");
+		try {
+			Randomization.RandomSetOfSubsets(v1, IntValue.gen(42), new IntervalValue(1, 42));
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains("The first argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n\"52\""));
+			return;
+		}
+		fail();
+	}
+
+	@Test
+	public void testV1Zero() {
+		final Value v1 = IntValue.gen(0);
+		final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(v1, IntValue.gen(42),
+				new IntervalValue(1, 42));
+
+		assertNotNull(randomSubset);
+		assertEquals(0, randomSubset.size());
+	}
+
+	@Test
+	public void testV2Zero() {
+		final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(0),
+				new IntervalValue(1, 42));
+		assertEquals(1, randomSubset.size());
+		// empty set is only member
+		assertTrue(randomSubset.member(new SetEnumValue()));
+	}
+
+	@Test
+	public void testV2Negative() {
+		try {
+			Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(-1), new IntervalValue(1, 42));
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains("The second argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n-1"));
+			return;
+		}
+		fail();
+	}
+
+	@Test
+	public void testV3Empty() {
+		try {
+			Randomization.RandomSetOfSubsets(IntValue.gen(42), IntValue.gen(42), new SetEnumValue());
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains(
+					"The first argument of RandomSetOfSubsets should be a nonnegative integer that is smaller than the subset's size of 2^0, but instead it is:\n42"));
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testV3AstronomicallyLarge() {
+		final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(42), IntValue.gen(42),
+				new IntervalValue(1, 256));
+
+		assertNotNull(randomSubset);
+		assertEquals(42, randomSubset.size());
+	}
+	
+	@Test
+	public void testV3isInfinite() {
+		try {
+			Randomization.RandomSetOfSubsets(IntValue.gen(42), IntValue.gen(42), Naturals.Nat());
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains(
+					"The third argument of RandomSetOfSubsets should be a finite set, but instead it is:\nNat"));
+			return;
+		}
+		fail();
+	}
+
+	@Test
+	public void testRSSV2Zero() {
+		final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(0),
+				new IntervalValue(1, 42));
+		assertEquals(1, randomSubset.size());
+		// empty set is only member
+		assertTrue(randomSubset.member(new SetEnumValue()));
+	}
+
+	@Test
+	public void testRSSV2Negative() {
+		try {
+			Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(-1), new IntervalValue(1, 42));
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains("The second argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n-1"));
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testRSSV2Cardinality() {
+		final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(32), IntValue.gen(5),
+				new IntervalValue(1, 5));
+		assertEquals(1, randomSubset.size());
+		// With probability 1 (n = 5), the operator - due to collisions - only generates
+		// a single subset which is the input set.
+		assertTrue(randomSubset.member(new IntervalValue(1, 5)));
+	}
+	
+	@Test
+	public void testRSSV2TwiceCardinality() {
+		try {
+			Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(10), new IntervalValue(1, 5));
+		} catch (final EvalException ee) {
+			assertTrue(ee.getMessage().contains(
+					"The second argument of RandomSetOfSubsets should be a nonnegative integer in range 0..Cardinality(S), but instead it is:\n10"));
+			return;
+		}
+		fail();
+	}
+}
diff --git a/tlatools/test/tlc2/module/SequencesTest.java b/tlatools/test/tlc2/module/SequencesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4c315bc2e41986aa61fb6b46139f92366520521
--- /dev/null
+++ b/tlatools/test/tlc2/module/SequencesTest.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.module;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.EvalException;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.Value;
+
+public class SequencesTest {
+
+	@Test
+	public void testHeadString() {
+		Value a = Sequences.Head(new StringValue("a"));
+		assertTrue(a instanceof StringValue);
+		assertEquals("a", ((StringValue) a).val.toString());
+
+		a = Sequences.Head(new StringValue("abc"));
+		assertTrue(a instanceof StringValue);
+		assertEquals("a", ((StringValue) a).val.toString());
+	}
+
+	@Test
+	public void testHeadStringEmpty() {
+		try {
+			Sequences.Head(new StringValue(""));
+		} catch (EvalException e) {
+			assertEquals(EC.TLC_MODULE_APPLY_EMPTY_SEQ, e.getErrorCode());
+			return;
+		}
+		fail();
+	}
+
+	@Test
+	public void testAppendString() {
+		Value a = Sequences.Append(new StringValue(""), new StringValue("a"));
+		assertTrue(a instanceof StringValue);
+		assertEquals("a", ((StringValue) a).val.toString());
+
+		a = Sequences.Append(new StringValue("abc"), new StringValue("d"));
+		assertTrue(a instanceof StringValue);
+		assertEquals("abcd", ((StringValue) a).val.toString());
+	}
+
+	@Test
+	public void testAppendStringNonString() {
+		try {
+			Sequences.Append(new StringValue(""), IntValue.ValZero);
+		} catch (EvalException e) {
+			assertEquals(EC.TLC_MODULE_EVALUATING, e.getErrorCode());
+			return;
+		}
+		fail();
+	}
+}
diff --git a/tlatools/test/tlc2/module/TLCTest.java b/tlatools/test/tlc2/module/TLCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..78a310b6d080cf42be789617ed2b32de7ec3479f
--- /dev/null
+++ b/tlatools/test/tlc2/module/TLCTest.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.module;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import tlc2.util.FP64;
+import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.FcnRcdValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SetEnumValue;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.Value;
+import tlc2.value.impl.ValueEnumeration;
+
+public class TLCTest {
+
+	@BeforeClass
+	public static void setup() {
+		FP64.Init();
+	}
+	
+	/**
+	 * func2 == <<1,2,3>>
+	 * ASSUME (3 :> 11 @@ func2) = <<1,2,11>>
+	 */
+	@Test
+	public void testA() {
+		final Value f = new FcnRcdValue(new Value [] { IntValue.gen(3) }, new Value [] { IntValue.gen(11) }, true);
+		final Value g = new TupleValue(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3) });
+
+		final Value  combined = TLC.CombineFcn(f, g);
+		Assert.assertTrue(combined instanceof FcnRcdValue);
+		final FcnRcdValue rcdVal = (FcnRcdValue) combined;
+		// Have to normalize to bring values/domain into natural order expected by assertions below
+		rcdVal.normalize();
+
+		// domain
+		Assert.assertEquals(3, rcdVal.domain.length);
+		Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3) }, rcdVal.domain);
+
+		// values
+		Assert.assertEquals(3, rcdVal.values.length);
+		Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(11) }, rcdVal.values);
+	}
+
+	/**
+	 * func2 == <<1,2,3>>
+	 * ASSUME (4 :> 11 @@ func2) = <<1,2,3,11>>
+	 */
+	@Test
+	public void testB() {
+		final Value f = new FcnRcdValue(new Value [] { IntValue.gen(4) }, new Value [] { IntValue.gen(11) }, true);
+		final Value g = new TupleValue(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3), IntValue.gen(11) });
+
+		final Value  combined = TLC.CombineFcn(f, g);
+		Assert.assertTrue(combined instanceof FcnRcdValue);
+		final FcnRcdValue rcdVal = (FcnRcdValue) combined;
+		// Have to normalize to bring values/domain into natural order expected by assertions below
+		rcdVal.normalize();
+		
+		// domain
+		Assert.assertEquals(4, rcdVal.domain.length);
+		Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3), IntValue.gen(4) },
+				rcdVal.domain);
+
+		// values
+		Assert.assertEquals(4, rcdVal.values.length);
+		Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3), IntValue.gen(11) },
+				rcdVal.values);
+	}
+
+	@Test
+	public void testPermutations() {
+		final SetEnumValue in = (SetEnumValue) new IntervalValue(1, 5).toSetEnum();
+		Assert.assertEquals(5, in.size());
+		
+		final Value  permutations = TLC.Permutations(in);
+		Assert.assertTrue(permutations instanceof Enumerable);
+		Assert.assertEquals(120, permutations.size());
+
+		final Set<Value > values = new HashSet<>(permutations.size());
+		
+		final ValueEnumeration elements = ((Enumerable) permutations).elements();
+		Value  val = null;
+		while ((val = elements.nextElement()) != null) {
+			Assert.assertEquals(in.size(), val.size());
+			values.add(val);
+		}
+		
+		Assert.assertEquals(120, values.size());
+	}
+}
diff --git a/tlatools/test/tlc2/output/CFGCopierTest.java b/tlatools/test/tlc2/output/CFGCopierTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eadeefc735e19b792bb827f07d2f838927b23cc3
--- /dev/null
+++ b/tlatools/test/tlc2/output/CFGCopierTest.java
@@ -0,0 +1,64 @@
+package tlc2.output;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CFGCopierTest {
+	private static final String INIT_NEXT_CFG = "\\* INIT definition\n"
+			+ "INIT\n"
+			+ "init_15767838108312000\n"
+			+ "\\* NEXT definition\n"
+			+ "NEXT\n"
+			+ "next_15767838108313000\n"
+			+ "\\* Action Constraint definition\n"
+			+ "ACTION_CONSTRAINT\n"
+			+ "action_constr_15767838108314000\n";
+	// Yes, this is an illegal CFG as it specifies both SPECIFICATION and INIT/NEXT
+	private static final String ORIGINAL_CFG = "\\* CONSTANT definitions\n"
+			+ "CONSTANT\n"
+			+ "N <- const_157376354642853000\n"
+			+ "\\* SPECIFICATION definition\n"
+			+ "SPECIFICATION\n"
+			+ "Spec\n"
+			+ "\\* INIT definition\n"
+			+ "INIT\n"
+			+ "BigInit\n"
+			+ "\\* NEXT definition\n"
+			+ "NEXT\n"
+			+ "SmallNext\n"
+			+ "\\* INVARIANT definition\n"
+			+ "INVARIANT\n"
+			+ "TypeInvariant\n"
+			+ "Invariant\n"
+			+ "\\* PROPERTY definition\n"
+			+ "PROPERTY\n"
+			+ "Termination\n"
+			+ "\\* Generated on Thu Nov 14 12:32:26 PST 2019\n";
+	private static final String NEW_CFG = "\\* CONSTANT definitions\n"
+			+ "CONSTANT\n"
+			+ "N <- const_157376354642853000\n"
+			+ "\\* INVARIANT definition\n"
+			+ "INVARIANT\n"
+			+ "TypeInvariant\n"
+			+ "Invariant\n"
+			+ "\\* PROPERTY definition\n"
+			+ "PROPERTY\n"
+			+ "Termination\n"
+			+ INIT_NEXT_CFG
+			+ "\n";
+
+	@Test
+	public void testCopy() throws IOException {
+		final CFGCopier copier = new CFGCopier("doesn't", "matter", null, INIT_NEXT_CFG);
+		final StringReader sr = new StringReader(ORIGINAL_CFG);
+		final StringWriter sw = new StringWriter();
+		
+		copier.copy(sr, sw);
+		// We compare the substring because there is a new generation time stamp at the end of the newly created config
+		Assert.assertEquals(NEW_CFG, sw.getBuffer().toString().substring(0, NEW_CFG.length()));
+	}
+}
diff --git a/tlatools/test/tlc2/output/ErrorPrinterTest.java b/tlatools/test/tlc2/output/MPTest.java
similarity index 50%
rename from tlatools/test/tlc2/output/ErrorPrinterTest.java
rename to tlatools/test/tlc2/output/MPTest.java
index 2b603cd30a70d420efad233268f50fa4a0caabcb..43c199632945a8ccc640441b620e75451a849fcd 100644
--- a/tlatools/test/tlc2/output/ErrorPrinterTest.java
+++ b/tlatools/test/tlc2/output/MPTest.java
@@ -1,21 +1,22 @@
 package tlc2.output;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
 import util.ToolIO;
 
-/**
- * @author Simon Zambrovski
- * @version $Id$
- */
-public class ErrorPrinterTest extends TestCase
+public class MPTest
 {
 
     /* (non-Javadoc)
      * @see junit.framework.TestCase#setUp()
      */
-    protected void setUp() throws Exception
+    @Before
+	public void setUp() throws Exception
     {
-        super.setUp();
         ToolIO.setMode(ToolIO.TOOL);
         ToolIO.reset();
     }
@@ -23,7 +24,8 @@ public class ErrorPrinterTest extends TestCase
     /**
      * Test method for {@link tlc2.output.MP#printError(int)}.
      */
-    public void testPrintErrorInt()
+    @Test
+	public void testPrintErrorInt()
     {
         MP.printError(EC.UNIT_TEST);
         String[] allMessages = ToolIO.getAllMessages();
@@ -34,7 +36,8 @@ public class ErrorPrinterTest extends TestCase
     /**
      * Test method for {@link tlc2.output.MP#printError(int, java.lang.String)}.
      */
-    public void testPrintErrorIntString()
+    @Test
+	public void testPrintErrorIntString()
     {
         String parameter = "EXPECTED";
         MP.printError(EC.UNIT_TEST, parameter);
@@ -46,7 +49,8 @@ public class ErrorPrinterTest extends TestCase
     /**
      * Test method for {@link tlc2.output.MP#printError(int, java.lang.String[])}.
      */
-    public void testPrintErrorIntStringArray()
+    @Test
+	public void testPrintErrorIntStringArray()
     {
         String[] parameters = new String[] { "EXPECTED", "EXPECTED2", "TOO MANY" };
         MP.printError(EC.UNIT_TEST, parameters);
@@ -55,5 +59,22 @@ public class ErrorPrinterTest extends TestCase
         assertEquals("Error: [" + parameters[0] + "][" + parameters[1] + "]", allMessages[0]);
     }
 
-
+    @Test
+    public void testPrintProgressStats() {
+        String[] parameters = new String[] {
+                "this.trace.getLevelForReporting()",
+                MP.format(3000000),
+                MP.format(5000),
+                MP.format(1222333444),
+                MP.format(10000),
+                MP.format(1234)
+        };
+        MP.printMessage(EC.TLC_PROGRESS_STATS, parameters);
+        String[] allMessages = ToolIO.getAllMessages();
+        assertEquals(1, allMessages.length);
+		assertTrue(allMessages[0], allMessages[0].contains(
+				"3,000,000 states generated (10,000 s/min), 5,000 distinct states found (1,234 ds/min), 1,222,333,444 states left on queue.")
+				|| allMessages[0].contains(
+						"3.000.000 states generated (10.000 s/min), 5.000 distinct states found (1.234 ds/min), 1.222.333.444 states left on queue."));
+    }
 }
diff --git a/tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java b/tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f2fb1ddb96c6de2f82b6e0a8c5b0ae79d0a3949
--- /dev/null
+++ b/tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java
@@ -0,0 +1,160 @@
+package tlc2.output;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import tla2sany.drivers.FrontEndException;
+import tla2sany.drivers.SANY;
+import tla2sany.modanalyzer.SpecObj;
+import tlc2.model.Formula;
+import tlc2.model.MCState;
+import tlc2.model.TraceExpressionInformationHolder;
+import util.TLAConstants;
+import util.TestPrintStream;
+
+/**
+ * The genesis for these tests is regressions that were introduced by beautification changes made as part of #393.
+ * 
+ * As future spec-generation methods are touched, something implementing them should be added below.
+ */
+public class SpecTraceExpressionWriterTest {
+	static private final String TRIVIAL_TWO_STATE_DEADLOCK_PREAMBLE
+			= "VARIABLE x, y\n"
+					+ "XIncr == (x' = x * 2)\n"
+					+ "            /\\ (x < 8)\n"
+					+ "            /\\ UNCHANGED y\n"
+					+ "YIncr == (y' = x + y)\n"
+					+ "            /\\ (y < 15)\n"
+					+ "            /\\ UNCHANGED x\n";
+	static private final String[] TRIVIAL_TWO_STATE_DEADLOCK_INIT
+			= new String[] {
+					"TestInit",
+					"TestInit == x \\in 1 .. 10 /\\ y \\in 1 .. 10\n"
+				};
+	static private final String[] TRIVIAL_TWO_STATE_DEADLOCK_NEXT
+			= new String[] {
+					"TestNext",
+					"TestNext == YIncr \\/ XIncr\n"
+				};
+	static private final String ERROR_STATE_IP
+			= "1: <Initial predicate>\n"
+					+ "/\\ x = 8\n"
+					+ "/\\ y = 7\n";
+	static private final String ERROR_STATE_1
+			= "2: <YIncr line 8, col 10 to line 10, col 26 of module Bla>\n"
+					+ "/\\ x = 8\n"
+					+"/\\ y = 15\n";
+
+	
+	private SpecTraceExpressionWriter writer;
+	private File tlaFile;
+	private File cfgFile;
+	
+	@Before
+	public void setUp() throws IOException {
+		tlaFile = File.createTempFile("sptewt_", ".tla");
+		tlaFile.deleteOnExit();
+		cfgFile = File.createTempFile("sptewt_", ".cfg");
+		cfgFile.deleteOnExit();
+		
+		final String tlaFilename = tlaFile.getName();
+		final int baseNameLength = tlaFilename.length() - TLAConstants.Files.TLA_EXTENSION.length();
+		final String specName = tlaFilename.substring(0, baseNameLength);
+		writer = new SpecTraceExpressionWriter();
+		writer.addPrimer(specName, "Naturals");
+		writer.appendContentToBuffers(TRIVIAL_TWO_STATE_DEADLOCK_PREAMBLE, null);
+	}
+	
+	private void concludeTest() throws FrontEndException, IOException {
+		writer.writeFiles(tlaFile, cfgFile);
+		
+		final SpecObj so = new SpecObj(tlaFile.getAbsolutePath(), null);
+		final TestPrintStream printStream = new TestPrintStream();
+		
+		final int result = SANY.frontEndMain(so, tlaFile.getAbsolutePath(), printStream);
+		if (result != 0) {
+			throw new FrontEndException("Parsing returned a non-zero success code (" + result + ")");
+		}
+	}
+	
+	private List<MCState> generateStatesForDeadlockCondition() { 
+		final List<MCState> states = new ArrayList<>();
+		
+		states.add(MCState.parseState(ERROR_STATE_IP));
+		states.add(MCState.parseState(ERROR_STATE_1));
+
+		return states;
+	}
+	
+	@Test
+	public void testInitNextWithNoError() throws Exception {
+		writer.addInitNextDefinitions(TRIVIAL_TWO_STATE_DEADLOCK_INIT, TRIVIAL_TWO_STATE_DEADLOCK_NEXT,
+									  "writerTestInit", "writerTextNext");
+
+		concludeTest();
+	}
+	
+	@Test
+	public void testInitNextWithError() throws Exception {
+		final List<MCState> trace = generateStatesForDeadlockCondition();
+		final StringBuilder tempCFGBuffer = new StringBuilder();
+		final StringBuilder[] tlaBuffers
+				= SpecTraceExpressionWriter.addInitNextToBuffers(tempCFGBuffer, trace, null, "STEWInit", "STEWNext",
+						 										 "STEWAC", TRIVIAL_TWO_STATE_DEADLOCK_NEXT[0], true);
+		
+		writer.appendContentToBuffers(tlaBuffers[0].toString(), tempCFGBuffer.toString());
+		writer.addTraceFunction(trace);
+		writer.appendContentToBuffers(tlaBuffers[1].toString(), null);
+		
+		concludeTest();
+	}
+	
+	@Test
+	public void testInitNextWithErrorAndTraceExpression() throws Exception {
+		final List<MCState> trace = generateStatesForDeadlockCondition();
+		writer.addTraceFunction(trace);
+
+		final List<Formula> expressions = new ArrayList<>();
+		expressions.add(new Formula("ENABLED XIncr"));
+		expressions.add(new Formula("y # 7"));
+		final TraceExpressionInformationHolder[] traceExpressions
+						= writer.createAndAddVariablesAndDefinitions(expressions, "writerTestTraceExpressions");
+		writer.addInitNext(trace, traceExpressions, "STEWInit", "STEWNext", "STEWAC", TRIVIAL_TWO_STATE_DEADLOCK_NEXT[0]);
+		
+		concludeTest();
+	}
+
+	
+	@Test
+	public void testMultilineTraceExpression() throws Exception {
+		final List<MCState> trace = generateStatesForDeadlockCondition();
+		writer.addTraceFunction(trace);
+
+		final List<Formula> expressions = new ArrayList<>();
+		expressions.add(new Formula("\n"
+				+ "/\\ y # 7\n"
+				+ "/\\ \\/ TRUE\n"
+				+ " \\* Comment"
+				+ "   \\/ FALSE"));
+		// Named expression
+		final Formula e = new Formula("namedExpression == \n"
+				+ "  (* A commend \n over two lines*)"
+				+ "  /\\ \\/ TRUE\n"
+				+ " \\* Comment"
+				+ "     \\/ FALSE");
+		assertTrue(e.isNamed());
+		expressions.add(e);
+		final TraceExpressionInformationHolder[] traceExpressions
+						= writer.createAndAddVariablesAndDefinitions(expressions, "writerTestTraceExpressions");
+		writer.addInitNext(trace, traceExpressions, "STEWInit", "STEWNext", "STEWAC", TRIVIAL_TWO_STATE_DEADLOCK_NEXT[0]);
+		
+		concludeTest();
+	}
+}
diff --git a/tlatools/test/tlc2/output/TLACopierTest.java b/tlatools/test/tlc2/output/TLACopierTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..654553dd4004c333fce14915b71eb9fa8028ac1c
--- /dev/null
+++ b/tlatools/test/tlc2/output/TLACopierTest.java
@@ -0,0 +1,72 @@
+package tlc2.output;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TLACopierTest {
+	private static final String INIT_NEXT_DEFINITIONS = "init_ldq ==\n"
+			+ "	TRUE\n"
+			+ "	\\/ FALSE\n"
+			+ "	\n"
+			+ "next_ldq ==\n"
+			+ "	FALSE\n";
+	private static final String ORIGINAL_SPEC = "---- MODULE MC ----\n"
+			+ "EXTENDS Queens, TLC\n"
+			+ "\n"
+			+ "\\* CONSTANT definitions @modelParameterConstants:0N\n"
+			+ "const_157376354642853000 == \n"
+			+ "3\n"
+			+ "----\n"
+			+ "\n"
+			+ "=============================================================================\n"
+			+ "\\* Modification History\n"
+			+ "\\* Created Thu Nov 14 12:32:26 PST 2019 by loki\n";
+	private static final String NEW_SPEC_EXTENDED = "---- MODULE Spectacle ----\n"
+			+ "EXTENDS Queens, TLC\n"
+			+ "\n"
+			+ "\\* CONSTANT definitions @modelParameterConstants:0N\n"
+			+ "const_157376354642853000 == \n"
+			+ "3\n"
+			+ "----\n"
+			+ "\n"
+			+ INIT_NEXT_DEFINITIONS + "\n"
+			+ "=============================================================================\n"
+			+ "\\* Modification History\n"
+			+ "\\* Created Thu Nov 14 12:32:26 PST 2019 by loki\n";
+	private static final String NEW_SPEC_NONEXTENDED = "---- MODULE Spectacle ----\n"
+			+ "EXTENDS Queens, TLC, TLC, Toolbox\n"
+			+ "\n"
+			+ "\\* CONSTANT definitions @modelParameterConstants:0N\n"
+			+ "const_157376354642853000 == \n"
+			+ "3\n"
+			+ "----\n"
+			+ "\n"
+			+ INIT_NEXT_DEFINITIONS + "\n"
+			+ "=============================================================================\n"
+			+ "\\* Modification History\n"
+			+ "\\* Created Thu Nov 14 12:32:26 PST 2019 by loki\n";
+	
+	@Test
+	public void testNonExtending() throws IOException {
+		final TLACopier copier = new TLACopier("MC", "Spectacle", null, INIT_NEXT_DEFINITIONS, false, false);
+		final StringReader sr = new StringReader(ORIGINAL_SPEC);
+		final StringWriter sw = new StringWriter();
+		
+		copier.copy(sr, sw);
+		Assert.assertEquals(NEW_SPEC_NONEXTENDED, sw.getBuffer().toString());
+	}
+	
+	@Test
+	public void testExtending() throws IOException {
+		final TLACopier copier = new TLACopier("MC", "Spectacle", null, INIT_NEXT_DEFINITIONS, true, true);
+		final StringReader sr = new StringReader(ORIGINAL_SPEC);
+		final StringWriter sw = new StringWriter();
+		
+		copier.copy(sr, sw);
+		Assert.assertEquals(NEW_SPEC_EXTENDED, sw.getBuffer().toString());
+	}
+}
diff --git a/tlatools/test/tlc2/tool/.gitattributes b/tlatools/test/tlc2/tool/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..918458c130d4d970d54568d475c092205fce9ebc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/.gitattributes
@@ -0,0 +1,3 @@
+## Declare DumpAsDotTest.dot to keep unix line endings on checkout even on Windows.
+## With "crlf" DumpAsDotTest fails on Windows.
+DumpAsDotTest.dot text eol=lf
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/ASTest.java b/tlatools/test/tlc2/tool/ASTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d8bcc269b8acb1f254d326df625d91749813b17
--- /dev/null
+++ b/tlatools/test/tlc2/tool/ASTest.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class ASTest extends ModelCheckerTestCase {
+
+	public ASTest() {
+		super("AS", "AS", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_STATES_AND_NO_NEXT_ACTION));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AbsoluteSpecPathTest.java b/tlatools/test/tlc2/tool/AbsoluteSpecPathTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..62b4d921aab72a793326020738dbf9bbc72721dd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AbsoluteSpecPathTest.java
@@ -0,0 +1,42 @@
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+
+import org.junit.Test;
+
+import tlc2.TLC;
+import tlc2.TestMPRecorder;
+import tlc2.output.EC;
+import tlc2.output.MP;
+
+public class AbsoluteSpecPathTest extends CommonTestCase {
+
+	public AbsoluteSpecPathTest() {
+		super(new TestMPRecorder());
+	}
+
+	@Test
+	public void test() throws Exception {
+		// Check that BASE_DIR is actually set to make sure we have an absolute path to
+		// work with. If this test gets executed from within the Eclipse IDE, manually
+		// set -Dbasedir=/path/to/tlatools/
+		assumeFalse(BASE_DIR.equals(""));
+		
+		MP.setRecorder(recorder);
+		
+		// Do not call TLC#main because we won't get control back (system.exit) to check
+		// assertions below.
+		final TLC tlc = new TLC();
+		tlc.handleParameters(new String[] {BASE_PATH + "Test2"});
+		tlc.process();
+		
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "5"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "5", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssertExpressionStack.java b/tlatools/test/tlc2/tool/AssertExpressionStack.java
new file mode 100644
index 0000000000000000000000000000000000000000..11c2624a4eb1dd6ad17e2dd60c8bb36125b4c898
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssertExpressionStack.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssertExpressionStack extends ModelCheckerTestCase {
+
+	public AssertExpressionStack() {
+		super("AssertExpressionStack");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		// Assert a proper nested expression has been recorded which represents the call stack.
+		assertFalse(recorder.recordedWithStringValue(EC.TLC_NESTED_EXPRESSION, "    The error call stack is empty.\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssignmentInitExpensiveTest.java b/tlatools/test/tlc2/tool/AssignmentInitExpensiveTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e43faa4135164bad5ea49a35ae441b6745cdda8
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssignmentInitExpensiveTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssignmentInitExpensiveTest extends ModelCheckerTestCase {
+
+	public AssignmentInitExpensiveTest() {
+		super("AssignmentInitExpensive");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10002", "1", "0"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssignmentInitNegTest.java b/tlatools/test/tlc2/tool/AssignmentInitNegTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cec19a9cca35c3ee1441d07e96dbc0f347885a83
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssignmentInitNegTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssignmentInitNegTest extends ModelCheckerTestCase {
+
+	public AssignmentInitNegTest() {
+		super("AssignmentInitNeg");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssignmentInitTest.java b/tlatools/test/tlc2/tool/AssignmentInitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e18f1fc888032751ec9740ee314675f05bc1ead
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssignmentInitTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssignmentInitTest extends ModelCheckerTestCase {
+
+	public AssignmentInitTest() {
+		super("AssignmentInit");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "5", "0"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssignmentNext2Test.java b/tlatools/test/tlc2/tool/AssignmentNext2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3bdff754b77e1eaa537d5540f1f74e54b748741
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssignmentNext2Test.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssignmentNext2Test extends ModelCheckerTestCase {
+
+	public AssignmentNext2Test() {
+		super("AssignmentNext2");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssignmentNext3Test.java b/tlatools/test/tlc2/tool/AssignmentNext3Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..a02f8a42855ceb747e807a31dbf3805888c2a5dc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssignmentNext3Test.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssignmentNext3Test extends ModelCheckerTestCase {
+
+	public AssignmentNext3Test() {
+		super("AssignmentNext3");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "26", "5", "0"));
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/AssignmentNextTest.java b/tlatools/test/tlc2/tool/AssignmentNextTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..accf6f366173fceeb5f25f6d16dad799b065d9c5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/AssignmentNextTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class AssignmentNextTest extends ModelCheckerTestCase {
+
+	public AssignmentNextTest() {
+		super("AssignmentNext");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/BagsTest.java b/tlatools/test/tlc2/tool/BagsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..dedf5edf806bbf04ba263fb71816964ac19ddd34
--- /dev/null
+++ b/tlatools/test/tlc2/tool/BagsTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class BagsTest extends ModelCheckerTestCase {
+
+	public BagsTest() {
+		super("BagsTest");
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/BugzillaBug279Test.java b/tlatools/test/tlc2/tool/BugzillaBug279Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..600d376390bebc53a9c620d61f390d81c8b4d0ab
--- /dev/null
+++ b/tlatools/test/tlc2/tool/BugzillaBug279Test.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import tlc2.value.Values;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SubsetValue;
+
+/**
+ * TLC bug caused by TLC's not preserving the semantics of CHOOSE
+ * 
+ *  Leslie Lamport 2012-03-08 00:26:08 UTC
+ *  
+ *  Running TLC on the spec written by Tom 
+ *  Rodeheffer that I will attach finds a deadlock (as it should) and then 
+ *  produces the following error when trying to generate the error trace:
+ *  
+ *      Failed to recover the initial state from its fingerprint.
+ *      This is probably a TLC bug(2).
+ *    
+ *  The problem occurs because the value that TLC computes for a CHOOSE 
+ *  depends on TLC's internal representation of its argument.  To compute 
+ *  the 2nd state of the trace, TLC sets the variables 'set' and 'fun' to S1
+ *  and  Ch(S1), respectively, where Ch(S1) equals CHOOSE of an expression 
+ *  containing S1.   Fingerprinting that state causes TLC to canonicalize 
+ *  the representation of the value of 'set', changing its representation of
+ *  the value of S1.  When TLC constructs the error trace, it must 
+ *  re-execute the spec to get the states in the trace.  When it tries to 
+ *  compute the 2nd state of the trace, it uses the canonicalized 
+ *  representation of S1, causing it to compute a different value of 
+ *  variable 'set' than it did the first time, so it doesn't find a state 
+ *  with the correct fingerprint as the next state of the trace.  (Note that
+ *  the error message is misleading, since it's not the intiial state that 
+ *  TLC fails to recover.)
+ *  
+ *  There seem to be two possible fixes.  The first is to canonicalize the 
+ *  argument of CHOOSE whenever it is evaluated.  The second is to create a 
+ *  separate deep copy of the state before fingerprinting it.  The first 
+ *  seems like the best solution, since I can't think of any practical case 
+ *  in which this would cause performance problems.  However, I will consult
+ *  with Yuan Yu before doing anything about this.
+ */
+public class BugzillaBug279Test extends ModelCheckerTestCase {
+
+	public BugzillaBug279Test() {
+		super("InitStateBug", "Bug279", ExitStatus.VIOLATION_DEADLOCK);
+	}
+	
+	@Override
+	protected boolean checkDeadLock() {
+		return true;
+	}
+	
+	@Override
+	protected boolean doDump() {
+		// For this test, dumping means enumerating the SUBSETs which creates too much
+		// overhead. The test is also about avoiding enumerating SUBSET. 
+		return false;
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "3", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_DEADLOCK_REACHED));
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(6);
+		expectedTrace.add("/\\ set = {}\n/\\ pc = 0\n/\\ fun = {}");
+		expectedTrace.add("/\\ set = SUBSET 1..20\n/\\ pc = 1\n/\\ fun = {5}");
+		expectedTrace.add(
+				"/\\ set = " + Values.ppr(new SubsetValue(new IntervalValue(1, 8)).toSetEnum().normalize())
+						+ "\n/\\ pc = 2\n/\\ fun = {5}");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/Test056.java b/tlatools/test/tlc2/tool/CodePlexBug21Test.java
similarity index 76%
rename from tlatools/test/tlc2/tool/liveness/Test056.java
rename to tlatools/test/tlc2/tool/CodePlexBug21Test.java
index f54c2737f82b4c44fae92447563a631a09e05726..6c10a5580fe9955bb2d1507abcffccdc71db74a5 100644
--- a/tlatools/test/tlc2/tool/liveness/Test056.java
+++ b/tlatools/test/tlc2/tool/CodePlexBug21Test.java
@@ -24,23 +24,30 @@
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
 
-package tlc2.tool.liveness;
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
 
-public class Test056 extends ModelCheckerTestCase {
+/**
+ * see http://tlaplus.codeplex.com/workitem/21
+ */
+public class CodePlexBug21Test extends ModelCheckerTestCase {
 
-	public Test056() {
-		super("test56");
+	public CodePlexBug21Test() {
+		super("CodePlexBug21");
 	}
-
+	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
-		assertTrue(recorder.recorded(EC.TLC_MODE_MC));
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "3"));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "6", "0"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 	}
 }
diff --git a/tlatools/test/tlc2/tool/CommonTestCase.java b/tlatools/test/tlc2/tool/CommonTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef430379929639a5718c2f5c711f07094fd935d0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/CommonTestCase.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.runner.RunWith;
+
+import tlc2.TLCGlobals;
+import tlc2.TestMPRecorder;
+import tlc2.TestMPRecorder.Coverage;
+import tlc2.output.EC;
+import tlc2.output.MPRecorder;
+import tlc2.tool.liveness.GraphNode;
+import tlc2.util.BitVector;
+import tlc2.util.BufferedRandomAccessFile;
+import util.IsolatedTestCaseRunner;
+
+@RunWith(IsolatedTestCaseRunner.class)
+public abstract class CommonTestCase {
+
+	protected static final String BASE_DIR = System.getProperty("basedir", "");
+	protected static final String TEST_MODEL = "test-model" + File.separator;
+	public static final String BASE_PATH = BASE_DIR + TEST_MODEL;
+
+	protected final TestMPRecorder recorder;
+
+	public CommonTestCase() {
+		this(new TestMPRecorder());
+	}
+	
+	public CommonTestCase(final TestMPRecorder testMPRecorder) {
+		recorder = testMPRecorder;
+	}
+
+	/**
+	 * Asserts that the actual trace and the expected error trace are equal.
+	 * 
+	 * @param actual
+	 *            The actual trace as recorded by {@link MPRecorder}.
+	 * @param expectedTrace
+	 *            The expected trace.
+	 */
+	protected void assertTraceWith(final List<Object> actual, final List<String> expectedTrace) {
+		assertEquals(expectedTrace.size(), actual.size());
+		for (int i = 0; i < expectedTrace.size(); i++) {
+			final Object[] objs = (Object[]) actual.get(i);
+			final TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
+			final String info = (String) stateInfo.info;
+			if (i == 0) {
+				// The first state has to be an initial state.
+				"<Initial predicate>".equals(info);
+			} else {
+				// ... all others are reachable via an action.
+				info.startsWith("<Action");
+			}
+			assertEquals(expectedTrace.get(i), 
+					   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
+			assertEquals(i+1, objs[1]);
+		}
+	}
+
+	/**
+	 * Asserts that the error trace ends in stuttering at the given number.
+	 * 
+	 * @param stateNum
+	 *            The number of the stuttering state
+	 */
+	protected void assertStuttering(int stateNum) {
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3));
+		List<Object> stutter = recorder.getRecords(EC.TLC_STATE_PRINT3);
+		assertTrue(stutter.size() > 0);
+		Object[] object = (Object[]) stutter.get(0);
+		assertEquals(stateNum, object[1]);
+	}
+
+	/**
+	 * Asserts that the error trace loops back to the state with the given
+	 * number.
+	 * 
+	 * @param i The loop back state number.
+	 */
+	protected void assertBackToState(int stateNum) {
+		assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE));
+		List<Object> loop = recorder.getRecords(EC.TLC_BACK_TO_STATE);
+		assertTrue(loop.size() > 0);
+		Object[] object = (Object[]) loop.get(0);
+		assertEquals(Integer.toString(stateNum), object[0]);
+	}
+
+	/**
+	 * Asserts that the error trace loops back to the state with the given
+	 * number.
+	 * 
+	 * @param i The loop back state number.
+	 * @param action The action label associated with the loop back marker
+	 */
+	protected void assertBackToState(int stateNum, final String action) {
+		assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE));
+		List<Object> loop = recorder.getRecords(EC.TLC_BACK_TO_STATE);
+		assertTrue(loop.size() > 0);
+		Object[] object = (Object[]) loop.get(0);
+		assertTrue(object.length > 1);
+		assertEquals(Integer.toString(stateNum), object[0]);
+		assertEquals(action, object[1]);
+	}
+
+	/**
+	 * Check the file size of the AbstractDiskGraph files to assert that the
+	 * expected amount of ptrs and nodes (outgoing arcs) have been written to
+	 * disk.
+	 * <p>
+	 * CAUTION: The order in which the transitions are inserted into the
+	 * {@link GraphNode} determines the size of the {@link BitVector}. I.e. if
+	 * the truth values of the first N nodes inserted are true, and the
+	 * remainder is false, the BitVector's size will correspond to N. However,
+	 * if the first N truth values are false, followed by M trues, the
+	 * BitVector's size is N + M.
+	 * <p>
+	 * See {@link GraphNode}'s constructor: it initializes {@link BitVector}
+	 * with capacity zero and subsequently grows BV when bits are set to true.
+	 * <p>
+	 * 
+	 * @see BitVector#read(BufferedRandomAccessFile)
+	 * @see BitVector#write(BufferedRandomAccessFile)
+	 * @see GraphNode#read(BufferedRandomAccessFile)
+	 * @see GraphNode#write(BufferedRandomAccessFile)
+	 * 
+	 * @param nodesSize
+	 * @param ptrsSize
+	 */
+	protected void assertNodeAndPtrSizes(final long nodesSize, final long ptrsSize) {
+		final String metadir = TLCGlobals.mainChecker.metadir;
+		assertNotNull(metadir);
+		
+		final File nodes = new File(metadir + File.separator + "nodes_0");
+		assertTrue(nodes.exists());
+		assertEquals(nodesSize, nodes.length());
+	
+		final File ptrs =  new File(metadir + File.separator + "ptrs_0");
+		assertTrue(ptrs.exists());
+		assertEquals(ptrsSize, ptrs.length());
+	}
+
+	// Checks if all uncovered (zero) lines are found and no more (don't care if the invocation and costs match).
+	protected void assertUncovered(final String expectedUncovered) {
+		final List<Coverage> expected = Arrays.asList(expectedUncovered.trim().split("\n")).stream()
+				.map(o -> new Coverage(o.split(":"))).collect(Collectors.toList());
+
+		final Set<Coverage> expectedZero = expected.stream().filter(Coverage::isZero).collect(Collectors.toSet());
+		final Set<Coverage> actualZeroCoverage = recorder.getZeroCoverage().stream().collect(Collectors.toSet());
+		assertEquals(expectedZero, actualZeroCoverage);
+	}
+	
+	protected void assertZeroUncovered() {
+		assertTrue(recorder.getZeroCoverage().isEmpty());
+	}
+	
+	protected void assertCoverage(final String expectedCoverage) {
+		// Lines can be reported multiple times if invoked from different actions!!!
+		
+		final List<Coverage> expected = Arrays.asList(expectedCoverage.split("\n")).stream()
+				.map(o -> new Coverage(o.split(":"))).collect(Collectors.toList());
+		
+		// Step A:
+		// Validation of coverage results is split into two steps. Step A checks if all
+		// uncovered (zero) lines are found, step B checks if non-zero lines exist.		
+		final Set<Coverage> expectedZero = expected.stream().filter(Coverage::isZero)
+				.filter(Coverage::isCoverage).collect(Collectors.toSet());
+		final Set<Coverage> actualZeroCoverage = recorder.getZeroCoverage().stream().collect(Collectors.toSet());
+		assertEquals(expectedZero, actualZeroCoverage);
+		
+		// Step B1 (coverage):
+		final List<Coverage> actualNonZeroCoverage = recorder.getNonZeroCoverage();
+		final List<Coverage> expectedNonZeroCoverage = expected.stream().filter(Coverage::isCoverage).
+				filter(c -> !c.isCost()).collect(Collectors.toList());
+		expectedNonZeroCoverage.removeAll(actualZeroCoverage);
+		for (int i = 0; i < actualNonZeroCoverage.size(); i++) {
+			final Coverage a = actualNonZeroCoverage.get(i);
+			final Coverage e = expectedNonZeroCoverage.get(i);
+			assertEquals(e, a);
+		}
+		assertTrue(expectedNonZeroCoverage.size() == actualNonZeroCoverage.size());
+		
+		// Step B2 (coverage with cost):
+		final List<Coverage> actualCostCoverage = recorder.getCostCoverage();
+		final List<Coverage> expectedCostCoverage = expected.stream().filter(Coverage::isCoverage)
+				.filter(Coverage::isCost).collect(Collectors.toList());
+		for (int i = 0; i < actualCostCoverage.size(); i++) {
+			final Coverage a = actualCostCoverage.get(i);
+			final Coverage e = expectedCostCoverage.get(i);
+			assertEquals(e, a);
+		}
+		assertTrue(expectedCostCoverage.size() == actualCostCoverage.size());
+		
+		// Step C (actions):
+		final List<Coverage> actualActions = recorder.getActionCoverage();
+		final List<Coverage> expectedActions = expected.stream().filter(Coverage::isAction).collect(Collectors.toList());
+		for (int i = 0; i < actualActions.size(); i++) {
+			final Coverage a = actualActions.get(i);
+			final Coverage e = expectedActions.get(i);
+			assertEquals(e, a);
+		}
+		assertTrue(expectedActions.size() == actualActions.size());
+	}
+}
diff --git a/tlatools/test/tlc2/tool/ContinueTest.java b/tlatools/test/tlc2/tool/ContinueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9129418a3313b93f77481eca055085f71418ac26
--- /dev/null
+++ b/tlatools/test/tlc2/tool/ContinueTest.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class ContinueTest extends ModelCheckerTestCase {
+
+	public ContinueTest() {
+		super("Continue", new String[] { "-continue" }, ExitStatus.SUCCESS);
+	}
+
+	@Test
+	public void testSpec() throws FileNotFoundException, IOException {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "32", "29", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "29"));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		
+		// With -continue, TLC simply prints two or more consecutive traces in no given
+		// order (determined by concurrent BFS) to stdout. This means that the
+		// MPRecorder just concatenates the traces and it is difficult to check them.
+		// For now we check that the concatenated trace has the expected number of states.
+		assertEquals(32, recorder.getRecords(EC.TLC_STATE_PRINT2).size());
+//		// Trace 1
+//		final List<String> expectedTrace = new ArrayList<String>(2);
+//		expectedTrace.add("/\\ x = 1\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 2\n/\\ y = 2");
+//		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+//		// Trace 2
+//		final List<String> expectedTrace = new ArrayList<String>(30);
+//		expectedTrace.add("/\\ x = 1\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 2\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 3\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 4\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 5\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 6\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 7\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 8\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 9\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 10\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 11\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 12\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 13\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 14\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 15\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 16\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 17\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 18\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 19\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 20\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 21\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 22\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 23\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 24\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 25\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 26\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 27\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 28\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 29\n/\\ y = 1");
+//		expectedTrace.add("/\\ x = 30\n/\\ y = 1");
+//		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertZeroUncovered();
+	}
+
+	@Override
+	protected int getNumberOfThreads() {
+		return 3;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/DepthFirstDieHardTest.java b/tlatools/test/tlc2/tool/DepthFirstDieHardTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..75997ec73551443a41d04fe7e57bcf6787f936c9
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DepthFirstDieHardTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DepthFirstDieHardTest extends ModelCheckerTestCase {
+
+	public DepthFirstDieHardTest() {
+		super("DieHard", "", new String[] {"-dfid", "7"}, ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT1));
+		final List<String> expectedTrace = new ArrayList<String>(7);
+		expectedTrace.add("/\\ action = \"nondet\"\n/\\ smallBucket = 0\n/\\ bigBucket = 0\n/\\ water_to_pour = 0");
+		expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 0\n/\\ bigBucket = 5\n/\\ water_to_pour = 0");
+		expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 2\n/\\ water_to_pour = 3");
+		expectedTrace.add("/\\ action = \"empty small\"\n/\\ smallBucket = 0\n/\\ bigBucket = 2\n/\\ water_to_pour = 3");
+		expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 2\n/\\ bigBucket = 0\n/\\ water_to_pour = 2");
+		
+		expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 2\n/\\ bigBucket = 5\n/\\ water_to_pour = 2");
+		
+		expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 4\n/\\ water_to_pour = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT1), expectedTrace);
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java b/tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6249216383b02c3cc08eab681dd481ff9f50212
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DepthFirstErrorTraceTest extends ModelCheckerTestCase {
+
+	public DepthFirstErrorTraceTest() {
+		super("DepthFirstErrorTrace", "", new String[] {"-dfid", "9"}, ExitStatus.VIOLATION_SAFETY);
+	}
+	
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT1));
+		final List<String> expectedTrace = new ArrayList<String>(5);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 1");
+		expectedTrace.add("x = 2");
+		expectedTrace.add("x = 3");
+		expectedTrace.add("x = 4");
+		expectedTrace.add("x = 5");
+		expectedTrace.add("x = 6");
+		expectedTrace.add("x = 7");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT1), expectedTrace);
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/DepthFirstTerminate.java b/tlatools/test/tlc2/tool/DepthFirstTerminate.java
new file mode 100644
index 0000000000000000000000000000000000000000..25f832a9970327e88e14b993779f263ea5a74cfd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DepthFirstTerminate.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DepthFirstTerminate extends ModelCheckerTestCase {
+
+	public DepthFirstTerminate() {
+		super("DepthFirstTerminate", "", new String[] { "-dfid", "50" });
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads()
+	 */
+	@Override
+	protected int getNumberOfThreads() {
+		// Run this test with as many threads possible to hopefully spot concurrency issues.
+		return Runtime.getRuntime().availableProcessors();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/DiameterTest.java b/tlatools/test/tlc2/tool/DiameterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..34d28c0f2d626a9971bad3e4351bbdb7dbbae00f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DiameterTest.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DiameterTest extends ModelCheckerTestCase {
+
+	public DiameterTest() {
+		super("DieHardTLA");
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished without errors and generated the expected
+		// amount of states
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "97", "16", "0"));
+
+		// The diameter is known to be 8 as reported by TLC running with a
+		// single worker. With multiple workers, it's possible to get a higher
+		// or a lower number.
+		final int level = recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH);
+		assertTrue(String.format("Level below threshold: %s", level), level >= 8);
+
+		assertZeroUncovered();
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads()
+	 */
+	@Override
+	protected int getNumberOfThreads() {
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/DistributedTrace.java b/tlatools/test/tlc2/tool/DistributedTrace.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fe2b89677972dc12fc8cfbb59134dfb76326864
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DistributedTrace.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DistributedTrace extends ModelCheckerTestCase {
+
+	public DistributedTrace() {
+		super("DistributedTrace");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(5);
+		expectedTrace.add("x = 10");
+		expectedTrace.add("x = 11");
+		expectedTrace.add("x = 12");
+		expectedTrace.add("x = 13");
+		expectedTrace.add("x = 14");
+		expectedTrace.add("x = 15");
+		expectedTrace.add("x = 16");
+		expectedTrace.add("x = 17");
+		expectedTrace.add("x = 18");
+		expectedTrace.add("x = 19");
+		expectedTrace.add("x = 20");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads()
+	 */
+	protected int getNumberOfThreads() {
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/DumpAsDotTest.dot b/tlatools/test/tlc2/tool/DumpAsDotTest.dot
new file mode 100644
index 0000000000000000000000000000000000000000..09c7b56bab8a6a27c66ec21a90730e0a67b5c251
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DumpAsDotTest.dot
@@ -0,0 +1,47 @@
+strict digraph DiskGraph {
+edge [colorscheme="paired12"]
+nodesep=0.35;
+subgraph cluster_graph {
+color="white";
+609737673425276830 [label="/\\ b = FALSE\n/\\ x = 0",style = filled]
+6816998822487979083 [label="/\\ b = TRUE\n/\\ x = 0",style = filled]
+3365478001808954030 [label="/\\ b = FALSE\n/\\ x = 1",style = filled]
+8671809759910816123 [label="/\\ b = TRUE\n/\\ x = 1",style = filled]
+5040481953810085374 [label="/\\ b = FALSE\n/\\ x = 2",style = filled]
+1377963776297717291 [label="/\\ b = TRUE\n/\\ x = 2",style = filled]
+7147721571019581646 [label="/\\ b = FALSE\n/\\ x = 3",style = filled]
+3881310712274735899 [label="/\\ b = TRUE\n/\\ x = 3",style = filled]
+609737673425276830 -> 8671809759910816123 [label="B",color="2",fontcolor="2"];
+609737673425276830 -> 609737673425276830 [style="dashed"];
+6816998822487979083 -> 609737673425276830 [label="A",color="3",fontcolor="3"];
+6816998822487979083 -> 6816998822487979083 [style="dashed"];
+3365478001808954030 -> 1377963776297717291 [label="B",color="2",fontcolor="2"];
+3365478001808954030 -> 3365478001808954030 [style="dashed"];
+8671809759910816123 -> 3365478001808954030 [label="A",color="3",fontcolor="3"];
+8671809759910816123 -> 8671809759910816123 [style="dashed"];
+5040481953810085374 -> 3881310712274735899 [label="B",color="2",fontcolor="2"];
+5040481953810085374 -> 5040481953810085374 [style="dashed"];
+1377963776297717291 -> 5040481953810085374 [label="A",color="3",fontcolor="3"];
+1377963776297717291 -> 1377963776297717291 [style="dashed"];
+7147721571019581646 -> -4210745456684007285 [label="B",color="2",fontcolor="2"];
+-4210745456684007285 [label="/\\ b = TRUE\n/\\ x = 4"];
+7147721571019581646 -> 7147721571019581646 [style="dashed"];
+3881310712274735899 -> 7147721571019581646 [label="A",color="3",fontcolor="3"];
+3881310712274735899 -> 3881310712274735899 [style="dashed"];
+-4210745456684007285 -> -7819220713745958050 [label="A",color="3",fontcolor="3"];
+-7819220713745958050 [label="/\\ b = FALSE\n/\\ x = 4"];
+-4210745456684007285 -> -4210745456684007285 [style="dashed"];
+-7819220713745958050 -> -2066378075513578053 [label="B",color="2",fontcolor="2"];
+-2066378075513578053 [label="/\\ b = TRUE\n/\\ x = 5"];
+-7819220713745958050 -> -7819220713745958050 [style="dashed"];
+-2066378075513578053 -> -2066378075513578053 [style="dashed"];
+{rank = same; 1377963776297717291;5040481953810085374;8671809759910816123;3365478001808954030;6816998822487979083;609737673425276830;3881310712274735899;7147721571019581646;}
+{rank = same; -4210745456684007285;}
+{rank = same; -7819220713745958050;}
+{rank = same; -2066378075513578053;}
+}
+subgraph cluster_legend {graph[style=bold];label = "Next State Actions" style="solid"
+node [ labeljust="l",colorscheme="paired12",style=filled,shape=record ]
+A [label="A",fillcolor=3]
+B [label="B",fillcolor=2]
+}}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/DumpAsDotTest.java b/tlatools/test/tlc2/tool/DumpAsDotTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..51400037d040a5367066f979d874470a63b65e57
--- /dev/null
+++ b/tlatools/test/tlc2/tool/DumpAsDotTest.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DumpAsDotTest extends ModelCheckerTestCase {
+
+	public DumpAsDotTest() {
+		super("MCa", "CodePlexBug08", new String[] { "-dump", "dot,colorize,actionlabels",
+				System.getProperty("java.io.tmpdir") + File.separator + "DumpAsDotTest" }, ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() throws IOException {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11", "0"));
+		
+		// -dump appends the ".dump" extension to the file name
+		final File dumpFile = new File(System.getProperty("java.io.tmpdir") + File.separator + "DumpAsDotTest.dot");
+		assertTrue(dumpFile.exists());
+		
+		// If the file exist, simply compare it to a correct and manually checked version.
+		final InputStream master = getClass().getResourceAsStream("DumpAsDotTest.dot");
+		assertTrue(Arrays.equals(getBytes(master), getBytes(new FileInputStream(dumpFile))));
+
+		assertZeroUncovered();
+	}
+	
+	// http://stackoverflow.com/a/17861016
+	public static byte[] getBytes(InputStream is) throws IOException {
+		final ByteArrayOutputStream os = new ByteArrayOutputStream();
+		try {
+			byte[] buffer = new byte[0xFFFF];
+			for (int len; (len = is.read(buffer)) != -1;) {
+				os.write(buffer, 0, len);
+			}
+			os.flush();
+			return os.toByteArray();
+		} finally {
+			os.close();
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/EmptySubsetEqTest.java b/tlatools/test/tlc2/tool/EmptySubsetEqTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..17faf52b0cf9ad97022804ff229d9a736ba2607f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/EmptySubsetEqTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class EmptySubsetEqTest extends ModelCheckerTestCase {
+
+	// This the supplements SubsetEqTest. It checks that TLC does not
+	// incorrectly reduce the expression SUBSET (1..3) \subseteq (1..4). The
+	// empty subset {} is not a subset of (1..4).
+	public EmptySubsetEqTest() {
+		super("EmptySubsetEq", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1", "1", "0"));
+
+		assertTrue(recorder.recordedWithStringValue(EC.GENERAL,
+				"TLC threw an unexpected exception.\nThis was probably caused by an "
+				+ "error in the spec or model.\nSee the User Output or TLC Console "
+				+ "for clues to what happened.\nThe exception was a "
+				+ "java.lang.RuntimeException\n: Attempted to check if the value:\n"
+				+ "{}\nis in the integer interval 1..4"));
+
+		// Expect an error trace consisting of a single state.
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("b = TRUE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertUncovered("line 8, col 9 to line 8, col 48 of module EmptySubsetEq: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/Test063.java b/tlatools/test/tlc2/tool/EmptyTest.java
similarity index 78%
rename from tlatools/test/tlc2/tool/liveness/Test063.java
rename to tlatools/test/tlc2/tool/EmptyTest.java
index f08e0ef08c4358511c84d14e79274f6d1db76c80..2e5f9b0d2e11b9f6016a3d4d952f2a2884ca422e 100644
--- a/tlatools/test/tlc2/tool/liveness/Test063.java
+++ b/tlatools/test/tlc2/tool/EmptyTest.java
@@ -24,21 +24,27 @@
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
 
-package tlc2.tool.liveness;
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
 
-public class Test063 extends ModelCheckerTestCase {
+public class EmptyTest extends ModelCheckerTestCase {
 
-	public Test063() {
-		super("test63");
+	public EmptyTest() {
+		super("Empty", "", new String[] {"-coverage", "1"});
 	}
 
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
-		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "72"));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "696", "216", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
 	}
 }
diff --git a/tlatools/test/tlc2/tool/EvalControlTest.java b/tlatools/test/tlc2/tool/EvalControlTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6eac3c251b203ee7c69a04d9d9c97ca1af8d7e49
--- /dev/null
+++ b/tlatools/test/tlc2/tool/EvalControlTest.java
@@ -0,0 +1,44 @@
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class EvalControlTest {
+
+	@Test
+	public void test() {
+		int control = EvalControl.Clear;
+
+		assertFalse(EvalControl.isEnabled(control));
+		assertFalse(EvalControl.isKeepLazy(control));
+		assertFalse(EvalControl.isPrimed(control));
+
+		control = EvalControl.setEnabled(control);
+		assertTrue(EvalControl.isEnabled(control));
+		assertFalse(EvalControl.isKeepLazy(control));
+		assertFalse(EvalControl.isPrimed(control));
+		
+		control = EvalControl.setKeepLazy(control);
+		assertTrue(EvalControl.isEnabled(control));
+		assertTrue(EvalControl.isKeepLazy(control));
+		assertFalse(EvalControl.isPrimed(control));
+
+		control = EvalControl.setPrimed(control);
+		assertTrue(EvalControl.isEnabled(control));
+		assertTrue(EvalControl.isKeepLazy(control));
+		assertTrue(EvalControl.isPrimed(control));
+	}
+
+	@Test
+	public void testIfEnabled() {
+		int control = EvalControl.Clear;
+		
+		assertFalse(EvalControl.isPrimed(EvalControl.setPrimedIfEnabled(control)));
+		
+		control = EvalControl.setEnabled(control);
+		assertTrue(EvalControl.isEnabled(control));
+		assertTrue(EvalControl.isPrimed(EvalControl.setPrimedIfEnabled(control)));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/EvaluatingValueTest.java b/tlatools/test/tlc2/tool/EvaluatingValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fb42252d63e2ba8ab40582ed2bcae44d0e5bf17
--- /dev/null
+++ b/tlatools/test/tlc2/tool/EvaluatingValueTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import tla2sany.semantic.ExprOrOpArgNode;
+import tlc2.output.EC;
+import tlc2.overrides.Evaluation;
+import tlc2.tool.coverage.CostModel;
+import tlc2.tool.impl.Tool;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import tlc2.util.Context;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.Value;
+import util.UniqueString;
+
+@RunWith(BlockJUnit4ClassRunner.class)
+public class EvaluatingValueTest extends ModelCheckerTestCase {
+
+	public EvaluatingValueTest() {
+		super("EvaluatingValueTest");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		// Two states: x = 1 and x = 42 (see action method below).
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+	}
+
+	@Evaluation(definition = "A", module = "EvaluatingValueTest")
+	public synchronized static Value action(final Tool tool, final ExprOrOpArgNode[] args, final Context c,
+			final TLCState s0, final TLCState s1, final int control, final CostModel cm) {
+
+		// Set value of x variable of successor state to 42. 
+		s1.bind(UniqueString.of("x"), IntValue.gen(42));
+		
+		return BoolValue.ValTrue;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java b/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee6e6474e1580c3de9e0e54a7819757a995ee2be
--- /dev/null
+++ b/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Ian Morris Nieves - initial design and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class FingerprintExceptionInitTest extends ModelCheckerTestCase {
+
+	public FingerprintExceptionInitTest() {
+		super("FingerprintExceptionInit", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished with a fingerprint exception and underlying overflow exception
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		String arg1 = "1) line 7, col 20 to line 7, col 32 of module FingerprintExceptionInit\n"
+			+ "0) line 7, col 13 to line 7, col 33 of module FingerprintExceptionInit\n";
+		String arg2 = "Overflow when computing the number of elements in:\n"
+			+ "SUBSET 1..36";
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_FINGERPRINT_EXCEPTION, arg1, arg2));
+		
+		assertUncovered("line 8, col 39 to line 8, col 64 of module FingerprintExceptionInit: 0\n" + 
+				"line 8, col 71 to line 8, col 76 of module FingerprintExceptionInit: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java b/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e78f039cb40b5e6a25ab05789a3fc8b67b3c4098
--- /dev/null
+++ b/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Ian Morris Nieves - initial design and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class FingerprintExceptionNextTest extends ModelCheckerTestCase {
+
+	public FingerprintExceptionNextTest() {
+		super("FingerprintExceptionNext", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished with a general exception, a fingerprint exception and underlying overflow exception
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recorded(EC.GENERAL));
+		String arg1 = "1) line 8, col 51 to line 8, col 63 of module FingerprintExceptionNext\n"
+			+ "0) line 8, col 44 to line 8, col 64 of module FingerprintExceptionNext\n";
+		String arg2 = "Overflow when computing the number of elements in:\n"
+			+ "SUBSET 1..32";
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_FINGERPRINT_EXCEPTION, arg1, arg2));
+		
+		assertUncovered("line 8, col 71 to line 8, col 76 of module FingerprintExceptionNext: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github179aTest.java b/tlatools/test/tlc2/tool/Github179aTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..659032fa67c77990324af1ff6efcb7f94a7be84a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github179aTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github179aTest extends ModelCheckerTestCase {
+
+	public Github179aTest() {
+		super("Github179a", ExitStatus.VIOLATION_ASSUMPTION);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE,
+				"public static tlc2.value.impl.Value tlc2.module.TLC.PrintT(tlc2.value.impl.Value)",
+				"Attempted to check equality of integer 1 with non-integer:\n" + 
+				"{1}"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github179bTest.java b/tlatools/test/tlc2/tool/Github179bTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..48cff9f0cf00ba0f6ce57d3746efd90770c943a9
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github179bTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github179bTest extends ModelCheckerTestCase {
+
+	public Github179bTest() {
+		super("Github179b", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE,
+				"public static tlc2.value.impl.Value tlc2.module.TLC.Print(tlc2.value.impl.Value,tlc2.value.impl.Value)",
+				"Attempted to check equality of integer 1 with non-integer:\n" + 
+				"{1}"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION,
+				"0. Line 13, column 1 to line 13, column 30 in Github179b\n" + 
+				"1. Line 13, column 1 to line 13, column 23 in Github179b\n" + 
+				"2. Line 9, column 27 to line 9, column 79 in Github179b\n" + 
+				"3. Line 9, column 40 to line 9, column 79 in Github179b\n" + 
+				"4. Line 9, column 43 to line 9, column 52 in Github179b\n" + 
+				"\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github179cTest.java b/tlatools/test/tlc2/tool/Github179cTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..12425d933c63950d600378b58fd7cd61835acae7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github179cTest.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github179cTest extends ModelCheckerTestCase {
+
+	public Github179cTest() {
+		super("Github179c", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE,
+				"public static tlc2.value.impl.Value tlc2.module.TLC.PrintT(tlc2.value.impl.Value)",
+				"Attempted to check equality of integer 1 with non-integer:\n" + 
+				"{1}"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION,
+				"0. Line 16, column 9 to line 16, column 42 in Github179c\n" + 
+				"1. Line 16, column 9 to line 16, column 33 in Github179c\n" + 
+				"2. Line 16, column 9 to line 16, column 25 in Github179c\n" + 
+				"3. Line 10, column 11 to line 12, column 39 in Github179c\n" + 
+				"4. Line 10, column 24 to line 12, column 39 in Github179c\n" + 
+				"5. Line 10, column 27 to line 10, column 36 in Github179c\n" + 
+				"\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github361Test.java b/tlatools/test/tlc2/tool/Github361Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..0124a26dd6b0630dc28d6de35223b51820ea0071
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github361Test.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github361Test extends ModelCheckerTestCase {
+
+	public Github361Test() {
+		super("Github361", ExitStatus.SUCCESS);
+	}
+
+	@Test
+	public void testSpec() {
+		// This implicitly tests SpecProcessor#processConstantDefns(ModuleNode), which
+		// must not fully initialize (fingerprint) values because it is for some
+		// definitions (such as Partitions in Github361.tla) too expensive.  This is
+		// the reason why it runs with multiple threads to make sure optimizations for
+		// single-threaded TLC hide bugs.
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+	}
+
+	@Override
+	protected int getNumberOfThreads() {
+		// See comment above.
+		return 2;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github362Test.java b/tlatools/test/tlc2/tool/Github362Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e9d564ff70c41c98ee531f86305e8c8986d5c16
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github362Test.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.TestPrintStream;
+import util.ToolIO;
+
+public class Github362Test extends ModelCheckerTestCase {
+
+  private TestPrintStream testPrintStream;
+
+  public Github362Test() {
+    super("Github362", ExitStatus.SUCCESS);
+  }
+
+  @Override
+  public void beforeSetUp() {
+    testPrintStream = new TestPrintStream();
+    ToolIO.out = testPrintStream;
+  }
+
+  @Test
+  public void testSpec() {
+    testPrintStream.assertSubstring("<<\"Evaluated initial state in A; overloadedName is: \", \"fizzbuzz\">>");
+    testPrintStream.assertSubstring("<<\"From A's perspective, B's overloadedName is: \", \"x\">>");
+    testPrintStream.assertSubstring("<<\"Evaluating initial state in B; overloadedName is \", \"x\">>");
+
+    testPrintStream.assertSubstring("<<\"Evaluated initial state in A; overloadedConst is: \", 4711>>");
+    testPrintStream.assertSubstring("<<\"From A's perspective, B's overloadedConst is: \", 42>>");
+    testPrintStream.assertSubstring("<<\"Evaluating initial state in B; overloadedConst is \", 42>>");
+    
+    assertTrue(recorder.recorded(EC.TLC_FINISHED));
+    assertTrue(recorder.recorded(EC.TLC_SUCCESS));
+  }
+
+}
diff --git a/tlatools/test/tlc2/tool/Github391Test.java b/tlatools/test/tlc2/tool/Github391Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..d699b05247c25739931d5dbae64ee6ff01d08811
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github391Test.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github391Test extends ModelCheckerTestCase {
+
+	public Github391Test() {
+		super("Github391", ExitStatus.SUCCESS);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github407.dump b/tlatools/test/tlc2/tool/Github407.dump
new file mode 100644
index 0000000000000000000000000000000000000000..c1dd5569b6d74802ca058a19b2fabae520f94548
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github407.dump
@@ -0,0 +1,12 @@
+State 1:
+state = {}
+
+State 2:
+state = {"d1"}
+
+State 3:
+state = {"d2"}
+
+State 4:
+state = {"d1", "d2"}
+
diff --git a/tlatools/test/tlc2/tool/Github407Test.java b/tlatools/test/tlc2/tool/Github407Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..65a842dd80f9fc82ba84733fa6db427d7de07835
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github407Test.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github407Test extends ModelCheckerTestCase {
+
+	public Github407Test() {
+		super("Github407",
+				new String[] { "-dump", System.getProperty("java.io.tmpdir") + File.separator + "Github407" },
+				ExitStatus.SUCCESS);
+	}
+
+	@Test
+	public void testSpec() throws FileNotFoundException, IOException {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+		
+		final File dumpFile = new File(System.getProperty("java.io.tmpdir") + File.separator + "Github407.dump");
+		assertTrue(dumpFile.exists());
+		
+		// If the file exist, simply compare it to a correct and manually checked version.
+		final InputStream master = getClass().getResourceAsStream("Github407.dump");
+		assertTrue(Arrays.equals(DumpAsDotTest.getBytes(master), DumpAsDotTest.getBytes(new FileInputStream(dumpFile))));
+
+		assertZeroUncovered();
+		
+	}
+
+	@Override
+	protected boolean doDump() {
+		// Create the non-dot dump explicitly through constructor above.
+		return false;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github432Test.java b/tlatools/test/tlc2/tool/Github432Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc871cff6da493c86e21a2d258af29ca7a1dd327
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github432Test.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github432Test extends ModelCheckerTestCase {
+	private static final AtomicInteger TEST_COUNTER = new AtomicInteger(0);
+	
+	private static final String CONFIG_FILE = "Github432.cfg";
+	private static final String CONFIG_FILE_BACKUP = "Github432.cfg_bak";
+	
+	private static final String HUMANS_TOKEN = "%1%";
+	private static final String OTHERS_TOKEN = "%2%";
+	
+	
+	private String[] expectedWarnings;
+	
+	public Github432Test() {
+		super("Github432");
+	}
+
+	@Override
+	protected void beforeSetUp() {
+		final int testNumber = TEST_COUNTER.getAndIncrement();
+		final String humans;
+		final String others;
+		
+		switch (testNumber) {
+			case 0:
+				humans = "Alice";
+				others = "Cat, Dog";
+				expectedWarnings = new String[] {"", "Humans", "has", "s"};
+				break;
+			case 1:
+				humans = "";
+				others = "Emu";
+				expectedWarnings = new String[] {"s", "Humans, and Others", "have", ""};
+				break;
+			case 2:
+				humans = "Frank, Glenda";
+				others = "";
+				expectedWarnings = new String[] {"", "Others", "has", "s"};
+				break;
+			default:
+				humans = "Hauser, Ignatio";
+				others = "Jackal, Kangaroo";
+				expectedWarnings = null;
+				break;
+		}
+		
+		try {
+			createConfigFile(humans, others);
+		} catch (final Exception e) {
+			revertConfigFile();
+			
+			Assert.fail(e.getMessage());
+		}
+	}
+	
+	@Override
+	protected void beforeTearDown() {
+		revertConfigFile();
+	}
+	
+	@Override
+	protected void assertExitStatus() {
+		// We don't care - and the spec actually fails for test-b
+	}
+	
+	private void compareToExpectedResults(final String[] array) {
+		Assert.assertNotNull("Reported parameters should not be null.", array);
+		Assert.assertEquals("Expected warnings should be the same cardinality of the reported parameters encountered.",
+							expectedWarnings.length, array.length);
+		for (int i = 0; i < array.length; i++) {
+			Assert.assertEquals(expectedWarnings[i], array[i]);
+		}
+	}
+	
+	@Test
+	public void testA() throws FileNotFoundException, IOException {
+		if (expectedWarnings != null) {
+			compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0));
+		} else {
+			Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL));
+		}
+	}
+	
+	@Test
+	public void testB() throws FileNotFoundException, IOException {
+		if (expectedWarnings != null) {
+			compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0));
+		} else {
+			Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL));
+		}
+	}
+	
+	@Test
+	public void testC() throws FileNotFoundException, IOException {
+		if (expectedWarnings != null) {
+			compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0));
+		} else {
+			Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL));
+		}
+	}
+	
+	@Test
+	public void testD() throws FileNotFoundException, IOException {
+		if (expectedWarnings != null) {
+			compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0));
+		} else {
+			Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL));
+		}
+	}
+	
+	private void createConfigFile(final String humans, final String others) throws IOException {
+		final File configFile = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE);
+		final File backup = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE_BACKUP);
+		
+		if (backup.exists()) {
+			Assert.fail("Github432 test state is incoherent: the backup file already exists at "
+							+ backup.getAbsolutePath());
+		}
+		
+		try {
+			Files.move(configFile.toPath(), backup.toPath(), StandardCopyOption.ATOMIC_MOVE);
+		} catch (final IOException e) {
+			Assert.fail(e.getMessage());
+		}
+		
+		try {
+			try (final BufferedWriter bw = new BufferedWriter(new FileWriter(configFile))) {
+				try (final BufferedReader br = new BufferedReader(new FileReader(backup))) {
+					boolean humansFound = false;
+					boolean othersFound = false;
+					String line;
+					while ((line = br.readLine()) != null) {
+						int index = humansFound ? -1 : line.indexOf(HUMANS_TOKEN);
+						if (index == -1) {
+							index = othersFound ? -1 : line.indexOf(OTHERS_TOKEN);
+
+							if (index == -1) {
+								bw.write(line);
+							} else {
+								final int secondStart = index + OTHERS_TOKEN.length();
+								final String convertedLine = line.substring(0, index) + others
+																	+ line.substring(secondStart);
+								bw.write(convertedLine);
+								othersFound = true;
+							}
+						} else {
+							final int secondStart = index + HUMANS_TOKEN.length();
+							final String convertedLine = line.substring(0, index) + humans
+																+ line.substring(secondStart);
+							bw.write(convertedLine);
+							humansFound = true;
+						}
+						
+						bw.newLine();
+					}
+				}
+			}
+		} catch (final IOException e) {
+			
+			revertConfigFile();
+			
+			Assert.fail(e.getMessage());
+		}
+	}
+	
+	private void revertConfigFile() {
+		final File backup = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE_BACKUP);
+		
+		if (backup.exists()) {
+			final File configFile = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE);
+
+			try {
+				Files.move(backup.toPath(), configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+			} catch (final IOException e) {
+				Assert.fail(e.getMessage());
+			}
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github461Test.java b/tlatools/test/tlc2/tool/Github461Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e05070fb0c52ddc02cc9418d49339f7baffd695
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github461Test.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github461Test extends ModelCheckerTestCase {
+
+	public Github461Test() {
+		super("Github461", EC.ExitStatus.VIOLATION_ASSERT);
+	}
+
+	@Test
+	public void testSpec() throws FileNotFoundException, IOException {
+		// Assert evaluation error.
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_VALUE_ASSERT_FAILED,
+				"\"Failure of assertion at line 8, column 4.\""));
+
+		// Assert an error trace.
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		
+		// Assert the correct trace.
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 1");
+		expectedTrace.add("x = 2");
+		expectedTrace.add("x = 3");
+		expectedTrace.add("x = 4");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		// Assert the underlying error message with stack trace.
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION,
+				"0. Line 9, column 5 to line 10, column 17 in Github461\n" + 
+				"1. Line 9, column 8 to line 9, column 65 in Github461\n" + 
+				"\n"));
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github798ITest.java b/tlatools/test/tlc2/tool/Github798ITest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dad1288fd45581996b2ffb0d18cdf2500080d7d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github798ITest.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github798ITest extends ModelCheckerTestCase {
+
+	public Github798ITest() {
+		super("Github798I", new String[] { "-config", "Github798I.tla" }, EC.ExitStatus.SUCCESS);
+	}
+
+	protected boolean noGenerateSpec() {
+		return true;
+	}
+
+	protected boolean doDumpTrace() {
+		return false;
+	}
+
+	@Test
+	public void testSpec() throws IOException {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/Github798NTest.java b/tlatools/test/tlc2/tool/Github798NTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cb00946cc9329cd4975877b47ecd6bc3ecbd94b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/Github798NTest.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class Github798NTest extends ModelCheckerTestCase {
+
+	public Github798NTest() {
+		super("Github798N", new String[] { "-config", "Github798N.tla" }, EC.ExitStatus.SUCCESS);
+	}
+
+	protected boolean noGenerateSpec() {
+		return true;
+	}
+
+	protected boolean doDumpTrace() {
+		return false;
+	}
+
+	@Test
+	public void testSpec() throws IOException {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/IncompleteNextMultipleActionsTest.java b/tlatools/test/tlc2/tool/IncompleteNextMultipleActionsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7bd8a3107b02283aef55e64d5eb484b751ec282
--- /dev/null
+++ b/tlatools/test/tlc2/tool/IncompleteNextMultipleActionsTest.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class IncompleteNextMultipleActionsTest extends ModelCheckerTestCase {
+
+	public IncompleteNextMultipleActionsTest() {
+		super("IncompleteNextMultipleActions", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = 0\n/\\ y = 0\n/\\ z = 0");
+		expectedTrace.add("/\\ x = 1\n/\\ y = null\n/\\ z = null");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		// Assert TLC indicates unassigned variable
+		assertTrue(recorder.recorded(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT));
+		final List<Object> records = recorder.getRecords(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT);
+		assertEquals("A1", ((String[]) records.get(0))[0]);
+		assertEquals("s are", ((String[]) records.get(0))[1]);
+		assertEquals("y, z", ((String[]) records.get(0))[2]);
+
+		assertUncovered("line 8, col 16 to line 8, col 21 of module IncompleteNextMultipleActions: 0\n"
+				+ "line 8, col 26 to line 8, col 31 of module IncompleteNextMultipleActions: 0\n"
+				+ "line 8, col 7 to line 8, col 11 of module IncompleteNextMultipleActions: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/IncompleteNextTest.java b/tlatools/test/tlc2/tool/IncompleteNextTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc16dcacfd17877197f599dbd63b05b131f447e1
--- /dev/null
+++ b/tlatools/test/tlc2/tool/IncompleteNextTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class IncompleteNextTest extends ModelCheckerTestCase {
+
+	public IncompleteNextTest() {
+		super("IncompleteNext", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = 0\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 1\n/\\ y = null");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		// Assert TLC indicates unassigned variable
+		assertTrue(recorder.recorded(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT));
+		final List<Object> records = recorder.getRecords(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT);
+		assertEquals(" is", ((String[]) records.get(0))[0]);
+		assertEquals("y", ((String[]) records.get(0))[1]);
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/InliningTest.java b/tlatools/test/tlc2/tool/InliningTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e980ade6d7da40cadea6fa526ee9bcb872e2ee6b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/InliningTest.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.consumer.RecordingFile;
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.Tool;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import tlc2.util.ExpectInlined;
+
+@SuppressWarnings("restriction")
+public class InliningTest extends ModelCheckerTestCase {
+
+	/*
+	 * The high-level idea is that we record the JVM's CompilerInlining while the
+	 * JVM executes the test case. Afterwards, we check that a bunch of methods that
+	 * are annotated with a marker have been correctly inlined.  For this to work,
+	 * the test has to run long enough for the JVM to warm up.
+	 * Thanks to https://twitter.com/ErikGahlin/status/1207018011674185728 for showing
+	 * how to use the JFR API.
+	 */
+	
+	private final Recording r = new Recording(); 
+
+	public InliningTest() {
+		super("InlineMC", "CodePlexBug08", ExitStatus.SUCCESS);
+	}
+	
+	@Override
+	protected void beforeSetUp() {
+		r.enable("jdk.CompilerInlining"); 
+		r.start(); 
+	}
+	
+	// testSpec runs after model-checking.
+	@Test
+	public void testSpec() throws IOException {
+		// ModelChecker has finished at this point.
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertZeroUncovered();
+		
+		// stop recording and read the jfr from from disk. Close the recording
+		// afterwards.
+		r.stop();
+		Path p = Paths.get("test.jfr"); // test.jfr is the default file name.
+		r.dump(p); 
+		final List<RecordedEvent> recordedEvents = RecordingFile.readAllEvents(p);
+		r.close();
+
+		// "hot method too big" and not for "callee is too large":
+		// https://www.lmax.com/blog/staff-blogs/2016/03/30/notes-hotspot-compiler-flags/
+		final Set<RecordedObject> notInlined = recordedEvents.stream()
+				.filter(ev -> ev.hasField("message"))
+				.filter(ev -> "hot method too big".equals(ev.getString("message")))
+				.map(ev -> (RecordedObject) ev.getValue("callee"))
+				.filter(ro -> ro.getString("type").startsWith("tlc2/tool/impl/Tool")
+						|| ro.getString("type").startsWith("tlc2/tool/impl/FastTool"))
+				.collect(Collectors.toSet());
+		
+		// Make sure the test ran long enough for compilation to detect methods as hot.
+		assertFalse(notInlined.isEmpty());
+		
+		// For now we only care that methods in Tool get correctly inlined
+		// because its methods are guaranteed to be on the hot path.
+		Method[] dm = Tool.class.getDeclaredMethods();
+		for (int i = 0; i < dm.length; i++) {
+			if (dm[i].getAnnotation(ExpectInlined.class) != null) {
+				notIn(dm[i], notInlined);
+			}
+		}
+		dm = FastTool.class.getDeclaredMethods();
+		for (int i = 0; i < dm.length; i++) {
+			if (dm[i].getAnnotation(ExpectInlined.class) != null) {
+				notIn(dm[i], notInlined);
+			}
+		}
+	}
+
+	// This matching is likely brittle and will fail in ways that everybody will
+	// agree should have been accounted for. When it does, please check if
+	// RecordedObject has finally been changed to RecordedMethod
+	// (https://twitter.com/ErikGahlin/status/1207536016858505217).
+	private void notIn(final Method method, final Set<RecordedObject> notInlined) {
+		final List<RecordedObject> methodNameMatches = notInlined.stream()
+				.filter(ro -> method.getName().equals(ro.getString("name"))).collect(Collectors.toList());
+		for (RecordedObject methodNameMatch : methodNameMatches) {
+			assertTrue(isNoMatch(methodNameMatch, method));
+		}
+	}
+
+	// I warned you that this doesn't work.
+	private boolean isNoMatch(RecordedObject methodNameMatch, Method method) {
+		final String desc = methodNameMatch.getString("descriptor");
+		final String[] params = desc.substring(1, desc.indexOf(")")).split(";");
+		if (method.getParameterCount() == params.length) {
+			Class<?>[] parameters = method.getParameterTypes();
+			for (int j = 0; j < params.length; j++) {
+				final String paramType = parameters[j].toString().replace(".", "/").replaceFirst("^(class|interface) ",
+						"L");
+				if (!params[j].equals(paramType)) {
+					return true;
+				}
+			}
+			System.out.println(methodNameMatch);
+//			return false;
+		}
+		return true;
+	}
+
+	@Override
+	protected boolean doCoverage() {
+		return false;
+	}
+
+	@Override
+	protected boolean doDump() {
+		return false;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/MinimalSetOfInitStatesTest.java b/tlatools/test/tlc2/tool/MinimalSetOfInitStatesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e454c39c3a9ff169540dd3f692d1b525c26baa0c
--- /dev/null
+++ b/tlatools/test/tlc2/tool/MinimalSetOfInitStatesTest.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class MinimalSetOfInitStatesTest extends ModelCheckerTestCase {
+
+	public MinimalSetOfInitStatesTest() {
+		super("MinimalSetOfInitStates");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Without the fix to tlc2.tool.Tool.getInitStates(ActionItemList, TLCState,
+		// IStateFunctor), the number of generated initial states would be 6 with
+		// 4 being distinct. The fix in getInitStates causes TLC to more efficiently
+		// evaluate the init predicate and avoid generating the two duplicates.
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "8", "s", "6"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "6", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/MinimalSetOfNextStatesTest.java b/tlatools/test/tlc2/tool/MinimalSetOfNextStatesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8297a265bd9418fa8c523fae735811dc4f31fd5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/MinimalSetOfNextStatesTest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class MinimalSetOfNextStatesTest extends ModelCheckerTestCase {
+
+	public MinimalSetOfNextStatesTest() {
+		super("MinimalSetOfNextStates");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "57", "7", "0")); // 57 instead of 71.
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+
+		assertUncovered("line 33, col 10 to line 33, col 15 of module MinimalSetOfNextStates: 0\n"
+				+ "line 41, col 10 to line 41, col 15 of module MinimalSetOfNextStates: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/MinimumDiameterTest.java b/tlatools/test/tlc2/tool/MinimumDiameterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1f96240de0bc70c0bba6260f745c58de797ff3f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/MinimumDiameterTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class MinimumDiameterTest extends ModelCheckerTestCase {
+
+	public MinimumDiameterTest() {
+		super("MinimumDiameter");
+	}
+
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+		// Minimum diameter with a single state is 1 (not zero)!
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+
+		assertZeroUncovered();
+	}
+
+	@Override
+	protected boolean doDump() {
+		return false;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/PrintTraceRaceTest.java b/tlatools/test/tlc2/tool/PrintTraceRaceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c602aa8b19f37fea26169bd943aea1185864b67e
--- /dev/null
+++ b/tlatools/test/tlc2/tool/PrintTraceRaceTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.TLAConstants;
+
+public class PrintTraceRaceTest extends ModelCheckerTestCase {
+
+	public PrintTraceRaceTest() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "PrintTraceRace", ExitStatus.FAILURE_SAFETY_EVAL);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "2", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
+
+		int i = 0; // State's position in records
+		Object[] objs = (Object[]) records.get(i++);
+		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
+		assertEquals("S = [q |-> <<>>, i |-> 1]", 
+				   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
+		assertEquals(i, objs[1]);
+		
+		objs = (Object[]) records.get(i++);
+		stateInfo = (TLCStateInfo) objs[0];
+		assertEquals("S = [q |-> <<1>>, i |-> 2]", 
+				   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
+		assertEquals(i, objs[1]);
+		
+		assertEquals(2, objs.length);
+
+		assertUncovered("line 15, col 12 to line 15, col 28 of module PrintTraceRace: 0");
+	}
+	
+	protected int getNumberOfThreads() {
+		// This bug only shows up with multiple threads.
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomElementT4Test.java b/tlatools/test/tlc2/tool/RandomElementT4Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..c45a2181a320b3b3e9d23260d8f7d721f83ba1e8
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomElementT4Test.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import tlc2.value.IValue;
+import tlc2.value.impl.IntValue;
+import util.UniqueString;
+
+public class RandomElementT4Test extends ModelCheckerTestCase {
+
+	public RandomElementT4Test() {
+		super("RandomElement", new String[] {"-seed", Long.toString(15041980L)}, ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
+		assertEquals(11, records.size());
+		
+		int cnt = 0;
+		for (Object r : records) {
+			final Object[] objs = (Object[]) r;
+			final TLCStateInfo info = (TLCStateInfo) objs[0];
+			final Map<UniqueString, IValue> vals = info.state.getVals();
+
+			final IValue y = vals.get(UniqueString.uniqueStringOf("y"));
+			assertEquals(cnt++, ((IntValue) y).val);
+			
+			final IValue x = info.state.getVals().get(UniqueString.uniqueStringOf("x"));
+			assertTrue(1 <= ((IntValue) x).val && ((IntValue) x).val <= 1000);
+			
+			final int statenum = (int) objs[1];
+			assertEquals(cnt, statenum);
+		}
+	}
+	
+	protected int getNumberOfThreads() {
+		// With 4 threads the counter-examples is not predictable anymore because it
+		// depends on thread scheduling.
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomElementTest.java b/tlatools/test/tlc2/tool/RandomElementTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0066f0134a4f9f857f151b2c3aa17ebc44e5a9af
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomElementTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class RandomElementTest extends ModelCheckerTestCase {
+
+	public RandomElementTest() {
+		super("RandomElement", new String[] {"-seed", Long.toString(8006803340504660123L)}, ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "932", "855", "388"));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<String> expectedTrace = new ArrayList<String>(11);
+		expectedTrace.add("/\\ x = 843\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 920\n/\\ y = 1");
+		expectedTrace.add("/\\ x = 483\n/\\ y = 2");
+		expectedTrace.add("/\\ x = 173\n/\\ y = 3");
+		expectedTrace.add("/\\ x = 590\n/\\ y = 4");
+		expectedTrace.add("/\\ x = 104\n/\\ y = 5");
+		expectedTrace.add("/\\ x = 785\n/\\ y = 6");
+		expectedTrace.add("/\\ x = 463\n/\\ y = 7");
+		expectedTrace.add("/\\ x = 443\n/\\ y = 8");
+		expectedTrace.add("/\\ x = 151\n/\\ y = 9");
+		expectedTrace.add("/\\ x = 767\n/\\ y = 10");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomElementXandYTest.java b/tlatools/test/tlc2/tool/RandomElementXandYTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..15effbe5bd21dddc8f3aeb0b38f0739273cbd722
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomElementXandYTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class RandomElementXandYTest extends ModelCheckerTestCase {
+
+	public RandomElementXandYTest() {
+		super("RandomElementXandY", new String[] {"-seed", Long.toString(8006642976694192746L)}, ExitStatus.VIOLATION_SAFETY); 
+		// 8006642976694192746L produces a trace of three states.
+		// 8006642976346685430L and 8006642974998076619L results in no violation of an invariant
+		// 8006642972812024640L a trace with two states
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+		
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+
+		final List<String> expectedTrace = new ArrayList<String>(11);
+		expectedTrace.add("/\\ x = 0\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 1\n/\\ y = 1");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubset.java b/tlatools/test/tlc2/tool/RandomSubset.java
new file mode 100644
index 0000000000000000000000000000000000000000..539455fc48d1fbe64e249111df0d224522a8bd63
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubset.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public abstract class RandomSubset extends ModelCheckerTestCase {
+
+	private final int x;
+	private final int y;
+
+	public RandomSubset(final long seed, final int x, final int y) {
+		// Initial seed with a randomly chosen but fixed value for x and y to be
+		// predictable. The two subclasses chose different values to test that different
+		// seeds result in different values.
+		super("RandomSubset", new String[] {"-seed", Long.toString(seed)}, ExitStatus.VIOLATION_SAFETY);
+		this.x = x;
+		this.y = y;
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2002"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2003", "2003", "2001"));
+		assertEquals(2, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ x = " + x + "\n" + "/\\ y = " + y + "\n" + "/\\ z = TRUE");
+		expectedTrace.add("/\\ x = " + x + "\n" + "/\\ y = " + y + "\n" + "/\\ z = FALSE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+	}
+
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubsetATest.java b/tlatools/test/tlc2/tool/RandomSubsetATest.java
new file mode 100644
index 0000000000000000000000000000000000000000..44e4429427dad340128b7d396f2ea5cadbe72346
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubsetATest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+public class RandomSubsetATest extends RandomSubset {
+
+	public RandomSubsetATest() {
+		super(15041980L, 47567, 100000003);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubsetBTest.java b/tlatools/test/tlc2/tool/RandomSubsetBTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..05edf09ddb478a1615b94b94c538077650934e79
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubsetBTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+public class RandomSubsetBTest extends RandomSubset {
+
+	public RandomSubsetBTest() {
+		super(918347981374L, 66058, 100000003);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubsetNextT4Test.java b/tlatools/test/tlc2/tool/RandomSubsetNextT4Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..04d2708f57481898553119367d864bacbf167efd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubsetNextT4Test.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import tlc2.value.IValue;
+import tlc2.value.impl.IntValue;
+import util.UniqueString;
+
+public class RandomSubsetNextT4Test extends ModelCheckerTestCase {
+
+	public RandomSubsetNextT4Test() {
+		super("RandomSubsetNext", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
+		assertEquals(11, records.size());
+		
+		int cnt = 0;
+		for (Object r : records) {
+			final Object[] objs = (Object[]) r;
+			final TLCStateInfo info = (TLCStateInfo) objs[0];
+			final Map<UniqueString, IValue> vals = info.state.getVals();
+
+			final IValue y = vals.get(UniqueString.uniqueStringOf("y"));
+			assertEquals(cnt++, ((IntValue) y).val);
+			
+			final IValue x = info.state.getVals().get(UniqueString.uniqueStringOf("x"));
+			assertTrue(1 <= ((IntValue) x).val && ((IntValue) x).val <= 1000);
+			
+			final int statenum = (int) objs[1];
+			assertEquals(cnt, statenum);
+		}
+	}
+	
+	protected int getNumberOfThreads() {
+		// With 4 threads the counter-examples is not predictable anymore because it
+		// depends on thread scheduling.
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubsetNextTest.java b/tlatools/test/tlc2/tool/RandomSubsetNextTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..81618cee0ff1b46affe9c6748f18bd8db05f2fe5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubsetNextTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class RandomSubsetNextTest extends ModelCheckerTestCase {
+
+	public RandomSubsetNextTest() {
+		super("RandomSubsetNext", new String[] {"-seed", Long.toString(15041980L)}, ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "67321", "7732", "999"));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<String> expectedTrace = new ArrayList<String>(11);
+		expectedTrace.add("/\\ x = 43\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 2\n/\\ y = 1");
+		expectedTrace.add("/\\ x = 95\n/\\ y = 2");
+		expectedTrace.add("/\\ x = 40\n/\\ y = 3");
+		expectedTrace.add("/\\ x = 6\n/\\ y = 4");
+		expectedTrace.add("/\\ x = 168\n/\\ y = 5");
+		expectedTrace.add("/\\ x = 225\n/\\ y = 6");
+		expectedTrace.add("/\\ x = 93\n/\\ y = 7");
+		expectedTrace.add("/\\ x = 42\n/\\ y = 8");
+		expectedTrace.add("/\\ x = 8\n/\\ y = 9");
+		expectedTrace.add("/\\ x = 30\n/\\ y = 10");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubsetSetOfFcnsTest.java b/tlatools/test/tlc2/tool/RandomSubsetSetOfFcnsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8140b06efc02d0024b5418d3701c5dc92384cfb2
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubsetSetOfFcnsTest.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class RandomSubsetSetOfFcnsTest extends ModelCheckerTestCase {
+
+	public RandomSubsetSetOfFcnsTest() {
+		super("RandomSubsetSetOfFcns");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1000"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2000", "1000", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+//
+//		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+//		
+//		final List<Object> actual = recorder.getRecords(EC.TLC_STATE_PRINT2);
+//		assertEquals(2, actual.size());
+//		
+//		final TLCStateInfo first = (TLCStateInfo) ((Object[]) actual.get(0))[0];
+//		assertTrue(((String) first.info).startsWith("<Initial predicate>"));
+//		final Map<UniqueString, Value> firstState = first.state.getVals();
+//		assertEquals(3, firstState.size());
+//		
+//		// Check x and y values are within defined ranges.
+//		final IntValue firstX = (IntValue) firstState.get(UniqueString.uniqueStringOf("x"));
+//		assertTrue(1 <= firstX.val && firstX.val <= 100000000);
+//		final IntValue firstY = (IntValue) firstState.get(UniqueString.uniqueStringOf("y"));
+//		assertTrue(100000000 <= firstY.val && firstX.val <= 100000010);
+//
+//		// Check z is true
+//		assertEquals(BoolValue.ValTrue, (BoolValue) firstState.get(UniqueString.uniqueStringOf("z")));
+//		
+//		final TLCStateInfo second = (TLCStateInfo) ((Object[]) actual.get(1))[0];
+//		assertTrue(((String) second.info).startsWith("<Next line 10, col 9 to line 11, col 21 of module RandomSubset>"));
+//		final Map<UniqueString, Value> secondState = second.state.getVals();
+//		assertEquals(3, secondState.size());
+//		// UNCHANGED x,y
+//		assertEquals(firstX.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("x"))).val);
+//		assertEquals(firstY.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("y"))).val);
+//		// Check z is false
+//		assertEquals(BoolValue.ValFalse, (BoolValue) secondState.get(UniqueString.uniqueStringOf("z")));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/RandomSubsetTest.java b/tlatools/test/tlc2/tool/RandomSubsetTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb562980cefd954f72780e821a444c5935089220
--- /dev/null
+++ b/tlatools/test/tlc2/tool/RandomSubsetTest.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import tlc2.value.IBoolValue;
+import tlc2.value.IValue;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import util.UniqueString;
+
+public class RandomSubsetTest extends ModelCheckerTestCase {
+
+	public RandomSubsetTest() {
+		super("RandomSubset", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2002"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2003", "2003", "2001"));
+		assertEquals(2, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		
+		final List<Object> actual = recorder.getRecords(EC.TLC_STATE_PRINT2);
+		assertEquals(2, actual.size());
+		
+		final TLCStateInfo first = (TLCStateInfo) ((Object[]) actual.get(0))[0];
+		assertTrue(((String) first.info).startsWith("<Initial predicate>"));
+		final Map<UniqueString, IValue> firstState = first.state.getVals();
+		assertEquals(3, firstState.size());
+		
+		// Check x and y values are within defined ranges.
+		final IntValue firstX = (IntValue) firstState.get(UniqueString.uniqueStringOf("x"));
+		assertTrue(1 <= firstX.val && firstX.val <= 100000000);
+		final IntValue firstY = (IntValue) firstState.get(UniqueString.uniqueStringOf("y"));
+		assertTrue(100000000 <= firstY.val && firstX.val <= 100000010);
+
+		// Check z is true
+		assertEquals(BoolValue.ValTrue, (IBoolValue) firstState.get(UniqueString.uniqueStringOf("z")));
+		
+		final TLCStateInfo second = (TLCStateInfo) ((Object[]) actual.get(1))[0];
+		assertTrue(((String) second.info).startsWith("<Next line 10, col 9 to line 11, col 21 of module RandomSubset>"));
+		final Map<UniqueString, IValue> secondState = second.state.getVals();
+		assertEquals(3, secondState.size());
+		// UNCHANGED x,y
+		assertEquals(firstX.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("x"))).val);
+		assertEquals(firstY.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("y"))).val);
+		// Check z is false
+		assertEquals(BoolValue.ValFalse, (IBoolValue) secondState.get(UniqueString.uniqueStringOf("z")));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/SetOfStatesTest.java b/tlatools/test/tlc2/tool/SetOfStatesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..68171cc4627bc428aafc2fe26647d586423a1876
--- /dev/null
+++ b/tlatools/test/tlc2/tool/SetOfStatesTest.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+import tlc2.tool.queue.DummyTLCState;
+import tlc2.util.SetOfStates;
+
+public class SetOfStatesTest extends TestCase {
+
+	public void testSizeEmpty() {
+		final SetOfStates s = new SetOfStates(16);
+		
+		assertEquals(16, s.capacity());
+		assertEquals(0, s.size());
+	}
+
+	public void testSize() {
+		final SetOfStates s = new SetOfStates(16);
+		s.put(new DummyTLCState(1L));
+
+		assertEquals(16, s.capacity());
+		assertEquals(1, s.size());
+	}
+
+	public void testGrow() {
+		final SetOfStates s = new SetOfStates(1);
+		
+		for(int i = 0; i < 32; i++) {
+			s.put(new DummyTLCState(i));
+		}
+
+		assertTrue(s.capacity() > 32);
+		assertEquals(32, s.size());
+	}
+
+	public void testIterate() {
+		final SetOfStates s = new SetOfStates(1);
+		
+		for(int i = 1; i <= 32; i++) {
+			assertFalse(s.put(new DummyTLCState(i)));
+		}
+		assertEquals(32, s.size());
+
+		// successor is not equal to predecessor
+		TLCState predecessor = null;
+		for (int i = 0; i < s.size(); i++) {
+			TLCState state = s.next();
+			assertNotSame(predecessor, state);
+			predecessor = state;
+		}
+		s.resetNext();
+		
+		// The combined sum of elements is correct
+		long sum = 0L;
+		for (int i = 0; i < s.size(); i++) {
+			final TLCState elem = s.next();
+			sum += elem.fingerPrint();
+		}
+		assertEquals((32 / 2) * (1 + 32), sum);
+	}
+
+	public void testDuplicates() {
+		final SetOfStates s = new SetOfStates(1);
+		
+		for(int i = 1; i <= 32; i++) {
+			assertFalse(s.put(new DummyTLCState(i)));
+		}
+		assertEquals(32, s.size());
+		
+		// Adding the same elements again fails
+		final Set<TLCState> states = new HashSet<TLCState>(s.size());
+		for (int i = 0; i < s.size(); i++) {
+			states.add(s.next());
+		}
+		for (TLCState aState : states) {
+			assertTrue(s.put(aState));
+		}
+		assertEquals(32, states.size());
+	}
+
+	public void testDuplicatesButNotEqual() {
+		// duplicates in terms of fingerprints, but not in terms of equality
+		// (symmetry).
+		final SetOfStates s = new SetOfStates(1);
+		
+		int id = 1;
+		for(int i = 1; i <= 32; i++) {
+			assertFalse(s.put(new EqualityDummyTLCState(i, id++)));
+		}
+		assertEquals(32, s.size());
+		
+		// Add 32 more elements with identical fp but different ids
+		for(int i = 1; i <= 32; i++) {
+			assertFalse(s.put(new EqualityDummyTLCState(i, id++)));
+		}
+		assertEquals(64, s.size());
+
+		// Add 32 more elements with identical fp and ids
+		id = 1;
+		for(int i = 1; i <= 32; i++) {
+			assertTrue(s.put(new EqualityDummyTLCState(i, id++)));
+		}
+		assertEquals(64, s.size());
+	}
+	
+	@SuppressWarnings("serial")
+	private static class EqualityDummyTLCState extends DummyTLCState {
+		
+		private final int id;
+
+		public EqualityDummyTLCState(final int fp, final int id) {
+			super(fp);
+			this.id = id;
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Object#hashCode()
+		 */
+		public int hashCode() {
+			final int prime = 31;
+			int result = 1;
+			result = prime * result + id;
+			result = (int) (prime * result + fingerPrint());
+			return result;
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			EqualityDummyTLCState other = (EqualityDummyTLCState) obj;
+			if (fingerPrint() != other.fingerPrint())
+				return false;
+			if (id != other.id)
+				return false;
+			return true;
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/SetPredValueTest.java b/tlatools/test/tlc2/tool/SetPredValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e38a9fb931b9c848a9d3df6701ac4f634390eb0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/SetPredValueTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class SetPredValueTest extends ModelCheckerTestCase {
+
+	public SetPredValueTest() {
+		super("SetPredValue");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/StandardModulesTest.java b/tlatools/test/tlc2/tool/StandardModulesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3f766e33b51db43f061ac9f5c1c2b68216c098a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/StandardModulesTest.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class StandardModulesTest extends ModelCheckerTestCase {
+
+	public StandardModulesTest() {
+		super("StandardModules");
+	}
+
+	@Test
+	public void testSpec() {
+		// Check that all standard modules parse (StandardModules.tla extends all of them).
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/SubsetEqTest.java b/tlatools/test/tlc2/tool/SubsetEqTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1c81b690e525a9b3fb841209724e42ee505328f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/SubsetEqTest.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class SubsetEqTest extends ModelCheckerTestCase {
+
+	public SubsetEqTest() {
+		super("SubsetEq");
+	}
+
+	@Test
+	public void testSpec() {
+		// This is a performance test. With the original implementation - where
+		// TLC does not reduce (SUBSET A \subseteq SUBSET B) - the test takes
+		// forever to generate the successor state. Reduced to (A \subseteq B),
+		// the test completes within seconds.
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		// No error trace!
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TLCGetLevelTest.java b/tlatools/test/tlc2/tool/TLCGetLevelTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c807d3064a9a9520a8c930920fb26c597c969abf
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TLCGetLevelTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TLCGetLevelTest extends ModelCheckerTestCase {
+
+	public TLCGetLevelTest() {
+		super("TLCGetLevel", ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "4", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = 0\n" 
+						+ "/\\ y = 0");
+		expectedTrace.add("/\\ x = 1\n" 
+						+ "/\\ y = 1");
+		expectedTrace.add("/\\ x = 2\n" 
+				        + "/\\ y = 2");
+		expectedTrace.add("/\\ x = 3\n" 
+						+ "/\\ y = 3");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertStuttering(5);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TLCGetNamedUndefinedTest.java b/tlatools/test/tlc2/tool/TLCGetNamedUndefinedTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd47a068673481c6e170bb4fe1cb4d4a338bc3bb
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TLCGetNamedUndefinedTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TLCGetNamedUndefinedTest extends ModelCheckerTestCase {
+
+	public TLCGetNamedUndefinedTest() {
+		super("TLCGetNamedUndefined", ExitStatus.ERROR_CONFIG_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recorded(EC.TLC_MODULE_TLCGET_UNDEFINED));
+		assertTrue(recorder.recorded(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TLCGetNonDeterminismTest.java b/tlatools/test/tlc2/tool/TLCGetNonDeterminismTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..87ebb1020f26ed16f06c1ecdb559eaa5698d1ee6
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TLCGetNonDeterminismTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TLCGetNonDeterminismTest extends ModelCheckerTestCase {
+
+	public TLCGetNonDeterminismTest() {
+		super("TLCGetNonDeterminism");
+	}
+	
+	@Test
+	@Ignore("No known fix/By design")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = 0\n" 
+						+ "/\\ y = 0");
+		expectedTrace.add("/\\ x = 1\n" 
+						+ "/\\ y = 1");
+		expectedTrace.add("/\\ x = 2\n" 
+				        + "/\\ y = 2");
+		expectedTrace.add("/\\ x = 3\n" 
+						+ "/\\ y = 3");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertStuttering(5);
+	}
+
+	@Override
+	protected int getNumberOfThreads() {
+		// This test passes with a single worker! There is a slim chance it passes with more workers.
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TLCSetInitTest.java b/tlatools/test/tlc2/tool/TLCSetInitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..478ef7a4d004a6dd97da92cf6e6be43db2db6afa
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TLCSetInitTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TLCSetInitTest extends ModelCheckerTestCase {
+
+	public TLCSetInitTest() {
+		super("TLCSetInit");
+	}
+
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TLCSetTest.java b/tlatools/test/tlc2/tool/TLCSetTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..69a2c87e501fc88e5d581593de8d5ca757e29bc5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TLCSetTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TLCSetTest extends ModelCheckerTestCase {
+
+	public TLCSetTest() {
+		super("TLCSet");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertZeroUncovered();
+	}
+
+	@Override
+	protected boolean doDump() {
+		return false;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TLCStates.java b/tlatools/test/tlc2/tool/TLCStates.java
new file mode 100644
index 0000000000000000000000000000000000000000..c05339e98b228baf2dbf441465ca5cc55a05e94a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TLCStates.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import tla2sany.semantic.ASTConstants;
+import tla2sany.semantic.OpDeclNode;
+import tlc2.value.impl.IntValue;
+import util.UniqueString;
+
+public abstract class TLCStates {
+
+	public static TLCState createDummyState() {
+		return createDummyState(1);
+	}
+
+	public static TLCState createDummyState(final int numVars) {
+		UniqueString.setVariableCount(numVars);
+
+		// Create variable declarations (no values yet).
+		final OpDeclNode[] variables = new OpDeclNode[numVars];
+		for (int i = 0; i < numVars; i++) {
+			UniqueString us = UniqueString.uniqueStringOf("v" + Integer.toString(i));
+			us.setLoc(i);
+			variables[i] = new OpDeclNode(us, ASTConstants.VariableDeclKind, 1, 0, null, null, null);
+		}
+
+		// Initialize the empty state (variable declarations are static/final per TLC
+		// run).
+		TLCStateMut.setVariables(variables);
+		final TLCState state = TLCState.Empty.createEmpty();
+		state.uid = 0;
+
+		// Assign values to variables.
+		for (int i = 0; i < numVars; i++) {
+			state.bind(variables[i].getName(), IntValue.gen(i));
+		}
+
+		return state;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TSnapShotTest.java b/tlatools/test/tlc2/tool/TSnapShotTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..55bf3cde2d2ee7a976f926c2bfb0e8f114dd9b9c
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TSnapShotTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.TLAConstants;
+
+public class TSnapShotTest extends ModelCheckerTestCase {
+
+	public TSnapShotTest() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "TSnapShot", ExitStatus.FAILURE_SAFETY_EVAL);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+	}
+	
+	protected int getNumberOfThreads() {
+		// This bug only shows up with multiple threads.
+		return 4;
+	}
+}
diff --git a/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java b/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfb79ba450383a06a7adb9c2d9e67e6f6ca50e1c
--- /dev/null
+++ b/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TraceWithLargeSetOfInitialStatesTest extends ModelCheckerTestCase {
+
+	public TraceWithLargeSetOfInitialStatesTest() {
+		super("TraceWithLargeSetOfInitialStatesTest", new String[] { "-maxSetSize", "10" }, ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("/\\ x = 1\n/\\ y = FALSE");
+		expectedTrace.add("/\\ x = 1\n/\\ y = TRUE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/UserModuleOverrideAnnotationTest.java b/tlatools/test/tlc2/tool/UserModuleOverrideAnnotationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2239121bb4b23fbe306b134677f1a8a26c267e0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/UserModuleOverrideAnnotationTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+@RunWith(BlockJUnit4ClassRunner.class)
+public class UserModuleOverrideAnnotationTest extends ModelCheckerTestCase {
+
+	public UserModuleOverrideAnnotationTest() {
+		super("UserModuleOverrideAnnotation");
+	}
+	
+	@Test
+	public void testSpec() {
+		recorder.recorded(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH);
+		recorder.recorded(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH);
+		recorder.recorded(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH);
+
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/UserModuleOverrideFromJarTest.java b/tlatools/test/tlc2/tool/UserModuleOverrideFromJarTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..48122f345fb66c16c8c9616988169d84c4f3c43f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/UserModuleOverrideFromJarTest.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+@RunWith(BlockJUnit4ClassRunner.class)
+public class UserModuleOverrideFromJarTest extends ModelCheckerTestCase {
+
+	public UserModuleOverrideFromJarTest() {
+		// This test only works when executed from customBuild.xml because it requires an extra classpath setting.
+		super("UserModuleOverrideBase");
+	}
+	
+	@Test
+	public void testSpec() {
+		final List<String[]> mismatches = recorder.getRecordAsStringArray(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH);
+		assertEquals(2, mismatches.size());
+		Collections.sort(mismatches, new Comparator<String[]>() {
+			@Override
+			public int compare(String[] o1, String[] o2) {
+				return o1[0].compareTo(o2[0]);
+			}
+		});
+		
+		// arity mismatch
+		String[] strs = mismatches.get(0);
+		assertEquals("Get2", strs[0]);
+		assertTrue(strs[1].endsWith("UserModuleOverrideFromJar.class")); // This contains a system dependent path.
+		assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverrideFromJar.Get2(tlc2.value.impl.Value)>", strs[2]);
+		
+		// name mismatch (no such operator)
+		strs = mismatches.get(1);
+		assertEquals("Get3", strs[0]);
+		assertTrue(strs[1].endsWith("UserModuleOverrideFromJar.class"));
+		assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverrideFromJar.Get3()>", strs[2]);
+
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/UserModuleOverrideTest.java b/tlatools/test/tlc2/tool/UserModuleOverrideTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..63c672bf1d6d1b575085507c5be85b9dc98b32f7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/UserModuleOverrideTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+@RunWith(BlockJUnit4ClassRunner.class)
+public class UserModuleOverrideTest extends ModelCheckerTestCase {
+
+	public UserModuleOverrideTest() {
+		super("UserModuleOverride");
+	}
+	
+	@Test
+	public void testSpec() {
+		final List<String[]> mismatches = recorder.getRecordAsStringArray(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH);
+		assertEquals(2, mismatches.size());
+		Collections.sort(mismatches, new Comparator<String[]>() {
+			@Override
+			public int compare(String[] o1, String[] o2) {
+				return o1[0].compareTo(o2[0]);
+			}
+		});
+		
+		// arity mismatch
+		String[] strs = mismatches.get(0);
+		assertEquals("Get2", strs[0]);
+		assertTrue(strs[1].endsWith("UserModuleOverride.class")); // This contains a system dependent path.
+		assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverride.Get2(tlc2.value.impl.Value)>", strs[2]);
+		
+		// name mismatch (no such operator)
+		strs = mismatches.get(1);
+		assertEquals("Get3", strs[0]);
+		assertTrue(strs[1].endsWith("UserModuleOverride.class"));
+		assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverride.Get3()>", strs[2]);
+
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/ViewMapTest.java b/tlatools/test/tlc2/tool/ViewMapTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2cc3934953326351cdb319b778e3becacfd5e21
--- /dev/null
+++ b/tlatools/test/tlc2/tool/ViewMapTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class ViewMapTest extends ModelCheckerTestCase {
+
+	public ViewMapTest() {
+		super("ViewMap", new String[] { "-view" }, ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertFalse(recorder.recorded(EC.TLC_BUG));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		
+		final List<String> expectedTrace = new ArrayList<String>(8);
+		expectedTrace.add("/\\ buffer = <<>>\n/\\ waitset = {}");
+		expectedTrace.add("/\\ buffer = <<>>\n/\\ waitset = {c1}");
+		expectedTrace.add("/\\ buffer = <<>>\n/\\ waitset = {c1, c2}");
+		expectedTrace.add("/\\ buffer = <<\"d\">>\n/\\ waitset = {c2}");
+
+		expectedTrace.add("/\\ buffer = <<\"d\">>\n/\\ waitset = {c2, p1}");
+		expectedTrace.add("/\\ buffer = << >>\n/\\ waitset = {p1}");
+		expectedTrace.add("/\\ buffer = << >>\n/\\ waitset = {c1, p1}");
+		expectedTrace.add("/\\ buffer = << >>\n/\\ waitset = {c1, c2, p1}");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertUncovered("line 91, col 60 to line 91, col 73 of module ViewMap: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/checkpoint/CheckpointOnViolationTest.java b/tlatools/test/tlc2/tool/checkpoint/CheckpointOnViolationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0415bab18a7e6c34f45f78e98395ee84a30f49c2
--- /dev/null
+++ b/tlatools/test/tlc2/tool/checkpoint/CheckpointOnViolationTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.checkpoint;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class CheckpointOnViolationTest extends ModelCheckerTestCase {
+
+	public CheckpointOnViolationTest() {
+		super("DieHard", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states. 
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "252", "54", "11"));
+		
+		// Check the violation
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		assertEquals(7, recorder.getRecords(EC.TLC_STATE_PRINT2).size());
+		
+		// Check that a checkpoint has been taken.
+		assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_START));
+		assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_END));
+		
+		assertZeroUncovered();
+	}
+
+	@Override
+	protected int doCheckpoint() {
+		// Request checkpoints but make it highly unlike for the test to ever create a
+		// checkpoint because the checkpoint interval was exceeded. We want to test
+		// TLC's functionality to take a checkpoint when time-bound model-checking is on
+		// and/or a violation has been found.
+		return (Integer.MAX_VALUE / 60000);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/checkpoint/CheckpointWhenTimeBoundTest.java b/tlatools/test/tlc2/tool/checkpoint/CheckpointWhenTimeBoundTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9142f70501d65c41c51a499dfbd259b6b051843
--- /dev/null
+++ b/tlatools/test/tlc2/tool/checkpoint/CheckpointWhenTimeBoundTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.checkpoint;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.TLC;
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class CheckpointWhenTimeBoundTest extends ModelCheckerTestCase {
+
+	public CheckpointWhenTimeBoundTest() {
+		super("InfiniteStateSpace", "checkpoint");
+	}
+
+	@Override
+	public void setUp() {
+		System.setProperty(TLC.class.getName() + ".stopAfter", "5"); // five seconds
+		super.setUp();
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states. 
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT1));
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2));
+		
+		// Check that a checkpoint has been taken.
+		assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_START));
+		assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_END));
+		
+		assertZeroUncovered();
+	}
+
+	@Override
+	protected int doCheckpoint() {
+		// Request checkpoints but make it highly unlike for the test to ever create a
+		// checkpoint because the checkpoint interval was exceeded. We want to test
+		// TLC's functionality to take a checkpoint when time-bound model-checking is on
+		// and/or a violation has been found.
+		return (Integer.MAX_VALUE / 60000);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/coverage/ACoverageTest.java b/tlatools/test/tlc2/tool/coverage/ACoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c09fcec324f1e276e1ea62c2c63c8c9ed072466
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/ACoverageTest.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ACoverageTest extends AbstractCoverageTest {
+
+    public ACoverageTest () {
+        super("A");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "3", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+		assertCoverage("<Init line 7, col 1 to line 7, col 4 of module A>: 1:1\n" + 
+				"  line 7, col 12 to line 7, col 16 of module A: 1\n" + 
+				"  line 8, col 12 to line 8, col 21 of module A: 1\n" + 
+				"  |line 5, col 11 to line 5, col 49 of module A: 1\n" + 
+				"  ||line 5, col 31 to line 5, col 49 of module A: 131072\n" + 
+				"  ||line 5, col 20 to line 5, col 27 of module A: 1\n" + // This used to be a level deeper and value 131072
+				"  |line 8, col 16 to line 8, col 20 of module A: 1\n" + 
+				"<A line 13, col 1 to line 13, col 4 of module A>: 1:3\n" + 
+				"  line 13, col 9 to line 13, col 19 of module A: 3\n" + 
+				"  |line 13, col 14 to line 13, col 19 of module A: 3\n" + 
+				"  ||line 11, col 11 to line 11, col 61 of module A: 3\n" + 
+				"  |||line 11, col 31 to line 11, col 61 of module A: 30\n" + 
+				"  ||||line 11, col 57 to line 11, col 61 of module A: 162\n" + 
+				"  ||||line 11, col 41 to line 11, col 52 of module A: 30\n" + 
+				"  |||line 11, col 24 to line 11, col 27 of module A: 3\n" + 
+				"  ||line 13, col 18 to line 13, col 18 of module A: 3\n" + 
+				"<B line 15, col 1 to line 15, col 4 of module A>: 1:3\n" + 
+				"  line 15, col 9 to line 15, col 19 of module A: 3\n" + 
+				"  |line 15, col 14 to line 15, col 19 of module A: 3\n" + 
+				"  ||line 11, col 11 to line 11, col 61 of module A: 3\n" + 
+				"  |||line 11, col 31 to line 11, col 61 of module A: 9\n" + 
+				"  ||||line 11, col 57 to line 11, col 61 of module A: 15\n" + 
+				"  ||||line 11, col 41 to line 11, col 52 of module A: 9\n" + 
+				"  |||line 11, col 24 to line 11, col 27 of module A: 3\n" + 
+				"  ||line 15, col 18 to line 15, col 18 of module A: 3");
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/AbstractCoverageTest.java b/tlatools/test/tlc2/tool/coverage/AbstractCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d733ef19520f486bef631d553a12ec2d6546732
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/AbstractCoverageTest.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public abstract class AbstractCoverageTest extends ModelCheckerTestCase {
+
+	public AbstractCoverageTest(String spec) {
+        super(spec, "coverage", new String[] {"-coverage", "9999"}); // To not interfere with testing, 9999 to make sure only final coverage is reported.
+	}
+}
diff --git a/tlatools/test/tlc2/tool/coverage/BCoverageTest.java b/tlatools/test/tlc2/tool/coverage/BCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb1386c47d105527daee3494b0911e3716f49556
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/BCoverageTest.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class BCoverageTest extends AbstractCoverageTest {
+
+    public BCoverageTest () {
+        super("B");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+		assertCoverage("<Init line 4, col 1 to line 4, col 4 of module B>: 1:1\n" + 
+				"  line 4, col 9 to line 4, col 17 of module B: 1\n" +
+				"<A line 8, col 1 to line 8, col 1 of module B>: 1:2\n" + 
+				"  line 8, col 6 to line 8, col 19 of module B: 2\n" + 
+				"<B line 10, col 1 to line 10, col 1 of module B>: 0:2\n" + 
+				"  line 10, col 6 to line 10, col 19 of module B: 2"); 
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/CCoverageTest.java b/tlatools/test/tlc2/tool/coverage/CCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5a40b16da90067cca0c51b1666fbbb5e01d9c1a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/CCoverageTest.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CCoverageTest extends AbstractCoverageTest {
+
+    public CCoverageTest () {
+        super("C");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "253", "20", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+		assertCoverage("<Init line 14, col 1 to line 14, col 4 of module C>: 3:3\n" + 
+				"  line 14, col 12 to line 14, col 21 of module C: 1\n" + 
+				"  line 15, col 12 to line 15, col 16 of module C: 3\n" + 
+				"<A line 17, col 1 to line 17, col 1 of module C>: 16:20\n" + 
+				"  line 17, col 9 to line 17, col 17 of module C: 40\n" + 
+				"  |line 17, col 9 to line 17, col 9 of module C: 20\n" + 
+				"  |line 17, col 15 to line 17, col 17 of module C: 20\n" + 
+				"  line 18, col 9 to line 18, col 17 of module C: 40\n" + 
+				"  |line 18, col 9 to line 18, col 9 of module C: 20\n" + 
+				"  |line 18, col 15 to line 18, col 17 of module C: 20\n" + 
+				"  line 19, col 9 to line 19, col 17 of module C: 20\n" + 
+				"  line 20, col 9 to line 20, col 19 of module C: 20\n" + 
+				"<B line 22, col 1 to line 22, col 1 of module C>: 0:20\n" + 
+				"  line 22, col 9 to line 22, col 17 of module C: 40\n" + 
+				"  |line 22, col 9 to line 22, col 9 of module C: 20\n" + 
+				"  |line 22, col 15 to line 22, col 17 of module C: 20\n" + 
+				"  line 12, col 30 to line 12, col 48 of module C: 640\n" + 
+				"  line 12, col 19 to line 12, col 26 of module C: 20\n" + 
+				"  line 23, col 12 to line 23, col 15 of module C: 20\n" + 
+				"  line 24, col 9 to line 24, col 25 of module C: 20\n" + 
+				"<C line 26, col 1 to line 26, col 1 of module C>: 0:0\n" + 
+				"  line 26, col 9 to line 26, col 14 of module C: 20\n" + 
+				"  line 27, col 9 to line 27, col 17 of module C: 0\n" + 
+				"  line 28, col 9 to line 28, col 18 of module C: 0\n" + 
+				"<D line 30, col 1 to line 30, col 1 of module C>: 1:210\n" + 
+				"  line 30, col 6 to line 30, col 16 of module C: 210\n" + 
+				"  |line 30, col 13 to line 30, col 16 of module C: 20\n" + 
+				"  line 30, col 21 to line 30, col 31 of module C: 210\n" + 
+				"<U1 line 32, col 1 to line 32, col 2 of module C>: 0:0\n" + 
+				"  line 32, col 7 to line 32, col 11 of module C: 20\n" + 
+				"  line 32, col 16 to line 32, col 29 of module C: 0\n" + 
+				"<U2 line 34, col 1 to line 34, col 2 of module C>: 0:0\n" + 
+				"  line 34, col 7 to line 34, col 11 of module C: 20\n" + 
+				"  line 34, col 16 to line 34, col 32 of module C: 0\n" + 
+				"<U3 line 36, col 1 to line 36, col 2 of module C>: 0:0\n" + 
+				"  line 36, col 7 to line 36, col 11 of module C: 20\n" + 
+				"  line 36, col 16 to line 36, col 26 of module C: 0\n" + 
+				"  line 36, col 31 to line 36, col 41 of module C: 0\n" + 
+				"<Inv line 48, col 1 to line 48, col 3 of module C>\n" + 
+				"  line 48, col 8 to line 54, col 26 of module C: 21\n" + 
+				"  |line 48, col 11 to line 48, col 19 of module C: 21\n" + 
+				"  |line 49, col 11 to line 49, col 19 of module C: 21\n" + 
+				"  |line 50, col 11 to line 50, col 22 of module C: 21\n" + 
+				"  ||line 46, col 17 to line 46, col 64 of module C: 21\n" + 
+				"  |||line 46, col 42 to line 46, col 64 of module C: 5376\n" + 
+				"  ||||line 46, col 55 to line 46, col 64 of module C: 21504\n" + 
+				"  ||||line 46, col 51 to line 46, col 51 of module C: 5376\n" + 
+				"  |||line 46, col 26 to line 46, col 38 of module C: 21:26880\n" + 
+				"  |line 51, col 23 to line 51, col 26 of module C: 21\n" + 
+				"  |line 52, col 14 to line 54, col 26 of module C: 21\n" + 
+				"  ||line 52, col 17 to line 52, col 27 of module C: 21\n" + 
+				"  ||line 53, col 17 to line 53, col 40 of module C: 21\n" + 
+				"  |||line 53, col 32 to line 53, col 40 of module C: 105\n" + 
+				"  |||line 53, col 26 to line 53, col 29 of module C: 21\n" + 
+				"  ||line 54, col 17 to line 54, col 26 of module C: 21\n" + 
+				"<Inv2 line 56, col 1 to line 56, col 4 of module C>\n" + 
+				"  line 56, col 9 to line 62, col 27 of module C: 21\n" + 
+				"  |line 56, col 12 to line 56, col 20 of module C: 21\n" + 
+				"  |line 57, col 12 to line 57, col 20 of module C: 21\n" + 
+				"  |line 58, col 12 to line 58, col 23 of module C: 21\n" + 
+				"  ||line 46, col 17 to line 46, col 64 of module C: 21\n" + 
+				"  |||line 46, col 42 to line 46, col 64 of module C: 10752\n" + 
+				"  ||||line 46, col 55 to line 46, col 64 of module C: 48384\n" + 
+				"  ||||line 46, col 51 to line 46, col 51 of module C: 10752\n" + 
+				"  |||line 46, col 26 to line 46, col 38 of module C: 21:59136\n" + 
+				"  |line 59, col 24 to line 59, col 27 of module C: 21\n" + 
+				"  |line 60, col 15 to line 62, col 27 of module C: 21\n" + 
+				"  ||line 60, col 15 to line 60, col 28 of module C: 21\n" + 
+				"  ||line 61, col 17 to line 62, col 27 of module C: 21\n" + 
+				"  |||line 61, col 32 to line 62, col 27 of module C: 105\n" + 
+				"  |||line 61, col 26 to line 61, col 29 of module C: 21");
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java b/tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6926887f6f2f03166f1f29e5d83150c4bbc6ac5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class CoverageStatisticsTest extends AbstractCoverageTest {
+
+    public CoverageStatisticsTest () {
+        super("CoverageStatistics");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "98", "19", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertCoverage("<Init line 12, col 1 to line 12, col 4 of module CoverageStatistics>: 3:3\n" + 
+				"  line 12, col 12 to line 12, col 21 of module CoverageStatistics: 1\n" + 
+				"  line 13, col 12 to line 13, col 16 of module CoverageStatistics: 3\n" + 
+				"<A line 15, col 1 to line 15, col 1 of module CoverageStatistics>: 16:19\n" + 
+				"  line 15, col 9 to line 15, col 17 of module CoverageStatistics: 38\n" + 
+				"  |line 15, col 9 to line 15, col 9 of module CoverageStatistics: 19\n" + 
+				"  |line 15, col 15 to line 15, col 17 of module CoverageStatistics: 19\n" + 
+				"  line 16, col 9 to line 16, col 17 of module CoverageStatistics: 38\n" + 
+				"  |line 16, col 9 to line 16, col 9 of module CoverageStatistics: 19\n" + 
+				"  |line 16, col 15 to line 16, col 17 of module CoverageStatistics: 19\n" + 
+				"  line 17, col 9 to line 17, col 17 of module CoverageStatistics: 19\n" + 
+				"  line 18, col 9 to line 18, col 19 of module CoverageStatistics: 19\n" + 
+				"<B line 20, col 1 to line 20, col 1 of module CoverageStatistics>: 0:19\n" + 
+				"  line 20, col 9 to line 20, col 17 of module CoverageStatistics: 38\n" + 
+				"  |line 20, col 9 to line 20, col 9 of module CoverageStatistics: 19\n" + 
+				"  |line 20, col 15 to line 20, col 17 of module CoverageStatistics: 19\n" + 
+				"  line 21, col 9 to line 21, col 25 of module CoverageStatistics: 19\n" + 
+				"<C line 26, col 1 to line 26, col 1 of module CoverageStatistics>: 0:0\n" + 
+				"  line 26, col 9 to line 26, col 14 of module CoverageStatistics: 19\n" + 
+				"  line 27, col 9 to line 27, col 17 of module CoverageStatistics: 0\n" + 
+				"  line 28, col 9 to line 28, col 18 of module CoverageStatistics: 0\n" + 
+				"<U1 line 30, col 1 to line 30, col 2 of module CoverageStatistics>: 0:0\n" + 
+				"  line 30, col 7 to line 30, col 11 of module CoverageStatistics: 19\n" + 
+				"  line 30, col 16 to line 30, col 29 of module CoverageStatistics: 0\n" + 
+				"<U2 line 32, col 1 to line 32, col 2 of module CoverageStatistics>: 0:0\n" + 
+				"  line 32, col 7 to line 32, col 11 of module CoverageStatistics: 19\n" + 
+				"  line 32, col 16 to line 32, col 32 of module CoverageStatistics: 0\n" + 
+				"<U3 line 34, col 1 to line 34, col 2 of module CoverageStatistics>: 0:0\n" + 
+				"  line 34, col 7 to line 34, col 11 of module CoverageStatistics: 19\n" + 
+				"  line 34, col 16 to line 34, col 26 of module CoverageStatistics: 0\n" + 
+				"  line 34, col 31 to line 34, col 41 of module CoverageStatistics: 0\n" + 
+				"<U4 line 36, col 1 to line 36, col 2 of module CoverageStatistics>: 0:0\n" + 
+				"  line 36, col 7 to line 36, col 11 of module CoverageStatistics: 19\n" + 
+				"  line 36, col 16 to line 36, col 26 of module CoverageStatistics: 0\n" + 
+				"  line 36, col 31 to line 36, col 41 of module CoverageStatistics: 0\n" + 
+				"<UC1 line 38, col 1 to line 38, col 3 of module CoverageStatistics>: 0:19\n" + 
+				"  line 38, col 8 to line 38, col 16 of module CoverageStatistics: 38\n" + 
+				"  |line 38, col 8 to line 38, col 8 of module CoverageStatistics: 19\n" + 
+				"  |line 38, col 14 to line 38, col 16 of module CoverageStatistics: 19\n" + 
+				"  line 38, col 21 to line 38, col 37 of module CoverageStatistics: 19\n" + 
+				"<UC2 line 40, col 1 to line 40, col 3 of module CoverageStatistics>: 0:19\n" + 
+				"  line 40, col 8 to line 40, col 16 of module CoverageStatistics: 38\n" + 
+				"  |line 40, col 8 to line 40, col 8 of module CoverageStatistics: 19\n" + 
+				"  |line 40, col 14 to line 40, col 16 of module CoverageStatistics: 19\n" + 
+				"  line 40, col 21 to line 40, col 31 of module CoverageStatistics: 19\n" + 
+				"  line 40, col 36 to line 40, col 46 of module CoverageStatistics: 19\n" + 
+				"<UC3 line 42, col 1 to line 42, col 3 of module CoverageStatistics>: 0:19\n" + 
+				"  line 42, col 8 to line 42, col 16 of module CoverageStatistics: 38\n" + 
+				"  |line 42, col 8 to line 42, col 8 of module CoverageStatistics: 19\n" + 
+				"  |line 42, col 14 to line 42, col 16 of module CoverageStatistics: 19\n" + 
+				"  line 42, col 21 to line 42, col 34 of module CoverageStatistics: 19");
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/DCoverageTest.java b/tlatools/test/tlc2/tool/coverage/DCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..205fde171dfa1e4de2fedf655c8fe90e9b1752eb
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/DCoverageTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class DCoverageTest extends AbstractCoverageTest {
+
+    public DCoverageTest () {
+        super("D");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "3", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+		assertCoverage("<Init line 5, col 1 to line 5, col 4 of module D>: 1:1\n" + 
+				"  line 5, col 9 to line 5, col 13 of module D: 1\n" + 
+				"<A line 11, col 1 to line 11, col 1 of module D>: 1:3\n" + 
+				"  line 11, col 6 to line 11, col 17 of module D: 3\n" + 
+				"  |line 11, col 11 to line 11, col 17 of module D: 3\n" + 
+				"  ||line 9, col 12 to line 9, col 47 of module D: 9\n" + 
+				"  |||line 9, col 15 to line 9, col 19 of module D: 9\n" + 
+				"  |||line 9, col 33 to line 9, col 47 of module D: 6\n" + 
+				"<B line 13, col 1 to line 13, col 1 of module D>: 1:3\n" + 
+				"  line 13, col 6 to line 13, col 17 of module D: 3\n" + 
+				"  |line 13, col 11 to line 13, col 17 of module D: 3\n" + 
+				"  ||line 9, col 12 to line 9, col 47 of module D: 27\n" + 
+				"  |||line 9, col 15 to line 9, col 19 of module D: 27\n" + 
+				"  |||line 9, col 33 to line 9, col 47 of module D: 24");
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/ECoverageTest.java b/tlatools/test/tlc2/tool/coverage/ECoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..944dbaa32237592ee8cd9a49cabfdf9095adeaae
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/ECoverageTest.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ECoverageTest extends AbstractCoverageTest {
+
+    public ECoverageTest () {
+        super("E");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "73", "9", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+		assertCoverage("<Init line 10, col 1 to line 10, col 4 of module E>: 1:1\n" + 
+				"  line 10, col 9 to line 10, col 13 of module E: 1\n" + 
+				"<Next line 12, col 1 to line 12, col 4 of module E>: 8:72\n" + 
+				"  line 12, col 9 to line 12, col 20 of module E: 72\n" + 
+				"  |line 12, col 16 to line 12, col 20 of module E: 9");
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/FCoverageTest.java b/tlatools/test/tlc2/tool/coverage/FCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..25c48c58cd10d58693b7f525d27222fbaaeccd6a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/FCoverageTest.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   loki der quaeler - initial API and implementation
+ *   Markus Alexander Kuppe
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class FCoverageTest extends AbstractCoverageTest {
+
+    public FCoverageTest () {
+        super("F");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "2", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+		assertCoverage("<Init line 8, col 1 to line 8, col 4 of module F>: 2:2\n" + 
+				"  line 8, col 9 to line 8, col 79 of module F: 2\n" + 
+				"  |line 8, col 15 to line 8, col 79 of module F: 1\n" + 
+				"  ||line 6, col 25 to line 6, col 56 of module F: 1:3\n" + 
+				"  |||line 6, col 37 to line 6, col 54 of module F: 5\n" + 
+				"  ||||line 6, col 37 to line 6, col 40 of module F: 5\n" + 
+				"  ||||line 6, col 45 to line 6, col 54 of module F: 4\n" + 
+				"  |||||line 6, col 47 to line 6, col 47 of module F: 4\n" + 
+				"  |||||line 6, col 50 to line 6, col 53 of module F: 2\n" + 
+				"  |||||line 8, col 63 to line 8, col 78 of module F: 4\n" + 
+				"  ||||||line 8, col 63 to line 8, col 73 of module F: 4\n" + 
+				"  ||||||line 8, col 78 to line 8, col 78 of module F: 2\n" + 
+				"  |||line 6, col 33 to line 6, col 33 of module F: 1\n" + 
+				"  ||line 8, col 18 to line 8, col 28 of module F: 1:6\n" + 
+				"  ||line 8, col 41 to line 8, col 45 of module F: 5\n" + 
+				"  ||line 8, col 63 to line 8, col 78 of module F: 4\n" + 
+				"  |||line 8, col 63 to line 8, col 73 of module F: 4\n" + 
+				"  |||line 8, col 78 to line 8, col 78 of module F: 2\n" + 
+				"<Next line 10, col 1 to line 10, col 4 of module F>: 0:2\n" + 
+				"  line 10, col 9 to line 10, col 19 of module F: 2");
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/GCoverageTest.java b/tlatools/test/tlc2/tool/coverage/GCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..340eeb8a17b325aa7df3b6934aedbab06d04fc95
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/GCoverageTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class GCoverageTest extends AbstractCoverageTest {
+
+    public GCoverageTest () {
+        super("G");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertCoverage("<Init line 6, col 1 to line 6, col 4 of module G>: 1:1\n" + 
+				"  line 6, col 9 to line 6, col 20 of module G: 1\n" + 
+				"<Next line 8, col 1 to line 8, col 4 of module G>: 0:1\n" + 
+				"  line 8, col 12 to line 8, col 25 of module G: 1\n" + 
+				"  line 9, col 12 to line 9, col 27 of module G: 2\n" + 
+				"  line 10, col 12 to line 10, col 23 of module G: 2\n" + 
+				"<Prop line 12, col 1 to line 12, col 4 of module G>\n" + 
+				"  line 12, col 9 to line 12, col 20 of module G: 1\n" + 
+				"  |line 8, col 12 to line 8, col 25 of module G: 1\n" + 
+				"  |line 9, col 12 to line 9, col 27 of module G: 1\n" + 
+				"  |line 10, col 12 to line 10, col 23 of module G: 1");
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/Github314CoverageTest.java b/tlatools/test/tlc2/tool/coverage/Github314CoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f8f86f0c87b0e705ae6d82147af14c1d2c7d1d7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/Github314CoverageTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Github314CoverageTest extends AbstractCoverageTest {
+
+    public Github314CoverageTest () {
+        super("Github314");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "3", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/Github377CoverageTest.java b/tlatools/test/tlc2/tool/coverage/Github377CoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d23707671092dd8bc8d71cf08f9c27c65dddc5e9
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/Github377CoverageTest.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Github377CoverageTest extends AbstractCoverageTest {
+
+    public Github377CoverageTest () {
+        super("Github377");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertCoverage("<Action line 5, col 9 to line 5, col 16 of module Github377>: 1:1\n" + 
+				"  line 5, col 9 to line 5, col 16 of module Github377: 1\n" + 
+				"<Action line 5, col 24 to line 5, col 34 of module Github377>: 0:1\n" + 
+				"  line 5, col 24 to line 5, col 34 of module Github377: 1\n" + 
+				"<1Inv line 9, col 1 to line 9, col 4 of module Github377>\n" + 
+				"  line 10, col 6 to line 10, col 69 of module Github377: 1\n" + 
+				"  line 11, col 5 to line 11, col 13 of module Github377: 1\n" + 
+				"<1InvNonRec line 15, col 1 to line 15, col 10 of module Github377>\n" + 
+				"  line 16, col 6 to line 16, col 40 of module Github377: 1\n" + 
+				"  line 17, col 5 to line 17, col 13 of module Github377: 1\n" + 
+				"<2Inv line 21, col 1 to line 21, col 4 of module Github377>\n" + 
+				"  line 25, col 10 to line 25, col 47 of module Github377: 1\n" + 
+				"  line 26, col 7 to line 26, col 11 of module Github377: 1\n" + 
+				"<2aInv line 28, col 1 to line 28, col 5 of module Github377>\n" + 
+				"  line 32, col 10 to line 33, col 21 of module Github377: 1\n" + 
+				"  line 34, col 7 to line 34, col 11 of module Github377: 1\n" + 
+				"<2bInv line 36, col 1 to line 36, col 5 of module Github377>\n" + 
+				"  line 40, col 10 to line 40, col 31 of module Github377: 1\n" + 
+				"  line 41, col 7 to line 41, col 11 of module Github377: 1\n" + 
+				"<3aInv line 44, col 1 to line 44, col 5 of module Github377>\n" + 
+				"  line 48, col 10 to line 50, col 21 of module Github377: 1\n" + 
+				"  line 51, col 7 to line 51, col 11 of module Github377: 1\n" + 
+				"<3bInv line 55, col 1 to line 55, col 5 of module Github377>\n" + 
+				"  line 58, col 11 to line 58, col 42 of module Github377: 1\n" + 
+				"  line 57, col 11 to line 57, col 70 of module Github377: 1\n" + 
+				"  line 60, col 13 to line 60, col 31 of module Github377: 1\n" + 
+				"  line 61, col 7 to line 61, col 11 of module Github377: 1\n" + 
+				"<4Inv line 66, col 1 to line 66, col 4 of module Github377>\n" + 
+				"  line 71, col 11 to line 71, col 18 of module Github377: 1\n" + 
+				"  line 72, col 7 to line 72, col 11 of module Github377: 1");
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/HCoverageTest.java b/tlatools/test/tlc2/tool/coverage/HCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dcb347651e6f5779b06e2003bd0c1f940543739
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/HCoverageTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class HCoverageTest extends AbstractCoverageTest {
+
+    public HCoverageTest () {
+        super("H");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "26", "6", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertCoverage("<Inv line 22, col 1 to line 22, col 3 of module H>: 1:31\n" + 
+				"  line 22, col 11 to line 22, col 16 of module H: 1\n" + 
+				"  line 23, col 11 to line 23, col 15 of module H: 31\n" + 
+				"<A line 11, col 1 to line 11, col 1 of module H>: 1:6\n" + 
+				"  line 11, col 6 to line 11, col 12 of module H: 6\n" + 
+				"<BandC line 13, col 1 to line 13, col 5 of module H (13 13 14 28)>: 2:15\n" + 
+				"  line 13, col 16 to line 13, col 26 of module H: 11\n" + 
+				"  |line 13, col 16 to line 13, col 16 of module H: 6\n" + 
+				"  |line 13, col 22 to line 13, col 26 of module H: 6\n" + 
+				"  line 14, col 16 to line 14, col 28 of module H: 15\n" + 
+				"  |line 14, col 23 to line 14, col 28 of module H: 5\n" + 
+				"<BandC line 13, col 1 to line 13, col 5 of module H (15 13 16 22)>: 1:3\n" + 
+				"  line 15, col 16 to line 15, col 21 of module H: 3\n" + 
+				"  |line 15, col 16 to line 15, col 16 of module H: 6\n" + 
+				"  line 16, col 16 to line 16, col 22 of module H: 3\n" + 
+				"<DandE line 18, col 1 to line 18, col 5 of module H (18 11 18 27)>: 1:1\n" + 
+				"  line 18, col 11 to line 18, col 16 of module H: 7\n" + 
+				"  |line 18, col 11 to line 18, col 11 of module H: 6\n" + 
+				"  line 18, col 21 to line 18, col 27 of module H: 1\n" + 
+				"<DandE line 18, col 1 to line 18, col 5 of module H (18 34 18 53)>: 0:0\n" + 
+				"  line 18, col 34 to line 18, col 40 of module H: 6\n" + 
+				"  line 18, col 45 to line 18, col 53 of module H: 0");
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/ICoverageTest.java b/tlatools/test/tlc2/tool/coverage/ICoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8dcaf43a062a01d78883fda0614012686635518
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/ICoverageTest.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ICoverageTest extends AbstractCoverageTest {
+
+    public ICoverageTest () {
+        super("I");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "20", "5", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertCoverage("<Action line 11, col 9 to line 11, col 25 of module I>: 5:5\n" + 
+				"  line 11, col 9 to line 11, col 25 of module I: 5\n" + 
+				"  |line 11, col 15 to line 11, col 25 of module I: 1:6\n" + 
+				"<Action line 11, col 52 to line 11, col 55 of module I>: 0:15\n" + 
+				"  line 11, col 52 to line 11, col 52 of module I: 15\n" +
+				"  |line 8, col 1 to line 9, col 22 of module I: 15\n" + 
+				"  ||line 9, col 8 to line 9, col 22 of module I: 15\n" + 
+				"  ||line 8, col 9 to line 8, col 15 of module I: 15\n" + 
+				"  line 11, col 54 to line 11, col 54 of module I: 15\n" +
+				"<Inv line 13, col 1 to line 13, col 3 of module I>\n" + 
+				"  line 13, col 8 to line 13, col 34 of module I: 5\n" + 
+				"  |line 13, col 27 to line 13, col 34 of module I: 15\n" + 
+				"  |line 13, col 17 to line 13, col 24 of module I: 5");
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/JCoverageTest.java b/tlatools/test/tlc2/tool/coverage/JCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a1bfde26c4517186850d89adf0f48ebba4d46fe
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/JCoverageTest.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class JCoverageTest extends AbstractCoverageTest {
+
+    public JCoverageTest () {
+        super("J");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "11", "6", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertCoverage("<Init line 5, col 1 to line 5, col 4 of module J>: 5:5\n" + 
+				"  line 5, col 12 to line 5, col 28 of module J: 1\n" + 
+				"  line 6, col 12 to line 6, col 40 of module J: 5\n" + 
+				"<Next line 12, col 1 to line 12, col 4 of module J>: 1:6\n" + 
+				"  line 12, col 12 to line 12, col 22 of module J: 6\n" + 
+				"  |line 12, col 17 to line 12, col 22 of module J: 6\n" + 
+				"  ||line 9, col 2 to line 10, col 19 of module J: 6\n" + 
+				"  |||line 9, col 7 to line 9, col 11 of module J: 6\n" + 
+				"  |||line 9, col 16 to line 9, col 40 of module J: 2\n" + 
+				"  |||line 10, col 7 to line 10, col 19 of module J: 4\n" + 
+				"  ||line 12, col 21 to line 12, col 21 of module J: 6\n" + 
+				"  line 13, col 12 to line 13, col 22 of module J: 6");
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/KCoverageTest.java b/tlatools/test/tlc2/tool/coverage/KCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a10a275bbfb7ff32abb2e2c2d2fbe07485c6539
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/KCoverageTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class KCoverageTest extends AbstractCoverageTest {
+
+    public KCoverageTest () {
+        super("K");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/LCoverageTest.java b/tlatools/test/tlc2/tool/coverage/LCoverageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..16cb8569cb2aad46741f67b155a8debd747d60d2
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/LCoverageTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class LCoverageTest extends AbstractCoverageTest {
+
+    public LCoverageTest () {
+        super("L");
+    }
+
+    @Test
+    public void testSpec () {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+		// No 'general' errors recorded
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH));
+    }
+}
diff --git a/tlatools/test/tlc2/tool/coverage/OpApplNodeWrapperTest.java b/tlatools/test/tlc2/tool/coverage/OpApplNodeWrapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..29b6f3ff8fe6b785bb704c4b64e341a3ca27b956
--- /dev/null
+++ b/tlatools/test/tlc2/tool/coverage/OpApplNodeWrapperTest.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.coverage;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import tla2sany.parser.SyntaxTreeNode;
+import tla2sany.semantic.AbortException;
+import tla2sany.semantic.ModuleNode;
+import tla2sany.semantic.OpApplNode;
+import tla2sany.semantic.SymbolNode;
+import tla2sany.xml.SymbolContext;
+import tlc2.TLCGlobals;
+import util.TestPrintStream;
+import util.ToolIO;
+import util.UniqueString;
+
+public class OpApplNodeWrapperTest {
+
+	@Before
+	public void setup() {
+		TLCGlobals.coverageInterval = 1;
+		ToolIO.out = new TestPrintStream();
+	}
+	
+	@Test
+	public void testReportCoverage01() {
+		final OpApplNodeWrapper root = new OpApplNodeWrapper();
+		root.report();
+		((TestPrintStream) ToolIO.out).assertEmpty();
+		
+		root.addChild(new OpApplNodeWrapper());
+	}
+	
+	@Test
+	public void testReportCoverage02() {
+		final OpApplNodeWrapper root = new OpApplNodeWrapper();
+		root.incInvocations(42);
+		
+		root.addChild(getNode(23));
+		root.addChild(getNode(24));
+		root.addChild(getNode(0)); // Not reported
+
+		root.report();
+		((TestPrintStream) ToolIO.out)
+				.assertContains("  Unknown location: 42\n" + 
+						"  |In module --TLA+ BUILTINS--: 23\n" + 
+						"  |In module --TLA+ BUILTINS--: 24");
+	}
+	
+	@Test
+	public void testReportCoverage03() {
+		final OpApplNodeWrapper root = new OpApplNodeWrapper();
+		root.incInvocations(42);
+		
+		OpApplNodeWrapper childA = getNode(23);
+		childA.addChild(getNode(546));
+		root.addChild(childA);
+		
+		OpApplNodeWrapper childB = getNode(24);
+		root.addChild(childB);
+		childB.addChild(getNode(0)); // Not reported because 0
+		
+		OpApplNodeWrapper childC = getNode(0);
+		root.addChild(childC); // Not reported
+
+		childC.addChild(getNode(17)); // Must be reported despite C being 0
+		
+		root.report();
+		((TestPrintStream) ToolIO.out)
+				.assertContains("  Unknown location: 42\n" + 
+						"  |In module --TLA+ BUILTINS--: 23\n" + 
+						"  ||In module --TLA+ BUILTINS--: 546\n" + 
+						"  |In module --TLA+ BUILTINS--: 24\n" + 
+						"  |In module --TLA+ BUILTINS--: 17");
+	}
+	
+	/*
+  line 8, col 12 to line 8, col 21 of module A: 1
+  |line 5, col 11 to line 5, col 49 of module A: 1
+  ||line 5, col 31 to line 5, col 49 of module A: 131072
+  ||line 5, col 20 to line 5, col 27 of module A: 131072
+  |||line 5, col 27 to line 5, col 27 of module A: 1
+  |line 8, col 16 to line 8, col 20 of module A: 1
+	 */
+	@Test
+	public void testReportCoverage04() {
+		final OpApplNodeWrapper root = new OpApplNodeWrapper();
+		root.incInvocations(1);
+		
+		OpApplNodeWrapper childA = getNode(1);
+		root.addChild(childA);
+		
+		childA.addChild(getNode(131072));
+		
+		OpApplNodeWrapper cChildA = getNode(131072);
+		childA.addChild(cChildA);
+		
+		cChildA.addChild(getNode(1));
+		
+		OpApplNodeWrapper childB = getNode(1);
+		root.addChild(childB);
+		
+		root.report();
+		((TestPrintStream) ToolIO.out)
+				.assertContains("  Unknown location: 1\n" + 
+						"  |In module --TLA+ BUILTINS--: 1\n" + 
+						"  ||In module --TLA+ BUILTINS--: 131072\n" + 
+						"  ||In module --TLA+ BUILTINS--: 131072\n" + 
+						"  |||In module --TLA+ BUILTINS--: 1\n" + 
+						"  |In module --TLA+ BUILTINS--: 1");
+	}
+	
+	// It is dummies all the way down...
+	
+	private OpApplNodeWrapper getNode(long count) {
+		final SymbolNode sn = new DummySymbolNode(Long.toString(count));
+		final OpApplNode node = new DummyOpApplNode(sn);
+		return new OpApplNodeWrapper(node, count);
+	}
+	
+	private static class DummySymbolNode extends SymbolNode {
+
+		protected DummySymbolNode(String name) {
+			super(1, SyntaxTreeNode.nullSTN, UniqueString.uniqueStringOf(name));
+		}
+
+		@Override
+		public int getArity() {
+			throw new UnsupportedOperationException("not implemented");
+		}
+
+		@Override
+		public boolean isLocal() {
+			throw new UnsupportedOperationException("not implemented");
+		}
+
+		@Override
+		public boolean match(OpApplNode test, ModuleNode mn) throws AbortException {
+			throw new UnsupportedOperationException("not implemented");
+		}
+
+		@Override
+		protected Element getSymbolElement(Document doc, SymbolContext context) {
+			throw new UnsupportedOperationException("not implemented");
+		}
+
+		@Override
+		protected String getNodeRef() {
+			throw new UnsupportedOperationException("not implemented");
+		}
+		
+	}
+	
+	private static class DummyOpApplNode extends OpApplNode {
+
+		public DummyOpApplNode(SymbolNode sn) {
+			super(sn);
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/DieHardDistributedTLCTest.java b/tlatools/test/tlc2/tool/distributed/DieHardDistributedTLCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..19685ccf7866ffb68f52ae3476c8abadbcf9a20a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/DieHardDistributedTLCTest.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class DieHardDistributedTLCTest extends DistributedTLCTestCase {
+
+	public DieHardDistributedTLCTest() {
+		super("DieHard", BASE_PATH, new String[] {"-deadlock"});
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(7);
+		expectedTrace.add("/\\ action = \"nondet\"\n/\\ smallBucket = 0\n/\\ bigBucket = 0\n/\\ water_to_pour = 0");
+		expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 0\n/\\ bigBucket = 5\n/\\ water_to_pour = 0");
+		expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 2\n/\\ water_to_pour = 3");
+		expectedTrace.add("/\\ action = \"empty small\"\n/\\ smallBucket = 0\n/\\ bigBucket = 2\n/\\ water_to_pour = 3");
+		expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 2\n/\\ bigBucket = 0\n/\\ water_to_pour = 2");
+		
+		expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 2\n/\\ bigBucket = 5\n/\\ water_to_pour = 2");
+		
+		expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 4\n/\\ water_to_pour = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0a80639754da196e541995d412bcd1a4a85d0c0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class DistributedDoInitFunctorEvalExceptionTest extends TLCServerTestCase {
+
+	public DistributedDoInitFunctorEvalExceptionTest() {
+		super("DoInitFunctorEvalException", "DoInitFunctor");
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recorded(EC.TLC_STATS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INITIAL_STATE,
+				"TLC expected a boolean value, but did not find one. line 15, col 15 to line 15, col 18 of module DoInitFunctorEvalException",
+				"x = 1\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantContinueTest.java b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantContinueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2af922ee3c6544c9c695b1356e937a0d2322d07
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantContinueTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DistributedDoInitFunctorInvariantContinueTest extends ModelCheckerTestCase {
+	
+	public DistributedDoInitFunctorInvariantContinueTest() {
+		super("DoInitFunctorInvariant", "DoInitFunctor", new String[] {"-continue"});
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "21", "11"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "NotNine", "x = 9\n"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantTest.java b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..da25cffc787b1917441045450700c4432b7c1bae
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DistributedDoInitFunctorInvariantTest extends ModelCheckerTestCase {
+	
+	public DistributedDoInitFunctorInvariantTest() {
+		super("DoInitFunctorInvariant", "DoInitFunctor", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_STATS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "NotNine", "x = 9\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java b/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..6155eecdcd5f95e363c2e6c427638ac9d7c97885
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.distributed;
+
+import java.security.Permission;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+
+import tlc2.TestMPRecorder;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.CommonTestCase;
+import tlc2.tool.distributed.fp.DistributedFPSet;
+
+public abstract class DistributedTLCTestCase extends CommonTestCase {
+	
+	protected final String[] arguments;
+	protected final int fpSets;
+	
+	private SecurityManager securityManager;
+	
+	public DistributedTLCTestCase(String spec, String path) {
+		this(spec, path, new String[] {});
+	}
+	
+	public DistributedTLCTestCase(String spec, String path, String[] args) {
+		this(spec, path, args, 0);
+	}
+	
+	public DistributedTLCTestCase(String spec, String path, String[] args, int fpSets) {
+		super(new FilteringTestMPRecorder());
+		this.arguments = new String[args.length + 1];
+		this.arguments[this.arguments.length - 1] = path + spec; // Add path to additional arguments
+		System.arraycopy(args, 0, arguments, 0, args.length);
+		
+		this.fpSets = fpSets;
+	}
+    
+	@Before
+	public void setUp() {
+		Assume.assumeTrue("DistributedTLCTestCase broken with OffHeapDiskFPSet.", false);
+		// Remember the original security manager and have it replaced with this
+		// custom one. Contrary to the original, this security manager
+		// intercepts calls to System.exit(int) and throws an exception instead
+		// of terminating the VM. This is done to prevent TLCWorker/TLCServer/...
+		// from terminating the JUnit test itself. To allow the JUnit test to
+		// later terminate the VM after test completion, we have to reinstate
+		// the original security manager again (see tearDown).
+		securityManager = System.getSecurityManager();
+		System.setSecurityManager(new NoExitSecurityManager());
+
+		MP.setRecorder(recorder);
+		
+		// Wait for all processes to terminate before the setup itself is done
+		final CountDownLatch latch = new CountDownLatch(fpSets + 2);
+		
+		// Workers
+		new Thread(new Runnable() {
+			public void run() {
+				try {
+					TLCWorker.main(new String[] { "localhost" });
+				} catch (Exception e) {
+					e.printStackTrace();
+				} finally {
+					latch.countDown();
+				}
+			}
+		}, "Worker").start();
+
+		// master
+		new Thread(new Runnable() {
+			public void run() {
+				try {
+					System.setProperty(TLCServer.class.getName() + ".expectedFPSetCount", Integer.toString(fpSets));
+					TLCServer.main(arguments);
+				} catch (Exception e) {
+					e.printStackTrace();
+				} finally {
+					latch.countDown();
+				}
+			}
+		}, "Master").start();
+
+		// FPSet
+		if (fpSets > 0) {
+			new Thread(new Runnable() {
+				public void run() {
+					try {
+						DistributedFPSet.main(new String[] { "localhost" });
+					} catch (Exception e) {
+						e.printStackTrace();
+					} finally {
+						latch.countDown();
+					}
+				}
+			}, "FPSet").start();
+		}
+
+		try {
+			latch.await();
+		} catch (InterruptedException e) {
+			Assert.fail();
+		}
+	}
+	
+	@After
+	public void tearDown() {
+		System.setSecurityManager(securityManager);
+	}
+	
+	private static class NoExitSecurityManager extends SecurityManager {
+		/* (non-Javadoc)
+		 * @see java.lang.SecurityManager#checkPermission(java.security.Permission)
+		 */
+		public void checkPermission(Permission perm) {
+			// allow anything.
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.SecurityManager#checkPermission(java.security.Permission, java.lang.Object)
+		 */
+		public void checkPermission(Permission perm, Object context) {
+			// allow anything.
+		}
+
+		/* (non-Javadoc)
+		 * @see java.lang.SecurityManager#checkExit(int)
+		 */
+		public void checkExit(int status) {
+			super.checkExit(status);
+			throw new NoExitException();
+		}
+	}
+	
+	@SuppressWarnings("serial")
+	public static class NoExitException extends RuntimeException {
+		// Want an easily distinguishable exception.
+	}
+	
+	private static class FilteringTestMPRecorder extends TestMPRecorder {
+		/* (non-Javadoc)
+		 * @see tlc2.TestMPRecorder#record(int, java.lang.Object[])
+		 */
+		public void record(int code, Object... objects) {
+			if (EC.GENERAL == code && objects instanceof String[]) {
+				// GENERAL errors contain the exceptions thrown because of the
+				// intercepted System.exit(int) calls. Remove them so that 
+				// tests can check for real EC.GENERAL errors.
+				final String msg = ((String[]) objects)[0];
+				if (msg.contains(NoExitException.class.getName())) {
+					return;
+				}
+			}
+			super.record(code, objects);
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/EWD840DistributedTLCTest.java b/tlatools/test/tlc2/tool/distributed/EWD840DistributedTLCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..881482d5eb733cb3d5a18ad54c6e8738b51d0b67
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/EWD840DistributedTLCTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class EWD840DistributedTLCTest extends DistributedTLCTestCase {
+
+	public EWD840DistributedTLCTest() {
+		super("MC06", BASE_PATH + "EWD840" + File.separator, new String[] {"-deadlock"});
+	}
+
+	@Test
+	public void test() {
+		// Can we do any assertions here?
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		// Number of generated states differs because of distributed TLC
+		assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "114942", 1));
+		assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "0", 2));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/EWD840DistributedWithFPSetTLCTest.java b/tlatools/test/tlc2/tool/distributed/EWD840DistributedWithFPSetTLCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0590e8c7e897ded458cc19e7089d13b56a136a1a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/EWD840DistributedWithFPSetTLCTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class EWD840DistributedWithFPSetTLCTest extends DistributedTLCTestCase {
+
+	public EWD840DistributedWithFPSetTLCTest() {
+		super("MC06", BASE_PATH + "EWD840" + File.separator, new String[] {"-deadlock"}, 1);
+	}
+
+	@Test
+	public void test() {
+		// Can we do any assertions here?
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		// Number of generated states differs because of distributed TLC
+		assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "114942", 1));
+		assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "0", 2));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/TLCServerTestCase.java b/tlatools/test/tlc2/tool/distributed/TLCServerTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..c714c36cdd0f5d6bb817ec607dd1d2d791cf7f75
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/TLCServerTestCase.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.RemoteException;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import tlc2.TLCGlobals;
+import tlc2.output.MP;
+import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.fp.MSBDiskFPSet;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.ToolIO;
+
+@RunWith(BlockJUnit4ClassRunner.class)
+public abstract class TLCServerTestCase extends ModelCheckerTestCase {
+
+	public TLCServerTestCase(String spec) {
+		super(spec);
+	}
+
+	public TLCServerTestCase(String spec, String path) {
+		super(spec, path);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#setUp()
+	 */
+	@Before
+	public void setUp() {
+		try {
+			MP.setRecorder(recorder);
+			
+			// Never create checkpoints. They distort performance tests and are
+			// of no use anyway.
+			TLCGlobals.chkptDuration = 0;
+			
+			final String fqSpec = BASE_DIR + TEST_MODEL + path + File.separator + spec;
+			final FPSetConfiguration fpSetConfig = new DummyFPSetConfig();
+			ToolIO.setUserDir(BASE_DIR + File.separator + TEST_MODEL + path + File.separator);
+			final TLCApp app = new TLCApp(fqSpec, spec, false, null, fpSetConfig);
+			final TLCServer server = new TLCServer(app);
+			server.modelCheck();
+			//TODO Implement exit status for distributed TLC
+			actualExitStatus = 0;
+		} catch (Exception e) {
+			fail(e.getMessage());
+		}
+	}
+	
+	@SuppressWarnings("serial")
+	private class DummyFPSetConfig extends FPSetConfiguration {
+
+		/* (non-Javadoc)
+		 * @see tlc2.tool.fp.FPSetConfiguration#getImplementation()
+		 */
+		public String getImplementation() {
+			return DummyFPSet.class.getName();
+		}
+	}
+	
+	@SuppressWarnings("serial")
+	protected static class DummyFPSet extends MSBDiskFPSet {
+
+		public DummyFPSet(FPSetConfiguration fpSetConfig) throws RemoteException {
+			super(fpSetConfig);
+		}
+
+		/* (non-Javadoc)
+		 * @see tlc2.tool.fp.DiskFPSet#exit(boolean)
+		 */
+		public void exit(boolean cleanup) throws IOException {
+			//ignore because superclass calls System.exit(0) but we want to check our assertions first.
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/Test057.java b/tlatools/test/tlc2/tool/distributed/TLCSetTest.java
similarity index 78%
rename from tlatools/test/tlc2/tool/liveness/Test057.java
rename to tlatools/test/tlc2/tool/distributed/TLCSetTest.java
index bb269600687a95fd40cb06bdfaf67822d987c69e..1ad6bbdc1d1d862b9db915dae18882ee27da3426 100644
--- a/tlatools/test/tlc2/tool/liveness/Test057.java
+++ b/tlatools/test/tlc2/tool/distributed/TLCSetTest.java
@@ -24,21 +24,28 @@
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
 
-package tlc2.tool.liveness;
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import tlc2.output.EC;
 
-public class Test057 extends ModelCheckerTestCase {
+/**
+ * Tests support for TLCSet/TLCGet in TLCServer
+ */
+public class TLCSetTest extends TLCServerTestCase {
 
-	public Test057() {
-		super("test57");
+	public TLCSetTest() {
+		super("TLCSet");
 	}
-
+	
+	@Test
 	public void testSpec() {
-		// ModelChecker has finished and generated the expected amount of states
-		assertTrue(recorder.recorded(EC.TLC_MODE_MC));
-		assertTrue(recorder.recorded(EC.TLC_FINISHED));
 		assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Invariant1", "x = 1"));
+		assertTrue(recorder.recorded(EC.TLC_FEATURE_UNSUPPORTED));
+		assertFalse(recorder.recorded(EC.GENERAL));
 	}
 }
diff --git a/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java b/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java
index a464be2ef48efe8dd477fce5220d6066d7d19ac7..6c0e2564ebb40799bd77b9702841a73ced8738bd 100644
--- a/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java
+++ b/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java
@@ -1,59 +1,72 @@
 package tlc2.tool.distributed;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import java.rmi.RemoteException;
 
+import org.junit.Test;
+
 import tlc2.tool.TLCState;
 import tlc2.tool.WorkerException;
 import tlc2.tool.distributed.selector.DummyTLCWorker;
 import tlc2.tool.queue.DummyTLCState;
-import junit.framework.TestCase;
 
-public class TLCWorkerSmartProxyTest extends TestCase {
+public class TLCWorkerSmartProxyTest {
 
 	private static final int ZERO = 0;
 	private static final int ONE = 1;
 	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE * (1/10);
 	
+	@Test
 	public void testGetNetworkOverheadMaxStateOne() throws RemoteException, WorkerException {
 		long calculationDuration = Long.MAX_VALUE;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[ONE]) > 0);
 	}
 	
+	@Test
 	public void testGetNetworkOverheadMinStateOne() throws RemoteException, WorkerException {
 		long calculationDuration = Long.MIN_VALUE;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[ONE]) > 0);
 	}
 	
+	@Test
 	public void testGetNetworkOverheadZeroStateOne() throws RemoteException, WorkerException {
 		long calculationDuration = ZERO;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[ONE]) > 0);
 	}
 
+	@Test
 	public void testGetNetworkOverheadMaxStateZero() throws RemoteException, WorkerException {
 		long calculationDuration = Long.MAX_VALUE;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[ZERO]) > 0);
 	}
 
+	@Test
 	public void testGetNetworkOverheadMinStateZero() throws RemoteException, WorkerException {
 		long calculationDuration = Long.MIN_VALUE;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[ZERO]) > 0);
 	}
 
+	@Test
 	public void testGetNetworkOverheadZeroStateZero() throws RemoteException, WorkerException {
 		long calculationDuration = ZERO;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[ZERO]) > 0);
 	}
 
+	@Test
 	public void testGetNetworkOverheadMinStateMax() throws RemoteException, WorkerException {
 		long calculationDuration = Long.MIN_VALUE;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[MAX_ARRAY_SIZE]) > 0);
 	}
 
+	@Test
 	public void testGetNetworkOverheadMaxStateMa() throws RemoteException, WorkerException {
 		long calculationDuration = Long.MAX_VALUE;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[MAX_ARRAY_SIZE]) > 0);
 	}
 
+	@Test
 	public void testGetNetworkOverheadZeroStateMax() throws RemoteException, WorkerException {
 		long calculationDuration = ZERO;
 		assertTrue(doTest(calculationDuration, new DummyTLCState[MAX_ARRAY_SIZE]) > 0);
diff --git a/tlatools/test/tlc2/tool/distributed/TSnapShotDistributedTLCTest.java b/tlatools/test/tlc2/tool/distributed/TSnapShotDistributedTLCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ad162cbfb621885616611fe2f3e5c1c87425ac6
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/TSnapShotDistributedTLCTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.distributed;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import util.TLAConstants;
+
+public class TSnapShotDistributedTLCTest extends DistributedTLCTestCase {
+
+	public TSnapShotDistributedTLCTest() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, BASE_PATH + "TSnapShot" + File.separator, new String[] {"-deadlock"});
+	}
+
+	@Test
+	public void test() {
+		// Can we do any assertions here?
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "0", 2));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java b/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java
index 3beb8e2a8eaa67b395b7694ecc38f0b8f0e521ba..860ea3cef22b040a6fe29e34dd955e7c7c5d5908 100644
--- a/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java
+++ b/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java
@@ -2,6 +2,11 @@
 
 package tlc2.tool.distributed.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.rmi.RemoteException;
 import java.util.HashMap;
@@ -10,17 +15,19 @@ import java.util.Map.Entry;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+
 import tlc2.tool.fp.FPSet;
 import tlc2.tool.fp.MemFPSet;
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
 
-public class DynamicFPSetManagerTest extends TestCase {
+public class DynamicFPSetManagerTest {
 
 	/**
 	 * Test that the ctor rejects invalid values.
 	 */
+	@Test
 	public void testCtorInvalidZero() throws RemoteException {
 		try {
 			new DynamicFPSetManager(0);
@@ -33,6 +40,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Test that the ctor rejects invalid values.
 	 */
+	@Test
 	public void testCtorInvalidMin1() throws RemoteException {
 		try {
 			new DynamicFPSetManager(-1);
@@ -46,6 +54,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the ctor correctly calculates its mask used to index fpset
 	 * servers for valid values.
 	 */
+	@Test
 	public void testCtor1() throws RemoteException {
 		DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(1);
 		long mask = dynamicFPSetManager.getMask();
@@ -56,6 +65,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the ctor correctly calculates its mask used to index fpset
 	 * servers for valid values.
 	 */
+	@Test
 	public void testCtor10() throws RemoteException {
 		DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(10);
 		long mask = dynamicFPSetManager.getMask();
@@ -66,6 +76,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the ctor correctly calculates its mask used to index fpset
 	 * servers for valid values.
 	 */
+	@Test
 	public void testCtor31() throws RemoteException {
 		DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(31);
 		long mask = dynamicFPSetManager.getMask();
@@ -76,6 +87,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the ctor correctly calculates its mask used to index fpset
 	 * servers for valid values.
 	 */
+	@Test
 	public void testCtor32() throws RemoteException {
 		DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(32);
 		long mask = dynamicFPSetManager.getMask();
@@ -86,6 +98,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the ctor correctly calculates its mask used to index fpset
 	 * servers for valid values.
 	 */
+	@Test
 	public void testCtor33() throws RemoteException {
 		DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(33);
 		long mask = dynamicFPSetManager.getMask();
@@ -96,6 +109,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the ctor correctly calculates its mask used to index fpset
 	 * servers for valid values.
 	 */
+	@Test
 	public void testCtorMax() throws RemoteException {
 		DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(Integer.MAX_VALUE);
 		long mask = dynamicFPSetManager.getMask();
@@ -106,6 +120,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the {@link DynamicFPSetManager} correctly indexes into the
 	 * table of FPSet servers.
 	 */
+	@Test
 	public void testGetIndexSingleFPSet() throws RemoteException {
 		// Simple case with a single FPSet server
 		final Map<Long, Integer> pairs = new HashMap<Long, Integer>();
@@ -121,6 +136,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Test that the {@link DynamicFPSetManager} correctly indexes into the
 	 * table of FPSet servers.
 	 */
+	@Test
 	public void testGetIndex10FPSet() throws RemoteException {
 		final Map<Long, Integer> pairs = new HashMap<Long, Integer>();
 		
@@ -161,7 +177,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 		
 		for (Entry<Long, Integer> pair : pairs.entrySet()) {
 			long fp = pair.getKey();
-			int index = dfm.getIndex(fp);
+			int index = dfm.getFPSetIndex(fp);
 			int expected = pair.getValue();
 			assertEquals(expected, index);
 		}
@@ -170,6 +186,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests that reassign doesn't accept invalid values
 	 */
+	@Test
 	public void testReassingInvalidMin1() throws RemoteException {
 		int expectedNumOfServers = 1;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -190,6 +207,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests that reassign doesn't accept invalid values
 	 */
+	@Test
 	public void testReassingInvalid2() throws RemoteException {
 		int expectedNumOfServers = 1;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -211,6 +229,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * Tests that reassign correctly terminates with -1 when reassignment to
 	 * next FPSet impossible (no FPSets left)
 	 */
+	@Test
 	public void testReassingTerminate() throws RemoteException {
 		int expectedNumOfServers = 1;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -225,6 +244,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests that reassign correctly assigns to the next FPSet
 	 */
+	@Test
 	public void testReassing() throws RemoteException {
 		int expectedNumOfServers = 10;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -268,6 +288,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests if the {@link FPSetManager} correctly fails over to the replacement {@link FPSet}
 	 */
+	@Test
 	public void testFailoverPut() throws RemoteException {
 		int expectedNumOfServers = 2;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -276,7 +297,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 		
 		final long fp = 2L;
 		
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(fp));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(fp));
 		
 		// Test DFM correctly behaves first time when TestFPSet works as expected
 		assertFalse(dfm.put(fp));
@@ -292,6 +313,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests if the {@link FPSetManager} correctly fails over to the replacement {@link FPSet}
 	 */
+	@Test
 	public void testFailoverPutBlock() throws RemoteException {
 		int expectedNumOfServers = 2;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -305,10 +327,10 @@ public class DynamicFPSetManagerTest extends TestCase {
 		final LongVec[] fps = new LongVec[numOfServers]; 
 		fps[0] = new LongVec();
 		fps[0].addElement(0L);
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(0L));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(0L));
 		fps[1] = new LongVec();
 		fps[1].addElement(1L);
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getIndex(1L));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getFPSetIndex(1L));
 		
 		/* Test DFM correctly behaves first time when TestFPSet works as expected */
 
@@ -345,6 +367,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests if the {@link FPSetManager} correctly terminates if all nested FPSets fail
 	 */
+	@Test
 	public void testFailoverTerminationPutBlock() throws RemoteException {
 		int expectedNumOfServers = 2;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -358,10 +381,10 @@ public class DynamicFPSetManagerTest extends TestCase {
 		final LongVec[] fps = new LongVec[numOfServers]; 
 		fps[0] = new LongVec();
 		fps[0].addElement(0L);
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(0L));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(0L));
 		fps[1] = new LongVec();
 		fps[1].addElement(1L);
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getIndex(1L));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getFPSetIndex(1L));
 		
 		/* Test DFM correctly behaves first time when TestFPSet works as expected */
 
@@ -397,6 +420,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	/**
 	 * Tests if the {@link FPSetManager} correctly terminates if all nested FPSets fail
 	 */
+	@Test
 	public void testFailoverTerminationPutBlockConcurrent() throws RemoteException {
 		int expectedNumOfServers = 2;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
@@ -411,10 +435,10 @@ public class DynamicFPSetManagerTest extends TestCase {
 		final LongVec[] fps = new LongVec[numOfServers]; 
 		fps[0] = new LongVec();
 		fps[0].addElement(0L);
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(0L));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(0L));
 		fps[1] = new LongVec();
 		fps[1].addElement(1L);
-		assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getIndex(1L));
+		assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getFPSetIndex(1L));
 		
 		/* Test DFM correctly behaves first time when TestFPSet works as expected */
 		final ExecutorService es = Executors.newCachedThreadPool();
@@ -461,6 +485,7 @@ public class DynamicFPSetManagerTest extends TestCase {
 	 * {@link DynamicFPSetManager} is indeed faulty (pay attention to
 	 * intermittent test failures).
 	 */
+	@Test
 	public void testPutBlockConcurrentOrder() throws IOException {
 		int expectedNumOfServers = 20;
 		final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers);
diff --git a/tlatools/test/tlc2/tool/distributed/fp/FPSetManagerTest.java b/tlatools/test/tlc2/tool/distributed/fp/FPSetManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0420022a50e12491171c5b0a7cb9a01e8ed6b915
--- /dev/null
+++ b/tlatools/test/tlc2/tool/distributed/fp/FPSetManagerTest.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.distributed.fp;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import tlc2.tool.distributed.fp.FPSetManager.FPSets;
+import tlc2.tool.fp.FPSet;
+import tlc2.tool.fp.FPSetConfiguration;
+import tlc2.tool.fp.FPSetFactory;
+
+public class FPSetManagerTest {
+
+	protected static final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "FPSetTest"
+			+ System.currentTimeMillis();
+
+	@Before
+	public void before() {
+		new File(tmpdir).mkdirs();
+	}
+
+	@Test
+	public void test2() throws IOException {
+		doTest(2);
+	}
+
+	@Test
+	public void test3() throws IOException {
+		doTest(3);
+	}
+
+	@Test
+	public void test4() throws IOException {
+		doTest(4);
+	}
+
+	@Test
+	public void test5() throws IOException {
+		doTest(5);
+	}
+
+	@Test
+	public void test8() throws IOException {
+		doTest(8);
+	}
+
+	private void doTest(int expectedNumOfServers) throws RemoteException, IOException, FPSetManagerException {
+		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
+		fpSetConfiguration.setFpBits(1); // two nested FPSets
+
+		final List<FPSets> sets = new ArrayList<FPSets>();
+		for (int i = 0; i < fpSetConfiguration.getMultiFPSetCnt(); i++) {
+			final FPSet fpSet = FPSetFactory.getFPSet(fpSetConfiguration);
+			fpSet.init(1, tmpdir, "test" + expectedNumOfServers);
+			sets.add(new FPSets(fpSet, "localhost" + i));
+		}
+
+		final IFPSetManager manager = new DynamicFPSetManager(expectedNumOfServers);
+		for (FPSets fpSets : sets) {
+			manager.register(fpSets.getFpset(), fpSets.getFpset().toString());
+		}
+
+		// Index uses LSBs
+		Assert.assertEquals(0, manager.getFPSetIndex(0));
+		Assert.assertEquals(1, manager.getFPSetIndex(1));
+		Assert.assertEquals(0, manager.getFPSetIndex(2));
+		Assert.assertEquals(1, manager.getFPSetIndex(3));
+		Assert.assertEquals(0, manager.getFPSetIndex((1L << 63) + 2L));
+		Assert.assertEquals(1, manager.getFPSetIndex((1L << 63) + 1L));
+
+		final Set<Long> fps = new HashSet<Long>();
+		// fps.add(0L); // Not accepted by nested FPSets
+		fps.add(1L);              // 00...0001
+		fps.add((1L << 62) + 1L); // 01...0001
+		fps.add((1L << 63) + 1L); // 10...0001
+		fps.add((3L << 62) + 1L); // 11...0001
+		fps.add(2L);              // 00...0010
+		fps.add((1L << 62) + 2L); // 01...0010
+		fps.add((1L << 63) + 2L); // 10...0010
+		fps.add((3L << 62) + 2L); // 11...0010
+		fps.add(3L);              // 00...0011
+		fps.add((1L << 62) + 3L); // 01...0011
+		fps.add((1L << 63) + 3L); // 10...0011
+		fps.add((3L << 62) + 3L); // 11...0011
+		fps.add(4L);              // 00...0100
+		fps.add((1L << 62) + 4L); // 01...0100
+		fps.add((1L << 63) + 4L); // 10...0100
+		fps.add((3L << 62) + 4L); // 11...0100
+		fps.add(5L);              // 00...0101
+		fps.add((1L << 62) + 5L); // 01...0101
+		fps.add((1L << 63) + 5L); // 10...0101
+		fps.add((3L << 62) + 5L); // 11...0101
+		fps.add(6L);              // 00...0110
+		fps.add((1L << 62) + 6L); // 01...0110
+		fps.add((1L << 63) + 6L); // 10...0110
+		fps.add((3L << 62) + 6L); // 11...0110
+		fps.add(7L);              // 00...0110
+		fps.add((1L << 62) + 7L); // 01...0111
+		fps.add((1L << 63) + 7L); // 10...0111
+		fps.add((3L << 62) + 7L); // 11...0111
+		fps.add(8L);              // 00...1000
+		fps.add((1L << 62) + 8L); // 01...1000
+		fps.add((1L << 63) + 8L); // 10...1000
+		fps.add((3L << 62) + 8L); // 11...1000
+
+		final Set<Long> unseen = new HashSet<Long>(fps);
+		for (Long fp : fps) {
+			// Unseen fingerprints must not be in set.
+			for (Long unseenFP : unseen) {
+				Assert.assertFalse(manager.contains(unseenFP));
+			}
+			Assert.assertTrue(unseen.remove(fp));
+
+			Assert.assertFalse(printBinaryString("", fp), manager.put(fp));
+			Assert.assertTrue(printBinaryString("", fp), manager.contains(fp));
+		}
+
+		Assert.assertEquals(fps.size(), manager.size());
+
+		Assert.assertTrue(manager.checkInvariant());
+	}
+
+	private String printBinaryString(final String id, final long a) {
+		return String.format(id + ":%64s", Long.toBinaryString(a)).replace(' ', '0');
+	}
+}
diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorEvalExceptionTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorEvalExceptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..71d6b10c37d507cf16c12c16ae256a859b069816
--- /dev/null
+++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorEvalExceptionTest.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.doinitfunctor;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DoInitFunctorEvalExceptionTest extends ModelCheckerTestCase {
+
+	public DoInitFunctorEvalExceptionTest() {
+		super("DoInitFunctorEvalException", "DoInitFunctor", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recorded(EC.TLC_STATS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INITIAL_STATE,
+				"TLC expected a boolean value, but did not find one. line 15, col 15 to line 15, col 18 of module DoInitFunctorEvalException",
+				"x = 1\n"));
+
+		assertUncovered("line 9, col 9 to line 9, col 14 of module DoInitFunctorEvalException: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantContinueTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantContinueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a68ee5cd21eaaa7cda6aa049981c7c3cc7ee3ce6
--- /dev/null
+++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantContinueTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.doinitfunctor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DoInitFunctorInvariantContinueTest extends ModelCheckerTestCase {
+	
+	public DoInitFunctorInvariantContinueTest() {
+		super("DoInitFunctorInvariantContinue", "DoInitFunctor", new String[] {"-continue"}/*, ExitStatus.VIOLATION_SAFETY*/);  //TODO The exit status is incorrect because TLC shows "no error" regardless of the number of violations with "-continue"
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "21", "11"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Inv", "x = 1\n"));
+		// Test that TLC - with continuation enabled - continues after finding the first inv violation.
+		final List<Object> records = recorder.getRecords(EC.TLC_INVARIANT_VIOLATED_INITIAL);
+		assertEquals(10, records.size());
+		for (int j = 0; j < records.size(); j++) {
+			final String[] violation = (String[]) records.get(j);
+			assertEquals("Inv", violation[0]);
+			assertEquals("x = " + (j+1) + "\n", violation[1]);
+		}
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantMinimalErrorStackTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantMinimalErrorStackTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..497dbbc4d6fdd5be04affefc435419e8d60c36e5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantMinimalErrorStackTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.doinitfunctor;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DoInitFunctorInvariantMinimalErrorStackTest extends ModelCheckerTestCase {
+
+	public DoInitFunctorInvariantMinimalErrorStackTest() {
+		super("DoInitFunctorMinimalErrorStack", "DoInitFunctor", ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1", "1", "1"));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_ARGUMENT_ERROR_AN,
+				"first", ">", "integer", "<<1, 1>>"));
+
+		String errorStack = "0. Line 8, column 3 to line 9, column 13 in DoInitFunctorMinimalErrorStack\n"
+				+ "1. Line 8, column 6 to line 8, column 25 in DoInitFunctorMinimalErrorStack\n"
+				+ "2. Line 9, column 6 to line 9, column 13 in DoInitFunctorMinimalErrorStack\n"
+				+ "3. Line 14, column 8 to line 14, column 13 in DoInitFunctorMinimalErrorStack\n\n";
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_NESTED_EXPRESSION, errorStack));
+
+		assertUncovered("line 11, col 20 to line 11, col 33 of module DoInitFunctorMinimalErrorStack: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantNoContinueTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantNoContinueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c93e93ab0b51c519161d0204b1740c9190932b80
--- /dev/null
+++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantNoContinueTest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.doinitfunctor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DoInitFunctorInvariantNoContinueTest extends ModelCheckerTestCase {
+	
+	public DoInitFunctorInvariantNoContinueTest() {
+		super("DoInitFunctorInvariantContinue", "DoInitFunctor", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_STATS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Inv", "x = 1\n"));
+		// Test that TLC - with continuation disabled - stops after finding the first inv violation/finds exactly one violation.
+		assertEquals(1, recorder.getRecords(EC.TLC_INVARIANT_VIOLATED_INITIAL).size());
+	}
+}
diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..06a7e815fc5af4d2c49a59e1e7059991fa787bd5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.doinitfunctor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DoInitFunctorInvariantTest extends ModelCheckerTestCase {
+	
+	public DoInitFunctorInvariantTest() {
+		super("DoInitFunctorInvariant", "DoInitFunctor", ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_STATS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Test that TLC - with continuation disabled - stops after finding the first inv violation/finds exactly one violation.
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "NotNine", "x = 9\n"));
+		assertEquals(1, recorder.getRecords(EC.TLC_INVARIANT_VIOLATED_INITIAL).size());
+	}
+}
diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorPropertyTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorPropertyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6133b07900ddcb2eead9cfbc83b9410a662a073
--- /dev/null
+++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorPropertyTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.doinitfunctor;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class DoInitFunctorPropertyTest extends ModelCheckerTestCase {
+	
+	public DoInitFunctorPropertyTest() {
+		super("DoInitFunctorProperty", "DoInitFunctor", ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.TLC_STATS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_PROPERTY_VIOLATED_INITIAL, "NotNine", "x = 9\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder1Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..40a246a50aecfbe6da630e1331642c54b93623b4
--- /dev/null
+++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder1Test.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.evalorder;
+
+public class InitEvalOrder1Test extends InitEvalOrderTest {
+
+	public InitEvalOrder1Test() {
+		super("InitEvalOrder1.cfg");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder2Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e29ed4d47486714557136eb77ca6163781530c1
--- /dev/null
+++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder2Test.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.evalorder;
+
+public class InitEvalOrder2Test extends InitEvalOrderTest {
+
+	public InitEvalOrder2Test() {
+		super("InitEvalOrder2.cfg");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder3Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder3Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..011bc55d58f001f3395839917f779584f6c3678d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder3Test.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.evalorder;
+
+public class InitEvalOrder3Test extends InitEvalOrderTest {
+
+	public InitEvalOrder3Test() {
+		super("InitEvalOrder3.cfg");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder4Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder4Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..969fb147a5264c3ac821fa9097da5e381e539b37
--- /dev/null
+++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder4Test.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.evalorder;
+
+public class InitEvalOrder4Test extends InitEvalOrderTest {
+
+	public InitEvalOrder4Test() {
+		super("InitEvalOrder4.cfg");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrderBasicTest.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderBasicTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e942b61465700843427a280397637a8456c2f30
--- /dev/null
+++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderBasicTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.evalorder;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class InitEvalOrderBasicTest extends ModelCheckerTestCase {
+
+	public InitEvalOrderBasicTest() {
+		super("InitEvalOrderBasic", "EvalOrder");
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrderTest.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f50d2109880c9d4c6096efcd47acc1e517157e9a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.evalorder;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public abstract class InitEvalOrderTest extends ModelCheckerTestCase {
+
+	public InitEvalOrderTest(String config) {
+		super("InitEvalOrder", "EvalOrder", new String[] {"-config", config});
+	}
+
+	@Test
+	public void test() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java b/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java
index e0bc397d688e87de99573b1e70669470400ee877..fc0864fd1652de90f3404df6d412a52ac277dd17 100644
--- a/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java
@@ -7,9 +7,10 @@ import java.io.IOException;
 import java.text.DecimalFormat;
 import java.util.Date;
 
-import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
 
-public abstract class AbstractFPSetTest extends TestCase {
+public abstract class AbstractFPSetTest {
 
 	protected static final long RNG_SEED = 15041980L;
 
@@ -17,9 +18,11 @@ public abstract class AbstractFPSetTest extends TestCase {
 					+ System.currentTimeMillis();
 	protected static final String filename = "FPSetTestTest";
 	protected static final DecimalFormat df = new DecimalFormat("###,###.###");
+	protected static final DecimalFormat pf = new DecimalFormat("#.##");
 
 	protected long previousTimestamp;
 	protected long previousSize;
+	protected long startTimestamp;
 	protected Date endTimeStamp;
 
 	private File dir;
@@ -28,14 +31,13 @@ public abstract class AbstractFPSetTest extends TestCase {
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#setUp()
 	 */
-	protected void setUp() throws Exception {
-		super.setUp();
-	
+	@Before
+	public void setUp() throws Exception {
 		// create temp folder
 		dir = new File(tmpdir);
 		dir.mkdirs();
 		
-		previousTimestamp = System.currentTimeMillis();
+		previousTimestamp = startTimestamp = System.currentTimeMillis();
 		previousSize = 0L;
 		
 		System.out.println("Test started at " + new Date());
@@ -44,6 +46,7 @@ public abstract class AbstractFPSetTest extends TestCase {
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#tearDown()
 	 */
+	@After
 	public void tearDown() {
 		if (endTimeStamp == null) {
 			endTimeStamp = new Date();
@@ -77,25 +80,46 @@ public abstract class AbstractFPSetTest extends TestCase {
 		if (fpSet instanceof FPSetStatistic) {
 			FPSetStatistic fpSetStats = (FPSetStatistic) fpSet;
 			long maxTblCnt = fpSetStats.getMaxTblCnt();
-			System.out.println("Maximum FPSet bucket count is: "
+			System.out.println("Maximum FPSet table count is: "
 					+ df.format(maxTblCnt) + " (approx: "
 					+ df.format(maxTblCnt * FPSet.LongSize >> 20) + " GiB)");
+			System.out.println("FPSet lock count is: " + fpSetStats.getLockCnt());
+			System.out.println("FPSet bucket count is: " + fpSetStats.getTblCapacity());
 		}
 
 		System.out.println("Testing " + fpSet.getClass().getCanonicalName());
 		return fpSet;
 	}
-
+	
 	// insertion speed
-	public void printInsertionSpeed(final long currentSize) {
-		final long currentTimestamp = System.currentTimeMillis();
+	public void printInsertionSpeed(final FPSet fpSet) {
 		// print every minute
-		final double factor = (currentTimestamp - previousTimestamp) / 60000d;
+		long now = System.currentTimeMillis();
+		final double factor = (now - previousTimestamp) / 60000d;
 		if (factor >= 1d) {
+			final long currentSize = fpSet.size();
 			long insertions = (long) ((currentSize - previousSize) * factor);
-			System.out.println(df.format(insertions) + " insertions/min");
-			previousTimestamp = currentTimestamp;
+			if (fpSet instanceof FPSetStatistic) {
+				FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet;
+				System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor");
+			} else {
+				System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min");
+			}
+			previousTimestamp = now;
 			previousSize = currentSize;
 		}
 	}
+	
+	public void printInsertionSpeed(final FPSet fpSet, long start, long end) {
+		final long size = fpSet.size();
+		// Normalize insertions to minutes.
+		final long duration = Math.max(end - start, 1); //ms (avoid div-by-zero)
+		final long insertions = (long) ((size / duration) * 60000L);
+		if (fpSet instanceof FPSetStatistic) {
+			FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet;
+			System.out.println(System.currentTimeMillis() + " s; " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor");
+		} else {
+			System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min");
+		}
+	}
 }
diff --git a/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java
index 1e0c623ee65e4a0b1ed59677e4db2737356a8b86..dc16b7db2e83ea897c2496027b4ce65011ed451f 100644
--- a/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java
@@ -1,66 +1,139 @@
 // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
 import java.rmi.RemoteException;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+
+import tlc2.tool.TLCState;
+import tlc2.tool.TLCTrace;
+import tlc2.tool.queue.DummyTLCState;
 
-public abstract class AbstractHeapBasedDiskFPSetTest extends TestCase {
+public abstract class AbstractHeapBasedDiskFPSetTest {
 	
 	/* Test the lower limits */
 	
+	@Test
 	public void testCtorLLMinus1() throws RemoteException {
 		doTest(getLowerLimit() - 1);
 	}
 	
+	@Test
 	public void testCtorLL() throws RemoteException {
 		doTest(getLowerLimit());
 	}
 
+	@Test
 	public void testCtorLLPlus1() throws RemoteException {
 		doTest(getLowerLimit() + 1);
 	}
 	
+	@Test
 	public void testCtorLLNextPow2Min1() throws RemoteException {
 		doTest((getLowerLimit() << 1) - 1);
 	}
 	
 	/* Test with a power far away from upper/lower limits */
 	
+	@Test
 	public void testCtorPow16Minus1() throws RemoteException {
 		doTest((1L << 16) - 1);
 	}
 	
+	@Test
 	public void testCtorPow16() throws RemoteException {
 		doTest(1L << 16);
 	}
 
+	@Test
 	public void testCtorPow16Plus1() throws RemoteException {
 		doTest((1L << 16) + 1);
 	}
 	
+	@Test
 	public void testCtorPow16NextPow2Min1() throws RemoteException {
 		doTest(((1L << 16) << 1) - 1);
 	}
 	
 	/* Test the upper limits */
 	
+	@Test
 	public void testCtorULMinus1() throws RemoteException {
 		doTest(getUpperLimit() - 1);
 	}
 	
+	@Test
 	public void testCtorUL() throws RemoteException {
 		doTest(getUpperLimit());
 	}
 
+	@Test
 	public void testCtorULPlus1() throws RemoteException {
 		doTest(getUpperLimit() + 1);
 	}
 	
+	@Test
 	public void testCtorULNextPow2Min1() throws RemoteException {
 		doTest((getUpperLimit() << 1) - 1);
 	}
 	
+	@Test
+	public void testFPSetRecovery() throws IOException {
+		final int limit = 99999;
+		final String metadir = System.getProperty("java.io.tmpdir");
+		final String filename = this.getClass().getCanonicalName();
+		
+		// First, create a trace file to recover from.
+		final TLCTrace trace = new TLCTrace(metadir, filename,
+				null);
+		
+		// Fill the trace file with random fingerprints
+		final TLCState predecessor = new DummyTLCState();
+		predecessor.uid = 1L;
+		// an init state
+		trace.writeState(predecessor.uid);
+		// successor states
+		for (long fp = predecessor.uid + 1; fp < limit; fp++) {
+			trace.writeState(predecessor, fp);
+			predecessor.uid = fp;
+		}
+		
+		// Create a checkpoint file
+		trace.beginChkpt();
+		trace.commitChkpt();
+		
+		// Create a DiskFPSet 
+		final DiskFPSet fpSet = getDiskFPSet(new FPSetConfiguration());
+		fpSet.init(1, metadir, filename);
+		fpSet.recover(trace);
+
+		// Verify successful recovery
+		assertEquals(limit-1, fpSet.size());
+		for (long fp = 1L; fp < limit; fp++) {
+			assertTrue(fpSet.contains(fp));
+		}
+	}
+	
+	@Test
+	public void testFPSetRecovery2() throws IOException {
+		final String metadir = System.getProperty("java.io.tmpdir");
+		final String filename = this.getClass().getCanonicalName() + "testFPSetRecovery2";
+		
+		final DiskFPSet fpSet = getDiskFPSet(new FPSetConfiguration());
+		fpSet.init(1, metadir, filename);
+
+		// Make sure the FPSet tries to flush to disk.
+		fpSet.forceFlush();
+		
+		for (long fp = 1; fp <= 1024; fp++) {
+			fpSet.recoverFP(fp);
+		}
+	}
+
 	/* Helper */
 
 	@SuppressWarnings("deprecation")
diff --git a/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java
index a7eb567f3300895d839b9d4d2e1529b9f2500ae5..ed22315a6bc97c30531fed3e26ad6fe1dfde51a4 100644
--- a/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java
@@ -1,8 +1,13 @@
 // Copyright (c) 2011 Microsoft Corporation.  All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 
+import org.junit.Test;
+
 public class Bug210DiskFPSetTest extends AbstractFPSetTest {
 
 	/* (non-Javadoc)
@@ -16,9 +21,10 @@ public class Bug210DiskFPSetTest extends AbstractFPSetTest {
 	}
 
 	/**
-	 * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=210
+	 * @see Bug #210 in general/bugzilla/index.html
 	 * @throws IOException
 	 */
+	@Test
 	public void testDiskLookupWithOverflow() throws IOException {
 		// set up an index whose upper bound is beyond 1/1024 of
 		// Integer.MAX_VALUE
diff --git a/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java
index 20e232d50824f4680dba97630313803bf30e992a..71df2e53717d5244ee5ff03b4841c5ac4186f3ef 100644
--- a/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java
@@ -1,9 +1,13 @@
 // Copyright (c) 2011 Microsoft Corporation.  All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.rmi.RemoteException;
 
+import org.junit.Test;
+
 /**
  *
  */
@@ -26,8 +30,9 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest {
 	}
 	
 	/**
-	 * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=242
+	 * @see Bug #242 in general/bugzilla/index.html
 	 */
+	@Test
 	public void testDiskFPSetWithHighMem() throws RemoteException {
 		try {
 			getFPSet(2097153638);
@@ -38,6 +43,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest {
 			fail(e.getMessage());
 		}
 	}
+	@Test
 	public void testDiskFPSetIntMaxValue() throws RemoteException {
 		try {
 			getFPSet(Integer.MAX_VALUE);
@@ -48,6 +54,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest {
 			fail(e.getMessage());
 		}
 	}
+	@Test
 	public void testDiskFPSetIntMinValue() throws RemoteException {
 		try {
 			getFPSet(Integer.MIN_VALUE);
@@ -57,6 +64,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest {
 		}
 		fail();
 	}
+	@Test
 	public void testDiskFPSetZero() throws RemoteException {
 		try {
 			getFPSet(0);
@@ -64,6 +72,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest {
 			fail(e.getMessage());
 		}
 	}
+	@Test
 	public void testDiskFPSetOne() throws RemoteException {
 		try {
 			getFPSet(1);
diff --git a/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java
index c7c07483b9562ba3f7b8257e0cf34d447ba30cf3..341fb5a7ccc99ce568b06661331c0319aa3e192b 100644
--- a/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java
@@ -2,20 +2,26 @@
 
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+
 import util.TLCRuntime;
 
 /**
  * @author Markus Alexander Kuppe
  */
-public class Bug246DiskFPSetTest extends TestCase {
+public class Bug246DiskFPSetTest {
 
 	/**
 	 * Tests if the DiskFPSet gets correctly flushed to disk (if the fp spaces is unevenly distributed) or causes an {@link OutOfMemoryError} 
 	 * @throws IOException 
 	 */
+	@Test
 	@SuppressWarnings("deprecation")
 	public void testLinearFillup() throws IOException {
 		final long vmMaxMemory = Runtime.getRuntime().maxMemory();
@@ -77,6 +83,7 @@ public class Bug246DiskFPSetTest extends TestCase {
 		}
 	}
 	
+	@Test
 	public void testFlushDiskFPSet() throws IOException {
 		
 //		// Dedicate 90% of VM memory to DiskFPSet 
diff --git a/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java b/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java
index 4d7a26d8af7bed453d888aa30004c2eed57de29b..4f0481ca2c5f321fc2bba66d88935a2cda29c96d 100644
--- a/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java
+++ b/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java
@@ -15,7 +15,7 @@ public class DummyFPSetConfiguration extends FPSetConfiguration {
 		return memoryInBytes;
 	}
 	
-	public void setMemoryInFingerprintCnt(int numberOfFingerprints) {
-		this.memoryInBytes = numberOfFingerprints * FPSet.LongSize;
+	public void setMemoryInFingerprintCnt(long length) {
+		this.memoryInBytes = length * FPSet.LongSize;
 	}
 }
diff --git a/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java b/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java
index 00950765cd3ea83f4a5053ade30a6b985990e835..8820e49907e56cbe526a368c1cb9810bb56c5ae2 100644
--- a/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java
+++ b/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java
@@ -1,20 +1,53 @@
 // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.rmi.NoSuchObjectException;
 import java.rmi.RemoteException;
 
-import junit.framework.TestCase;
+import org.junit.Assume;
+import org.junit.Test;
+
+import tlc2.tool.distributed.fp.FPSetRMI;
+import util.TLCRuntime;
 
 @SuppressWarnings("deprecation")
-public class FPSetFactoryTest extends TestCase {
+public class FPSetFactoryTest {
 	// Has to be larger than util.TLCRuntime.MinFpMemSize. For off-heap/non-heap
 	// tests it should not exceed what the VM allocates by default (usually
 	// 64mb). 64mb is also the default used by util.TLCRuntime.getNonHeapPhysicalMemory().
 	private static final long MEMORY = 64L * 1024L * 1024L;
 
+	/* Test diskfpset subclasses which always require two instances */
+	
+	@Test
+	public void testGetDiskFPSet() {
+		assertTrue(FPSetFactory.isDiskFPSet(DiskFPSet.class.getName()));
+		assertTrue(FPSetFactory.isDiskFPSet(HeapBasedDiskFPSet.class.getName()));
+		assertTrue(FPSetFactory.isDiskFPSet(OffHeapDiskFPSet.class.getName()));
+		assertTrue(FPSetFactory.isDiskFPSet(LSBDiskFPSet.class.getName()));
+		assertTrue(FPSetFactory.isDiskFPSet(MSBDiskFPSet.class.getName()));
+
+		assertFalse(FPSetFactory.isDiskFPSet(FPSet.class.getName()));
+		assertFalse(FPSetFactory.isDiskFPSet(FPSetRMI.class.getName()));
+		
+		assertFalse(FPSetFactory.isDiskFPSet(MultiFPSet.class.getName()));
+		
+		assertFalse(FPSetFactory.isDiskFPSet(MemFPSet.class.getName()));
+		assertFalse(FPSetFactory.isDiskFPSet(MemFPSet1.class.getName()));
+		assertFalse(FPSetFactory.isDiskFPSet(MemFPSet2.class.getName()));
+		
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
+		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
+		assertTrue(fpSetConfiguration.allowsNesting());
+	}
+	
 	/* Test single FPSet with default memory */
 
+	@Test
 	public void testGetFPSetMSB() throws RemoteException {
 		// Explicitly set MSBDiskFPSet to overwrite any previous setting (if any)
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
@@ -22,13 +55,16 @@ public class FPSetFactoryTest extends TestCase {
 		doTestGetFPSet(MSBDiskFPSet.class, fpSetConfiguration);
 	}
 
+	@Test
 	public void testGetFPSetLSB() throws RemoteException {
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
 		doTestGetFPSet(LSBDiskFPSet.class, fpSetConfiguration);
 	}
 
+	@Test
 	public void testGetFPSetOffHeap() throws RemoteException {
+		Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64);
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
 		doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration);
@@ -36,6 +72,7 @@ public class FPSetFactoryTest extends TestCase {
 
 	/* Test single FPSet with explicit memory */
 	
+	@Test
 	public void testGetFPSetMSBWithMem() throws RemoteException {
 		// Explicitly set MSBDiskFPSet to overwrite any previous setting (if any)
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
@@ -44,13 +81,11 @@ public class FPSetFactoryTest extends TestCase {
 		fpSetConfiguration.setRatio(1.0d);
 		FPSet fpSet = doTestGetFPSet(MSBDiskFPSet.class, fpSetConfiguration);
 		assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes());
-		
-		// non-multifpsets can be cast to diskfpsets to expose statistics
-		// interface
-		final DiskFPSet diskFPSet = (DiskFPSet) fpSet;
-		assertTrue((MEMORY / FPSet.LongSize) > diskFPSet.getMaxTblCnt());
+
+		doTestNested(MSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet);
 	}
 
+	@Test
 	public void testGetFPSetLSBWithMem() throws RemoteException {
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
@@ -59,30 +94,12 @@ public class FPSetFactoryTest extends TestCase {
 		FPSet fpSet = doTestGetFPSet(LSBDiskFPSet.class, fpSetConfiguration);
 		assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes());
 
-		// non-multifpsets can be cast to diskfpsets to expose statistics
-		// interface
-		final DiskFPSet diskFPSet = (DiskFPSet) fpSet;
-		// LSB implementation can only allocate half of its memory for primary
-		// fingerprint storage (see auxiliary storage)
-		assertTrue(((MEMORY / FPSet.LongSize) / 2) > diskFPSet.getMaxTblCnt());
-	}
-
-	public void testGetFPSetOffHeapWithMem() throws RemoteException {
-		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
-		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
-		fpSetConfiguration.setMemory(MEMORY);
-		fpSetConfiguration.setRatio(1.0d);
-		FPSet fpSet = doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration);
-		assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes());
-
-		// non-multifpsets can be cast to diskfpsets to expose statistics
-		// interface
-		final DiskFPSet diskFPSet = (DiskFPSet) fpSet;
-		assertEquals((MEMORY / FPSet.LongSize), diskFPSet.getMaxTblCnt());
+		doTestNested(LSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet);
 	}
 	
 	/* Test single FPSet with explicit memory and ratio */
 	
+	@Test
 	public void testGetFPSetMSBWithMemAndRatio() throws RemoteException {
 		// Explicitly set MSBDiskFPSet to overwrite any previous setting (if any)
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
@@ -91,13 +108,11 @@ public class FPSetFactoryTest extends TestCase {
 		fpSetConfiguration.setRatio(.5d);
 		FPSet fpSet = doTestGetFPSet(MSBDiskFPSet.class, fpSetConfiguration);
 		assertEquals(MEMORY / 2, fpSet.getConfiguration().getMemoryInBytes());
-		
-		// non-multifpsets can be cast to diskfpsets to expose statistics
-		// interface
-		final DiskFPSet diskFPSet = (DiskFPSet) fpSet;
-		assertTrue((MEMORY / FPSet.LongSize) > diskFPSet.getMaxTblCnt());
+
+		doTestNested(MSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet);
 	}
 
+	@Test
 	public void testGetFPSetLSBWithMemAndRatio() throws RemoteException {
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
@@ -106,43 +121,24 @@ public class FPSetFactoryTest extends TestCase {
 		FPSet fpSet = doTestGetFPSet(LSBDiskFPSet.class, fpSetConfiguration);
 		assertEquals(MEMORY / 2, fpSet.getConfiguration().getMemoryInBytes());
 
-		// non-multifpsets can be cast to diskfpsets to expose statistics
-		// interface
-		final DiskFPSet diskFPSet = (DiskFPSet) fpSet;
-		// LSB implementation can only allocate half of its memory for primary
-		// fingerprint storage (see auxiliary storage)
-		assertTrue((MEMORY / FPSet.LongSize) > diskFPSet.getMaxTblCnt());
-	}
-
-	public void testGetFPSetOffHeapWithMemAndRatio() throws RemoteException {
-		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
-		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
-		fpSetConfiguration.setMemory(MEMORY);
-		fpSetConfiguration.setRatio(.5d);
-		FPSet fpSet = doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration);
-		
-		// Offheap allocates all 100% of memory as it's the only consumer of
-		// non-heap memory
-		assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes());
 
-		// non-multifpsets can be cast to diskfpsets to expose statistics
-		// interface
-		final DiskFPSet diskFPSet = (DiskFPSet) fpSet;
-		assertEquals((MEMORY / FPSet.LongSize), diskFPSet.getMaxTblCnt());
+		doTestNested(LSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet);
 	}
 	
 	/* Test MultiFPSet with default memory */
 	
+	@Test
 	public void testGetFPSetMultiFPSet() throws RemoteException {
 		// Explicitly set MSBDiskFPSet to overwrite any previous setting (if any)
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
 		fpSetConfiguration.setFpBits(1);
-		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration);
+		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration);
 		
 		doTestNested(MSBDiskFPSet.class, fpSetConfiguration, mFPSet);
 	}
 	
+	@Test
 	public void testGetFPSetLSBMultiFPSet() throws RemoteException {
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
@@ -152,17 +148,20 @@ public class FPSetFactoryTest extends TestCase {
 		doTestNested(LSBDiskFPSet.class, fpSetConfiguration, mFPSet);
 	}
 	
+	@Test
 	public void testGetFPSetOffHeapMultiFPSet() throws RemoteException {
+		Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64);
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
 		fpSetConfiguration.setFpBits(1);
-		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration);
+		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration);
 
 		doTestNested(OffHeapDiskFPSet.class, fpSetConfiguration, mFPSet);
 	}
 	
 	/* Test MultiFPSet with explicit memory */
 
+	@Test
 	public void testGetFPSetMultiFPSetWithMem() throws RemoteException {
 		// Explicitly set MSBDiskFPSet to overwrite any previous setting (if any)
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
@@ -170,11 +169,12 @@ public class FPSetFactoryTest extends TestCase {
 		fpSetConfiguration.setMemory(MEMORY);
 		fpSetConfiguration.setFpBits(1);
 		fpSetConfiguration.setRatio(1.0d);
-		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration);
+		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration);
 		
 		doTestNested(MSBDiskFPSet.class, fpSetConfiguration, mFPSet);
 	}
 	
+	@Test
 	public void testGetFPSetLSBMultiFPSetWithMem() throws RemoteException {
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
@@ -186,22 +186,60 @@ public class FPSetFactoryTest extends TestCase {
 		doTestNested(LSBDiskFPSet.class, fpSetConfiguration, mFPSet);
 	}
 	
+	@Test
 	public void testGetFPSetOffHeapMultiFPSetWithMem() throws RemoteException {
+		Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64);
 		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
 		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
 		fpSetConfiguration.setMemory(MEMORY);
 		fpSetConfiguration.setFpBits(1);
 		fpSetConfiguration.setRatio(1.0d);
-		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration);
+		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration);
 
 		doTestNested(OffHeapDiskFPSet.class, fpSetConfiguration, mFPSet);
 	}
-	
+
+	@Test
+	public void testGetFPSetOffHeapMultiFPSet42() throws RemoteException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
+		
+		final long nonHeapPhysicalMemory = TLCRuntime.getInstance().getNonHeapPhysicalMemory();
+		
+		final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration();
+		assertEquals(nonHeapPhysicalMemory, fpSetConfiguration.getMemoryInBytes());
+		assertEquals(nonHeapPhysicalMemory / FPSet.LongSize, fpSetConfiguration.getMemoryInFingerprintCnt());
+
+		final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration);
+
+		final FPSetConfiguration multiConfig = mFPSet.getConfiguration();
+		assertEquals(OffHeapDiskFPSet.class.getName(), multiConfig.getImplementation());
+		assertEquals(1, multiConfig.getFpBits());
+		assertEquals(2, multiConfig.getMultiFPSetCnt());
+		assertEquals(nonHeapPhysicalMemory, multiConfig.getMemoryInBytes());
+		assertEquals(nonHeapPhysicalMemory / FPSet.LongSize, multiConfig.getMemoryInFingerprintCnt());
+		
+		final FPSet[] fpSets = mFPSet.getFPSets();
+		assertEquals(2, fpSets.length);
+		
+		for (FPSet fpSet : fpSets) {
+			final OffHeapDiskFPSet offFPset = (OffHeapDiskFPSet) fpSet;
+			final FPSetConfiguration offConfig = offFPset.getConfiguration();
+			assertEquals(OffHeapDiskFPSet.class.getName(), offConfig.getImplementation());
+			assertEquals(1, offConfig.getFpBits());
+			assertEquals(2, offConfig.getMultiFPSetCnt());
+			assertEquals(nonHeapPhysicalMemory / 2L, offConfig.getMemoryInBytes());
+			assertEquals((nonHeapPhysicalMemory / FPSet.LongSize) / 2L, offConfig.getMemoryInFingerprintCnt());
+			assertEquals((offConfig.getMemoryInBytes() / FPSet.LongSize), offConfig.getMemoryInFingerprintCnt());
+		}
+	}
+
 	/* Helper methods */
 	
 	private FPSet doTestGetFPSet(final Class<? extends FPSet> class1, final FPSetConfiguration fpSetConfig) throws RemoteException, NoSuchObjectException {
 		final FPSet fpSet = FPSetFactory.getFPSet(fpSetConfig);
-		assertTrue(class1.isAssignableFrom(fpSet.getClass()));
+		if (!FPSetFactory.isDiskFPSet(class1.getName())) {
+			assertTrue(class1.isAssignableFrom(fpSet.getClass()));
+		}
 		
 		return fpSet;
 	}
diff --git a/tlatools/test/tlc2/tool/fp/LongArrayTest.java b/tlatools/test/tlc2/tool/fp/LongArrayTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a697289756087f1b88734efd197fe9627e24a9a1
--- /dev/null
+++ b/tlatools/test/tlc2/tool/fp/LongArrayTest.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import util.TLCRuntime;
+
+public class LongArrayTest {
+	
+	@Before
+	public void setup() {
+		Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64);
+	}
+
+	@Test
+	public void testGetAndSet() throws IOException {
+		final int elements = 100;
+
+		final LongArray array = new LongArray(elements);
+		array.zeroMemory();
+		
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(0L, array.get(i));
+		}
+
+		
+		for (long i = 0L; i < elements; i++) {
+			array.set(i, i);
+		}
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(i, array.get(i));
+		}
+
+		
+		for (long i = 0L; i < elements; i++) {
+			array.set(i, Long.MAX_VALUE - i);
+		}
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(Long.MAX_VALUE - i, array.get(i));
+		}
+		
+
+		for (long i = 0L; i < elements; i++) {
+			array.set(i, Long.MIN_VALUE + i);
+		}
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(Long.MIN_VALUE + i, array.get(i));
+		}
+	}
+	
+	@Test
+	public void testOutOfRangePositive() throws IOException {
+		final LongArray array = new LongArray(1);
+		try {
+			array.get(1);
+		} catch (AssertionError e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testOutOfRangeNegative() throws IOException {
+		final LongArray array = new LongArray(1);
+		try {
+			array.get(-1);
+		} catch (AssertionError e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testGetAndTrySet() throws IOException {
+		final int elements = 100;
+
+		final LongArray array = new LongArray(elements);
+		array.zeroMemory();
+		
+		// Assert zero successful
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(0L, array.get(i));
+		}
+
+		// trySet linear elements.
+		for (long i = 0L; i < elements; i++) {
+			assertTrue(array.trySet(i, 0, i));
+		}
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(i, array.get(i));
+		}
+
+		// Replace with largest possible values
+		for (long i = 0L; i < elements; i++) {
+			array.trySet(i, i, Long.MAX_VALUE - i);
+		}
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(Long.MAX_VALUE - i, array.get(i));
+		}
+		
+
+		// Replace with smallest possible values
+		for (long i = 0L; i < elements; i++) {
+			array.trySet(i, Long.MAX_VALUE - i, Long.MIN_VALUE + i);
+		}
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(Long.MIN_VALUE + i, array.get(i));
+		}
+	}
+	
+	@Test
+	public void testZeroMemory() throws IOException {
+		for (int k = 1; k < 8; k++) {
+			for (int i = 1; i < 128; i++) {
+				final LongArray array = new LongArray(i);
+				array.zeroMemory(k);
+				for (int j = 0; i < j; i++) {
+					assertEquals(0L, array.get(j));
+				}
+				for (int j = 0; i < j; i++) {
+					array.set(j, -1L);
+				}
+			}
+		}
+	}
+	
+	@Test
+	public void testSwap() throws IOException {
+		final int elements = 10321;
+
+		final LongArray array = new LongArray(elements);
+		array.zeroMemory();
+		
+		for (long i = 0L; i < elements; i++) {
+			long value = Long.MAX_VALUE - i;
+			array.set(i, value);
+		}
+		
+		for (int i = 0; i < (elements / 2); i++) {
+			array.swapCopy(i, (elements - 1) - i);
+		}
+		
+		for (long i = 0L; i < elements; i++) {
+			assertEquals(Long.MAX_VALUE - (elements -1) + i, array.get(i));
+		}
+	}
+	
+	@Test
+	public void testSwapRandom() throws IOException {
+		final int elements = 21383;
+		
+		final List<Long> vals = new ArrayList<Long>();
+		final Random rnd = new Random();
+		
+		for (int i = 0; i < elements; i++) {
+			vals.add(rnd.nextLong());
+		}
+		
+		final LongArray array = new LongArray(elements);
+		array.zeroMemory();
+		
+		for (int i = 0; i < elements; i++) {
+			array.set(i, vals.get(i));
+		}
+		
+		for (int i = 0; i < (elements / 2); i++) {
+			array.swapCopy(i, (elements - 1) - i);
+		}
+		
+		Collections.reverse(vals);
+		
+		for (int i = 0; i < elements; i++) {
+			assertEquals((long) vals.get(i), array.get(i));
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/fp/LongArraysTest.java b/tlatools/test/tlc2/tool/fp/LongArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9713ff757e698b1965a524934c0cad33acb0bda0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/fp/LongArraysTest.java
@@ -0,0 +1,874 @@
+// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved.
+package tlc2.tool.fp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static tlc2.tool.fp.OffHeapDiskFPSet.EMPTY;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import tlc2.tool.fp.LongArrays.LongComparator;
+import tlc2.tool.fp.OffHeapDiskFPSet.Indexer;
+
+public class LongArraysTest {
+	
+	@Before
+	public void setup() {
+		Assume.assumeTrue(LongArray.isSupported());
+	}
+	
+	@Test
+	public void testEmpty1() {
+		doTest(new ArrayList<Long>(0), 1L, 0, new OffHeapDiskFPSet.Indexer(0, 1));
+	}
+	
+	@Test
+	public void testEmpty2() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+
+		doTest(expected, 1L, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1));
+	}
+	
+	@Test
+	public void testBasic1() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(5L);
+		expected.add(8L);
+		expected.add(1L);
+		expected.add(7L);
+		expected.add(0L);
+		expected.add(3L);
+		final LongArray array = new LongArray(expected);
+		LongArrays.sort(array);
+		
+		// This amounts to a regular/basic insertion sort because there are no
+		// sentinels in the array. doTest fails for this array, because the
+		// indices calculated by the indexer are invalid.
+		for (int i = 1; i < array.size(); i++) {
+			assertTrue(array.get(i - 1L) < array.get(i));
+		}
+	}
+
+	@Test
+	public void testBasic2() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(74236458333421747L);
+		expected.add(9185197375878056627L);
+		expected.add(9017810141411942826L);
+		expected.add(481170446028802552L);
+		expected.add(587723185270146839L);
+		expected.add(764880467681476738L);
+		expected.add(1028380228728529428L);
+		expected.add(1246117495100367611L);
+		expected.add(1353681884824400499L);
+		expected.add(1963327988900916594L);
+		expected.add(2157942654452711468L);
+		expected.add(2211701751588391467L);
+		expected.add(2197266581704230150L);
+		expected.add(2391118405386569995L);
+		expected.add(2754416910109403115L);
+		expected.add(3528296600587602855L);
+		expected.add(3766154305485605955L);
+		expected.add(4172091881329434331L);
+		expected.add(4273360576593753745L);
+		expected.add(4338054185482857322L);
+		expected.add(4487790251341705673L);
+		expected.add(4760603841378765728L);
+		expected.add(4897534821030901381L);
+		expected.add(5057347369431494228L);
+		expected.add(5185984701076703188L);
+		expected.add(5255556356599253415L);
+		expected.add(4911921657882287345L);
+		expected.add(5512811886280168498L);
+		expected.add(5627022814159167180L);
+		expected.add(5630009759945037387L);
+		expected.add(5592096823142754761L);
+		expected.add(5880489878946290534L);
+		expected.add(6796173646113527960L);
+		expected.add(6887096685265647763L);
+		expected.add(6946033094922439935L);
+		expected.add(7100083311060830826L);
+		expected.add(7575172208974668528L);
+		expected.add(8240485391672917634L);
+		expected.add(8572429495433200993L);
+		expected.add(8804495173596718076L);
+		expected.add(8771524479740786626L);
+		expected.add(8986659781390119011L);
+		expected.add(9136953010061430590L);
+		expected.add(9195197379878056627L);		
+		final LongArray array = new LongArray(expected);
+		LongArrays.sort(array);
+		
+		// This amounts to a regular/basic insertion sort because there are no
+		// sentinels in the array. doTest fails for this array, because the
+		// indices calculated by the indexer are invalid.
+		for (int i = 1; i < array.size(); i++) {
+			assertTrue(array.get(i - 1L) < array.get(i));
+		}
+	}
+
+	@Test
+	public void test0() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(22102288204167208L);
+		expected.add(225160948165161873L);
+		expected.add(0L);
+		expected.add(1638602644344629957L);
+		expected.add(1644442600000000000L);
+		expected.add(0L);
+
+		doTest(expected, 1L, 3, new OffHeapDiskFPSet.Indexer(expected.size(), 1));
+	}
+	
+	@Test
+	public void test1() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(22102288204167208L);
+		expected.add(225160948165161873L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(810435887525385357L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1638602644344629957L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2068351286375637679L);
+		expected.add(0L);
+		expected.add(2528370576879701538L);
+		expected.add(2453870502940122045L);
+		expected.add(0L);
+		expected.add(3145830401686811393L);
+		expected.add(3192897355035876677L);
+		expected.add(3527505876050247287L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(4563398963865761585L);
+		expected.add(0L);
+		expected.add(4858869653769863593L);
+		expected.add(5180223017321191209L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(5635076245116608576L);
+		expected.add(5649139415351271641L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6703691584433488410L);
+		expected.add(0L);
+		expected.add(7143040549630863225L);
+		expected.add(7205281130519852628L);
+		expected.add(7012967342342885117L);
+		expected.add(7709106021212022085L);
+		expected.add(7908712604546919197L);
+		expected.add(7246110956693059329L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8781691546738212390L);
+		expected.add(8897195185152846807L);
+		expected.add(0L);
+
+		doTest(expected);
+	}
+
+	@Test
+	public void test2() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(22102288204167208L);
+		expected.add(225160948165161873L);
+		expected.add(810435887525385357L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1638602644344629957L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2068351286375637679L);
+		expected.add(0L);
+		expected.add(2528370576879701538L);
+		expected.add(2453870502940122045L);
+		expected.add(0L);
+		expected.add(3145830401686811393L);
+		expected.add(3192897355035876677L);
+		expected.add(3527505876050247287L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(4563398963865761585L);
+		expected.add(0L);
+		expected.add(4858869653769863593L);
+		expected.add(5180223017321191209L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(5635076245116608576L);
+		expected.add(5649139415351271641L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6703691584433488410L);
+		expected.add(0L);
+		expected.add(7143040549630863225L);
+		expected.add(7205281130519852628L);
+		expected.add(7012967342342885117L);
+		expected.add(7709106021212022085L);
+		expected.add(7908712604546919197L);
+		expected.add(7246110956693059329L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8781691546738212390L);
+		expected.add(8897195185152846807L);
+		expected.add(0L);
+
+		doTest(expected);
+	}
+
+	@Test
+	public void test3() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(9183932681676589496L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(329728050397015749L);
+		expected.add(436139026681109109L);
+		expected.add(556905678415593173L);
+		expected.add(0L);
+		expected.add(796460649423573389L);
+		expected.add(797798112015065380L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1632374027957690827L);
+		expected.add(1756811852021281877L);
+		expected.add(0L);
+		expected.add(1881448932687659007L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2342821865031748924L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2736147834640710575L);
+		expected.add(2864022862265935958L);
+		expected.add(2773542629236699928L);
+		expected.add(2957298868366608281L);
+		expected.add(0L);
+		expected.add(3330257111892751888L);
+		expected.add(3295675356431597478L);
+		expected.add(3395836867027940583L);
+		expected.add(3681469222400184316L);
+		expected.add(3754947896063147473L);
+		expected.add(3698681814958844261L);
+		expected.add(3951382885893085878L);
+		expected.add(0L);
+		expected.add(4188454649677385650L);
+		expected.add(4129247165607948084L);
+		expected.add(4365409305525871332L);
+		expected.add(4526757821913904014L);
+		expected.add(4254202026550171921L);
+		expected.add(4557871951994955815L);
+		expected.add(4806497834029622101L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(5236202638577037427L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(5936146187640212534L);
+		expected.add(0L);
+		expected.add(6127434886073515781L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6547025209145878563L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6931928829149329960L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(7244186580741581738L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(7634041392899269082L);
+		expected.add(7590982629575593986L);
+		expected.add(0L);
+		expected.add(7954723745221262664L);
+		expected.add(0L);
+		expected.add(8156105620374757718L);
+		expected.add(8305398393196381769L);
+		expected.add(8318253237689249492L);
+		expected.add(8487954051864981042L);
+		expected.add(8411933954485687818L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(9175849669163144218L);
+		
+		doTest(expected);
+	}
+	
+	@Test
+	public void test4() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(9136953010061430590L);
+		expected.add(74236458333421747L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(481170446028802552L);
+		expected.add(587723185270146839L);
+		expected.add(0L);
+		expected.add(764880467681476738L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1028380228728529428L);
+		expected.add(0L);
+		expected.add(1246117495100367611L);
+		expected.add(1353681884824400499L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1963327988900916594L);
+		expected.add(0L);
+		expected.add(2157942654452711468L);
+		expected.add(2211701751588391467L);
+		expected.add(2197266581704230150L);
+		expected.add(2391118405386569995L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2754416910109403115L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(3528296600587602855L);
+		expected.add(0L);
+		expected.add(3766154305485605955L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(4172091881329434331L);
+		expected.add(4273360576593753745L);
+		expected.add(4338054185482857322L);
+		expected.add(4487790251341705673L);
+		expected.add(0L);
+		expected.add(4760603841378765728L);
+		expected.add(0L);
+		expected.add(4897534821030901381L);
+		expected.add(5057347369431494228L);
+		expected.add(5185984701076703188L);
+		expected.add(5255556356599253415L);
+		expected.add(4911921657882287345L);
+		expected.add(5512811886280168498L);
+		expected.add(5627022814159167180L);
+		expected.add(5630009759945037387L);
+		expected.add(5592096823142754761L);
+		expected.add(5880489878946290534L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6796173646113527960L);
+		expected.add(6887096685265647763L);
+		expected.add(6946033094922439935L);
+		expected.add(7100083311060830826L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(7575172208974668528L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8240485391672917634L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8572429495433200993L);
+		expected.add(0L);
+		expected.add(8804495173596718076L);
+		expected.add(8771524479740786626L);
+		expected.add(8986659781390119011L);
+		expected.add(9017810141411942826L);
+		expected.add(9195197379878056627L);		
+		
+		doTest(expected);
+	}
+
+	@Test
+	public void test5() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(9185197375878056627L);
+		expected.add(74236458333421747L);
+		expected.add(9017810141411942826L);
+		expected.add(0L);
+		expected.add(481170446028802552L);
+		expected.add(587723185270146839L);
+		expected.add(0L);
+		expected.add(764880467681476738L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1028380228728529428L);
+		expected.add(0L);
+		expected.add(1246117495100367611L);
+		expected.add(1353681884824400499L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1963327988900916594L);
+		expected.add(0L);
+		expected.add(2157942654452711468L);
+		expected.add(2211701751588391467L);
+		expected.add(2197266581704230150L);
+		expected.add(2391118405386569995L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2754416910109403115L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(3528296600587602855L);
+		expected.add(0L);
+		expected.add(3766154305485605955L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(4172091881329434331L);
+		expected.add(4273360576593753745L);
+		expected.add(4338054185482857322L);
+		expected.add(4487790251341705673L);
+		expected.add(0L);
+		expected.add(4760603841378765728L);
+		expected.add(0L);
+		expected.add(4897534821030901381L);
+		expected.add(5057347369431494228L);
+		expected.add(5185984701076703188L);
+		expected.add(5255556356599253415L);
+		expected.add(4911921657882287345L);
+		expected.add(5512811886280168498L);
+		expected.add(5627022814159167180L);
+		expected.add(5630009759945037387L);
+		expected.add(5592096823142754761L);
+		expected.add(5880489878946290534L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6796173646113527960L);
+		expected.add(6887096685265647763L);
+		expected.add(6946033094922439935L);
+		expected.add(7100083311060830826L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(7575172208974668528L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8240485391672917634L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8572429495433200993L);
+		expected.add(0L);
+		expected.add(8804495173596718076L);
+		expected.add(8771524479740786626L);
+		expected.add(8986659781390119011L);
+		expected.add(9136953010061430590L);
+		expected.add(9195197379878056627L);		
+		
+		doTest(expected);
+	}
+	
+	@Test
+	public void test6() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(1L);
+		expected.add(9185197375878056627L);
+		expected.add(9017810141411942826L);
+		expected.add(0L);
+		expected.add(481170446028802552L);
+		expected.add(587723185270146839L);
+		expected.add(0L);
+		expected.add(764880467681476738L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1028380228728529428L);
+		expected.add(0L);
+		expected.add(1246117495100367611L);
+		expected.add(1353681884824400499L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(1963327988900916594L);
+		expected.add(0L);
+		expected.add(2157942654452711468L);
+		expected.add(2211701751588391467L);
+		expected.add(2197266581704230150L);
+		expected.add(2391118405386569995L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(2754416910109403115L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(3528296600587602855L);
+		expected.add(0L);
+		expected.add(3766154305485605955L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(4172091881329434331L);
+		expected.add(4273360576593753745L);
+		expected.add(4338054185482857322L);
+		expected.add(4487790251341705673L);
+		expected.add(0L);
+		expected.add(4760603841378765728L);
+		expected.add(0L);
+		expected.add(4897534821030901381L);
+		expected.add(5057347369431494228L);
+		expected.add(5185984701076703188L);
+		expected.add(5255556356599253415L);
+		expected.add(4911921657882287345L);
+		expected.add(5512811886280168498L);
+		expected.add(5627022814159167180L);
+		expected.add(5630009759945037387L);
+		expected.add(5592096823142754761L);
+		expected.add(5880489878946290534L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(6796173646113527960L);
+		expected.add(6887096685265647763L);
+		expected.add(6946033094922439935L);
+		expected.add(7100083311060830826L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(7575172208974668528L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8240485391672917634L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(8572429495433200993L);
+		expected.add(0L);
+		expected.add(8804495173596718076L);
+		expected.add(8771524479740786626L);
+		expected.add(8986659781390119011L);
+		expected.add(9136953010061430590L);
+		expected.add(9195197379878056627L);		
+		
+		doTest(expected);
+	}
+	
+	@Test
+	public void test7() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(1L);
+		expected.add(0L);
+		expected.add(4L);
+		expected.add(0L);
+		expected.add(6L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(13L);
+		
+		doTest(expected, 1, 0, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13));
+	}
+	
+	@Test
+	public void test8() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(1L);
+		expected.add(11L);
+		expected.add(3L);
+		expected.add(4L);
+		expected.add(5L);
+		expected.add(6L);
+		expected.add(7L);
+		expected.add(8L);
+		expected.add(9L);
+		expected.add(10L);
+		expected.add(12L);
+		
+		final OffHeapDiskFPSet.Indexer indexer = new OffHeapDiskFPSet.Indexer(expected.size(), 1, 12);
+		final LongArray array = new LongArray(expected);
+		final LongComparator comparator = getComparator(indexer);
+		LongArrays.sort(array, 0, array.size() - 1L + 3, comparator);
+		verify(expected, 3, indexer, array);
+
+	}
+	
+	@Test
+	public void test9a() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(12L);
+		expected.add(1L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(11L);
+		
+		doTest(expected, 1, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13));
+	}
+	
+	@Test
+	public void test9b() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(11L);
+		expected.add(1L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(12L);
+		
+		doTest(expected, 1, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13));
+	}
+	
+	@Test
+	public void test9c() {
+		final List<Long> expected = new ArrayList<Long>();
+		expected.add(1L);
+		expected.add(12L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(0L);
+		expected.add(11L);
+		
+		doTest(expected, 1, 3, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13));
+	}
+	
+	private void doTest(final List<Long> expected) {
+		final Indexer indexer = new OffHeapDiskFPSet.Indexer(expected.size(), 1);
+		for (int i = 1; i < (expected.size() / 2); i++) {
+			doTest(expected, i, 2, indexer);
+		}
+	}
+	
+	private void doTest(final  List<Long>  expected, final long partitions, final int reprobe, final Indexer indexer) {
+		final LongArray array = new LongArray(expected);
+		final LongComparator comparator = getComparator(indexer);
+		final long length = expected.size() / partitions;
+		
+		// Sort each disjunct partition.
+		for (long i = 0; i < partitions; i++) {
+			final long start = i * length;
+			final long end = i + 1L == partitions ? array.size() - 1L: start + length;
+			LongArrays.sort(array, start, end, comparator);
+		}
+		// Stitch the disjunct partitions together. Only need if more than one
+		// partition, but done with one partition anyway to see that it causes
+		// no harm.
+		for (long i = 0; i < partitions; i++) {
+			final long end = getEnd(partitions, array, length, i);
+			LongArrays.sort(array, end - reprobe, end + reprobe, comparator);
+		}
+		
+		verify(expected, reprobe, indexer, array);
+	}
+
+	private long getEnd(final long partitions, final LongArray array, final long length, long idx) {
+		return idx + 1L == partitions ? array.size() - 1L: (idx + 1L) * length;
+	}
+
+	private static LongComparator getComparator(final Indexer indexer) {
+		return new LongComparator() {
+			public int compare(final long fpA, final long posA, final long fpB, final long posB) {
+				// Elements not in Nat \ {0} remain at their current
+				// position.
+				if (fpA <= EMPTY || fpB <= EMPTY) {
+					return 0;
+				}
+				
+				final boolean wrappedA = indexer.getIdx(fpA) > posA;
+				final boolean wrappedB = indexer.getIdx(fpB) > posB;
+				
+				if (wrappedA == wrappedB && posA > posB) {
+					return fpA < fpB ? -1 : 1;
+				} else if ((wrappedA ^ wrappedB)) {
+					if (posA < posB && fpA < fpB) {
+						// Swap fpB, which is at the end of array a, with fpA.
+						// fpA is less than fpB. fpB was inserted into array a
+						// before fpA.
+						return -1;
+					}
+					if (posA > posB && fpA > fpB) {
+						return -1;
+					}
+				}
+				return 0;
+			}
+		};
+	}
+
+	private void verify(final List<Long> expected, final int reprobe, final Indexer indexer, final LongArray array) {
+		// Verify that negative and EMPTY elements remain at their position.
+		// Lets call them sentinels.
+		int sentinel = 0;
+		OUTER: for (int j = 0; j < expected.size(); j++) {
+			final long l = expected.get(j);
+			if (l == EMPTY) {
+				// EMPTY remain at their original positions.
+				assertEquals(EMPTY, array.get(j));
+				sentinel++;
+			} else if (l < EMPTY) {
+				// Negative remain at their original positions.
+				assertEquals(l, array.get(j));
+				sentinel++;
+			} else {
+				// Verify that all non-sentinels are still
+				// array members.
+				for (int k = 0; k < array.size(); k++) {
+					if (array.get(k) == l) {
+						continue OUTER;
+					}
+				}
+				fail(String.format("long %s not found.", l));
+			}
+		}
+		
+		// Verify elements stayed within their lookup range.
+		for (int pos = 0; pos < array.size(); pos++) {
+			final long l = array.get(pos);
+			if (l <= EMPTY) {
+				continue;
+			}
+			final long idx = indexer.getIdx(l);
+			assertTrue(String.format("%s, pos: %s, idx: %s, r: %s (was at: %s)", l, pos, idx, reprobe,
+					expected.indexOf(l)), isInRange(idx, reprobe, pos, array.size()));
+		}
+		
+		// Verify that non-sentinels are sorted is ascending order. Take
+		// care of wrapped elements too. A) First find the first non-sentinel,
+		// non-wrapped element.
+		long pos = 0;
+		final List<Long> seen = new ArrayList<Long>(expected.size());
+		while (pos < array.size()) {
+			long e = array.get(pos);
+			if (e <= EMPTY || indexer.getIdx(e) > pos) {
+				// Either sentinel or wrapped.
+				pos++;
+				continue;
+			}
+			seen.add(e);
+			pos++;
+			break;
+		}
+		// B) Collect all elements into seen but skip those at the beginning that
+		// wrapped, and those that didn't wrap at the end (array.size + reprobe).
+		for (; pos < array.size() + reprobe; pos++) {
+			long actual = array.get(pos % array.size());
+			if (actual <= EMPTY) {
+				continue;
+			}
+			final long idx = indexer.getIdx(actual);
+			if (pos < array.size() && idx > pos) {
+				// When not wrapped, ignore elements belonging to the end that wrapped.
+				continue;
+			}
+			if (pos > array.size() - 1L && idx + reprobe < pos) {
+				// When wrapped, ignore elements at beginning which do not
+				// belong to the end.
+				continue;
+			}
+			seen.add(actual);
+		}
+		// C) Verify that all elements are sorted.
+		for (int i = 1; i < seen.size(); i++) {
+			final long lo = seen.get(i - 1);
+			final long hi = seen.get(i);
+			assertTrue(String.format("%s > %s", lo, hi), lo < hi);
+		}
+		// D) Verify we saw all expected elements.
+		assertEquals(expected.size() - sentinel, seen.size());
+	}
+	
+	@Test
+	public void testIsInRange() {
+		assertTrue(isInRange(0, 0, 0, 4));
+
+		assertFalse(isInRange(0, 0, 1, 4));
+		assertFalse(isInRange(0, 0, 2, 4));
+		assertFalse(isInRange(0, 0, 3, 4));
+		assertFalse(isInRange(0, 0, 4, 4));
+
+		assertTrue(isInRange(0, 1, 1, 4));
+		assertFalse(isInRange(0, 1, 2, 4));
+		assertTrue(isInRange(0, 2, 2, 4));
+		assertFalse(isInRange(0, 2, 3, 4));
+		assertTrue(isInRange(0, 3, 3, 4));
+		assertFalse(isInRange(0, 3, 4, 4));
+		
+		assertTrue(isInRange(3, 0, 3, 4));
+		assertTrue(isInRange(3, 1, 0, 4));
+		assertTrue(isInRange(3, 2, 1, 4));
+		assertFalse(isInRange(3, 2, 2, 4));
+	}
+
+	private static boolean isInRange(long idx, int reprobe, int pos, long size) {
+		if (idx + reprobe >= size && pos < idx) {
+			return pos <= (idx + reprobe) % size;
+		} else {
+			return idx <= pos && pos <= idx + reprobe;
+		}
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java b/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java
index e63d7d08339e639a8ac6905d8ec5e82931e0d13f..ad9daec5719ec4182332b68f91626f695ac0d237 100644
--- a/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java
+++ b/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java
@@ -1,10 +1,17 @@
 // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.rmi.RemoteException;
 import java.util.NoSuchElementException;
 
+import org.junit.Test;
+
 import tlc2.tool.fp.MSBDiskFPSet.TLCIterator;
 
 public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest {
@@ -30,6 +37,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest {
 		return 1L << 31;
 	}
 	
+	@Test
 	public void testGetLast() throws IOException {
 		final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet();
 		
@@ -62,6 +70,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest {
 		fail();
 	}
 
+	@Test
 	public void testHighFingerprint1() throws RemoteException, IOException {
 		final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet();
 		assertFalse(msbDiskFPSet.put(9223368718049406096L));
@@ -71,6 +80,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest {
 		assertTrue(msbDiskFPSet.put(9223368718049406096L));
 	}
 
+	@Test
 	public void testHighFingerprint2() throws RemoteException, IOException {
 		final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet();
 		assertFalse(msbDiskFPSet.put(9223335424116589377L));
@@ -83,6 +93,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest {
 	/*
 	 * Try to get the last element with no elements in the set.
 	 */
+	@Test
 	public void testGetLastNoBuckets() throws IOException {
 		final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet();
 		
@@ -95,39 +106,6 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest {
 		}
 		fail();
 	}
-	
-	public void testGetLastWithLowerBound() throws IOException {
-		final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet();
-		
-		// Add the largest possible fingerprint into the fpset. It will end up
-		// in the largest bucket. Check that the MSB iterator returns it.
-		final long highFP = 1L << 62;
-		msbDiskFPSet.put(highFP);
-		TLCIterator tlcIterator = new MSBDiskFPSet.TLCIterator(msbDiskFPSet.tbl);
-		assertEquals(highFP, tlcIterator.getLast());
-		
-		// Flush the set to disk (simulating e.g. a checkpoint), a new iterator
-		// won't find the element anymore because it intentionally only searches
-		// for elements that are *not* on disk.
-		msbDiskFPSet.flusher.flushTable();
-		new MSBDiskFPSet.TLCIterator(msbDiskFPSet.tbl);
-		try {
-			tlcIterator.getLast();
-		} catch (NoSuchElementException e) {
-			// This exception is expected.
-			
-			// Now add the smallest possible element into the set. It will end
-			// up in the smallest bucket.
-			final long lowFP = 1;
-			msbDiskFPSet.put(lowFP);
-			// check that the iterator finds lower bound as the last element.
-			tlcIterator = new MSBDiskFPSet.TLCIterator(msbDiskFPSet.tbl);
-			final long lowerBound = highFP - 1L;
-			assertEquals(lowerBound, tlcIterator.getLast(lowerBound));
-			return;
-		}
-		fail();
-	}
 
 	private MSBDiskFPSet getMSBDiskFPSet() throws RemoteException, IOException {
 		// Create an MSBDiskFPSet usable in this unit test with memory allocated
diff --git a/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java b/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java
index 1ed2997cb7a6991c13d1d34f01ff8fefcf52680b..3800893be9604d1b3b65b70d14ae5091bf082b9e 100644
--- a/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java
@@ -1,28 +1,42 @@
 // Copyright (c) 2011 Microsoft Corporation.  All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.fail;
+
+import java.io.File;
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
 
-import junit.framework.TestCase;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * @author Markus Alexander Kuppe
  */
-public class MultiFPSetTest extends TestCase {
+public class MultiFPSetTest {
+
+	protected static final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "MultiFPSetTest"
+			+ System.currentTimeMillis();
 
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#setUp()
 	 */
-	protected void setUp() throws Exception {
-		super.setUp();
+	@Before
+	public void setUp() throws Exception {
+		new File(tmpdir).mkdirs();
 	}
 
 	/**
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#new}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testCTorLowerMin() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, MemFPSet.class.getName());
 		try {
+			System.setProperty(FPSetFactory.IMPL_PROPERTY, MemFPSet.class.getName());
 			FPSetConfiguration conf = new FPSetConfiguration();
 			conf.setFpBits(0);
 			new MultiFPSet(conf);
@@ -36,6 +50,7 @@ public class MultiFPSetTest extends TestCase {
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#new}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testCTorMin() throws IOException {
 		try {
 			FPSetConfiguration conf = new FPSetConfiguration();
@@ -51,6 +66,7 @@ public class MultiFPSetTest extends TestCase {
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#new}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testCTorMax() throws IOException {
 		try {
 			FPSetConfiguration conf = new FPSetConfiguration();
@@ -77,6 +93,7 @@ public class MultiFPSetTest extends TestCase {
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#new}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testCTorHigherMax() throws IOException {
 		try {
 			FPSetConfiguration conf = new FPSetConfiguration();
@@ -92,6 +109,7 @@ public class MultiFPSetTest extends TestCase {
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#put(long)}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testPutMax() throws IOException {
 		FPSetConfiguration conf = new FPSetConfiguration();
 		conf.setFpBits(1);
@@ -109,6 +127,7 @@ public class MultiFPSetTest extends TestCase {
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#put(long)}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testPutMin() throws IOException {
 		FPSetConfiguration conf = new FPSetConfiguration();
 		conf.setFpBits(1);
@@ -126,6 +145,7 @@ public class MultiFPSetTest extends TestCase {
 	 * Test method for {@link tlc2.tool.fp.MultiFPSet#put(long)}.
 	 * @throws IOException Not supposed to happen
 	 */
+	@Test
 	public void testPutZero() throws IOException {
 		FPSetConfiguration conf = new FPSetConfiguration();
 		conf.setFpBits(1);
@@ -138,4 +158,623 @@ public class MultiFPSetTest extends TestCase {
 			fail();
 		}
 	}
+	
+	@Test
+	public void testGetFPSet() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(1);
+		
+		MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSet");
+		
+		final long a = (1L << 62) + 1; // 01...0
+		printBinaryString("a01...1", a);
+		final long b = 1L; // 0...1
+		printBinaryString("b00...1", b);
+		
+		FPSet aFPSet = mfps.getFPSet(a);
+		Assert.assertTrue(aFPSet == mfps.getFPSet(b));
+		
+		// Initially neither a nor b are in the set.
+		Assert.assertFalse(aFPSet.contains(a));
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add a to the set and verify it's in the
+		// set and b isn't.
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add b to the set as well. Now both
+		// are supposed to be set members.
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+
+		Assert.assertTrue(aFPSet.contains(a));
+		Assert.assertTrue(aFPSet.contains(b));
+		Assert.assertEquals(2, aFPSet.size());
+		
+		// Get the other FPSet
+		FPSet[] fpSets = mfps.getFPSets();
+		Set<FPSet> s = new HashSet<FPSet>();
+		for (int i = 0; i < fpSets.length; i++) {
+			s.add(fpSets[i]);
+		}
+		s.remove(aFPSet);
+		FPSet bFPSet = (FPSet) s.toArray()[0];
+		
+		Assert.assertFalse(bFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(b));
+		Assert.assertEquals(0, bFPSet.size());
+		
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+
+	@Test
+	public void testGetFPSet0() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(1);
+		
+		MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSet0");
+		
+		final long a = (1L << 63) + 1; // 10...1
+		printBinaryString("a1...1", a);
+		final long b = 1L;             // 00...1
+		printBinaryString("b0...1", b);
+		final long c = (1L << 62) + 1; // 01...1
+		printBinaryString("c1...1", c);
+		final long d = (3L << 62) + 1; // 11...1
+		printBinaryString("d0...1", d);
+		
+		FPSet aFPSet = mfps.getFPSet(a);
+		FPSet bFPSet = mfps.getFPSet(b);
+		Assert.assertTrue(aFPSet != bFPSet);
+		
+		// Initially neither a nor b are in the set.
+		Assert.assertFalse(aFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(b));
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		// Add a to the set and verify it's in the
+		// set and b isn't.
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		// Add b to the set as well. Now both
+		// are supposed to be set members.
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(c));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+		
+		Assert.assertFalse(mfps.put(d));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertTrue(mfps.contains(d));
+		
+		for (FPSet fpSet : mfps.getFPSets()) {
+			Assert.assertEquals(2, fpSet.size());
+			// Expect to have two buckets
+			Assert.assertEquals(2, ((FPSetStatistic) fpSet).getTblLoad());
+		}
+		
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+	
+	@Test
+	public void testGetFPSet1() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(2);
+		final MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSet1");
+		
+		final long a = 1L; // 00...1
+		printBinaryString("a02", a);
+		final long b = (1L << 62) + 1; // 01...1
+		printBinaryString("b02", b);
+		final long c = (1L << 63) + 1; // 10...1
+		printBinaryString("c02", c);
+		final long d = (3L << 62) + 1; // 11...1
+		printBinaryString("d02", d);
+		
+		final Set<FPSet> s = new HashSet<FPSet>();
+		final FPSet aFPSet = mfps.getFPSet(a);
+		s.add(aFPSet);
+		final FPSet bFPSet = mfps.getFPSet(b);
+		s.add(bFPSet);
+		final FPSet cFPSet = mfps.getFPSet(c);
+		s.add(cFPSet);
+		final FPSet dFPSet = mfps.getFPSet(d);
+		s.add(dFPSet);
+		Assert.assertEquals(4, s.size());
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(c));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(d));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertTrue(mfps.contains(d));
+		
+		for (FPSet fpSet : s) {
+			Assert.assertEquals(1, fpSet.size());
+			// Expect to have two buckets
+			Assert.assertEquals(1, ((FPSetStatistic) fpSet).getTblLoad());
+		}
+		
+		// a & c and b & d have collisions at the individual DiskFPSet level.
+		Assert.assertTrue(aFPSet.contains(a));
+		Assert.assertFalse(aFPSet.contains(b));
+		Assert.assertTrue(aFPSet.contains(c)); // expected collision
+		Assert.assertFalse(aFPSet.contains(d));
+		
+		Assert.assertTrue(bFPSet.contains(b));
+		Assert.assertFalse(bFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(c));
+		Assert.assertTrue(bFPSet.contains(d)); // expected collision
+
+		Assert.assertTrue(cFPSet.contains(c));
+		Assert.assertFalse(cFPSet.contains(b));
+		Assert.assertTrue(cFPSet.contains(a)); // expected collision
+		Assert.assertFalse(cFPSet.contains(d));
+
+		Assert.assertTrue(dFPSet.contains(d));
+		Assert.assertTrue(dFPSet.contains(b)); // expected collision
+		Assert.assertFalse(dFPSet.contains(c));
+		Assert.assertFalse(dFPSet.contains(a));
+
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+
+	@Test
+	public void testGetFPSetL() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(1);
+		
+		MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSetL");
+		
+		final long a = (1L << 62) + 1;
+		printBinaryString("a01", a);
+		final long b = 1L;
+		printBinaryString("b01", b);
+		
+		FPSet aFPSet = mfps.getFPSet(a);
+		Assert.assertTrue(aFPSet == mfps.getFPSet(b));
+		
+		// Initially neither a nor b are in the set.
+		Assert.assertFalse(aFPSet.contains(a));
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add a to the set and verify it's in the
+		// set and b isn't.
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add b to the set as well. Now both
+		// are supposed to be set members.
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+
+		Assert.assertTrue(aFPSet.contains(a));
+		Assert.assertTrue(aFPSet.contains(b));
+		Assert.assertEquals(2, aFPSet.size());
+		
+		// Get the other FPSet
+		FPSet[] fpSets = mfps.getFPSets();
+		Set<FPSet> s = new HashSet<FPSet>();
+		for (int i = 0; i < fpSets.length; i++) {
+			s.add(fpSets[i]);
+		}
+		s.remove(aFPSet);
+		FPSet bFPSet = (FPSet) s.toArray()[0];
+		
+		Assert.assertFalse(bFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(b));
+		Assert.assertEquals(0, bFPSet.size());
+
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+
+	@Test
+	public void testGetFPSet0L() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(1);
+		
+		MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSet0L");
+		
+		final long a = (1L << 63) + 1;
+		printBinaryString("a01", a);
+		final long b = 1L;
+		printBinaryString("b01", b);
+		
+		FPSet aFPSet = mfps.getFPSet(a);
+		FPSet bFPSet = mfps.getFPSet(b);
+		Assert.assertTrue(aFPSet != bFPSet);
+		
+		// Initially neither a nor b are in the set.
+		Assert.assertFalse(aFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(b));
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add a to the set and verify it's in the
+		// set and b isn't.
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add b to the set as well. Now both
+		// are supposed to be set members.
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+	
+	@Test
+	public void testGetFPSet1L() throws IOException {
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(2);
+		final MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSet1L");
+		
+		final long a = 1L; // 00...1
+		printBinaryString("a02", a);
+		final long b = (1L << 62) + 1; // 01...1
+		printBinaryString("b02", b);
+		final long c = (1L << 63) + 1; // 10...1
+		printBinaryString("c02", c);
+		final long d = (3L << 62) + 1; // 11...1
+		printBinaryString("d02", d);
+		
+		final Set<FPSet> s = new HashSet<FPSet>();
+		final FPSet aFPSet = mfps.getFPSet(a);
+		s.add(aFPSet);
+		final FPSet bFPSet = mfps.getFPSet(b);
+		s.add(bFPSet);
+		final FPSet cFPSet = mfps.getFPSet(c);
+		s.add(cFPSet);
+		final FPSet dFPSet = mfps.getFPSet(d);
+		s.add(dFPSet);
+		Assert.assertEquals(4, s.size());
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(c));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(d));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertTrue(mfps.contains(d));
+		
+		for (FPSet fpSet : s) {
+			Assert.assertEquals(1, fpSet.size());
+		}
+		
+		// a & c and b & d have collisions at the individual DiskFPSet level.
+		Assert.assertTrue(aFPSet.contains(a));
+		Assert.assertFalse(aFPSet.contains(b));
+		Assert.assertTrue(aFPSet.contains(c)); // expected collision
+		Assert.assertFalse(aFPSet.contains(d));
+		
+		Assert.assertTrue(bFPSet.contains(b));
+		Assert.assertFalse(bFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(c));
+		Assert.assertTrue(bFPSet.contains(d)); // expected collision
+
+		Assert.assertTrue(cFPSet.contains(c));
+		Assert.assertFalse(cFPSet.contains(b));
+		Assert.assertTrue(cFPSet.contains(a)); // expected collision
+		Assert.assertFalse(cFPSet.contains(d));
+
+		Assert.assertTrue(dFPSet.contains(d));
+		Assert.assertTrue(dFPSet.contains(b)); // expected collision
+		Assert.assertFalse(dFPSet.contains(c));
+		Assert.assertFalse(dFPSet.contains(a));
+
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+	
+	@Test
+	public void testGetFPSetOffHeap() throws IOException {
+		if (!System.getProperty("sun.arch.data.model").equals("64")) {
+			// LongArray only works on 64bit architectures. See comment in
+			// LongArray ctor.
+			return;
+		}
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(1);
+		
+		MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSetOffHeap");
+		
+		final long a = (1L << 62) + 1; // 01...0
+		printBinaryString("a01...1", a);
+		final long b = 1L; // 0...1
+		printBinaryString("b00...1", b);
+		
+		FPSet aFPSet = mfps.getFPSet(a);
+		Assert.assertTrue(aFPSet == mfps.getFPSet(b));
+		
+		// Initially neither a nor b are in the set.
+		Assert.assertFalse(aFPSet.contains(a));
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add a to the set and verify it's in the
+		// set and b isn't.
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+
+		// Add b to the set as well. Now both
+		// are supposed to be set members.
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+
+		Assert.assertTrue(aFPSet.contains(a));
+		Assert.assertTrue(aFPSet.contains(b));
+		Assert.assertEquals(2, aFPSet.size());
+		
+		// Get the other FPSet
+		FPSet[] fpSets = mfps.getFPSets();
+		Set<FPSet> s = new HashSet<FPSet>();
+		for (int i = 0; i < fpSets.length; i++) {
+			s.add(fpSets[i]);
+		}
+		s.remove(aFPSet);
+		FPSet bFPSet = (FPSet) s.toArray()[0];
+		
+		Assert.assertFalse(bFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(b));
+		Assert.assertEquals(0, bFPSet.size());
+		
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+
+	@Test
+	public void testGetFPSetOffHeap0() throws IOException {
+		if (!System.getProperty("sun.arch.data.model").equals("64")) {
+			// LongArray only works on 64bit architectures. See comment in
+			// LongArray ctor.
+			return;
+		}
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(1);
+		
+		MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSetOffHeap0");
+		
+		final long a = (1L << 63) + 1; // 10...1
+		printBinaryString("a1...1", a);
+		final long b = 1L;             // 00...1
+		printBinaryString("b0...1", b);
+		final long c = (1L << 62) + 1; // 01...1
+		printBinaryString("c1...1", c);
+		final long d = (3L << 62) + 1; // 11...1
+		printBinaryString("d0...1", d);
+		
+		FPSet aFPSet = mfps.getFPSet(a);
+		FPSet bFPSet = mfps.getFPSet(b);
+		Assert.assertTrue(aFPSet != bFPSet);
+		
+		// Initially neither a nor b are in the set.
+		Assert.assertFalse(aFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(b));
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		// Add a to the set and verify it's in the
+		// set and b isn't.
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		// Add b to the set as well. Now both
+		// are supposed to be set members.
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(c));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+		
+		Assert.assertFalse(mfps.put(d));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertTrue(mfps.contains(d));
+		
+		for (FPSet fpSet : mfps.getFPSets()) {
+			Assert.assertEquals(2, fpSet.size());
+			// Expect to have two buckets
+			Assert.assertEquals(2, ((FPSetStatistic) fpSet).getTblLoad());
+		}
+		
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+	
+	@Test
+	public void testGetFPSetOffHeap1() throws IOException {
+		if (!System.getProperty("sun.arch.data.model").equals("64")) {
+			// LongArray only works on 64bit architectures. See comment in
+			// LongArray ctor.
+			return;
+		}
+		System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName());
+		final FPSetConfiguration conf = new FPSetConfiguration();
+		conf.setFpBits(2);
+		final MultiFPSet mfps = new MultiFPSet(conf);
+		mfps.init(1, tmpdir, "testGetFPSetOffHeap1");
+		
+		final long a = 1L; // 00...1
+		printBinaryString("a02", a);
+		final long b = (1L << 62) + 1; // 01...1
+		printBinaryString("b02", b);
+		final long c = (1L << 63) + 1; // 10...1
+		printBinaryString("c02", c);
+		final long d = (3L << 62) + 1; // 11...1
+		printBinaryString("d02", d);
+		
+		final Set<FPSet> s = new HashSet<FPSet>();
+		final FPSet aFPSet = mfps.getFPSet(a);
+		s.add(aFPSet);
+		final FPSet bFPSet = mfps.getFPSet(b);
+		s.add(bFPSet);
+		final FPSet cFPSet = mfps.getFPSet(c);
+		s.add(cFPSet);
+		final FPSet dFPSet = mfps.getFPSet(d);
+		s.add(dFPSet);
+		Assert.assertEquals(4, s.size());
+		
+		Assert.assertFalse(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(a));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertFalse(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(b));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertFalse(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(c));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertFalse(mfps.contains(d));
+
+		Assert.assertFalse(mfps.put(d));
+		Assert.assertTrue(mfps.contains(a));
+		Assert.assertTrue(mfps.contains(b));
+		Assert.assertTrue(mfps.contains(c));
+		Assert.assertTrue(mfps.contains(d));
+		
+		for (FPSet fpSet : s) {
+			Assert.assertEquals(1, fpSet.size());
+			// Expect to have two buckets
+			Assert.assertEquals(1, ((FPSetStatistic) fpSet).getTblLoad());
+		}
+		
+		// a & c and b & d have collisions at the individual DiskFPSet level.
+		Assert.assertTrue(aFPSet.contains(a));
+		Assert.assertFalse(aFPSet.contains(b));
+		Assert.assertTrue(aFPSet.contains(c)); // expected collision
+		Assert.assertFalse(aFPSet.contains(d));
+		
+		Assert.assertTrue(bFPSet.contains(b));
+		Assert.assertFalse(bFPSet.contains(a));
+		Assert.assertFalse(bFPSet.contains(c));
+		Assert.assertTrue(bFPSet.contains(d)); // expected collision
+
+		Assert.assertTrue(cFPSet.contains(c));
+		Assert.assertFalse(cFPSet.contains(b));
+		Assert.assertTrue(cFPSet.contains(a)); // expected collision
+		Assert.assertFalse(cFPSet.contains(d));
+
+		Assert.assertTrue(dFPSet.contains(d));
+		Assert.assertTrue(dFPSet.contains(b)); // expected collision
+		Assert.assertFalse(dFPSet.contains(c));
+		Assert.assertFalse(dFPSet.contains(a));
+
+		Assert.assertTrue(mfps.checkInvariant());
+	}
+
+	private void printBinaryString(final String id, final long a) {
+//		System.out.println(String.format(id + ":%64s", Long.toBinaryString(a)).replace(' ', '0'));
+	}
 }
diff --git a/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3980851c00b6fc2fde73661eed700e38dddba699
--- /dev/null
+++ b/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static tlc2.tool.fp.DiskFPSet.MARK_FLUSHED;
+import static tlc2.tool.fp.OffHeapDiskFPSet.EMPTY;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.rmi.RemoteException;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import util.TLCRuntime;
+
+public class OffHeapDiskFPSetTest {
+	
+	protected static final String filename = "OffHeapDiskFPSetTest";
+	
+	@Before
+	public void setup() {
+		Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64);
+	}
+
+//	@Test
+//	public void testInsertAndEvictRnd() throws Exception {
+//		Random rnd = new Random();
+//		for (int i = 0; i < 1000; i++) {
+//			doTest(System.currentTimeMillis(), rnd.nextInt(255) + 1);
+//		}
+//	}
+	
+	@Test
+	public void testInsertAndEvict1() throws Exception {
+		doTest(1473793977852L, 87);
+	}
+	
+	@Test
+	public void testInsertAndEvict2() throws Exception {
+		doTest(1473793976137L, 87);
+	}
+	
+	@Test
+	public void testInsertAndEvict3() throws Exception {
+		doTest(1473839150698L, 46);
+	}
+	
+	@Test
+	public void testInsertAndEvict4() throws Exception {
+		doTest(1473839150698L, 46);
+	}
+	
+	@Test
+	public void testInsertAndEvict5() throws Exception {
+		doTest(1473839322351L, 23);
+	}
+
+	@Test
+	public void testInsertAndEvict6() throws Exception {
+		doTest(1473839380539L, 23);
+	}
+
+	@Test
+	public void testInsertAndEvict7() throws Exception {
+		doTest(1473839422899L, 11);
+	}
+
+	@Test
+	public void testInsertAndEvict8() throws Exception {
+		doTest(1473839543883L, 11);
+	}
+	
+	@Test
+	public void testInsertAndEvict9() throws Exception {
+		doTest(1473871461079L, 64);
+	}
+
+	@Test
+	public void testInsertAndEvict10() throws Exception {
+		doTest(1473871462765L, 64);
+	}
+
+	@Test
+	public void testInsertAndEvict11() throws Exception {
+		doTest(1473871522834L, 32);
+	}
+	
+	@Test
+	public void testInsertAndEvict12() throws Exception {
+		doTest(1473871526136L, 32);
+	}
+
+	@Test
+	public void testInsertAndEvict13() throws Exception {
+		doTest(1473873732723L, 47);
+	}
+
+	@Test
+	public void testInsertAndEvict14() throws Exception {
+		doTest(1473871294851L, 93);
+	}
+
+	@Test
+	public void testInsertAndEvict15() throws Exception {
+		doTest(1473871365625L, 93);
+	}
+
+	@Test
+	public void testInsertAndEvict16() throws Exception {
+		doTest(1473871209569L, 157);
+	}
+
+	private void doTest(final long rgenseed, final long length) throws RemoteException, IOException, NoSuchFieldException, IllegalAccessException {
+		final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration();
+		fpSetConfig.setMemoryInFingerprintCnt(length);
+		
+		final DiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig);
+		fpSet.init(1, createTmpFile(), filename);
+
+		// Insert n randomly choosen positive longs.
+		Random random = new Random(rgenseed);
+		for (int i = 0; i < length / 2; i++) {
+			assertFalse(fpSet.put(getFingerprint(random)));
+		}
+
+		// Get the current content of LongArray for later comparison of special elements.
+		Field field = OffHeapDiskFPSet.class.getDeclaredField("array");
+		field.setAccessible(true);
+		final long[] expected = LongArrays.toArray((LongArray) field.get(fpSet));
+		
+		// Flush/Evict the first time and assure its successful.
+		assertTrue(fpSet.getGrowDiskMark() == 0);
+		fpSet.forceFlush();
+		fpSet.contains(1L); // contains triggers eviction
+		assertTrue(fpSet.getGrowDiskMark() == 1);
+		
+		// Special elements (EMPTY or marked evicted) do not change positions
+		// when sorted.
+		final LongArray actual = (LongArray) field.get(fpSet);
+		for (int i = 0; i < expected.length; i++) {
+			if (expected[i] == EMPTY) {
+				assertEquals(
+						String.format(
+								"Expected empty position with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s",
+								new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }),
+						EMPTY, actual.get(i));
+			} else if (expected[i] < EMPTY) {
+				assertEquals(
+						String.format(
+								"Expected negative position with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s",
+								new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }),
+						EMPTY, actual.get(i));
+			}
+		}
+		
+		random = new Random(rgenseed);
+		for (int i = 0; i < length / 2; i++) {
+			final long fp = getFingerprint(random);
+			assertTrue(String.format("Failed to find fp %s/%s with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s",
+					new Object[] { fp, (fp | MARK_FLUSHED), rgenseed, length, Arrays.toString(expected),
+							actual.toString() }),
+					fpSet.contains(fp));
+		}
+		
+		assertTrue(
+				String.format("Invariant violated with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s",
+						new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }),
+				fpSet.checkInvariant());
+
+		// Clear the index to secondary/disk which hides secondary from
+		// DiskFPSet.
+		field = DiskFPSet.class.getDeclaredField("index");
+		field.setAccessible(true);
+		field.set(fpSet, null);
+
+		// Confirm that even without secondary, it's possible to lookup the
+		// fingerprints.
+		random = new Random(rgenseed);
+		for (int i = 0; i < length / 2; i++) {
+			final long fp = getFingerprint(random);
+			assertTrue(String.format("Failed to find fp %s/%s with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s",
+					new Object[] { fp, (fp | MARK_FLUSHED), rgenseed, length, Arrays.toString(expected),
+							actual.toString() }),
+					fpSet.contains(fp));
+		}
+		
+		fpSet.close();
+	}
+	
+	@Test
+	public void testOffset1Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		final long length = DiskFPSet.NumEntriesPerPage;
+		doTestOffset(length, 1474536306841L);
+	}
+	
+	@Test
+	public void testOffset3Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		long length = DiskFPSet.NumEntriesPerPage * 3L;
+		doTestOffset(length, 1474536306841L);
+	}
+	
+	@Test
+	public void testOffset5Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		long length = DiskFPSet.NumEntriesPerPage * 5L;
+		doTestOffset(length, 1474536306841L);
+	}
+	
+	@Test
+	public void testOffset9Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		long length = DiskFPSet.NumEntriesPerPage * 9L;
+		doTestOffset(length, 1474536306841L);
+	}
+
+	private void doTestOffset(long length, long rgenseed) throws RemoteException, IOException, NoSuchMethodException,
+			IllegalAccessException, InvocationTargetException {
+		
+		final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration();
+		fpSetConfig.setMemoryInFingerprintCnt(length);
+		
+		final OffHeapDiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig);
+		fpSet.init(1, createTmpFile(), filename);
+
+		final SortedSet<Long> longs = new TreeSet<Long>();
+		
+		// Insert n randomly chosen positive longs.
+		Random random = new Random(rgenseed);
+		for (int i = 0; i < length / 2; i++) {
+			long fingerprint = getFingerprint(random);
+			assertFalse(fpSet.put(fingerprint));
+			longs.add(fingerprint);
+		}
+		fpSet.forceFlush();
+		assertFalse(fpSet.contains(1L)); // contains triggers flush
+		
+		final Method field = OffHeapDiskFPSet.class.getDeclaredMethod("getDiskOffset", new Class[] {int.class, long.class});
+		field.setAccessible(true);
+		
+		for (long i = 0L; i < longs.size(); i++) {
+			long fp = longs.first();
+			assertEquals(String.format("Length: %s with seed: %s", length, rgenseed), i + 1L,
+					field.invoke(fpSet, 0, fp + 1L));
+			longs.remove(fp);
+		}
+	}
+	
+	private static String createTmpFile() {
+		final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "OffHeapDiskFPSetTest"
+				+ System.currentTimeMillis();
+		new File(tmpdir).mkdirs();
+		return tmpdir;
+	}
+
+	private static long getFingerprint(Random random) {
+		final long fp = (((long) random.nextInt(Integer.MAX_VALUE - 1) + 1) << 32) | (random.nextInt() & 0xffffffffL);
+		return fp;
+	}
+
+	@Test
+	public void testWriteIndex() throws NoSuchFieldException, SecurityException, IllegalArgumentException,
+			IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException {
+		final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration();
+		fpSetConfig.setMemoryInFingerprintCnt(1);
+
+		final OffHeapDiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig);
+		final Method method = OffHeapDiskFPSet.class.getDeclaredMethod("writeIndex",
+				new Class[] { long[].class, java.io.RandomAccessFile.class, long.class });
+		method.setAccessible(true);
+
+		// length of array times NumEntriesPerPage has to exceed Integer.MAX_VALUE
+		final int length = 99999999;
+		assertTrue(length * DiskFPSet.NumEntriesPerPage < 1);
+
+		try {
+			method.invoke(fpSet, new long[length], new DummyRandomAccessFile(File.createTempFile("foo", "bar"), "rw"),
+					Integer.MAX_VALUE);
+		} catch (InvocationTargetException e) {
+			Throwable targetException = e.getTargetException();
+			fail(targetException.getMessage());
+		}
+	}
+
+	private static class DummyRandomAccessFile extends java.io.RandomAccessFile {
+
+		public DummyRandomAccessFile(File file, String mode) throws FileNotFoundException {
+			super(file, mode);
+		}
+
+		@Override
+		public int read() throws IOException {
+			return 0;
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java b/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3f8c35b5c93a890d2f58014727c0e96144f342c
--- /dev/null
+++ b/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.fp;
+
+import java.rmi.RemoteException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import tlc2.tool.fp.OffHeapDiskFPSet.Indexer;
+
+public class OffHeapIndexerTest {
+
+	@Test
+	public void testBitshifting() throws RemoteException {
+		final int fpBits = 1;
+		final long positions = 128L;
+		final int logPos = 8;
+		doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits));
+	}
+
+	@Test
+	public void testRescale() throws RemoteException {
+		final int fpBits = 1;
+		final long positions = 96L;
+		
+		final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits);
+
+		// indexer spreads over all positions
+		Assert.assertEquals(0, indexer.getIdx(1, 0));
+		Assert.assertEquals(48, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L)));
+		Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits)));
+		
+		// Correctly wraps around when end of array is reached
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1));
+		// Correctly wraps around when end of array is reached twice
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1));
+	}
+
+	@Test
+	public void testRescale99() throws RemoteException {
+		final int fpBits = 1;
+		final long positions = 99L;
+		
+		final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits);
+
+		// indexer spreads over all positions
+		Assert.assertEquals(0, indexer.getIdx(1));
+		Assert.assertEquals(49, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L)));
+		Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits)));
+		
+		// Correctly wraps around when end of array is reached
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1));
+		// Correctly wraps around when end of array is reached twice
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1));
+	}
+
+	@Test
+	public void testBitshifting2() throws RemoteException {
+		final int fpBits = 2;
+		final long positions = 128L;
+		final int logPos = 9;
+		doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits));
+	}
+
+	@Test
+	public void testRescale2() throws RemoteException {
+		final int fpBits = 2;
+		final long positions = 96L;
+		
+		final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits);
+
+		// indexer spreads over all positions
+		Assert.assertEquals(0, indexer.getIdx(1));
+		Assert.assertEquals(48, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L)));
+		Assert.assertEquals(95, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits)));
+		
+		// Correctly wraps around when end of array is reached
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1));
+		// Correctly wraps around when end of array is reached twice
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 97));
+	}
+
+	@Test
+	public void testRescale299() throws RemoteException {
+		final int fpBits = 2;
+		final long positions = 99L;
+		
+		final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits);
+
+		// indexer spreads over all positions
+		Assert.assertEquals(0, indexer.getIdx(1));
+		Assert.assertEquals(49, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L)));
+		Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits)));
+		
+		// Correctly wraps around when end of array is reached
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1));
+		// Correctly wraps around when end of array is reached twice
+		Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1));
+	}
+
+	@Test
+	public void testRescaleMaximum() throws RemoteException {
+		final int fpBits = 1;
+		final long positions = 11L;
+		
+		final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits, 11L);
+
+		// indexer spreads over all positions
+		Assert.assertEquals(0, indexer.getIdx(1));
+		Assert.assertEquals(10, indexer.getIdx(11));
+		
+		// Correctly wraps around when end of array is reached
+		Assert.assertEquals(0, indexer.getIdx(11, 1));
+	}
+	
+	@Test
+	public void testBitshiftOvershoot() throws RemoteException {
+		final int fpBits = 1;
+		final long positions = 536870912L;
+		
+		final Indexer indexer = new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits);
+		Assert.assertEquals(0, indexer.getIdx(9223371952792813846L, 5));
+	}
+	
+	private void doTest(final int fpBits, final long positions, final int logPos, final Indexer indexer) {
+		Assert.assertTrue(Double.compare(Math.pow(2, logPos - fpBits), positions) == 0);
+		
+		Assert.assertEquals(fpBits, Long.numberOfLeadingZeros((positions << (Long.SIZE - logPos)) - 1));
+		
+		for (long l = 0; l < positions; l++) {
+			final long fp = l << (Long.SIZE - logPos);
+			Assert.assertEquals(l, indexer.getIdx(fp));
+			final long fpNext = ((l+1L) << (Long.SIZE - logPos)) - 1;
+			Assert.assertEquals(l, indexer.getIdx(fpNext));
+		}
+		Assert.assertEquals(0, indexer.getIdx(positions << (Long.SIZE - logPos)));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java b/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2e68d96e9ba38ec270eff8ec1765704ff19ddc5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java
@@ -0,0 +1,70 @@
+package tlc2.tool.fp;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import tlc2.tool.fp.OffHeapDiskFPSet.Iterator;
+
+public class OffHeapIteratorTest {
+	
+	@Before
+	public void setup() {
+		Assume.assumeTrue(LongArray.isSupported());
+	}
+
+	@Test
+	public void testNext() {
+		final long elements = 32L;
+		final LongArray array = new LongArray(2L * elements);
+		for (int i = 0; i < array.size(); i++) {
+			array.set(i, i+1);
+		}
+
+		final Iterator itr = new OffHeapDiskFPSet.Iterator(array, elements,
+				new OffHeapDiskFPSet.Indexer(2L * elements, 1));
+
+		long actual = 1L;
+		while (itr.hasNext()) {
+			long next = itr.next();
+			assertEquals(actual, next);
+			actual++;
+		}
+		assertEquals(elements, actual - 1);
+		
+		for (int i = 0; i < array.size(); i++) {
+			assertEquals(i + 1, array.get(i));
+		}
+	}
+
+	@Test
+	public void testMarkNext() {
+		final int elements = 32;
+		final LongArray array = new LongArray(2L * elements);
+		for (int i = 0; i < array.size(); i++) {
+			array.set(i, i+1);
+		}
+
+		final Iterator itr = new OffHeapDiskFPSet.Iterator(array, elements,
+				new OffHeapDiskFPSet.Indexer(2L * elements, 1));
+
+		long actual = 1L;
+		while (itr.hasNext()) {
+			long next = itr.markNext();
+			assertEquals(actual, next);
+			actual++;
+		}
+		assertEquals(elements, actual - 1);
+		
+		for (int i = elements; i < array.size(); i++) {
+			assertEquals(elements + 1, array.get(elements));
+		}
+		for (int i = 0; i < elements; i++) {
+			long expected = i + 1 | 0x8000000000000000L;
+			actual = array.get(i);
+			assertEquals(expected, actual);
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java
index 368810968d02fa695d04d7ade537e5a6b0bb4ee4..cfd4d3fa09bfd9ebf768156272bd4fe4ce716b22 100644
--- a/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java
+++ b/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java
@@ -1,11 +1,18 @@
 // Copyright (c) 2011 Microsoft Corporation.  All rights reserved.
 package tlc2.tool.fp;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.util.LongVec;
 
 public class ShortDiskFPSetTest extends AbstractFPSetTest {
@@ -32,6 +39,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} returns true for zero fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testWithoutZeroFP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse("Succeeded to look up 0 fp", fpSet.contains(0l));
@@ -41,6 +49,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} returns true for min fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testWithoutMinFP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse("Succeeded to look up 0 fp", fpSet.contains(Long.MIN_VALUE));
@@ -50,6 +59,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} returns true for max fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testWithoutMaxFP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse("Succeeded to look up 0 fp", fpSet.contains(Long.MAX_VALUE));
@@ -59,12 +69,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} accepts a 0 fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testZeroFP() throws IOException {
 		// skip known failures which aren't likely to be fixed anytime soon
-		// @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213
+		// @see Bug #213 in general/bugzilla/index.html
 		if(!runKnownFailures) {
 			System.out
-					.println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213");
+					.println("Skipping test failing due to Bug #213 in general/bugzilla/index.html");
 			return;
 		}
 
@@ -77,12 +88,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} accepts a min fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testMinFP() throws IOException {
 		// skip known failures which aren't likely to be fixed anytime soon
-		// @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213
+		// @see Bug #213 in general/bugzilla/index.html
 		if(!runKnownFailures) {
 			System.out
-					.println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213");
+					.println("Skipping test failing due to Bug #213 in general/bugzilla/index.html");
 			return;
 		}
 		
@@ -96,6 +108,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} accepts a min - 1  fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testMinMin1FP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		// zeroing the msb in DiskFPSet turns Long.Min_Value into 0
@@ -108,6 +121,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} accepts a -1 fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testNeg1FP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse(fpSet.put(-1l));
@@ -118,6 +132,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} accepts a +1 fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testPos1FP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse(fpSet.put(1l));
@@ -128,6 +143,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Tests if {@link DiskFPSet#diskLookup(long)} accepts a max fp
 	 * @throws IOException 
 	 */
+	@Test
 	public void testMaxFP() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse(fpSet.put(Long.MAX_VALUE));
@@ -141,6 +157,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException
 	 */
+	@Test
 	public void testValues() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 
@@ -267,6 +284,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	@SuppressWarnings("deprecation")
 	public void testDiskLookupWithFpOnLoPage() throws IOException {
 		int freeMemory = 1000; // causes 16 in memory entries
@@ -290,12 +308,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testMemLookupWithZeros() throws IOException {
 		// skip known failures which aren't likely to be fixed anytime soon
-		// @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213
+		// @see Bug #213 in general/bugzilla/index.html
 		if(!runKnownFailures) {
 			System.out
-					.println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213");
+					.println("Skipping test failing due to Bug #213 in general/bugzilla/index.html");
 			return;
 		}
 		
@@ -310,12 +329,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testMemLookupWithMin() throws IOException {
 		// skip known failures which aren't likely to be fixed anytime soon
-		// @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213
+		// @see Bug #213 in general/bugzilla/index.html
 		if(!runKnownFailures) {
 			System.out
-					.println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213");
+					.println("Skipping test failing due to Bug #213 in general/bugzilla/index.html");
 			return;
 		}
 		
@@ -330,6 +350,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testMemLookupWithMax() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration(.75d));
 		assertFalse(fpSet.memInsert(Long.MAX_VALUE));
@@ -342,6 +363,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testDiskLookupWithZeros() throws IOException {
 		final long fp = 0L;
 
@@ -376,6 +398,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testDiskLookupWithMin() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse(fpSet.memInsert(Long.MIN_VALUE & 0x7FFFFFFFFFFFFFFFL));
@@ -391,6 +414,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testDiskLookupWithMax() throws IOException {
 		final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration());
 		assertFalse(fpSet.memInsert(Long.MAX_VALUE));
@@ -405,6 +429,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testDiskLookupWithMaxOnPage() throws IOException {
 		testDiskLookupOnPage(Long.MAX_VALUE);
 	}
@@ -414,12 +439,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testDiskLookupWithZerosOnPage() throws IOException {
 		// skip known failures which aren't likely to be fixed anytime soon
-		// @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213
+		// @see Bug #213 in general/bugzilla/index.html
 		if(!runKnownFailures) {
 			System.out
-					.println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213");
+					.println("Skipping test failing due to Bug #213 in general/bugzilla/index.html");
 			return;
 		}
 		testDiskLookupOnPage(0l);
@@ -430,12 +456,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * 
 	 * @throws IOException 
 	 */
+	@Test
 	public void testDiskLookupWithLongMinValueOnPage() throws IOException {
 		// skip known failures which aren't likely to be fixed anytime soon
-		// @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213
+		// @see Bug #213 in general/bugzilla/index.html
 		if(!runKnownFailures) {
 			System.out
-					.println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213");
+					.println("Skipping test failing due to Bug #213 in general/bugzilla/index.html");
 			return;
 		}
 		
@@ -466,6 +493,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Test that both implementations - put(long) & putBlock(LongVec) - yield
 	 * the same results.
 	 */
+	@Test
 	public void testComparePutAndPutBlock() throws IOException {
 		final FPSet putFpSet = (FPSet) getFPSetInitialized();
 		final FPSet putBlockFpSet = (FPSet) getFPSetInitialized();
@@ -483,6 +511,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 	 * Test that both implementations - contains(long) & containsBlock(LongVec) - yield
 	 * the same results.
 	 */
+	@Test
 	public void testCompareContainsAndContainsBlock() throws IOException {
 		final FPSet containsFpSet = (FPSet) getFPSetInitialized();
 		final FPSet containsBlockFpSet = (FPSet) getFPSetInitialized();
@@ -496,6 +525,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 		assertEquals(containsFpSet.contains(fp), containsBlockRes);
 	}
 
+	@Test
 	public void testContainsBlock() throws IOException {
 		final FPSet fpSet = (FPSet) getFPSetInitialized();
 		
@@ -512,6 +542,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest {
 		assertFalse(fpSet.containsBlock(fpv).get(0));
 	}
 	
+	@Test
 	public void testPutBlock() throws IOException {
 		final FPSet fpSet = (FPSet) getFPSetInitialized();
 		
diff --git a/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java b/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java
index 07f347f360dc46568a3a506f3abbbf935dfa7149..7b79f655b62b0c605b1fcf3c04653cf97ac8dd28 100644
--- a/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java
+++ b/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java
@@ -2,21 +2,27 @@
 
 package tlc2.tool.fp.iterator;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.util.NoSuchElementException;
 
-import tlc2.tool.fp.MSBDiskFPSet;
+import org.junit.Before;
+import org.junit.Test;
 
-import junit.framework.TestCase;
+import tlc2.tool.fp.MSBDiskFPSet;
 
-public class TLCIteratorTest extends TestCase {
+public class TLCIteratorTest {
 
 	private MSBDiskFPSet.TLCIterator itr;
 
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#setUp()
 	 */
-	protected void setUp() throws Exception {
-		super.setUp();
+	@Before
+	public void setUp() throws Exception {
 		itr = new MSBDiskFPSet.TLCIterator(getBuffer());
 	}
 	
@@ -52,6 +58,7 @@ public class TLCIteratorTest extends TestCase {
 	/**
 	 * Test method for {@link tlc2.tool.fp.TLCIterator#next()}.
 	 */
+	@Test
 	public void testNext() {
 		long predecessor = -1l;
 		
@@ -72,6 +79,7 @@ public class TLCIteratorTest extends TestCase {
 	/**
 	 * Test method for {@link tlc2.tool.fp.TLCIterator#next()}.
 	 */
+	@Test
 	public void testNoNext() {
 		// read up all real elements
 		while (itr.hasNext()) {
@@ -91,19 +99,8 @@ public class TLCIteratorTest extends TestCase {
 	/**
 	 * Test method for {@link tlc2.tool.fp.TLCIterator#getLast()}.
 	 */
+	@Test
 	public void testGetLast() {
 		assertEquals(getLast(), itr.getLast());
 	}
-
-	public void testGetLastLowBound() {
-		assertEquals(getLast() + 1, itr.getLast(getLast() + 1));
-	}
-	
-	/*
-	 * Use a smaller lower bound element than last in the buffer.
-	 */
-	public void testGetLastLowerBoundNoHit() {
-		assertEquals(getLast(), itr.getLast(20));
-	}
-
 }
diff --git a/tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG b/tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG
new file mode 100644
index 0000000000000000000000000000000000000000..f3e3c82c44e40dfe061993d47d498799596bb3c5
Binary files /dev/null and b/tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG differ
diff --git a/tlatools/test/tlc2/tool/liveness/April20aTest.java b/tlatools/test/tlc2/tool/liveness/April20aTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..213e7537a416e60d284460916c1d69131f3e87f6
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April20aTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class April20aTest extends ModelCheckerTestCase {
+
+	public April20aTest() {
+		super("April20aMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(6);
+		expectedTrace.add("/\\ x = 0\n" 
+						+ "/\\ y = 0");
+		expectedTrace.add("/\\ x = m2\n" 
+						+ "/\\ y = 1");
+		expectedTrace.add("/\\ x = m1\n" 
+				        + "/\\ y = 2");
+		expectedTrace.add("/\\ x = 0\n" 
+						+ "/\\ y = 0");
+		expectedTrace.add("/\\ x = m1\n" 
+						+ "/\\ y = 1");
+		expectedTrace.add("/\\ x = m2\n" 
+						+ "/\\ y = 2");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1, "<Action line 17, col 12 to line 19, col 20 of module April20a>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/April20bTest.java b/tlatools/test/tlc2/tool/liveness/April20bTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee37bd5a7260643136777f79194eac2463a24d47
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April20bTest.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class April20bTest extends ModelCheckerTestCase {
+
+	public April20bTest() {
+		super("April20bMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(6);
+		expectedTrace.add("/\\ x = <<>>\n" 
+						+ "/\\ y = 0");
+		expectedTrace.add("/\\ x = <<m2>>\n" 
+						+ "/\\ y = 1");
+		expectedTrace.add("/\\ x = <<m2, m1>>\n" 
+				        + "/\\ y = 2");
+		expectedTrace.add("/\\ x = <<>>\n" 
+						+ "/\\ y = 0");
+		expectedTrace.add("/\\ x = <<m1>>\n" 
+						+ "/\\ y = 1");
+		expectedTrace.add("/\\ x = <<m1, m2>>\n" 
+						+ "/\\ y = 2");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1, "<Action line 17, col 12 to line 19, col 24 of module April20b>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/April21Test.java b/tlatools/test/tlc2/tool/liveness/April21Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..a826dde2f5cdd69d514de87bfb6fc65cad857747
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April21Test.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class April21Test extends ModelCheckerTestCase {
+
+	public April21Test() {
+		super("April21MC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("x = m1");
+		expectedTrace.add("x = m2");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1, "<Action line 10, col 9 to line 10, col 16 of module April21>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/April22Test.java b/tlatools/test/tlc2/tool/liveness/April22Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..432d9d455b33c0db08d95d5475dfbca6473a32d7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April22Test.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class April22Test extends ModelCheckerTestCase {
+
+	public April22Test() {
+		super("April22MC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has not found a temporal violation
+		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/April25Test.java b/tlatools/test/tlc2/tool/liveness/April25Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..7fa4922943f06b8e49bc304774c5596160fbe70f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April25Test.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class April25Test extends ModelCheckerTestCase {
+
+	public April25Test() {
+		super("April25MC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("x = m1");
+		expectedTrace.add("x = m2");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1, "<Action line 10, col 9 to line 10, col 16 of module April25>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/April29Test.java b/tlatools/test/tlc2/tool/liveness/April29Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d6e78a61d5652bf06b57ad99bafaa0725796537
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April29Test.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.TLC;
+import tlc2.output.EC;
+import tlc2.tool.AbstractChecker;
+import tlc2.tool.liveness.GraphNode.Transition;
+import tlc2.util.BitVector;
+import tlc2.util.LongVec;
+
+/**
+ * April29 and April29d exemplify how two different specifications have isomorph
+ * state graphs under symmetry. Without symmetry, April29dTest does *not*
+ * violate its liveness property. Under symmetry, TLC incorrectly finds a
+ * violation and produces a bogus (identical to April29Test) counterexample.
+ */
+public class April29Test extends ModelCheckerTestCase {
+
+	public April29Test() {
+		super("April29MC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() throws IOException {
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("/\\ x = a\n/\\ y = 0");
+		expectedTrace.add("/\\ x = a\n/\\ y = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertStuttering(3);
+		
+		// Verify the nodes and arcs in the behavior graph in terms of nodes and arcs.
+		final ILiveCheck liveCheck = (ILiveCheck) getField(AbstractChecker.class, "liveCheck",
+				getField(TLC.class, "instance", tlc));
+		assertEquals(1, liveCheck.getNumChecker());
+		
+		final ILiveChecker checker = liveCheck.getChecker(0);
+		final DiskGraph graph = (DiskGraph) checker.getDiskGraph();
+		graph.makeNodePtrTbl();
+		assertEquals(2, graph.size()); // two nodes
+
+		final LongVec initNodes = graph.getInitNodes();
+		assertEquals(2, initNodes.size()); // <<fp,tidx>>, thus a single init state
+		
+		final int slen = checker.getSolution().getCheckState().length;
+		assertEquals(0, slen);
+		final int alen = checker.getSolution().getCheckAction().length;
+		assertEquals(3, alen);
+		
+		final GraphNode init = graph.getNode(initNodes.elementAt(0));
+		Set<Transition> transitions = init.getTransition(slen, alen);
+		assertEquals(2, transitions.size()); // One self-loop and one successor
+		
+		// Remove self loop from init's transitions. The remaining transitions are
+		// the true successors (here just a single).
+		final List<Transition> outs = new ArrayList<Transition>();
+		for (Transition t : transitions) {
+			if (t.getFP() != init.stateFP) {
+				outs.add(t);
+			}
+		}
+		assertEquals(1, outs.size());
+		
+		final GraphNode successor = graph.getNode(outs.get(0).getFP());
+		transitions = successor.getTransition(slen, alen);
+		assertEquals(1, transitions.size());
+		
+		// Verify both outgoing transitions are self loops with the expected
+		// action predicates. transitions is a Set and thus adding equal
+		// instance does not increase the set's size.
+		BitVector bv = new BitVector(alen + slen);
+		bv.set(0);
+		bv.set(2);
+		transitions.add(new Transition(successor.stateFP, -1, bv));
+		assertEquals(1, transitions.size());
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/April29dTest.java b/tlatools/test/tlc2/tool/liveness/April29dTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..122c49f550203ec5dae68340add6a583aeeb454a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/April29dTest.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.TLC;
+import tlc2.output.EC;
+import tlc2.tool.AbstractChecker;
+import tlc2.tool.liveness.GraphNode.Transition;
+import tlc2.util.BitVector;
+import tlc2.util.LongVec;
+
+/**
+ * @see April29Test
+ */
+public class April29dTest extends ModelCheckerTestCase {
+
+	public April29dTest() {
+		super("April29dMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() throws IOException {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Verify the nodes and arcs in the behavior graph in terms of nodes and arcs.
+		final ILiveCheck liveCheck = (ILiveCheck) getField(AbstractChecker.class, "liveCheck",
+				getField(TLC.class, "instance", tlc));
+		assertEquals(1, liveCheck.getNumChecker());
+		
+		final ILiveChecker checker = liveCheck.getChecker(0);
+		final DiskGraph graph = (DiskGraph) checker.getDiskGraph();
+		graph.makeNodePtrTbl();
+		assertEquals(2, graph.size()); // two nodes
+
+		final LongVec initNodes = graph.getInitNodes();
+		assertEquals(2, initNodes.size()); // <<fp,tidx>>, thus a single init state
+		
+		final int slen = checker.getSolution().getCheckState().length;
+		assertEquals(0, slen);
+		final int alen = checker.getSolution().getCheckAction().length;
+		assertEquals(3, alen);
+		
+		final GraphNode init = graph.getNode(initNodes.elementAt(0));
+		Set<Transition> transitions = init.getTransition(slen, alen);
+		assertEquals(2, transitions.size()); // One self-loop and one successor
+		
+		// Remove self loop from init's transitions. The remaining transitions are
+		// the true successors (here just a single).
+		final List<Transition> outs = new ArrayList<Transition>();
+		for (Transition t : transitions) {
+			if (t.getFP() != init.stateFP) {
+				outs.add(t);
+			}
+		}
+		assertEquals(1, outs.size());
+		
+		final GraphNode successor = graph.getNode(outs.get(0).getFP());
+		transitions = successor.getTransition(slen, alen);
+		assertEquals(2, transitions.size());
+		
+		// Verify both outgoing transitions are self loops with the expected
+		// action predicates. transitions is a Set and thus adding equal
+		// instance does not increase the set's size.
+		BitVector bv = new BitVector(alen + slen);
+		bv.set(0);
+		bv.set(2);
+		transitions.add(new Transition(successor.stateFP, -1, bv));
+		bv = new BitVector(alen + slen);
+		bv.set(2);
+		transitions.add(new Transition(successor.stateFP, -1, bv));
+		assertEquals(2, transitions.size());
+
+		// Assert TLC has found a temporal violation and a counter example
+		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d318270f571745d5f318f5fa53c71a54b92ceefc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public abstract class BidirectionalTransitions1BTest extends ModelCheckerTestCase {
+
+	public BidirectionalTransitions1BTest(final String config) {
+		super("BidirectionalTransitions", new String[] {"-config", config}, ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "3", "0"));
+
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(3);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 2");
+		expectedTrace.add("x = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BxTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BxTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f723b7000bb15fc41b8bad6df7d9b31e56cbc6d6
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BxTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+public class BidirectionalTransitions1BxTest extends BidirectionalTransitions1BTest {
+
+	public BidirectionalTransitions1BxTest() {
+		super("BidirectionalTransitions1Bx.cfg");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1ByTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1ByTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ac3fb7c4262a0a4cedb8d0a5c9bcc3df1400979
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1ByTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+public class BidirectionalTransitions1ByTest extends BidirectionalTransitions1BTest {
+
+	public BidirectionalTransitions1ByTest() {
+		super("BidirectionalTransitions1By.cfg");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1Test.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..67b2717e6bb9a084b8e3fa68063e28106c5f963a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1Test.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class BidirectionalTransitions1Test extends ModelCheckerTestCase {
+
+	public BidirectionalTransitions1Test() {
+		super("BidirectionalTransitions", new String[] {"-config", "BidirectionalTransitions1.cfg"});
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "3", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6879a0c45624f5fd9d6ff1141c4b080e7592848d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public abstract class BidirectionalTransitions2CTest extends ModelCheckerTestCase {
+
+	public BidirectionalTransitions2CTest(final String config) {
+		super("BidirectionalTransitions", new String[] { "-config", config }, ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "4", "0"));
+
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(3);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 1");
+		expectedTrace.add("x = 2");
+		expectedTrace.add("x = 3");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CxTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CxTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7475a9b6ea72b257b80afa6c73130ee9c336a3cd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CxTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+public class BidirectionalTransitions2CxTest extends BidirectionalTransitions2CTest {
+
+	public BidirectionalTransitions2CxTest() {
+		super("BidirectionalTransitions2Cx");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CyTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e120364ce1bea66be87ca34a8063b68e91393b0a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CyTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+public class BidirectionalTransitions2CyTest extends BidirectionalTransitions2CTest {
+
+	public BidirectionalTransitions2CyTest() {
+		super("BidirectionalTransitions2Cy");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2Test.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..e72686cf17f294df8c8bedc3a249a55705734bdf
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2Test.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class BidirectionalTransitions2Test extends ModelCheckerTestCase {
+
+	public BidirectionalTransitions2Test() {
+		super("BidirectionalTransitions", new String[] {"-config", "BidirectionalTransitions2.cfg"});
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTest.java b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4cd7caa5a655c0c2f7071b0e918855e25cec985f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class ChooseTableauSymmetryTest extends ModelCheckerTestCase {
+
+	public ChooseTableauSymmetryTest() {
+		super("ChooseTableauSymmetryMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "6", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(7);
+		// Trace prefix
+		expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"busy\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"done\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(4, "<Action line 7, col 13 to line 8, col 47 of module ChooseTableauSymmetry>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTestA.java b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTestA.java
new file mode 100644
index 0000000000000000000000000000000000000000..13198ab605c01fff06a9ce0542acd157c4219220
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTestA.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ChooseTableauSymmetryTestA extends ModelCheckerTestCase {
+
+	public ChooseTableauSymmetryTestA() {
+		super("ChooseTableauSymmetryMCa", "symmetry", ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "6", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(7);
+		// Trace prefix
+		expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"busy\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"busy\")");
+		expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"busy\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(3, "<Ready line 7, col 13 to line 8, col 47 of module ChooseTableauSymmetry>");
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java
index 3db7fa50cee308b072b1d8244c215d7f7f3e7f6e..a4d22559a1f8886d4feff0e96b74bc7d17412490 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java
@@ -26,10 +26,16 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -37,78 +43,73 @@ import tlc2.tool.TLCStateInfo;
 public class CodePlexBug08AgentRingTest extends ModelCheckerTestCase {
 
 	public CodePlexBug08AgentRingTest() {
-		super("AgentRingMC", "CodePlexBug08");
+		super("AgentRingMC", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
 		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "361", "120"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		assertNodeAndPtrSizes(8496L, 2880L);
 		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
-
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ Agent = [Loc |-> 0, LastLoad |-> 0, ReadyToMove |-> TRUE, Task |-> 0]\n"
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 0, ReadyToMove |-> TRUE, Task |-> 0]\n"
 				   + "/\\ CanCreate = TRUE\n"
-				   + "/\\ Nodes = (0 :> [Load |-> 0] @@ 1 :> [Load |-> 0])", 
-				   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 0] @@ 1 :> [Load |-> 0])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 0, ReadyToMove |-> TRUE, Task |-> 0]\n"
 				   + "/\\ CanCreate = TRUE\n"
-				   + "/\\ Nodes = (0 :> [Load |-> 0] @@ 1 :> [Load |-> 0])", 
-				   stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 0])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
 				   + "/\\ CanCreate = TRUE\n"
-				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 0])", 
-				   stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 0])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
 				   + "/\\ CanCreate = TRUE\n"
-				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])", 
-				   stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])");
 		// The two states below violate the liveness property [](~CanCreate /\
 		// (\A i,j \in NodeRange : Nodes[i].Load = Nodes[j].Load) =>
 		// [](Agent.Task = 0)). State 5 has CanCreate = FALSE and Task=0 and
 		// state six changes Task back to 1.
-		
-		// state 5
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 1, ReadyToMove |-> TRUE, Task |-> 1]\n"
 				   + "/\\ CanCreate = FALSE\n"
-				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])", 
-				   stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])");
 
-		// state 6
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 1, ReadyToMove |-> TRUE, Task |-> 1]\n"
+		expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 1, ReadyToMove |-> FALSE, Task |-> 1]\n"
 				   + "/\\ CanCreate = FALSE\n"
-				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])", 
-				   stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		// ...more states to follow
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 2, ReadyToMove |-> TRUE, Task |-> 1]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 2, ReadyToMove |-> FALSE, Task |-> 1]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 2, ReadyToMove |-> TRUE, Task |-> 0]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 2, ReadyToMove |-> FALSE, Task |-> 0]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 2, ReadyToMove |-> TRUE, Task |-> 0]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])");
+		expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 2, ReadyToMove |-> FALSE, Task |-> 0]\n"
+				   + "/\\ CanCreate = FALSE\n"
+				   + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(10);
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java
index 066f3d233231e041f737e85090b72e6abb869c45..814dc1d96c20846863cd60240e83182ab0a0e029 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java
@@ -26,10 +26,16 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -37,40 +43,69 @@ import tlc2.tool.TLCStateInfo;
 public class CodePlexBug08EWD840FL1Test extends ModelCheckerTestCase {
 
 	public CodePlexBug08EWD840FL1Test() {
-		super("EWD840MC1", "CodePlexBug08");
+		super("EWD840MC1", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
 		
+		assertNodeAndPtrSizes(7560068L, 279616L);
+		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ tpos = 0\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ tpos = 0\n"
-				   + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
-				   + "/\\ tcolor = \"black\"\n"
-				   + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", 
-				   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		// Omitted check of 8 in-between states, just make sure four more states exist
-		assertEquals("Expect five states prior to stuttering", 8, records.size());
-		
-		// state six is back to state 1
-		assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE));
-		List<Object> stutter = recorder.getRecords(EC.TLC_BACK_TO_STATE);
-		assertTrue(stutter.size() > 0);
-		Object[] object = (Object[]) stutter.get(0);
-		assertEquals("1", object[0]);
+		assertBackToState(1);
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3af646ba5360d97194ec8577bbe43e01c7f2eb05
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+/**
+ * see http://tlaplus.codeplex.com/workitem/8
+ */
+public class CodePlexBug08EWD840FL2FromCheckpointTest extends ModelCheckerTestCase {
+
+	public CodePlexBug08EWD840FL2FromCheckpointTest() {
+		super("EWD840MC2", "CodePlexBug08", new String[] {"-gzip", "-recover", BASE_DIR + TEST_MODEL + "CodePlexBug08" + File.separator + "checkpoint"}, ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	
+	@Override
+	public void setUp() {
+		try {
+			/* Recreate checkpoint.zip whenever file format changes:
+			 * 
+			 * 1) Run CodePlexBug08EWD840FL2Test with "-gzip"
+			 * 2) Connect to running test via JMX and request TLC checkpoint to be taken
+			 * 3) Terminate CodePlexBug08EWD840FL2Test once checkpoint successfully taken
+			 * 4) Locate the directory with the checkpoint data
+			 * 5) Replace the content of checkpoint.zip with the content of 4)
+			 * 6) Update the number below on states found...
+			 */
+			String prefix = BASE_DIR + TEST_MODEL + "CodePlexBug08" + File.separator;
+			ZipFile zipFile = new ZipFile(prefix + "checkpoint.zip");
+			Enumeration<?> enu = zipFile.entries();
+			while (enu.hasMoreElements()) {
+				ZipEntry zipEntry = (ZipEntry) enu.nextElement();
+
+				File file = new File(prefix + zipEntry.getName());
+				if (zipEntry.getName().endsWith("/")) {
+					file.mkdirs();
+					continue;
+				}
+
+				File parent = file.getParentFile();
+				if (parent != null) {
+					parent.mkdirs();
+				}
+
+				InputStream is = zipFile.getInputStream(zipEntry);
+				FileOutputStream fos = new FileOutputStream(file);
+				byte[] bytes = new byte[1024];
+				int length;
+				while ((length = is.read(bytes)) >= 0) {
+					fos.write(bytes, 0, length);
+				}
+				is.close();
+				fos.close();
+
+			}
+			zipFile.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		super.setUp();
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_RECOVER_START));
+		// Recovery completed. 1032 states examined. 996 states on queue.
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKPOINT_RECOVER_END, "1510", "39"));
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2334", "1566","0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(54038132L, 831296L);
+		
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ tpos = 0\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");	
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		// last state points back to state 1
+		assertBackToState(1);
+	}
+
+
+	@Override
+	protected int getNumberOfThreads() {
+		return 3;
+	}
+	
+	
+}
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java
index f38a278c40c879b6d4c8ebf4a5d4f9a2ca343ee3..d81be0bbfc73c0b8589dc61991156983cd13f989 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java
@@ -26,11 +26,16 @@
 
 package tlc2.tool.liveness;
 
-import java.io.File;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
-import tlc2.TLCGlobals;
+import org.junit.Test;
+
 import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -38,38 +43,68 @@ import tlc2.output.EC;
 public class CodePlexBug08EWD840FL2Test extends ModelCheckerTestCase {
 
 	public CodePlexBug08EWD840FL2Test() {
-		super("EWD840MC2", "CodePlexBug08");
+		super("EWD840MC2", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566"));
-		
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566","0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
 		
+		assertNodeAndPtrSizes(54037212L, 831296L);
+		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ tpos = 0\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		
 		// last state points back to state 1
-		assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE));
-		List<Object> stutter = recorder.getRecords(EC.TLC_BACK_TO_STATE);
-		assertTrue(stutter.size() > 0);
-		Object[] object = (Object[]) stutter.get(0);
-		assertEquals("1", object[0]);
-		
-		// Check the file size of the AbstractDiskGraph files to check if the
-		// expected amount of ptrs and nodes (outgoing arcs) have been written
-		// to disk.
-		final String metadir = TLCGlobals.mainChecker.metadir;
-		assertNotNull(metadir);
-		final File nodes = new File(metadir + File.separator + "nodes_0");
-		final File ptrs =  new File(metadir + File.separator + "ptrs_0");
-		assertTrue(nodes.exists());
-		assertTrue(ptrs.exists());
-		assertEquals(53958732L, nodes.length());
-		assertEquals(831296L, ptrs.length());
+		assertBackToState(1);
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java
index 571312fb4417d3aa612999fcafd06ea1eae94057..9181588a17ff35d7fd00ae31a79147898d420953 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java
@@ -26,10 +26,16 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -37,40 +43,62 @@ import tlc2.tool.TLCStateInfo;
 public class CodePlexBug08EWD840FL3Test extends ModelCheckerTestCase {
 
 	public CodePlexBug08EWD840FL3Test() {
-		super("EWD840MC3", "CodePlexBug08");
+		super("EWD840MC3", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
 		
+		assertNodeAndPtrSizes(143808L, 25040L);
+		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
-
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ tpos = 0\n"
-				   + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
-				   + "/\\ tcolor = \"black\"\n"
-				   + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", 
-				   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		// Omitted check of 8 in-between states, just make sure four more states exist
-		assertEquals("Expect five states prior to stuttering", 8, records.size());
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ tpos = 0\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 2\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"white\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 1\n"
+		                + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
+		                + "/\\ tcolor = \"black\"\n"
+		                + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		
-		// state eight points back to state 1
-		assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE));
-		List<Object> stutter = recorder.getRecords(EC.TLC_BACK_TO_STATE);
-		assertTrue(stutter.size() > 0);
-		Object[] object = (Object[]) stutter.get(0);
-		assertEquals("1", object[0]);
+		// last state loops back to state 1
+		assertBackToState(1);
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java
index 22074f3783d44c6e3488c5e77eb086905fa069d9..94a18f4f9e5e696f4fe02fb5a65ffa3074f44c33 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java
@@ -26,10 +26,16 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -37,45 +43,38 @@ import tlc2.tool.TLCStateInfo;
 public class CodePlexBug08EWD840FL4Test extends ModelCheckerTestCase {
 
 	public CodePlexBug08EWD840FL4Test() {
-		super("EWD840MC4", "CodePlexBug08");
+		super("EWD840MC4", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
 		
+		assertNodeAndPtrSizes(135540L, 23456L);
+		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
-
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ tpos = 0\n"
+		final List<String> expectedTrace = new ArrayList<String>();
+		expectedTrace.add("/\\ tpos = 0\n"
 				   + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
 				   + "/\\ tcolor = \"black\"\n"
-				   + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", 
-				   stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ tpos = 3\n"
+				   + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		expectedTrace.add("/\\ tpos = 3\n"
 				   + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n"
 				   + "/\\ tcolor = \"white\"\n"
-				   + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", 
-				   stateInfo.toString().trim());
+				   + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		
 		// state 3 is stuttering
-		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3));
-		List<Object> stutter = recorder.getRecords(EC.TLC_STATE_PRINT3);
-		assertTrue(stutter.size() > 0);
-		Object[] object = (Object[]) stutter.get(0);
-		assertEquals(3, object[1]);
+		assertStuttering(3);
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java
index 3009401350f143c2068b1dee1b5c9e4ebbde03f4..cc1e1f50b4efee1321b65153a8b898674815c7e5 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java
@@ -26,10 +26,17 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
+import util.TLAConstants;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -37,57 +44,35 @@ import tlc2.tool.TLCStateInfo;
 public class CodePlexBug08Test extends ModelCheckerTestCase {
 
 	public CodePlexBug08Test() {
-		super("MC", "CodePlexBug08");
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11"));
-		
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
 		
+		assertNodeAndPtrSizes(744L, 320L);
+		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 2");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 3");
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 3");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 4");
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 4");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 5");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 2", stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 3", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 3", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
+		assertStuttering(7);
 
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 4", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 4", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 5", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		// Assert the error trace contains a stuttering step at position 5
-		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3));
-		records = recorder.getRecords(EC.TLC_STATE_PRINT3);
-		objs = (Object[]) records.get(0);
-		assertEquals(++i, objs[1]);
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java
index e08f7c820af45c0992fbde3f271c88a20e66fa80..da9653beb6618bafa8e1eb9793e54b7647555d57 100644
--- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java
+++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java
@@ -26,10 +26,16 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
 
 /**
  * see http://tlaplus.codeplex.com/workitem/8
@@ -37,67 +43,38 @@ import tlc2.tool.TLCStateInfo;
 public class CodePlexBug08aTest extends ModelCheckerTestCase {
 
 	public CodePlexBug08aTest() {
-		super("MCa", "CodePlexBug08");
+		super("MCa", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
 		
+		assertNodeAndPtrSizes(816L, 352L);
+		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
-
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 1", stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 2", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 2", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 3", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 3", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 4", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = FALSE\n/\\ x = 4", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("/\\ b = TRUE\n/\\ x = 5", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 1");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 2");
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 2");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 3");
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 3");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 4");
+		expectedTrace.add("/\\ b = FALSE\n/\\ x = 4");
+		expectedTrace.add("/\\ b = TRUE\n/\\ x = 5");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		
 		// Assert the error trace contains a stuttering step at position 5
-		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3));
-		records = recorder.getRecords(EC.TLC_STATE_PRINT3);
-		objs = (Object[]) records.get(0);
-		assertEquals(++i, objs[1]);
+		assertStuttering(9);
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java b/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java
index 8150f3bc59f1acdbc73de6c5b81fe84f649b319b..daa793eb5aeea0eaef8a7114ee73a3cfa10c09d7 100644
--- a/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java
+++ b/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java
@@ -26,18 +26,25 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.File;
 import java.io.IOException;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
-import tlc2.util.statistics.BucketStatistics;
+import tlc2.util.statistics.FixedSizedBucketStatistics;
 import tlc2.util.statistics.IBucketStatistics;
 
-public class DiskGraphTest extends TestCase {
+public class DiskGraphTest {
 
-	private static final IBucketStatistics GRAPH_STATS = new BucketStatistics("Test Dummy", 16);
+	private static final IBucketStatistics GRAPH_STATS = new FixedSizedBucketStatistics("Test Dummy", 16);
 	private static final int NUMBER_OF_SOLUTIONS = 1;
 	private static final int NO_TABLEAU = -1;
 	private static final int NUMBER_OF_ACTIONS = 0;
@@ -62,6 +69,7 @@ public class DiskGraphTest extends TestCase {
 	}
 	
 	// No init node makes DiskGraph#getPath never break from the while loop
+	@Test
 	public void testGetPathWithoutInitNoTableau() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 		dg.addNode(new GraphNode(1L, NO_TABLEAU));
@@ -76,6 +84,7 @@ public class DiskGraphTest extends TestCase {
 
 	// Create a linear minimal graph (2 nodes) and check if the graph is
 	// returned by getPath afterwards.
+	@Test
 	public void testGetMinimalPathWithoutTableau() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -119,8 +128,9 @@ public class DiskGraphTest extends TestCase {
 	 * 
 	 * The specialty here is that there are *two* init nodes and one of them has *no* successors.
 	 * 
-	 * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=293
+	 * @see Bug #293 in general/bugzilla/index.html
 	 */
+	@Test
 	public void testPathWithTwoInitNodes() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -168,6 +178,7 @@ public class DiskGraphTest extends TestCase {
 	/*
 	 * Make sure the same logical node isn't counted twice.
 	 */
+	@Test
 	public void testAddSameGraphNodeTwice() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 		dg.addNode(new GraphNode(1L, 1));
@@ -179,6 +190,7 @@ public class DiskGraphTest extends TestCase {
 	/*
 	 * Test that it is possible to "update" a GraphNode's outgoing transitions.
 	 */
+	@Test
 	public void testLookupExistingNode() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 		
@@ -226,6 +238,7 @@ public class DiskGraphTest extends TestCase {
 	 * Test that adding a GraphNode twice (same fingerprint & tableau idx) but
 	 * with different successors afterwards yields the union of the successors.
 	 */
+	@Test
 	public void testAddSameGraphNodeTwiceCorrectSuccessors() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -266,6 +279,7 @@ public class DiskGraphTest extends TestCase {
 	 * checking runs periodically on an incomplete state/behavior graph, a
 	 * liveness violation is found and the path of the error trace gets explored.
 	 */
+	@Test
 	public void testGetPathPartialGraph() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 		
diff --git a/tlatools/test/tlc2/tool/liveness/EmptyOrderOfSolutionsTest.java b/tlatools/test/tlc2/tool/liveness/EmptyOrderOfSolutionsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a336a1d3c846f4936f8ac34636c7fd298935c29d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/EmptyOrderOfSolutionsTest.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class EmptyOrderOfSolutionsTest extends ModelCheckerTestCase {
+
+	public EmptyOrderOfSolutionsTest() {
+		super("EmptyOrderOfSolutions", ExitStatus.FAILURE_LIVENESS_EVAL);
+	}
+
+	@Test
+	public void testSpec() throws IOException {
+		assertTrue(recorder.recorded(EC.TLC_LIVE_FORMULA_TAUTOLOGY));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/ErrorTraceConstructionTest.java b/tlatools/test/tlc2/tool/liveness/ErrorTraceConstructionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..088864085c1ab0e6e1770608d46ec3b5afa0be86
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/ErrorTraceConstructionTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ErrorTraceConstructionTest extends ModelCheckerTestCase {
+
+	public ErrorTraceConstructionTest() {
+		super("ErrorTraceConstructionMC", "symmetry", ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(288L, 128L);
+	
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = 0\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 1");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 2");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 3");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 4");
+		expectedTrace.add("/\\ x = 1\n/\\ y = 5");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 6");
+		expectedTrace.add("/\\ x = 0\n/\\ y = 7");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(4, "<N7 line 32, col 7 to line 34, col 19 of module ErrorTraceConstruction>");
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java b/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java
index ac2592cf1a156f0bc61cec09c1f7823c4960dc1a..ccd827982cb68113ca6026035c68e85819dc4a87 100644
--- a/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java
+++ b/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java
@@ -26,14 +26,18 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class GraphNodeTest extends TestCase {
+public class GraphNodeTest {
 
+	@Test
 	public void testAllocateRealign() {
 		// Create a random graph node (fingerprint/tableau don't matter)
 		final GraphNode node = new GraphNode(0, 0);
@@ -48,13 +52,14 @@ public class GraphNodeTest extends TestCase {
 		int overallocated = node.realign();
 		assertTrue("Allocation overallocated", overallocated == 0);
 
-		assertTrue("Lost a transition during this allocation business", node.transExists(1, -1));
-		assertTrue("Lost a transition during this allocation business", node.transExists(2, -1));
-		assertTrue("Lost a transition during this allocation business", node.transExists(3, -1));
-		assertTrue("Lost a transition during this allocation business", node.transExists(4, -1));
-		assertTrue("Lost a transition during this allocation business", node.transExists(5, -1));
+		assertTrue("Lost a transition during the allocation business", node.transExists(1, -1));
+		assertTrue("Lost a transition during the allocation business", node.transExists(2, -1));
+		assertTrue("Lost a transition during the allocation business", node.transExists(3, -1));
+		assertTrue("Lost a transition during the allocation business", node.transExists(4, -1));
+		assertTrue("Lost a transition during the allocation business", node.transExists(5, -1));
 	}
 
+	@Test
 	public void testRealign() {
 		// Create a random graph node (fingerprint/tableau don't matter)
 		final GraphNode node = new GraphNode(0, 0);
@@ -67,9 +72,10 @@ public class GraphNodeTest extends TestCase {
 		overallocated = node.realign();
 		assertTrue("Allocation overallocated", overallocated == 0);
 
-		assertTrue("Lost a transition during this allocation business", node.transExists(1, -1));
+		assertTrue("Lost a transition during the allocation business", node.transExists(1, -1));
 	}
 
+	@Test
 	public void testAllocateNested() {
 		// Create a random graph node (fingerprint/tableau don't matter)
 		final GraphNode node = new GraphNode(0, 0);
@@ -91,6 +97,7 @@ public class GraphNodeTest extends TestCase {
 		}
 	}
 
+	@Test
 	public void testAllocateNestedRandom() {
 		// Create a random graph node (fingerprint/tableau don't matter)
 		final GraphNode node = new GraphNode(0, 0);
@@ -117,6 +124,7 @@ public class GraphNodeTest extends TestCase {
 		}
 	}
 
+	@Test
 	public void testAllocateNegative() {
 		// Create a random graph node (fingerprint/tableau don't matter)
 		final GraphNode node = new GraphNode(0, 0);
@@ -124,6 +132,7 @@ public class GraphNodeTest extends TestCase {
 		assertTrue("overallocated", node.realign() == 0);
 	}
 	
+	@Test
 	public void testAllocateAndSuccessorSize() {
 		// Hint to allocate 100 transitions and make sure the actual number of
 		// transitions is 1.
diff --git a/tlatools/test/tlc2/tool/liveness/LivenessConstraintWarning.java b/tlatools/test/tlc2/tool/liveness/LivenessConstraintWarning.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2eee814faaffc7d8228ab3f179c63048e779e26
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/LivenessConstraintWarning.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class LivenessConstraintWarning extends ModelCheckerTestCase {
+
+	public LivenessConstraintWarning() {
+		super("LivenessConstraintWarning");
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recorded(EC.TLC_FEATURE_LIVENESS_CONSTRAINTS));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java b/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2ca8be69fc2ffb7fc0fdd5ef9562c342f012884
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class LivenessSymmetryWarning extends ModelCheckerTestCase {
+
+	public LivenessSymmetryWarning() {
+		super("April25MC", "symmetry");
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recorded(EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/LoopTest.java b/tlatools/test/tlc2/tool/liveness/LoopTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bd0f27d98c21afdc48ebe8c29f32380871ef528
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/LoopTest.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+/**
+ * System LOOP as described by Manna & Pneuli on page 423ff
+ */
+public class LoopTest extends ModelCheckerTestCase {
+	
+	public LoopTest() {
+		super("SystemLoop", "Loop", ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(136L, 64L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 1");
+		expectedTrace.add("x = 2");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		// Without any fairness defined, state 4 is stuttering instead of moving
+		// on to state x=3.
+		assertStuttering(4);
+		
+		//TODO This error trace is not the shortest one. The shortest one would
+		// be stuttering after the initial state x=0 and not after x=2 with x=3
+		// as the last successor in the behavior. However, the SCC search
+		// implemented in LiveWorker#checkSccs checks the path end to start and
+		// not start to end.
+		// If liveness is (forcefully) triggered after the initial state, stuttering
+		// after the initial state is correctly detected.
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/LoopTestForcedPartial.java b/tlatools/test/tlc2/tool/liveness/LoopTestForcedPartial.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1cb43c74a5696db1cf8af9b80267b3fc402f572
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/LoopTestForcedPartial.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.AbstractChecker;
+
+/**
+ * Identical to {@link LoopTest}, except that liveness checking uses
+ * {@link AddAndCheckLiveCheck}. This way, TLC correctly produces the shortest
+ * possible counterexample.
+ */
+public class LoopTestForcedPartial extends ModelCheckerTestCase {
+	
+	static {
+		AbstractChecker.LIVENESS_TESTING_IMPLEMENTATION = true;
+	}
+	
+	public LoopTestForcedPartial() {
+		super("SystemLoop", "Loop", ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("x = 0");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		// Stuttering after the init state.
+		assertStuttering(2);
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/LoopTestWeakFair.java b/tlatools/test/tlc2/tool/liveness/LoopTestWeakFair.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf8a7900bb5ed5e9c5ed2c9b437d6d025696fd9a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/LoopTestWeakFair.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+/**
+ * System LOOP as described by Manna & Pneuli on page 423ff
+ */
+public class LoopTestWeakFair extends ModelCheckerTestCase {
+
+	public LoopTestWeakFair() {
+		super("SystemLoop", "Loop", new String[] { "-config", "SystemLoopWeakFair" });
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "4"));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/May09Test.java b/tlatools/test/tlc2/tool/liveness/May09Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..1534c255879b67f6e80ceb80d5b48ca6aa1085dd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/May09Test.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+/**
+ * May09Test and its counterpart May09dTest prove that the algorithm with
+ * redundant arcs with different action predicate labels in the liveness graph
+ * does not work even when liveness properties containing conjuncts over the
+ * elements of the symmetry set are rewritten to a disjunct and out-arcs are
+ * counted (compare to {@link April29Test} and {@link April29dTest}).
+ * 
+ * <pre>
+ *	[]<>P(a) /\ []<>P(b) rewritten to: []<>P(a) \/ []<>P(b)
+ * </pre>
+ * 
+ * (where P is either a State or Action predicate and a,b are all elements of
+ * the symmetry set)
+ * 
+ * Trying to deduce []<>P(b) from the occurrence of []<>P(a) (or vice versa)
+ * fails because the two specs (Spec & SpecD) under symmetry produce the same
+ * state graph. Without symmetry, Spec does not satisfy []<>P(a) /\ []<>P(b),
+ * only SpecD does. Under symmetry, a version of TLC that does not rewrite the
+ * liveness property finds the bogus counterexample for SpecD.  
+ * 
+ * A yet unexplored research hypothesis is, that labeling all arcs with the
+ * permutation that the to-state represents in the liveness graph. This information
+ * would allow us to distinguish the graphs of May09 and May09d.
+ */
+public class May09Test extends ModelCheckerTestCase {
+
+	public May09Test() {
+		super("May09MC", "symmetry");
+	}
+
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() throws IOException {
+		// Assert TLC has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("/\\ x = a\n/\\ y = 0");
+		expectedTrace.add("/\\ x = a\n/\\ y = 1");
+		expectedTrace.add("/\\ x = a\n/\\ y = 2");
+		expectedTrace.add("/\\ x = a\n/\\ y = 3");
+		expectedTrace.add("/\\ x = a\n/\\ y = 4");
+		expectedTrace.add("/\\ x = a\n/\\ y = 5");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(2, "<Action line 19, col 10 to line 21, col 18 of module May09>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/May09dTest.java b/tlatools/test/tlc2/tool/liveness/May09dTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6150fe234bf68ecb59e306546747518ac5fbfb5a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/May09dTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+
+import java.io.IOException;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+/**
+ * see {@link May09dTest}May09Test
+ */
+public class May09dTest extends ModelCheckerTestCase {
+
+	public May09dTest() {
+		super("May09dMC", "symmetry");
+	}
+
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() throws IOException {
+		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java b/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java
index ba50d0b6e2c57183fc956e6939d662b7abb8edeb..7249b45070590c4e1a3ee56bf347607299016422 100644
--- a/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java
+++ b/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java
@@ -25,50 +25,105 @@
  ******************************************************************************/
 package tlc2.tool.liveness;
 
-import java.io.File;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
+
 import tlc2.TLC;
+import tlc2.TLCGlobals;
 import tlc2.TestMPRecorder;
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
 import tlc2.output.MP;
+import tlc2.tool.CommonTestCase;
+import tlc2.tool.ModelChecker;
+import util.FileUtil;
+import util.SimpleFilenameToStream;
 import util.ToolIO;
 
-public abstract class ModelCheckerTestCase extends TestCase {
-
-	private static final String BASE_DIR = System.getProperty("basedir", "");
-	private static final String TEST_MODEL = "test-model" + File.separator;
+public abstract class ModelCheckerTestCase extends CommonTestCase {
 	
-	private String path = "";
-	private final String spec;
-	protected final TestMPRecorder recorder = new TestMPRecorder();
-	private String[] extraArguments = new String[0];
-
+	protected String path = "";
+	protected String spec;
+	protected String[] extraArguments = new String[0];
+	protected TLC tlc;
+	protected int actualExitStatus = -1;
+	protected int expectedExitStatus = ExitStatus.SUCCESS;
 
 	public ModelCheckerTestCase(String spec) {
-		this.spec = spec;
+		this(spec, ExitStatus.SUCCESS);
+	}
+
+	public ModelCheckerTestCase(String spec, final int exitStatus) {
+		this(spec, "", exitStatus);
 	}
 
 	public ModelCheckerTestCase(String spec, String path) {
-		this(spec);
-		this.path = path;
+		this(spec, path, ExitStatus.SUCCESS);
+	}
+	
+	public ModelCheckerTestCase(String spec, String[] extraArguments) {
+		this(spec, "", extraArguments, ExitStatus.SUCCESS);
+	}
+	
+	public ModelCheckerTestCase(String spec, String[] extraArguments, final int exitStatus) {
+		this(spec, "", extraArguments, exitStatus);
 	}
 	
 	public ModelCheckerTestCase(String spec, String path, String[] extraArguments) {
-		this(spec, path);
+		this(spec, path, extraArguments, ExitStatus.SUCCESS);
+	}
+	
+	public ModelCheckerTestCase(String spec, String path, String[] extraArguments, final int exitStatus) {
+		this(spec, path, exitStatus);
 		this.extraArguments  = extraArguments; 
 	}
 	
+	public ModelCheckerTestCase(final String spec, final String path, final int exitStatus) {
+		super(new TestMPRecorder());
+		this.spec = spec;
+		this.path = path;
+		this.expectedExitStatus = exitStatus;
+	}
+
+	protected void beforeSetUp() {
+		// No-op
+	}
+
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	@Before
 	public void setUp() {
+		beforeSetUp();
+		
+		// some tests might want to access the liveness graph after model
+		// checking completed. Thus, prevent the liveness graph from being
+		// closed too earlier.
+		System.setProperty(ModelChecker.class.getName() + ".vetoCleanup", "true");
+
 		try {
 			// TEST_MODEL is where TLC should look for user defined .tla files
 			ToolIO.setUserDir(BASE_DIR + TEST_MODEL + path);
 			
 			MP.setRecorder(recorder);
 			
-			final TLC tlc = new TLC();
+			// Increase the liveness checking threshold to prevent liveness
+			// checking of an incomplete graph. Most tests check that the 
+			// state queue is empty and fail if not. This is only given 
+			// when liveness checking is executed when all states have been
+			// generated.
+			TLCGlobals.livenessThreshold = Double.MAX_VALUE;
+			
+			tlc = new TLC();
+			tlc.setResolver(new SimpleFilenameToStream());
 			// * We want *no* deadlock checking to find the violation of the
 			// temporal formula
 			// * We use (unless overridden) a single worker to simplify
@@ -80,7 +135,17 @@ public abstract class ModelCheckerTestCase extends TestCase {
 			// *Don't* check for deadlocks. All tests are interested in liveness
 			// checks which are shielded away by deadlock checking. TLC finds a
 			// deadlock (if it exists) before it finds most liveness violations.
-			args.add("-deadlock");
+			if (!checkDeadLock()) {
+				args.add("-deadlock");
+			}
+			
+			args.add("-fp");
+			args.add("0");
+			
+			if (doCoverage()) {
+				args.add("-coverage");
+				args.add("1");
+			}
 			
 			args.add("-workers");
 			args.add(Integer.toString(getNumberOfThreads()));
@@ -88,7 +153,14 @@ public abstract class ModelCheckerTestCase extends TestCase {
 			// Never create checkpoints. They distort performance tests and are
 			// of no use anyway.
 			args.add("-checkpoint");
-			args.add("0");
+			args.add(Integer.toString(doCheckpoint()));
+			
+			// Always print the state graph in dot file notation.
+			if (doDump()) {
+				args.add("-dump");
+				args.add("dot");
+				args.add("${metadir}" + FileUtil.separator + getClass().getCanonicalName() + ".dot");
+			}
 
 			args.addAll(Arrays.asList(extraArguments));
 			
@@ -96,14 +168,78 @@ public abstract class ModelCheckerTestCase extends TestCase {
 			tlc.handleParameters(args.toArray(new String[args.size()]));
 			
 			// Run the ModelChecker
-			tlc.process();
+			final int errorCode = tlc.process();
+			actualExitStatus = EC.ExitStatus.errorConstantToExitStatus(errorCode);
 			
 		} catch (Exception e) {
 			fail(e.getMessage());
 		}
 	}
 
+	protected void beforeTearDown() {
+		// No-op
+	}
+	
+	@After
+	public void tearDown() {
+		beforeTearDown();
+		
+		assertExitStatus();
+	}
+	
+	protected void assertExitStatus() {
+		assertEquals(expectedExitStatus, actualExitStatus);
+	}
+	
+	protected boolean doCoverage() {
+		return true;
+	}
+	
+	/**
+	 * @return True if TLC is to be called with "-deadlock".
+	 */
+	protected boolean checkDeadLock() {
+		return false;
+	}
+	
+	protected boolean doDump() {
+		return true;
+	}
+
+	protected int doCheckpoint() {
+		return 0;
+	}
+
+	/**
+	 * @return The number of worker threads TLC should use.
+	 */
 	protected int getNumberOfThreads() {
 		return 1;
 	}
+	
+	protected void setExitStatus(final int exitStatus) {
+		this.expectedExitStatus = exitStatus;
+	}
+	
+	/**
+	 * E.g. 
+	 * ILiveCheck liveCheck = (ILiveCheck) getField(AbstractChecker.class, "liveCheck",
+	 * 				getField(TLC.class, "instance", tlc));
+	 */
+	protected Object getField(Class<?> targetClass, String fieldName, Object instance) {
+		try {
+			Field field = targetClass.getDeclaredField(fieldName);
+			field.setAccessible(true);
+			return field.get(instance);
+		} catch (NoSuchFieldException e) {
+			e.printStackTrace();
+		} catch (SecurityException e) {
+			e.printStackTrace();
+		} catch (IllegalArgumentException e) {
+			e.printStackTrace();
+		} catch (IllegalAccessException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/NQTest.java b/tlatools/test/tlc2/tool/liveness/NQTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a31ef1797f5fb027c89346ea9eb7ad715965ad9
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/NQTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+import util.TLAConstants;
+
+public class NQTest extends ModelCheckerTestCase {
+
+	public NQTest() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "symmetry" + File.separator + "NQ");
+	}
+	
+	/*
+	 * WRT @Ignore:
+	 * Without symmetry defined, TLC finds no counterexample. With symmetry,
+	 * which is the case for this test, TLC incorrectly finds a counterexample
+	 * which it subsequently fails to recreate. In the end, this causes TLC to
+	 * claim that the spec is not symmetric.
+	 * 
+	 * So why does TLC think the PossibleErrorModel is P-Satisfiable and
+	 * consequently tries to produce a counterexample?
+	 */
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1049", "363", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has *not* found a temporal violation and a counter example
+		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert *no* error trace
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/NQaTest.java b/tlatools/test/tlc2/tool/liveness/NQaTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdb678cf644c0faae5a4c2f7d0076807143b4813
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/NQaTest.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NQaTest extends ModelCheckerTestCase {
+
+	public NQaTest() {
+		super("MCa", "symmetry" + File.separator + "NQ");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1049", "363", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// Assert an error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		
+		fail("Check actual error trace and its completeness");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/NoSymmetryTableauModelCheckerTest.java b/tlatools/test/tlc2/tool/liveness/NoSymmetryTableauModelCheckerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a03385af36d77ad0a9daaca19cec43ccfe1810d5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/NoSymmetryTableauModelCheckerTest.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class NoSymmetryTableauModelCheckerTest extends ModelCheckerTestCase {
+
+	public NoSymmetryTableauModelCheckerTest() {
+		super("NoSymmetryLivenessTableauMC", "symmetry");
+	}
+	
+	@Test
+	public void testSpec() {
+		// ModelChecker intends to check liveness
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED1, "8", "s"));
+		
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5492", "1272", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		// Assert it has not found a temporal violation nor a counter example
+		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/OneBitMutexNoSymmetryTest.java b/tlatools/test/tlc2/tool/liveness/OneBitMutexNoSymmetryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..765435664b315f101d36e9287be4a31fecf5aa31
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/OneBitMutexNoSymmetryTest.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class OneBitMutexNoSymmetryTest extends ModelCheckerTestCase {
+
+	public OneBitMutexNoSymmetryTest() {
+		super("OneBitMutexNoSymmetryMC", "symmetry" + File.separator + "OneBitMutex", ExitStatus.VIOLATION_LIVENESS);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "244", "127", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(11700L, 3728L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(17);
+		//1
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"ncs\" @@ B :> \"ncs\")");
+		//2
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n"
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n"
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"ncs\")");
+		//3
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n"
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n"
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e1\")");
+		//4
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {A})\n"
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")");
+		//5
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e3\")");
+		//6
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")");
+		//7
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"cs\")");
+		//8 (Loops back to)
+		expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n" 
+						+ "/\\ other = (A :> A @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e2\" @@ B :> \"cs\")");
+		//9
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e3\" @@ B :> \"cs\")");
+		//10
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e3\" @@ B :> \"f\")");
+		//11
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e4\" @@ B :> \"f\")");
+		//12
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e4\" @@ B :> \"ncs\")");
+		//13
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e4\" @@ B :> \"e1\")");
+		//14
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e5\" @@ B :> \"e1\")");
+		//15
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e1\")");
+		//16
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {A})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")");
+		//17
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e3\")");
+		//18
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")");
+		//19
+		expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e2\" @@ B :> \"e2\")");
+		//20
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e3\" @@ B :> \"e2\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(9, "<e2 line 66, col 13 to line 74, col 21 of module OneBitMutex>");
+
+		assertUncovered("line 80, col 38 to line 80, col 69 of module OneBitMutex: 0\n"
+				+ "line 96, col 16 to line 96, col 47 of module OneBitMutex: 0\n"
+				+ "line 97, col 16 to line 97, col 50 of module OneBitMutex: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/OneBitMutexTest.java b/tlatools/test/tlc2/tool/liveness/OneBitMutexTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fce4d244ed2bd8174673b3760ce9ad5646d14482
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/OneBitMutexTest.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class OneBitMutexTest extends ModelCheckerTestCase {
+
+	public OneBitMutexTest() {
+		super("OneBitMutexMC", "symmetry" + File.separator + "OneBitMutex");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "133", "68", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(7380L, 2368L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(17);
+		//1
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"ncs\" @@ B :> \"ncs\")");
+		//2
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n"
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n"
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"ncs\")");
+		//3
+		expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n"
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> FALSE)\n"
+						+ "/\\ pc = (A :> \"e2\" @@ B :> \"ncs\")");
+		//4
+		expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n"
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e2\" @@ B :> \"e1\")");
+		//5
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e3\" @@ B :> \"e1\")");
+		//6
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {A})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e3\" @@ B :> \"e2\")");
+		//7
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e3\" @@ B :> \"e3\")");
+		//8 (Loops back to)
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e4\" @@ B :> \"e3\")");
+		//9
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> TRUE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e4\" @@ B :> \"e4\")");
+		//10
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> TRUE)\n" 
+						+ "/\\ pc = (A :> \"e5\" @@ B :> \"e4\")");
+		//11
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e5\" @@ B :> \"e5\")");
+		//12
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e5\")");
+		//13
+		expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" 
+						+ "/\\ other = (A :> B @@ B :> A)\n"
+						+ "/\\ x = (A :> FALSE @@ B :> FALSE)\n" 
+						+ "/\\ pc = (A :> \"e1\" @@ B :> \"e1\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(4, "<Action line 60, col 13 to line 64, col 29 of module OneBitMutex>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3.java
new file mode 100644
index 0000000000000000000000000000000000000000..9590eef845a27ab8dd32ab836b1aed0c2052946e
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+import util.TLAConstants;
+
+public class SymmetryModelCheckerTest3 extends ModelCheckerTestCase {
+
+	public SymmetryModelCheckerTest3() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		// Violates Prop1 with Spec2. TLC finds no violation.
+		//
+		// The reason why TLC does not find the violation is due to the fact that
+		// the action predicate from state s to t is not being taken into
+		// consideration during the liveness graph checks. It is not
+		// being considered because it is a redundant and thus omitted arc in
+		// the liveness graph, but its label (which corresponds to the action
+		// property evaluation) is different.
+		//
+		// Without symmetry, the liveness graph consists of four distinct nodes
+		// {(x=a, y=0), (x=a, y=1), (x=b, y=0), (x=b, y=1)} with transitions
+		// (ignoring self-loops) from {1:2, 2:1, 2:3, 3:4, 4:3, 4:1}. The arc
+		// that violates Prop1 (<>[][x'=x] "x does not change") is the trans.
+		// from node 2. (x=a, y=1) to node 3. (x=b, y=0). TLC correctly finds
+		// a violation and produces the correct counterexample which is asserted
+		// below.
+		//
+		// See test-model/symmetry/MC_Graph.png for a visualization.
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(84L, 32L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = a\n/\\ y = 0");
+		expectedTrace.add("/\\ x = a\n/\\ y = 1"); // <= x changes after this state
+		expectedTrace.add("/\\ x = b\n/\\ y = 0");
+		expectedTrace.add("/\\ x = b\n/\\ y = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(1, "<Action line 27, col 12 to line 29, col 27 of module SymmetryLiveness3>");
+
+		// With symmetry defined, the state/liveness graph is collapsed into
+		// just two states: {(x=a, y=0), (x=b, y=1)} and transitions going
+		// from {1:2, 2:1} (self-loops omitted). Thus, TLC never checks the
+		// transition from (x=a, y=1) -> (x=b, y=0) which violates Prop1.
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3a.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3a.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2d1ee5821880900693fdf440c15392568dd9215
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3a.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SymmetryModelCheckerTest3a extends ModelCheckerTestCase {
+
+	public SymmetryModelCheckerTest3a() {
+		super("MCa", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		// Violates Prop1 of Spec1 which TLC correctly detects. However, it
+		// produced a bogus counterexample. It does not use the symmetric state
+		// that is actually supposed to be on the error trace. It uses the
+		// smallest state under symmetry (the one all symmetric states get
+		// mapped to).
+		//
+		// Spec1 and its action N1 more precisely can never make Prop1 eventually
+		// _globally_ true. The second disjunct flips x's state forever. This
+		// violation is correctly detected by TLC. However, due to the fact that
+		// the x=b state is symmetry reduced and thus discarded from the
+		// liveness graph causes the counter-example to incorrectly show a
+		// violating state with x=a (where x=a logically does not violate
+		// Prop1).
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "2", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(72L, 32L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = a\n/\\ y = 0");
+		expectedTrace.add("/\\ x = a\n/\\ y = 1"); // <= x changes after this state
+		expectedTrace.add("/\\ x = b\n/\\ y = 0");
+		expectedTrace.add("/\\ x = b\n/\\ y = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(1);
+		
+		// Surprisingly enough, it turns out that LiveWorker#printTrace actually
+		// generates a partially valid trace but simply fails to correctly print
+		// it. It omits the final/last state with x=b from being printed. The
+		// reason why printTrace(..) correctly generates the states is due to
+		// the way the error trace gets generated. Starting from an init state,
+		// it uses the predecessor and the successors fingerprint to generate
+		// the successor state. For this model this means the error trace
+		// contains the states <<(x=a, y=0), (x=a, y=1), (x=b, y=0)>>. The state
+		// (x=b, y=0) is generated from its fingerprint f and its predecessor
+		// state (x=a, y=1) AND the action N1. Since N1 mandates that x flips,
+		// tool generates a state with x=b despite the declared symmetry.
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLong.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLong.java
new file mode 100644
index 0000000000000000000000000000000000000000..897af5e7d1c0d4c4461a3a2046d7ffe831979854
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLong.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SymmetryModelCheckerTestLong extends ModelCheckerTestCase {
+
+	public SymmetryModelCheckerTestLong() {
+		super("LongMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "8", "5", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(192L, 80L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = a\n/\\ y = 0");
+		expectedTrace.add("/\\ x = a\n/\\ y = 1");
+		expectedTrace.add("/\\ x = a\n/\\ y = 2");
+		expectedTrace.add("/\\ x = a\n/\\ y = 3");
+		expectedTrace.add("/\\ x = a\n/\\ y = 4"); // <= x changes after this state
+		expectedTrace.add("/\\ x = b\n/\\ y = 0");
+		expectedTrace.add("/\\ x = b\n/\\ y = 1");
+		expectedTrace.add("/\\ x = b\n/\\ y = 2");
+		expectedTrace.add("/\\ x = b\n/\\ y = 3");
+		expectedTrace.add("/\\ x = b\n/\\ y = 4");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(1, "<Action line 47, col 12 to line 49, col 27 of module SymmetryLivenessLong>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLonga.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLonga.java
new file mode 100644
index 0000000000000000000000000000000000000000..b92e7632b62a070edc93ea1c3952998e1a484b91
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLonga.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SymmetryModelCheckerTestLonga extends ModelCheckerTestCase {
+
+	public SymmetryModelCheckerTestLonga() {
+		super("LongMCa", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "5", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(180L, 80L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("/\\ x = a\n/\\ y = 0");
+		expectedTrace.add("/\\ x = a\n/\\ y = 1");
+		expectedTrace.add("/\\ x = a\n/\\ y = 2");
+		expectedTrace.add("/\\ x = a\n/\\ y = 3");
+		expectedTrace.add("/\\ x = a\n/\\ y = 4"); // <= x changes after this state
+		expectedTrace.add("/\\ x = b\n/\\ y = 0");
+		expectedTrace.add("/\\ x = b\n/\\ y = 1");
+		expectedTrace.add("/\\ x = b\n/\\ y = 2");
+		expectedTrace.add("/\\ x = b\n/\\ y = 3");
+		expectedTrace.add("/\\ x = b\n/\\ y = 4");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(1, "<Action line 25, col 12 to line 27, col 27 of module SymmetryLivenessLong>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryTableauLiveCheckTest.java b/tlatools/test/tlc2/tool/liveness/SymmetryTableauLiveCheckTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..508212d0c01ed43009be3cd8d3f74a3303a9e6a4
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/SymmetryTableauLiveCheckTest.java
@@ -0,0 +1,380 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.tool.Action;
+import tlc2.tool.ITool;
+import tlc2.tool.StateVec;
+import tlc2.tool.TLCState;
+import tlc2.tool.liveness.GraphNode.Transition;
+import tlc2.tool.queue.DummyTLCState;
+import tlc2.util.BitVector;
+import tlc2.util.SetOfStates;
+import tlc2.util.statistics.DummyBucketStatistics;
+
+public class SymmetryTableauLiveCheckTest {
+
+	private ITool tool;
+	
+	@Before
+	public void createTool() {
+		this.tool = EasyMock.createNiceMock(ITool.class);
+	}
+
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testTableau() throws IOException {
+		final ILiveCheck lc = getLiveCheckWithTwoNodeTableau();
+		
+		final SetOfStates nexts = new SetOfStates(1);
+		final AbstractDiskGraph diskGraph = lc.getChecker(0).getDiskGraph();
+		
+		// Add init state v
+		final TLCState v = new DummyTLCState(100L);
+		lc.addInitState(tool, v, v.fingerPrint());
+		
+		// one init node (two elements in LongVec)
+		assertEquals(1, diskGraph.getInitNodes().size() / 2);
+
+		// Add v > s
+		final TLCState s = new DummyTLCState(200L);
+		nexts.put(s);
+		lc.addNextState(null, v, v.fingerPrint(), nexts);
+		
+		assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); // only tidx0 is an init node
+		
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize());
+		
+		// Add s > t
+		nexts.clear();
+		final TLCState t = new DummyTLCState(300L);
+		nexts.put(t);
+		lc.addNextState(null, s, s.fingerPrint(), nexts);
+		
+		assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize());
+		
+		assertEquals(2, diskGraph.getNode(s.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize());
+
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize());
+
+		// add s > u
+		nexts.clear();
+		final TLCState u = new DummyTLCState(400L);
+		nexts.put(u);
+		lc.addNextState(null, s, s.fingerPrint(), nexts);
+		
+		Assert.fail("finish incomplete test! Assertions below are partially bogus.");
+		
+		assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize());
+		
+		assertEquals(4, diskGraph.getNode(s.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize());
+
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize());
+
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 1).succSize());
+
+		assertTrue(diskGraph.checkInvariants(0, 0));
+	}
+	
+	private ILiveCheck getLiveCheckWithTwoNodeTableau() throws IOException {
+		final TBGraphNode node1 = EasyMock.createNiceMock(TBGraphNode.class);
+		EasyMock.expect(node1.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject()))
+				.andReturn(true).anyTimes();
+		EasyMock.expect(node1.nextSize()).andReturn(0).anyTimes();
+		EasyMock.expect(node1.getIndex()).andReturn(1).anyTimes();
+		EasyMock.replay(node1);
+
+		final TBGraphNode node0 = EasyMock.createMock(TBGraphNode.class);
+		EasyMock.expect(node0.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject()))
+				.andReturn(true).anyTimes();
+		EasyMock.expect(node0.nextSize()).andReturn(2).anyTimes();
+		EasyMock.expect(node0.nextAt(0)).andReturn(node0).anyTimes();
+		EasyMock.expect(node0.nextAt(1)).andReturn(node1).anyTimes();
+		EasyMock.expect(node0.getIndex()).andReturn(0).anyTimes();
+		EasyMock.replay(node0);
+
+		final TBGraph tbGraph = new TBGraph();
+		tbGraph.addElement(node0);
+		tbGraph.addElement(node1);
+		tbGraph.setInitCnt(1);
+
+		// Configure OOS mock to react to the subsequent invocation. This is a
+		// essentially the list of calls being made on OOS during
+		// LiveCheck#addInitState and LiveCheck#addNextState
+		final OrderOfSolution oos = EasyMock.createNiceMock(OrderOfSolution.class);
+		EasyMock.expect(oos.hasTableau()).andReturn(true);
+		EasyMock.expect(oos.getTableau()).andReturn(tbGraph).anyTimes();
+		EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes();
+		EasyMock.expect(oos.getCheckState()).andReturn(new LiveExprNode[0]).anyTimes();
+		EasyMock.expect(oos.checkState(null, (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes();
+		EasyMock.replay(oos);
+		
+		return new LiveCheck(tool,
+				new OrderOfSolution[] { oos }, System.getProperty("java.io.tmpdir"), new DummyBucketStatistics(), null);
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSymmetry() throws IOException {
+		final TLCState s = new DummyTLCState(200L);
+		final TLCState s1 = new DummyTLCState(s.fingerPrint()); // symmetric sibling of s
+		final TLCState t = new DummyTLCState(300L);
+	
+		final ILiveCheck lc = getLiveCheckWithTwoNodeTableauSymmetry(s, s1, t);
+		
+		final SetOfStates nexts = new SetOfStates(1);
+		final AbstractDiskGraph diskGraph = lc.getChecker(0).getDiskGraph();
+		
+		// Add init state v
+		final TLCState v = new DummyTLCState(100L);
+		lc.addInitState(tool, v, v.fingerPrint());
+		
+		// one init node (two elements in LongVec)
+		assertEquals(1, diskGraph.getInitNodes().size() / 2);
+
+		// Add v > s
+		nexts.put(s);
+		lc.addNextState(null, v, v.fingerPrint(), nexts);
+		
+		GraphNode vgn = diskGraph.getNode(v.fingerPrint(), 0);
+		assertEquals(2, vgn.succSize()); // s is consistent with tidx0 and tidx1, but not with tidx2
+		final Set<Transition> tvgn = vgn.getTransition();
+		assertTrue(tvgn.contains(new Transition(s.fingerPrint(), 0, new BitVector(0))));
+		assertTrue(tvgn.contains(new Transition(s.fingerPrint(), 1, new BitVector(0))));
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); // only tidx0 is an init node
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize()); // only tidx0 is an init node
+		
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 2).succSize());
+		
+		// Add s > t
+		nexts.clear();
+		nexts.put(t);
+		lc.addNextState(null, s, s.fingerPrint(), nexts);
+		
+		assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize());
+		
+		// Even though t is consistent with all three tableau nodes (0,1,2),
+		// (fingerprint X tableau idx) node <<200.0>> checks only its direct
+		// succeccors tidx0, tidx1. Not tidx2;
+		assertEquals(2, diskGraph.getNode(s.fingerPrint(), 0).succSize()); // 300.0, 300.1
+		// <<200,1>> checks tidx1 and tidx2
+		assertEquals(2, diskGraph.getNode(s.fingerPrint(), 1).succSize()); // 300.1, 300.2
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 2).succSize());
+
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 2).succSize());
+
+		// Add additional init state u
+		final TLCState u = new DummyTLCState(400L);
+		lc.addInitState(tool, u, u.fingerPrint());
+		
+		// two init nodes now (four elements in LongVec)
+		assertEquals(2, diskGraph.getInitNodes().size() / 2);
+
+		assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize());
+		
+		assertEquals(2, diskGraph.getNode(s.fingerPrint(), 0).succSize());
+		assertEquals(2, diskGraph.getNode(s.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(s.fingerPrint(), 2).succSize());
+
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 2).succSize());
+
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 2).succSize());
+	
+		// add a symmetric s1 (same fingerprint as s)
+		nexts.clear();
+		nexts.put(s1);
+		lc.addNextState(null, u, u.fingerPrint(), nexts);
+		
+		assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize());
+
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(t.fingerPrint(), 2).succSize());
+
+		assertEquals(1, diskGraph.getNode(u.fingerPrint(), 0).succSize());
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 1).succSize());
+		assertEquals(0, diskGraph.getNode(u.fingerPrint(), 2).succSize());
+		
+		Assert.fail("finish incomplete test! Assertions below are partially bogus.");
+		
+		//TODO GN are equals in terms of <<state, tidx>> but not necessarily transitions
+		assertEquals(diskGraph.getNode(s1.fingerPrint(), 0), diskGraph.getNode(s.fingerPrint(), 0));
+		assertEquals(diskGraph.getNode(s1.fingerPrint(), 1), diskGraph.getNode(s.fingerPrint(), 1));
+		assertEquals(diskGraph.getNode(s1.fingerPrint(), 2), diskGraph.getNode(s.fingerPrint(), 2));
+
+		// s1 and s are represented by the same GraphNode
+		final GraphNode node200_0 = diskGraph.getNode(s.fingerPrint(), 0);
+		assertEquals(2, node200_0.succSize());
+		final Set<Transition> transitions200_0 = node200_0.getTransition();
+		assertTrue(transitions200_0.contains(new Transition(t.fingerPrint(), 0, new BitVector(0))));
+		assertTrue(transitions200_0.contains(new Transition(t.fingerPrint(), 1, new BitVector(0))));
+		
+		final GraphNode node200_1 = diskGraph.getNode(s.fingerPrint(), 1);
+		assertEquals(2, node200_1.succSize());
+		final Set<Transition> transitions200_1 = node200_1.getTransition();
+		assertTrue(transitions200_1.contains(new Transition(t.fingerPrint(), 1, new BitVector(0))));
+		assertTrue(transitions200_1.contains(new Transition(t.fingerPrint(), 2, new BitVector(0))));
+	
+		final GraphNode node200_2 = diskGraph.getNode(s.fingerPrint(), 2);
+		assertEquals(0, node200_2.succSize());
+
+		assertTrue(diskGraph.checkInvariants(0, 0));
+	}
+	
+	/**
+	 * @param s The smallest state under symmetry
+	 * @param sSymmetric A symmetric state to s
+	 */
+	private ILiveCheck getLiveCheckWithTwoNodeTableauSymmetry(final TLCState s, final TLCState sSymmetric, final TLCState t) throws IOException {
+		final TBGraphNode node2 = EasyMock.createMock(TBGraphNode.class);
+		// consistency
+		final Capture<TLCState> capture = new Capture<TLCState>();
+		EasyMock.expect(node2.isConsistent(EasyMock.capture(capture), (ITool) EasyMock.anyObject())).andAnswer(new IAnswer<Boolean>() {
+			public Boolean answer() throws Throwable {
+				final TLCState value = capture.getValue();
+				if (value == s) {
+					return false;
+				}
+				return true;
+			}
+		}).anyTimes();
+		// index
+		EasyMock.expect(node2.getIndex()).andReturn(2).anyTimes();
+		// nextSize
+		EasyMock.expect(node2.nextSize()).andReturn(1).anyTimes();
+		// nextAt
+		EasyMock.expect(node2.nextAt(0)).andReturn(node2).anyTimes();
+		EasyMock.replay(node2);
+		
+		final TBGraphNode node1 = EasyMock.createMock(TBGraphNode.class);
+		// consistency
+		final Capture<TLCState> capture1 = new Capture<TLCState>();
+		EasyMock.expect(node1.isConsistent(EasyMock.capture(capture1), (ITool) EasyMock.anyObject())).andAnswer(new IAnswer<Boolean>() {
+			public Boolean answer() throws Throwable {
+				final TLCState value = capture1.getValue();
+				if (value == sSymmetric) {
+					return false;
+				}
+				return true;
+			}
+		}).anyTimes();
+		// index
+		EasyMock.expect(node1.getIndex()).andReturn(1).anyTimes();
+		// nextSize
+		EasyMock.expect(node1.nextSize()).andReturn(2).anyTimes();
+		// nextAt
+		EasyMock.expect(node1.nextAt(0)).andReturn(node1).anyTimes();
+		EasyMock.expect(node1.nextAt(1)).andReturn(node2).anyTimes();
+		EasyMock.replay(node1);
+
+		final TBGraphNode node0 = EasyMock.createMock(TBGraphNode.class);
+		// consistency (simpler to node1 and node2)
+		EasyMock.expect(node0.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject())).andReturn(true).anyTimes();
+		// index
+		EasyMock.expect(node0.getIndex()).andReturn(0).anyTimes();
+		// nextSize
+		EasyMock.expect(node0.nextSize()).andReturn(2).anyTimes();
+		// nextAt
+		EasyMock.expect(node0.nextAt(0)).andReturn(node0).anyTimes();
+		EasyMock.expect(node0.nextAt(1)).andReturn(node1).anyTimes();
+		EasyMock.replay(node0);
+
+		final TBGraph tbGraph = new TBGraph();
+		tbGraph.addElement(node0);
+		tbGraph.addElement(node1);
+		tbGraph.addElement(node2);
+		tbGraph.setInitCnt(1);
+
+		// Configure OOS mock to react to the subsequent invocation. This is a
+		// essentially the list of calls being made on OOS during
+		// LiveCheck#addInitState and LiveCheck#addNextState
+		final OrderOfSolution oos = EasyMock.createNiceMock(OrderOfSolution.class);
+		EasyMock.expect(oos.hasTableau()).andReturn(true);
+		EasyMock.expect(oos.getTableau()).andReturn(tbGraph).anyTimes();
+		EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes();
+		EasyMock.expect(oos.getCheckState()).andReturn(new LiveExprNode[0]).anyTimes();
+		EasyMock.expect(oos.checkState(null, (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes();
+		EasyMock.replay(oos);
+		
+		final ITool tool = EasyMock.createNiceMock(ITool.class);
+		EasyMock.expect(tool.hasSymmetry()).andReturn(true);
+		final Capture<TLCState> nextStates = new Capture<TLCState>();
+		EasyMock.expect(tool.getNextStates((Action) EasyMock.anyObject(), EasyMock.capture(nextStates))).andAnswer(new IAnswer<StateVec>() {
+			public StateVec answer() throws Throwable {
+			    final StateVec nss = new StateVec(0);
+			    // s > t for sSymmetric
+			    TLCState state = nextStates.getValue();
+			    if (state == sSymmetric) {
+			    	nss.addElement(t);
+			    }
+				return nss;
+			}
+		});
+		EasyMock.expect(tool.isInModel((TLCState) EasyMock.anyObject())).andReturn(true).anyTimes();
+		EasyMock.expect(tool.isInActions((TLCState) EasyMock.anyObject(), (TLCState) EasyMock.anyObject())).andReturn(true).anyTimes();
+		EasyMock.replay(tool);
+		return new LiveCheck(tool,
+				new OrderOfSolution[] { oos }, "states", new DummyBucketStatistics(), null);
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryTableauModelCheckerTest.java b/tlatools/test/tlc2/tool/liveness/SymmetryTableauModelCheckerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..286fa498606ad4b5ed650ae382a5a43de291ecf0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/SymmetryTableauModelCheckerTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SymmetryTableauModelCheckerTest extends ModelCheckerTestCase {
+
+	public SymmetryTableauModelCheckerTest() {
+		super("SymmetryLivenessTableauMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		// ModelChecker intends to check liveness
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "2"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "8", "s", "2"));
+		
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "779", "168", "0"));
+
+		// Assert it has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		// The spec's 'NoVal' value is what violates symmetry.
+		assertTrue(recorder.recordedWithStringValue(EC.GENERAL,
+				"TLC threw an unexpected exception.\n" 
+						+ "This was probably caused by an error in the spec or model.\n"
+						+ "The error occurred when TLC was checking liveness.\n"
+						+ "The exception was a tlc2.tool.EvalException\n"
+						+ ": Failed to recover the next state from its fingerprint during\n"
+						+ "liveness error trace re-construction. This indicates that the\n"
+						+ "spec is in fact not symmetric (Please report a TLC bug if the\n"
+						+ "spec is known to be symmetric)."));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java b/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java
index 9eaa09015f05e1e71f0aafb6573c3f3401cf861d..ac0cb958289c0c260e94c75f76868b84de3a3737 100644
--- a/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java
+++ b/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java
@@ -26,16 +26,23 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 
+import org.junit.Test;
+
 import tlc2.util.BitVector;
 import tlc2.util.LongVec;
-import tlc2.util.statistics.BucketStatistics;
+import tlc2.util.statistics.FixedSizedBucketStatistics;
 import tlc2.util.statistics.IBucketStatistics;
 
 public class TableauDiskGraphTest extends DiskGraphTest {
 
-	private static final IBucketStatistics GRAPH_STATS = new BucketStatistics("Test Dummy", 16);
+	private static final IBucketStatistics GRAPH_STATS = new FixedSizedBucketStatistics("Test Dummy", 16);
 	private static final int NUMBER_OF_SOLUTIONS = 1;
 	private static final int NUMBER_OF_ACTIONS = 0;
 	private static final BitVector NO_ACTIONS = null;
@@ -70,6 +77,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	 *	                +------+                           
 	 *
 	 */
+	@Test
 	public void testGetShortestPath() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -157,6 +165,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
  	 *
 	 * (Drawn with http://asciiflow.com/)
 	 */
+	@Test
 	public void testUnifyingNodeInPath() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -243,6 +252,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	 *	                      |         |                       
 	 *	                      +---------+                        
 	 */
+	@Test
 	public void testUnifyingNodeShortestPath() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -332,8 +342,9 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	 * 
 	 * The specialty here is that there are *two* init nodes and one them has *no* successors.
 	 * 
-	 * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=293
+	 * @see Bug #293 in general/bugzilla/index.html
 	 */
+	@Test
 	public void testPathWithTwoInitNodesWithTableau() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -382,6 +393,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	 * Test that getPath returns the correct init state if the graph contains
 	 * multiple initial states with the same fingerprint but different tableau idxs.
 	 */
+	@Test
 	public void testGetPathWithTwoInits() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 		
@@ -414,6 +426,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	/*
 	 * Test how an initial node transitions through the done state
 	 */
+	@Test
 	public void testNodeSetDone() throws IOException {
 		final TableauDiskGraph dg = (TableauDiskGraph) getDiskGraph();
 		final long fingerprint = 1L;
@@ -436,6 +449,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	 * Tests a path where both states have an identical fingerprint and only
 	 * differ in the tableau idx.
 	 */
+	@Test
 	public void testGetPathWithTwoNodesWithSameFingerprint() throws IOException {
 		final AbstractDiskGraph dg = getDiskGraph();
 
@@ -465,6 +479,7 @@ public class TableauDiskGraphTest extends DiskGraphTest {
 	/*
 	 * Test that it is possible to "update" a GraphNode's outgoing transitions.
 	 */
+	@Test
 	public void testLookupExistingNodeWithTidx() throws IOException {
 		final TableauDiskGraph dg = (TableauDiskGraph) getDiskGraph();
 		
diff --git a/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java b/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java
index 6c9dfcfabf00fe687ef6147880554e9ac9de9145..c2c1c300a8171085df5c6036443b4a7a6d30dffa 100644
--- a/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java
+++ b/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java
@@ -26,12 +26,17 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.util.Arrays;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class TableauNodePtrTableTest extends TestCase {
+public class TableauNodePtrTableTest {
 
+	@Test
 	public void testSetDone() {
 		final TableauNodePtrTable tbl = new TableauNodePtrTable(0); // init with 0 so that grow is tested
 		
@@ -56,6 +61,7 @@ public class TableauNodePtrTableTest extends TestCase {
 	}
 	
 	// Test various methods which apparently all yield pretty much the same result
+	@Test
 	public void testRedundantMethodYieldSameResult() {
 		final TableauNodePtrTable tbl = new TableauNodePtrTable(0); // init with 0 so that grow is tested
 		
diff --git a/tlatools/test/tlc2/tool/liveness/TableauSymmetryTest.java b/tlatools/test/tlc2/tool/liveness/TableauSymmetryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..697e7aef2110aae13024b0584056dd95e1318f99
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/TableauSymmetryTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class TableauSymmetryTest extends ModelCheckerTestCase {
+
+	public TableauSymmetryTest() {
+		super("TableauSymmetryMC", "symmetry");
+	}
+	
+	@Test
+	@Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.")
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "6", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert it has found the temporal violation and also a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertNodeAndPtrSizes(624L, 224L);
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(7);
+		// Trace prefix
+		expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"busy\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"done\")");
+		expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(4, "<Action line 7, col 13 to line 8, col 47 of module TableauSymmetry>");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/Test3.java b/tlatools/test/tlc2/tool/liveness/Test3.java
index 6067d34831692d2a3d27809834b009fd105e2780..d87879af307516db66a7b62ae037b2b2e33258f1 100644
--- a/tlatools/test/tlc2/tool/liveness/Test3.java
+++ b/tlatools/test/tlc2/tool/liveness/Test3.java
@@ -26,21 +26,29 @@
 
 package tlc2.tool.liveness;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
-import tlc2.tool.TLCStateInfo;
+import tlc2.output.EC.ExitStatus;
 
 public class Test3 extends ModelCheckerTestCase {
 
 	public Test3() {
-		super("Test3");
+		super("Test3", ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
-		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
@@ -48,33 +56,15 @@ public class Test3 extends ModelCheckerTestCase {
 		
 		// Assert the error trace
 		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
-		List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2);
-
-		int i = 0; // State's position in records
-		Object[] objs = (Object[]) records.get(i++);
-		TLCStateInfo stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("x = 0", stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("x = 1", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
+		final List<String> expectedTrace = new ArrayList<String>(4);
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 1");
+		expectedTrace.add("x = 0");
+		expectedTrace.add("x = 2");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("x = 0", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		objs = (Object[]) records.get(i++);
-		stateInfo = (TLCStateInfo) objs[0];
-		assertEquals("x = 2", stateInfo.toString().trim());
-		assertEquals(i, objs[1]);
-		
-		// Assert the error trace ends with "Back to state 1"
-		assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE));
-		records = recorder.getRecords(EC.TLC_BACK_TO_STATE);
-		objs = (Object[]) records.get(0);
-		assertEquals("1", objs[0]);
+		assertBackToState(1);
+
+	assertZeroUncovered();
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/TwoPhaseCommitTest.java b/tlatools/test/tlc2/tool/liveness/TwoPhaseCommitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ce85eba6de9539e5aa5c3b822aef521895abfc5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/TwoPhaseCommitTest.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+
+import java.io.File;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import util.TLAConstants;
+
+public class TwoPhaseCommitTest extends ModelCheckerTestCase {
+
+	public TwoPhaseCommitTest() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "symmetry" + File.separator + "TwoPhaseCommit");
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+	assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestA.java b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestA.java
new file mode 100644
index 0000000000000000000000000000000000000000..a469e74413a17fb5d5873d06ab9b9dbf43982a69
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestA.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class UnsymmetricModelCheckerTestA extends ModelCheckerTestCase {
+
+	public UnsymmetricModelCheckerTestA() {
+		super("UnsymmetricMCA", "symmetry", ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker intends to check liveness
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "2", "s", "1"));
+		
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0"));
+	
+		// Assert it has found a temporal violation and a counter example
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+		
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(2);
+		expectedTrace.add("x = a");
+		expectedTrace.add("x = 1");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+
+		assertBackToState(1);
+
+		assertUncovered("line 19, col 31 to line 19, col 36 of module Unsymmetric: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestB.java b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestB.java
new file mode 100644
index 0000000000000000000000000000000000000000..1012a8feb1de618e944903d26bc6536c95ecbc13
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestB.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class UnsymmetricModelCheckerTestB extends ModelCheckerTestCase {
+
+	public UnsymmetricModelCheckerTestB() {
+		super("UnsymmetricMCB", "symmetry");
+	}
+	
+	@Test
+	public void testSpec() {
+		// ModelChecker intends to check liveness
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "1"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "2", "s", "1"));
+		
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0"));
+	
+		// Contrary to UMCTA, B doesn't find a counter-example. This is due to
+		// the CHOOSE on S and the selected initial state.
+
+		assertUncovered("line 37, col 31 to line 37, col 36 of module Unsymmetric: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java b/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java
index 4d8398abe178ab98538a93540f6592fe644ec02f..192c88e8a186bad2ce5b5ccc4648f99dd63633bd 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java
@@ -26,9 +26,16 @@
 
 package tlc2.tool.liveness.simulation;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
 import tlc2.tool.TLCStateInfo;
 import tlc2.tool.liveness.ModelCheckerTestCase;
 
@@ -37,13 +44,15 @@ public abstract class AbstractExampleTestCase extends ModelCheckerTestCase {
 	public AbstractExampleTestCase(final String cfg) {
 		// Checks the depth parameter too. Depth <= 100 will cause simluation to
 		// go on forever.
-		super(cfg, "simulation", new String[] {"-simulate", "-depth", "11"});
+		super(cfg, "simulation", new String[] { "-simulate", "-depth", "11" }, ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
 		assertTrue(recorder.recordedWithStringValue(EC.TLC_STATS_SIMU, "12"));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java
index 55fc30dde773c7b7d7eebf5a090f35ef03f07b80..a2ee64447c80ac68c598b663bc82f30e9bb4439c 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java
@@ -37,6 +37,6 @@ public class LiveCheckSimulationTest2 extends SuccessfulSimulationTestCase {
 
 	public LiveCheckSimulationTest2() {
 		super("Test2", "/", new String[] {"-simulate", "-depth", "10"});
-		TLC.traceNum = 50;
+		TLC.setTraceNum(50);
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java
index c18e2b3b3d72f78cc7f1c6a9f8301eb868b0a575..26413ee3bc93b791f59f6ef0589d40c21f42c0f0 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java
@@ -26,10 +26,17 @@
 
 package tlc2.tool.liveness.simulation;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.TLC;
 import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
 import tlc2.tool.Simulator;
 import tlc2.tool.TLCStateInfo;
 import tlc2.tool.liveness.ModelCheckerTestCase;
@@ -44,16 +51,18 @@ public class LiveCheckSimulationTest2a extends ModelCheckerTestCase {
 	}
 
 	public LiveCheckSimulationTest2a() {
-		super("Test2a", "/", new String[] {"-simulate", "-depth", "10"});
+		super("Test2a", "/", new String[] {"-simulate", "-depth", "10"}, ExitStatus.VIOLATION_LIVENESS);
 		
 		// Stop after 100 traces due to lack of general timeout regardless of outcome
-		TLC.traceNum = 100;
+		TLC.setTraceNum(100);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
 		assertTrue(recorder.recorded(EC.TLC_STATS_SIMU));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java
index 9c353715ef591a824bacfc9fcf603cb9baaf6df1..b090a8f57ee02036357c58d966134a5ee12c7d22 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java
@@ -26,16 +26,17 @@
 
 package tlc2.tool.liveness.simulation;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import java.io.IOException;
-import java.util.Enumeration;
 
 import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
 
-import junit.framework.TestCase;
-import tlc2.tool.Action;
-import tlc2.tool.StateVec;
+import tlc2.tool.ITool;
 import tlc2.tool.TLCState;
-import tlc2.tool.Tool;
 import tlc2.tool.liveness.AbstractDiskGraph;
 import tlc2.tool.liveness.ILiveCheck;
 import tlc2.tool.liveness.LiveCheck;
@@ -44,10 +45,17 @@ import tlc2.tool.liveness.OrderOfSolution;
 import tlc2.tool.liveness.TBGraph;
 import tlc2.tool.liveness.TBGraphNode;
 import tlc2.tool.queue.DummyTLCState;
-import tlc2.util.LongVec;
+import tlc2.util.SetOfStates;
 import tlc2.util.statistics.DummyBucketStatistics;
 
-public class LiveCheckTest extends TestCase {
+public class LiveCheckTest {
+
+	private ITool tool;
+	
+	@Before
+	public void createTool() {
+		this.tool = EasyMock.createNiceMock(ITool.class);
+	}
 
 	// Add identical state twice, which can happen in simulation mode when the
 	// trace is: <<100L, 200L, ..., 100L, 200L>> (as in a cycle that is
@@ -56,10 +64,12 @@ public class LiveCheckTest extends TestCase {
 	// change.
 	// Note that adding/updating the state would result in a larger
 	// on-disk file, but doesn't seem to invalidate the simulation validity.
+	@Test
 	public void testAddIdenticalNodeTwiceNoTableau() throws IOException {
 		addIdenticalNodeTwice(false, -1);
 	}
 
+	@Test
 	public void testAddIdenticalNodeTwiceWithTableau() throws IOException {
 		addIdenticalNodeTwice(true, 0);
 	}
@@ -77,61 +87,49 @@ public class LiveCheckTest extends TestCase {
 		assertNotNull(diskGraph);
 		
 		final TLCState state = new DummyTLCState();
-		liveCheck.addInitState(state, 100L);
+		liveCheck.addInitState(tool, state, 100L);
 
-		final StateVec stateVec = new StateVec(1);
-		stateVec.addElement(new DummyTLCState());
-
-		final LongVec longVec = new LongVec(1);
-		longVec.addElement(200L);
+		final SetOfStates setOfStates = new SetOfStates(1);
+		setOfStates.put(200L, new DummyTLCState(200L));
 		
 		// Add state 100L the first time, then add its successor
-		liveCheck.addNextState(state, 100L, stateVec, longVec);
-		liveCheck.addNextState(state, 200L, stateVec, longVec);
+		liveCheck.addNextState(tool, state, 100L, setOfStates);
+		liveCheck.addNextState(tool, state, 200L, setOfStates);
 		assertEquals(0, diskGraph.getPtr(100L, tableauId));
 
 		// Add state 100L again and check that it does *not* 
 		// end up in disk graph.
-		liveCheck.addNextState(state, 100L, stateVec, longVec);
+		liveCheck.addNextState(tool, state, 100L, setOfStates);
 		assertEquals(0, diskGraph.getPtr(100L, tableauId));
 	}
 
 	private ILiveCheck getLiveCheck() throws IOException {
+		final ITool tool = EasyMock.createNiceMock(ITool.class);
 		// Configure OOS mock to react to the subsequent invocation. This is
 		// essentially the list of calls being made on OOS during
 		// LiveCheck#addInitState and LiveCheck#addNextState
 		final OrderOfSolution oos = EasyMock.createNiceMock(OrderOfSolution.class);
 		EasyMock.expect(oos.hasTableau()).andReturn(false);
 		EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes();
-		EasyMock.expect(oos.checkState((TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes();
+		EasyMock.expect(oos.checkState((ITool) EasyMock.anyObject(), (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes();
 		EasyMock.replay(oos);
 		
-		return new LiveCheck(EasyMock.createNiceMock(Tool.class), new Action[0],
+		return new LiveCheck(tool,
 				new OrderOfSolution[] { oos }, System.getProperty("java.io.tmpdir"), new DummyBucketStatistics());
 	}
-
+	
 	private ILiveCheck getLiveCheckWithTableau() throws IOException {
 		final TBGraphNode node = EasyMock.createMock(TBGraphNode.class);
-		EasyMock.expect(node.isConsistent((TLCState) EasyMock.anyObject(), (Tool) EasyMock.anyObject())).andReturn(true)
+		EasyMock.expect(node.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject())).andReturn(true)
 				.anyTimes();
 		EasyMock.expect(node.nextSize()).andReturn(1).anyTimes();
 		EasyMock.expect(node.nextAt(0)).andReturn(node).anyTimes();
+		EasyMock.expect(node.getIndex()).andReturn(0).anyTimes();
 		EasyMock.replay(node);
 		
-		final TBGraph tbGraph = EasyMock.createNiceMock(TBGraph.class);
-		EasyMock.expect(tbGraph.elements()).andReturn(new Enumeration<TBGraphNode>() {
-			boolean has = true;
-			public boolean hasMoreElements() {
-				return has;
-			}
-			public TBGraphNode nextElement() {
-				has = false;
-				return node;
-			}
-		}).anyTimes();
-		EasyMock.expect(tbGraph.getInitCnt()).andReturn(1).anyTimes();
-		EasyMock.expect(tbGraph.getNode(EasyMock.anyInt())).andReturn(node).anyTimes();
-		EasyMock.replay(tbGraph);
+		final TBGraph tbGraph = new TBGraph();
+		tbGraph.addElement(node);
+		tbGraph.setInitCnt(1);
 		
 		// Configure OOS mock to react to the subsequent invocation. This is a
 		// essentially the list of calls being made on OOS during
@@ -140,10 +138,10 @@ public class LiveCheckTest extends TestCase {
 		EasyMock.expect(oos.hasTableau()).andReturn(true);
 		EasyMock.expect(oos.getTableau()).andReturn(tbGraph).anyTimes();
 		EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes();
-		EasyMock.expect(oos.checkState((TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes();
+		EasyMock.expect(oos.checkState((ITool) EasyMock.anyObject(), (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes();
 		EasyMock.replay(oos);
 		
-		return new LiveCheck(EasyMock.createNiceMock(Tool.class), new Action[0],
+		return new LiveCheck(tool,
 				new OrderOfSolution[] { oos }, System.getProperty("java.io.tmpdir"), new DummyBucketStatistics());
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java
index bcd53fdd3ceb1a6aeca9b8010a2b29afc6527ab9..1cb5fffdf0ab1f0edf267dfdc6261bc2f33db371 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java
@@ -32,6 +32,6 @@ public class SimulationTest2 extends SuccessfulSimulationTestCase {
 
 	public SimulationTest2() {
 		super("Test2", "/", new String[] {"-simulate", "-depth", "6"});
-		TLC.traceNum = 50;
+		TLC.setTraceNum(50);
 	}
 }
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java
index 22ddaa47664d0b012006718ff467d16d8a074b90..2fab67fdfef3c27cedd857c5dc40acbbc32578c8 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java
@@ -26,9 +26,16 @@
 
 package tlc2.tool.liveness.simulation;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.util.List;
 
+import org.junit.Test;
+
 import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
 import tlc2.tool.TLCStateInfo;
 import tlc2.tool.liveness.ModelCheckerTestCase;
 
@@ -38,13 +45,15 @@ import tlc2.tool.liveness.ModelCheckerTestCase;
 public class SimulationTest2a extends ModelCheckerTestCase {
 
 	public SimulationTest2a() {
-		super("Test2a", "/", new String[] {"-simulate", "-depth", "6"});
+		super("Test2a", "/", new String[] {"-simulate", "-depth", "6"}, ExitStatus.VIOLATION_LIVENESS);
 	}
 	
+	@Test
 	public void testSpec() {
 		// ModelChecker has finished and generated the expected amount of states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
 		assertTrue(recorder.recorded(EC.TLC_STATS_SIMU));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f2729afb2ce1be36dc18a3d71f4a5e392c38c45
--- /dev/null
+++ b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.liveness.simulation;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class SimulationTestAssumption extends SuccessfulSimulationTestCase {
+
+	public SimulationTestAssumption() {
+		super("Test4", "/", new String[] { "-simulate", "num=1" }, EC.ExitStatus.VIOLATION_ASSUMPTION);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_ASSUMPTION_FALSE));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java b/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java
index 66d3c3b40b8d0abbdc0b53d91f8530bb94cc4e87..78577e5c81fec309767be51bf0c0b48539c2e41c 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java
@@ -26,19 +26,28 @@
 
 package tlc2.tool.liveness.simulation;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
 import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
 import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.TLAConstants;
 
 public class StutteringTest extends ModelCheckerTestCase {
 
 	public StutteringTest() {
-		super("MC", "CodePlexBug08", new String[] { "-simulate" });
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "CodePlexBug08", new String[] { "-simulate" }, ExitStatus.VIOLATION_LIVENESS);
 	}
 
+	@Test
 	public void testSpec() {
 		// Simulation has finished and generated states
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
 		assertTrue(recorder.recorded(EC.TLC_STATS_SIMU));
+		assertFalse(recorder.recorded(EC.GENERAL));
 
 		// Assert it has found the temporal violation and also a counter example
 		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java b/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java
index 35e7b77443fe372d31b073554a885efb609a783d..ba1f69f9f0baf3785a86d9186d014f8fb1e9b352 100644
--- a/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java
+++ b/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java
@@ -26,6 +26,11 @@
 
 package tlc2.tool.liveness.simulation;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
 import tlc2.output.EC;
 import tlc2.tool.liveness.ModelCheckerTestCase;
 
@@ -43,6 +48,11 @@ public abstract class SuccessfulSimulationTestCase extends ModelCheckerTestCase
 		super(spec, path, extraArguments);
 	}
 	
+	public SuccessfulSimulationTestCase(String spec, String path, String[] extraArguments, int exitStatus) {
+		super(spec, path, extraArguments, exitStatus);
+	}
+	
+	@Test
 	public void testSpec() {
 		// Simulation must *NOT* show a counterexample. Regular model-checking
 		// shows that the liveness property holds.
@@ -53,6 +63,7 @@ public abstract class SuccessfulSimulationTestCase extends ModelCheckerTestCase
 
 		// Finished...
 		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
 		// No temporal violation
 		assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
 		// No counterexample
diff --git a/tlatools/test/tlc2/tool/other/CheckFP.java b/tlatools/test/tlc2/tool/other/CheckFP.java
index ae8025227726e77ca5737874ce65324fbbb6f675..2ad6f3227304dda705998a3e13c35ec6d85ad1b5 100644
--- a/tlatools/test/tlc2/tool/other/CheckFP.java
+++ b/tlatools/test/tlc2/tool/other/CheckFP.java
@@ -19,7 +19,8 @@ import tlc2.util.BufferedRandomAccessFile;
 public class CheckFP {
   public static void main(String args[]) {
     try {
-      BufferedRandomAccessFile raf = new BufferedRandomAccessFile(args[0], "r");
+      @SuppressWarnings("resource")
+	  BufferedRandomAccessFile raf = new BufferedRandomAccessFile(args[0], "r");
       long fileLen = raf.length();
       long dis = Long.MAX_VALUE;
       int cnt = 0;
diff --git a/tlatools/test/tlc2/tool/other/FileClassLoader.java b/tlatools/test/tlc2/tool/other/FileClassLoader.java
index c65f4b08e9a1a470b84c1ce7c7ab824382e5563c..41f30d21a50e9fda82cd30daf01bfc152ef7a849 100644
--- a/tlatools/test/tlc2/tool/other/FileClassLoader.java
+++ b/tlatools/test/tlc2/tool/other/FileClassLoader.java
@@ -14,6 +14,7 @@ import java.io.IOException;
  * it seems to be a helper utility used during the development 
  * @deprecated according to the paths it is not used (SZ February 19, 2009) 
  */
+@SuppressWarnings({ "rawtypes", "unchecked" })
 public class FileClassLoader extends ClassLoader {
   /* Load a class from a file. */
   private String dir;
diff --git a/tlatools/test/tlc2/tool/queue/DummyTLCState.java b/tlatools/test/tlc2/tool/queue/DummyTLCState.java
index 7120cd6de9544f1bd653f9535087805fc1cea9ae..dbaedada12287e4da6a399cfef909e974239a3c9 100644
--- a/tlatools/test/tlc2/tool/queue/DummyTLCState.java
+++ b/tlatools/test/tlc2/tool/queue/DummyTLCState.java
@@ -1,31 +1,41 @@
 package tlc2.tool.queue;
 
-import tla2sany.semantic.SemanticNode;
+import java.util.HashSet;
+import java.util.Set;
+
+import tla2sany.semantic.OpDeclNode;
 import tla2sany.semantic.SymbolNode;
 import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
-import tlc2.value.Value;
+import tlc2.value.IValue;
 import util.UniqueString;
 
 @SuppressWarnings("serial")
 public class DummyTLCState extends TLCState {
 
+	private final long fp;
+
 	public DummyTLCState() {
 		uid = 0;
 		TLCState.Empty = this;
+		this.fp = 0L;
 	}
 	
+	public DummyTLCState(long fp) {
+		this.fp = fp;
+	}
+
 	/* (non-Javadoc)
 	 * @see tlc2.tool.TLCState#bind(util.UniqueString, tlc2.value.Value, tla2sany.semantic.SemanticNode)
 	 */
-	public TLCState bind(UniqueString name, Value value, SemanticNode expr) {
+	public TLCState bind(UniqueString name, IValue value) {
 		return null;
 	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.TLCState#bind(tla2sany.semantic.SymbolNode, tlc2.value.Value, tla2sany.semantic.SemanticNode)
 	 */
-	public TLCState bind(SymbolNode id, Value value, SemanticNode expr) {
+	public TLCState bind(SymbolNode id, IValue value) {
 		return null;
 	}
 
@@ -39,7 +49,7 @@ public class DummyTLCState extends TLCState {
 	/* (non-Javadoc)
 	 * @see tlc2.tool.TLCState#lookup(util.UniqueString)
 	 */
-	public Value lookup(UniqueString var) {
+	public IValue lookup(UniqueString var) {
 		return null;
 	}
 
@@ -82,7 +92,7 @@ public class DummyTLCState extends TLCState {
 	 * @see tlc2.tool.TLCState#fingerPrint()
 	 */
 	public long fingerPrint() {
-		return 0;
+		return this.fp;
 	}
 
 	/* (non-Javadoc)
@@ -92,6 +102,13 @@ public class DummyTLCState extends TLCState {
 		return false;
 	}
 
+	/* (non-Javadoc)
+	 * @see tlc2.tool.TLCState#getUnassigned()
+	 */
+	public final Set<OpDeclNode> getUnassigned() {
+		return new HashSet<OpDeclNode>();
+	}
+
 	/* (non-Javadoc)
 	 * @see tlc2.tool.TLCState#createEmpty()
 	 */
@@ -103,7 +120,7 @@ public class DummyTLCState extends TLCState {
 	 * @see tlc2.tool.TLCState#toString()
 	 */
 	public String toString() {
-		return "Dummy#" + uid;
+		return "Dummy#" + uid + ":" + fp;
 	}
 
 	/* (non-Javadoc)
diff --git a/tlatools/test/tlc2/tool/queue/StateQueueTest.java b/tlatools/test/tlc2/tool/queue/StateQueueTest.java
index f86316116e316ccf2a5c39d9b7026ae5bf437e64..855d34066ccafda706839c925f641a162cd47ef0 100644
--- a/tlatools/test/tlc2/tool/queue/StateQueueTest.java
+++ b/tlatools/test/tlc2/tool/queue/StateQueueTest.java
@@ -1,21 +1,29 @@
 package tlc2.tool.queue;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+
 import tlc2.tool.TLCState;
-import junit.framework.TestCase;
 
-public class StateQueueTest extends TestCase {
+public class StateQueueTest {
 
 	protected IStateQueue sQueue;
 
 	/* (non-Javadoc)
 	 * @see junit.framework.TestCase#setUp()
 	 */
-	protected void setUp() throws Exception {
-		super.setUp();
+	@Before
+	public void setUp() throws Exception {
 		sQueue = new MemStateQueue("");
 	}
 
 	// add and remove a single state
+	@Test
 	public void testEnqueue() {
 		final TLCState expected = new DummyTLCState();
 		sQueue.enqueue(expected);
@@ -24,18 +32,21 @@ public class StateQueueTest extends TestCase {
 	}
 
 	// dequeue from empty 
+	@Test
 	public void testsDequeueEmpty() {
 		TLCState state = sQueue.sDequeue();
 		assertNull(state);
 	}
 	
 	// dequeue from empty 
+	@Test
 	public void testDequeueEmpty() {
 		TLCState state = sQueue.dequeue();
 		assertNull(state);
 	}
 	
 	// dequeue from not empty 
+	@Test
 	public void testsDequeueNotEmpty() {
 		DummyTLCState expected = new DummyTLCState();
 		sQueue.sEnqueue(expected);
@@ -46,6 +57,7 @@ public class StateQueueTest extends TestCase {
 	}
 	
 	// dequeue from not empty 
+	@Test
 	public void testDequeueNotEmpty() {
 		DummyTLCState expected = new DummyTLCState();
 		sQueue.enqueue(expected);
@@ -56,6 +68,7 @@ public class StateQueueTest extends TestCase {
 	}
 
 	// add 10 states and check size
+	@Test
 	public void testEnqueueAddNotSame() {
 		final int j = 10;
 		for (int i = 0; i < j; i++) {
@@ -65,6 +78,7 @@ public class StateQueueTest extends TestCase {
 	}
 	
 	// add same states 10 times and check size
+	@Test
 	public void testEnqueueAddSame() {
 		final DummyTLCState state = new DummyTLCState();
 		final int j = 10;
@@ -75,6 +89,7 @@ public class StateQueueTest extends TestCase {
 	}
 
 	// uncommon input with empty queue sDequeue
+	@Test
 	public void testsDequeueAbuseEmpty() {
 		expectRuntimeException(sQueue, 0);
 		expectRuntimeException(sQueue, -1);
@@ -84,6 +99,7 @@ public class StateQueueTest extends TestCase {
 	
 	// uncommon input with non-empty queue
 	// unfortunately sDequeue behaves differently depending what's its internal state
+	@Test
 	public void testsDequeueAbuseNonEmpty() {
 		sQueue.sEnqueue(new DummyTLCState()); // make sure isAvail = true
 
@@ -97,7 +113,7 @@ public class StateQueueTest extends TestCase {
 	private void expectRuntimeException(IStateQueue aQueue, int size)  {
 		try {
 			aQueue.sDequeue(size);
-		} catch(RuntimeException e) {
+		} catch(RuntimeException|AssertionError e) {
 			return;
 		}
 		fail("expected to throw RuntimeException with <= input");
diff --git a/tlatools/test/tlc2/tool/simulation/NQSpecTest.java b/tlatools/test/tlc2/tool/simulation/NQSpecTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bd0830733ac9dcf07a6c996698f45e47585aae1
--- /dev/null
+++ b/tlatools/test/tlc2/tool/simulation/NQSpecTest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.simulation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Test;
+
+import tlc2.TLC;
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.TLAConstants;
+
+public class NQSpecTest extends ModelCheckerTestCase {
+
+	public NQSpecTest() {
+		super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "simulation" + File.separator + "NQSpec", new String[] { "-simulate" });
+		TLC.setTraceNum(100);
+	}
+
+	@Test
+	public void testSpec() {
+		// Simulation has finished and generated states
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java b/tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..96afcecf19f228ad4f8d340deb3f506e6464fac8
--- /dev/null
+++ b/tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java
@@ -0,0 +1,430 @@
+package tlc2.tool.simulation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.LongAdder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import tlc2.TLCGlobals;
+import tlc2.TestMPRecorder;
+import tlc2.output.EC;
+import tlc2.tool.CommonTestCase;
+import tlc2.tool.ITool;
+import tlc2.tool.SimulationWorker;
+import tlc2.tool.SimulationWorker.SimulationWorkerError;
+import tlc2.tool.SimulationWorker.SimulationWorkerResult;
+import tlc2.tool.StateVec;
+import tlc2.tool.TLCState;
+import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.Tool;
+import tlc2.tool.liveness.ILiveCheck;
+import tlc2.tool.liveness.NoOpLiveCheck;
+import util.FileUtil;
+import util.SimpleFilenameToStream;
+import util.TLAConstants;
+import util.ToolIO;
+import util.UniqueString;
+
+/**
+ * Correctness tests for the SimulationWorker.
+ */
+public class SimulationWorkerTest extends CommonTestCase {
+	
+	public SimulationWorkerTest() {
+		super(new TestMPRecorder());
+	}
+	
+	@Before
+	public void setUp() throws Exception{
+		ToolIO.setUserDir(BASE_PATH + File.separator + "simulation" + File.separator + "BasicMultiTrace");
+	}
+	
+	@After
+	public void tearDown() throws Exception{
+        FileUtil.deleteDir(TLCGlobals.metaRoot, true);
+	}
+	
+	/**
+	 * Return a value from a TLCState as a string.
+	 */
+	public String getStateVal(TLCState s, String name) {
+		UniqueString us = UniqueString.uniqueStringOf(name);
+		return s.getVals().get(us).toString();
+	}
+
+	@Test
+	public void testSuccessfulRun() throws Exception {
+		Tool tool = new FastTool("", "BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, new SimpleFilenameToStream());
+
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		StateVec initStates = tool.getInitStates();
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 1000, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		assertFalse(res.isError());
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testInvariantViolation() throws Exception {
+		Tool tool = new FastTool("", "BasicMultiTrace", "MCInv", new SimpleFilenameToStream());
+		
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		StateVec initStates = tool.getInitStates();
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		int maxTraceNum = 3;
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, maxTraceNum, false,
+				null, liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode);
+		assertEquals(3, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+		
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(1), "branch"));
+		
+		assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(2), "branch"));
+		
+		assertEquals("2", getStateVal(err.state, "depth"));
+		assertEquals("6", getStateVal(err.state, "branch"));
+		
+		// The worker should continue to generate random traces even after an invariant violation, so we should be
+		// able to receive more results. The worker should generate 2 more results before hitting the maximum trace count.
+		// For the next traces generated, we check their contents to make sure that the worker is actually producing traces
+		// "randomly" i.e. not generating the same trace every time.
+		res = resultQueue.take();
+		assertTrue(res.isError());
+		err = res.error();
+		assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode);
+		assertEquals(3, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+		
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth"));
+		assertEquals("2", getStateVal(err.stateTrace.elementAt(1), "branch"));
+		
+		assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth"));
+		assertEquals("2", getStateVal(err.stateTrace.elementAt(2), "branch"));
+		
+		res = resultQueue.take();
+		assertTrue(res.isError());
+		err = res.error();
+		assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode);
+		assertEquals(3, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+		
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth"));
+		assertEquals("5", getStateVal(err.stateTrace.elementAt(1), "branch"));
+		
+		assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth"));
+		assertEquals("5", getStateVal(err.stateTrace.elementAt(2), "branch"));
+		
+		// The worker should push one final OK result onto the queue upon termination.
+		res = resultQueue.take();
+		assertFalse(res.isError());
+		
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testActionPropertyViolation() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCActionProp", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		
+		SimulationWorkerResult res = resultQueue.take();
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, err.errorCode);
+		assertEquals(2, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+		
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(1), "branch"));
+		
+		assertEquals("1", getStateVal(err.state, "depth"));
+		assertEquals("6", getStateVal(err.state, "branch"));
+		
+		// Check another result.
+		res = resultQueue.take();
+		assertTrue(res.isError());
+		err = res.error();
+		assertEquals(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, err.errorCode);
+		assertEquals(2, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+		
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth"));
+		assertEquals("10", getStateVal(err.stateTrace.elementAt(1), "branch"));
+		
+		assertEquals("1", getStateVal(err.state, "depth"));
+		assertEquals("10", getStateVal(err.state, "branch"));		
+				
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testInvariantBadEval() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCBadInvNonInitState", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_INVARIANT_EVALUATION_FAILED, err.errorCode);
+		assertEquals(1, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+
+		assertEquals("0", getStateVal(err.state, "depth"));
+		assertEquals("1", getStateVal(err.state, "branch"));
+
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testActionPropertyBadEval() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCActionPropBadEval", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		
+		SimulationWorkerResult res = resultQueue.take();
+		
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, err.errorCode);
+				
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testUnderspecifiedNext() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCUnderspecNext", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, err.errorCode);
+		assertEquals(1, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+				
+		assertEquals(null, err.state.getVals().get(UniqueString.uniqueStringOf("depth")));
+		assertEquals("0", getStateVal(err.state, "branch"));
+
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testDeadlock() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, true, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_DEADLOCK_REACHED, err.errorCode);
+		
+		System.out.println(err.stateTrace.toString());
+		assertEquals(7, err.stateTrace.size());
+		
+		// Check the generated trace.
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth"));
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch"));
+		
+		assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(1), "branch"));
+		
+		assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(2), "branch"));
+		
+		assertEquals("2", getStateVal(err.stateTrace.elementAt(3), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(3), "branch"));
+		
+		assertEquals("3", getStateVal(err.stateTrace.elementAt(4), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(4), "branch"));
+
+		assertEquals("4", getStateVal(err.stateTrace.elementAt(5), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(5), "branch"));
+
+		assertEquals("5", getStateVal(err.stateTrace.elementAt(6), "depth"));
+		assertEquals("6", getStateVal(err.stateTrace.elementAt(6), "branch"));
+
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testModelStateConstraint() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCWithConstraint", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		assertFalse(res.isError());
+		worker.join();
+		assertTrue(resultQueue.isEmpty());
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testModelActionConstraint() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCWithActionConstraint", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		assertFalse(res.isError());
+		worker.join();
+		assertTrue(resultQueue.isEmpty());
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testWorkerInterruption() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCInv", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+		
+		// If we set the trace limit to the max, the worker should effectively run forever. We verify that after it generates
+		// a result, we can cancel it and the worker will terminate.
+		long traceNum = Long.MAX_VALUE;
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, traceNum, false, null,
+				liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		
+		// Check one result.
+		SimulationWorkerResult res = resultQueue.take();
+		
+		assertTrue(res.isError());
+		SimulationWorkerError err = res.error();
+		assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode);
+		assertEquals(3, err.stateTrace.size());
+		
+		// Cancel the worker.
+		worker.interrupt();
+		worker.join();
+		assertFalse(worker.isAlive());
+	}
+
+	@Test
+	public void testTraceDepthObeyed() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", "MCInv", new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+
+		// At this trace depth, the worker should never find the invariant violation.
+		long traceDepth = 1;
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, traceDepth, 100, false,
+				null, liveCheck, new LongAdder(), new LongAdder());
+		worker.start(initStates);
+		SimulationWorkerResult res = resultQueue.take();
+		assertFalse(res.isError());
+		
+		worker.join();
+		assertTrue(resultQueue.isEmpty());
+		assertFalse(worker.isAlive());
+	}
+	
+	@Test
+	public void testStateAndTraceGenerationCount() throws Exception {
+		ITool tool = new FastTool("", "BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, new SimpleFilenameToStream());
+		
+		StateVec initStates = tool.getInitStates();
+		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
+		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
+
+		// Have the worker generate a specified number of traces of a fixed length.
+		LongAdder numOfGenStates = new LongAdder();
+		LongAdder numOfGenTraces = new LongAdder();
+		long traceDepth = 5;
+		long traceNum = 5;
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, traceDepth, traceNum, false,
+				null, liveCheck, numOfGenStates, numOfGenTraces);
+
+		worker.start(initStates);
+		worker.join();
+		assertFalse(worker.isAlive());
+		assertEquals(70, numOfGenStates.longValue());
+		assertEquals(5, numOfGenTraces.longValue());
+	}
+}
diff --git a/tlatools/test/tlc2/tool/simulation/SimulatorMultiThreadTest.java b/tlatools/test/tlc2/tool/simulation/SimulatorMultiThreadTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2623a1ff89498c1889f5d100dcf8e15c8c1489d3
--- /dev/null
+++ b/tlatools/test/tlc2/tool/simulation/SimulatorMultiThreadTest.java
@@ -0,0 +1,18 @@
+package tlc2.tool.simulation;
+
+/**
+ * Correctness tests of a multi-threaded Simulator.
+ * 
+ * This test intends to verify that, from a correctness perspective, running the
+ * Simulator with multiple threads is equivalent to running it with a single
+ * thread i.e. it should pass all of the same tests.
+ */
+public class SimulatorMultiThreadTest extends SimulatorTest {
+
+	public int numWorkers() {
+		// A somewhat arbitrary value, but 4 threads seems like a reasonable amount of
+		// parallelism.
+		return 4;
+	}
+
+}
diff --git a/tlatools/test/tlc2/tool/simulation/SimulatorTest.java b/tlatools/test/tlc2/tool/simulation/SimulatorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..70756893fda447ef31d7fcdd00b242bdafae2138
--- /dev/null
+++ b/tlatools/test/tlc2/tool/simulation/SimulatorTest.java
@@ -0,0 +1,155 @@
+package tlc2.tool.simulation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import tlc2.TLCGlobals;
+import tlc2.TestMPRecorder;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.CommonTestCase;
+import tlc2.tool.Simulator;
+import tlc2.util.FP64;
+import tlc2.util.RandomGenerator;
+import util.FileUtil;
+import util.SimpleFilenameToStream;
+import util.TLAConstants;
+import util.ToolIO;
+
+/**
+ * Correctness tests for a Simulator.
+ */
+public class SimulatorTest extends CommonTestCase {
+	
+	RandomGenerator rng = new RandomGenerator();
+
+	public SimulatorTest() {
+		super(new TestMPRecorder());
+		MP.setRecorder(recorder);
+	}
+	
+	@Before
+	public void setUp() throws Exception{
+		// Make the each unit test execution as deterministic as possible.
+		rng = new RandomGenerator(0);
+		ToolIO.setUserDir(BASE_PATH + File.separator + "simulation" + File.separator + "BasicMultiTrace");
+		
+		// Printing the error trace entails fingerprint its states.
+		FP64.Init();
+	}
+	
+	@After
+	public void tearDown() throws Exception{
+        FileUtil.deleteDir(TLCGlobals.metaRoot, true);
+	}
+	
+	/**
+	 * The number of threads to run the Simulator with. Can be overridden by
+	 * sub-classes that want to test with different values.
+	 */
+	public int numWorkers() {
+		return 1;
+	}
+
+	public void runSimulatorTest(String specFile, String configFile, Boolean deadlock, int traceDepth, long traceNum) {
+		try {
+			Simulator simulator = new Simulator(specFile, configFile, null, deadlock, traceDepth, traceNum, rng, 0,
+					new SimpleFilenameToStream(), numWorkers());
+			simulator.simulate();
+		} catch (Exception e) {
+			e.printStackTrace();
+			fail(e.getMessage());
+		}
+	}
+	
+	@Test
+	public void testSuccessfulSimulation() {	
+		runSimulatorTest("BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, false, 100, 100);
+		assertFalse(recorder.recorded(EC.TLC_INVARIANT_VIOLATED_INITIAL));
+		assertTrue(recorder.recorded(EC.TLC_PROGRESS_SIMU));
+	}
+	
+	@Test
+	public void testInvariantViolationInitialState() {	
+		runSimulatorTest("BasicMultiTrace", "MCInvInitState", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_INITIAL, "InvInitState"));
+	}
+	
+	@Test
+	public void testInvariantViolation() {	
+		runSimulatorTest("BasicMultiTrace", "MCInv", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, "Inv"));
+	}
+	
+	@Test
+	public void testInvariantBadEvalInitState() {
+		runSimulatorTest("BasicMultiTrace", "MCBadInvInitState", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recorded(EC.TLC_INITIAL_STATE));
+	}
+	
+	@Test
+	public void testInvariantBadEvalNonInitState() {
+		runSimulatorTest("BasicMultiTrace", "MCBadInvNonInitState", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_EVALUATION_FAILED, "InvBadEvalNonInitState"));
+	}
+	
+	@Test
+	public void testUnderspecifiedInit() {
+		runSimulatorTest("BasicMultiTrace", "MCUnderspecInit", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recorded(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL));
+	}
+	
+	@Test
+	public void testInvariantViolationContinue() {	
+		TLCGlobals.continuation = true;
+		runSimulatorTest("BasicMultiTrace", "MCInv", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, "Inv"));
+	}
+	
+	@Test
+	public void testDontContinueOnRuntimeSpecError() {
+		// We should only continue simulating if a worker encounters a safety or liveness violation.
+		// Errors in the spec e.g. runtime evaluation errors should terminate all workers, even if
+		// continue=true. We set the traceCnt to something extremely high, so that this test should
+		// hang if the simulator does not terminate on the first error.
+		TLCGlobals.continuation = true;
+		runSimulatorTest("BasicMultiTrace", "MCBadInvNonInitState", false, 100, Long.MAX_VALUE);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_EVALUATION_FAILED, "InvBadEvalNonInitState"));
+	}
+	
+	@Test
+	public void testLivenessViolation() {	
+		FP64.Init();
+		runSimulatorTest("BasicMultiTrace", "MCLivenessProp", false, 100, 100);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+	}
+	
+	@Test
+	public void testLivenessViolationIgnoresContinue() {	
+		FP64.Init();
+		TLCGlobals.continuation = true;
+		// We should always terminate after the first liveness violation, regardless of
+		// the "continue" parameter.
+		runSimulatorTest("BasicMultiTrace", "MCLivenessProp", false, 100, Long.MAX_VALUE);
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1"));
+		assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED));
+		assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE));
+	}
+	
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest1.java b/tlatools/test/tlc2/tool/suite/ETest1.java
new file mode 100644
index 0000000000000000000000000000000000000000..07ddcc64130234566fe126674c71d0ef81dc14d7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest1.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import org.junit.Test;
+
+import tlc2.output.EC.ExitStatus;
+
+public class ETest1 extends SuiteETestCase {
+
+	public ETest1() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertSubstring("*** Errors: 1\n\n" + "line 18, col 12 to line 18, col 17 of module etest1\n\n"
+				+ "The operator Foo requires 1 arguments.");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest10.java b/tlatools/test/tlc2/tool/suite/ETest10.java
new file mode 100644
index 0000000000000000000000000000000000000000..f69ed798ecb498dc2bde45471b126ea605fd7e20
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest10.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest10 extends SuiteETestCase {
+
+	public ETest10() {
+		super(ExitStatus.FAILURE_SPEC_EVAL);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL,
+				"Attempted to check if the value:\n\"a\"\nis an element of Int."));
+
+		assertUncovered("line 13, col 12 to line 13, col 15 of module etest10: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest11.java b/tlatools/test/tlc2/tool/suite/ETest11.java
new file mode 100644
index 0000000000000000000000000000000000000000..a06acff72eb2f69a433f3a5062c42e110aff6abb
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest11.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest11 extends SuiteETestCase {
+
+	public ETest11() {
+		super(ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL,
+				"Attempted to check if the value:\n1\nis an element of STRING."));
+
+		assertUncovered("line 13, col 12 to line 13, col 15 of module etest11: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest12.java b/tlatools/test/tlc2/tool/suite/ETest12.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f2a55bf4dd48f0f66ae9403d62b26b8b77e985a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest12.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest12 extends SuiteETestCase {
+
+	public ETest12() {
+		super(ExitStatus.VIOLATION_ASSUMPTION);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+				"Attempted to apply the operator overridden by the Java method\n"
+				+ "public static tlc2.value.impl.IntValue tlc2.module.FiniteSets.Cardinality(tlc2.value.impl.Value),\n"
+				+ "but it produced the following error:\n"
+				+ "Overflow when computing the number of elements in:"
+				+ "\n[0..2 -> 1..2000]"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest13.java b/tlatools/test/tlc2/tool/suite/ETest13.java
new file mode 100644
index 0000000000000000000000000000000000000000..732b0d5859cc46915e9f8fbfd95881e57d46bc20
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest13.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest13 extends SuiteETestCase {
+
+	public ETest13() {
+		super(ExitStatus.VIOLATION_ASSUMPTION);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+				"Attempted to apply the operator overridden by the Java method\n"
+				+ "public static tlc2.value.impl.IntValue tlc2.module.FiniteSets.Cardinality(tlc2.value.impl.Value),\n"
+				+ "but it produced the following error:\n"
+				+ "Overflow when computing the number of elements in:"
+				+ "\n[0..2 -> 1..2000]"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest14.java b/tlatools/test/tlc2/tool/suite/ETest14.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5d33ce56d52c8d204493e8c3f7c14b4e5e1b96b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest14.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest14 extends SuiteETestCase {
+
+	public ETest14() {
+		super(ExitStatus.VIOLATION_ASSUMPTION);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+				"Attempted to compute the value of an expression of form\nCHOOSE x \\in S: P, but no element of S satisfied P.\nline 5, col 7 to line 5, col 28 of module etest14"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest15.java b/tlatools/test/tlc2/tool/suite/ETest15.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6d6cbdb919a2f535e230628766a5718eae4eaac
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest15.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest15 extends SuiteETestCase {
+
+	public ETest15() {
+		super(ExitStatus.VIOLATION_ASSUMPTION);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+				"Attempted to compare integer 2 with non-integer:\n<<3>>"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest16.java b/tlatools/test/tlc2/tool/suite/ETest16.java
new file mode 100644
index 0000000000000000000000000000000000000000..7113568ae522c1e05142479d1e141b8aa6de3e70
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest16.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import org.junit.Test;
+
+import tlc2.output.EC.ExitStatus;
+
+public class ETest16 extends SuiteETestCase {
+
+	public ETest16() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+
+	@Test
+	public void testSpec() {
+		assertSubstring("*** Errors: 2\n\n" + "line 5, col 27 to line 5, col 27 of module etest16\n\n"
+				+ "Non-unique fields in constructor.\n\n\n"
+				+ "line 7, col 27 to line 7, col 27 of module etest16\n\n"
+				+ "Non-unique fields in constructor.");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest2.java b/tlatools/test/tlc2/tool/suite/ETest2.java
new file mode 100644
index 0000000000000000000000000000000000000000..be48e0e0ad51854db5457670ab687510521fa165
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest2.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest2 extends SuiteETestCase {
+
+	public ETest2() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("*** Errors: 1\n\n"
+				+ "line 18, col 12 to line 18, col 14 of module etest2\n\n"
+				+ "The operator Foo requires 2 arguments.");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest3.java b/tlatools/test/tlc2/tool/suite/ETest3.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a67bf0c00d91124b7b41c04d065fcd2d481181a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest3.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest3 extends SuiteETestCase {
+
+	public ETest3() {
+		super(ExitStatus.VIOLATION_DEADLOCK);
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#checkDeadLock()
+	 */
+	protected boolean checkDeadLock() {
+		return true;
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_DEADLOCK_REACHED));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "2", "0"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest4.java b/tlatools/test/tlc2/tool/suite/ETest4.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6747c7502d159cd65fc5d6dfb4d0a29146ada2c
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest4.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest4 extends SuiteETestCase {
+
+	public ETest4() {
+		super(ExitStatus.FAILURE_SPEC_EVAL);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0"));
+		String s = "0. Line 13, column 9 to line 16, column 51 in etest4\n"
+			+ "1. Line 13, column 12 to line 13, column 16 in etest4\n"
+			+ "2. Line 15, column 12 to line 15, column 26 in etest4\n"
+			+ "3. Line 15, column 12 to line 15, column 22 in etest4\n\n";
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, s));
+
+		assertUncovered("line 18, col 9 to line 18, col 19 of module etest4: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest5.java b/tlatools/test/tlc2/tool/suite/ETest5.java
new file mode 100644
index 0000000000000000000000000000000000000000..59238d5adf5394df92f0cfbc4c95fdedd747660d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest5.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest5 extends SuiteETestCase {
+
+	public ETest5() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("*** Errors: 1\n\n"
+				+ "line 13, col 17 to line 13, col 20 of module etest5\n\n"
+				+ "Unknown operator: `Init'.");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest6.java b/tlatools/test/tlc2/tool/suite/ETest6.java
new file mode 100644
index 0000000000000000000000000000000000000000..874649a8ebb5c24447ab0a375c98a61d3ff7ccb0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest6.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest6 extends SuiteETestCase {
+
+	public ETest6() {
+		super(ExitStatus.FAILURE_SPEC_EVAL);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL,
+				"In evaluation, the identifier x is either undefined or not an operator.\nline 16, col 23 to line 16, col 23 of module etest6"));
+
+		assertUncovered("line 13, col 14 to line 13, col 43 of module etest6: 0\n"
+				+ "line 19, col 13 to line 19, col 23 of module etest6: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest7.java b/tlatools/test/tlc2/tool/suite/ETest7.java
new file mode 100644
index 0000000000000000000000000000000000000000..95f4e38bdaf398e33f9cc93099336b1a55313bf2
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest7.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest7 extends SuiteETestCase {
+
+	public ETest7() {
+		super(ExitStatus.ERROR_CONFIG_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT,
+				"C", "Foo"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest8.java b/tlatools/test/tlc2/tool/suite/ETest8.java
new file mode 100644
index 0000000000000000000000000000000000000000..c08b0c2b2b21ac4942a60f11557cbaf65b07d076
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest8.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest8 extends SuiteETestCase {
+
+	public ETest8() {
+		super(new String[]{"-simulate"}, ExitStatus.ERROR /*ExitStatus.VIOLATION_ASSERT*/); //TODO Simulator doesn't report correct exit status.
+	}
+	
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#checkDeadLock()
+	 */
+	protected boolean checkDeadLock() {
+		return false;
+	}
+	
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recorded(EC.TLC_STATS_SIMU));
+		assertFalse(recorder.recorded(EC.TLC_DEADLOCK_REACHED));
+		
+		assertUncovered("line 18, col 15 to line 18, col 22 of module etest8: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/ETest9.java b/tlatools/test/tlc2/tool/suite/ETest9.java
new file mode 100644
index 0000000000000000000000000000000000000000..5546e91b72110c79dcfe637d41ff27e2b4934cfc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/ETest9.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class ETest9 extends SuiteETestCase {
+
+	public ETest9() {
+		super(ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL,
+				"Attempted to check if the value:\n\"a\"\nis an element of Nat."));
+
+		assertUncovered("line 13, col 12 to line 13, col 15 of module etest9: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/SuiteETestCase.java b/tlatools/test/tlc2/tool/suite/SuiteETestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..87924b0ab44dd6c2808997301d2281e8926c8012
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/SuiteETestCase.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.suite;
+import static org.junit.Assert.fail;
+
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.ToolIO;
+
+public abstract class SuiteETestCase extends ModelCheckerTestCase {
+
+	private TestPrintStream testPrintStream = new TestPrintStream();
+
+	public SuiteETestCase() {
+		this(ExitStatus.SUCCESS);
+	}
+
+	public SuiteETestCase(int exitStatus) {
+		super("setBySetUp", "suite", exitStatus);
+	}
+	
+	public SuiteETestCase(String[] params) {
+		this(params, ExitStatus.SUCCESS);
+	}
+
+	public SuiteETestCase(String[] params, int exitStatus) {
+		super("setBySetUp", "suite", params, exitStatus);
+	}
+
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#setUp()
+	 */
+	public void setUp() {
+		// Set spec name to the name of the unit tests
+		spec = getClass().getSimpleName().toLowerCase();
+		
+		// Intercept tool out to check SANY parser errors
+		ToolIO.out = testPrintStream;
+		ToolIO.err = testPrintStream;
+		
+		super.setUp();
+	}
+	
+	
+	protected void assertSubstring(String substring) {
+		for (String string : testPrintStream.strings) {
+			if (string.contains(substring)) {
+				return;
+			}
+		}
+		fail("Substring not found");
+	}
+
+	private static class TestPrintStream extends PrintStream {
+
+		private final List<String> strings = new ArrayList<String>();
+		
+		public TestPrintStream() {
+	        super(new PipedOutputStream());
+		}
+
+		/* (non-Javadoc)
+		 * @see java.io.PrintStream#println(java.lang.String)
+		 */
+		public void println(String x) {
+			strings.add(x);
+			System.out.println(x);
+			super.println(x);
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/SuiteTestCase.java b/tlatools/test/tlc2/tool/suite/SuiteTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d3ff58afa48b3a7e5f155382fdc581800cd494c
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/SuiteTestCase.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.suite;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public abstract class SuiteTestCase extends ModelCheckerTestCase {
+
+	private String initStates = "1";
+	private String leftStates = "0";
+	private String distinctStates = "1";
+	private String stateGenerated = "2";
+	private String uncovered;
+	
+	public SuiteTestCase() {
+		super("setBySetUp", "suite");
+	}
+
+	public SuiteTestCase(String stateGenerated, String distinctStates, String leftStates, String initStates) {
+		this();
+		this.stateGenerated = stateGenerated;
+		this.distinctStates = distinctStates;
+		this.leftStates = leftStates;
+		this.initStates = initStates;
+	}
+
+	public SuiteTestCase(String stateGenerated, String distinctStates, String leftStates, String initStates, final String uncovered) {
+		this(stateGenerated, distinctStates, leftStates, initStates);
+		this.uncovered = uncovered;
+	}
+	/* (non-Javadoc)
+	 * @see tlc2.tool.liveness.ModelCheckerTestCase#setUp()
+	 */
+	public void setUp() {
+		// Set spec name to the name of the unit tests
+		spec = getClass().getSimpleName().toLowerCase();
+		
+		super.setUp();
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, stateGenerated, distinctStates, leftStates));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, initStates));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		
+		if (this.uncovered != null) {
+			assertUncovered(this.uncovered);
+		} else {
+			assertZeroUncovered();
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test1.java b/tlatools/test/tlc2/tool/suite/Test1.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e825ddc217a79a8d06d4cfbfa48bcf30773b436
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test1.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test1 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test10.java b/tlatools/test/tlc2/tool/suite/Test10.java
new file mode 100644
index 0000000000000000000000000000000000000000..a384f5ea6b9d1b9eb41d029c56eadc294132fec1
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test10.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test10 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test11.java b/tlatools/test/tlc2/tool/suite/Test11.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b2eee23bdf974c42875609fe8d1bb9611a6e6e3
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test11.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test11 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test12.java b/tlatools/test/tlc2/tool/suite/Test12.java
new file mode 100644
index 0000000000000000000000000000000000000000..21b4ede643f1de455a62774e6112f22bdd2cc922
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test12.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test12 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test13.java b/tlatools/test/tlc2/tool/suite/Test13.java
new file mode 100644
index 0000000000000000000000000000000000000000..f82a5bd4ae74e9f3af821918a02ea20ead1123be
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test13.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test13 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test14.java b/tlatools/test/tlc2/tool/suite/Test14.java
new file mode 100644
index 0000000000000000000000000000000000000000..b296543ddab0197a42981d3b1ae6f19ccf6e9c77
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test14.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test14 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test15.java b/tlatools/test/tlc2/tool/suite/Test15.java
new file mode 100644
index 0000000000000000000000000000000000000000..b69446cbb1bd85e9dea3942195cc99957fd52e58
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test15.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test15 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test16.java b/tlatools/test/tlc2/tool/suite/Test16.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae6e21b654de39c4c9fbf3f30719bc21f921f541
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test16.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test16 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test17.java b/tlatools/test/tlc2/tool/suite/Test17.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ed73263a9ea7e940f58e90011ab57f254f96ba0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test17.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test17 extends SuiteTestCase {
+
+	public Test17() {
+		super("7", "2", "0", "1", "line 101, col 20 to line 101, col 23 of module test17: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test18.java b/tlatools/test/tlc2/tool/suite/Test18.java
new file mode 100644
index 0000000000000000000000000000000000000000..d042607122e44878e758534cd5982985defdbbca
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test18.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test18 extends SuiteTestCase {
+	public Test18() {
+		super("14", "1", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test19.java b/tlatools/test/tlc2/tool/suite/Test19.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c690fa6834cdb998bdcb2bd3972b1cdb0f3a674
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test19.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test19 extends SuiteTestCase {
+	public Test19() {
+		super("800000", "400000", "0", "400000");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test2.java b/tlatools/test/tlc2/tool/suite/Test2.java
new file mode 100644
index 0000000000000000000000000000000000000000..fca4bbefb021ad215436e2cc6cbe736acf14c830
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test2.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test2 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test20.java b/tlatools/test/tlc2/tool/suite/Test20.java
new file mode 100644
index 0000000000000000000000000000000000000000..75505ceb7e84c0db434a5f047352b5b3f7674294
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test20.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test20 extends SuiteTestCase {
+	public Test20() {
+		super("5", "1", "0", "1", "  |||line 16, col 9 to line 16, col 12 of module test20: 0\n" + 
+				"  |||line 17, col 9 to line 17, col 12 of module test20: 0\n" + 
+				"  |||line 25, col 16 to line 25, col 33 of module test20: 0\n" + 
+				"  |||line 29, col 17 to line 29, col 34 of module test20: 0\n" + 
+				"  |||line 34, col 17 to line 34, col 34 of module test20: 0\n" + 
+				"  ||||line 16, col 9 to line 16, col 12 of module test20: 0\n" + 
+				"  ||||line 17, col 9 to line 17, col 12 of module test20: 0\n" + 
+				"  ||||line 25, col 16 to line 25, col 33 of module test20: 0\n" + 
+				"  ||||line 29, col 17 to line 29, col 34 of module test20: 0\n" + 
+				"  ||||line 34, col 17 to line 34, col 34 of module test20: 0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test201.java b/tlatools/test/tlc2/tool/suite/Test201.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7d3a47d1ce7fd585e0cfcf81fd801ae970e84de
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test201.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test201 extends SuiteTestCase {
+	public Test201() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test202.java b/tlatools/test/tlc2/tool/suite/Test202.java
new file mode 100644
index 0000000000000000000000000000000000000000..32b0aeee9089bacd1ef3a960173edcf659779f57
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test202.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test202 extends SuiteTestCase {
+	public Test202() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test203.java b/tlatools/test/tlc2/tool/suite/Test203.java
new file mode 100644
index 0000000000000000000000000000000000000000..579c071bef6bca72efc53859f7488eadb78274bc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test203.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test203 extends SuiteTestCase {
+	public Test203() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test204.java b/tlatools/test/tlc2/tool/suite/Test204.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f294a1837f8f7049d2e100bf6315b351cb2cc7a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test204.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test204 extends SuiteTestCase {
+	public Test204() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test205.java b/tlatools/test/tlc2/tool/suite/Test205.java
new file mode 100644
index 0000000000000000000000000000000000000000..168e55ac3088f8d633969937f4298dd84b90bf57
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test205.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test205 extends SuiteTestCase {
+	public Test205() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test206.java b/tlatools/test/tlc2/tool/suite/Test206.java
new file mode 100644
index 0000000000000000000000000000000000000000..7436d21083dcd1f903eea86a4ece23cd18371489
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test206.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test206 extends SuiteTestCase {
+	public Test206() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test207.java b/tlatools/test/tlc2/tool/suite/Test207.java
new file mode 100644
index 0000000000000000000000000000000000000000..a65c36343fd9ccd6348b833e8f5b08baedcf25dc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test207.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test207 extends SuiteTestCase {
+	public Test207() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test208.java b/tlatools/test/tlc2/tool/suite/Test208.java
new file mode 100644
index 0000000000000000000000000000000000000000..249a535e9678e4f7e8fcddf7c2472074be5be9bd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test208.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test208 extends SuiteTestCase {
+	public Test208() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test209.java b/tlatools/test/tlc2/tool/suite/Test209.java
new file mode 100644
index 0000000000000000000000000000000000000000..c32c251ec76dea3dfaf312902322cac666b57d96
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test209.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test209 extends SuiteTestCase {
+	public Test209() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test21.java b/tlatools/test/tlc2/tool/suite/Test21.java
new file mode 100644
index 0000000000000000000000000000000000000000..344b694f43559ef73e07d2658023ee448be1392d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test21.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test21 extends SuiteTestCase {
+	public Test21() {
+		super("8", "2", "0", "1", "line 59, col 16 to line 59, col 21 of module test21: 0\n" + 
+				"line 60, col 16 to line 60, col 19 of module test21: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test210.java b/tlatools/test/tlc2/tool/suite/Test210.java
new file mode 100644
index 0000000000000000000000000000000000000000..9974e3a5c6b6d9f669c80dc4e13e14e6d4178fae
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test210.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test210 extends SuiteETestCase {
+	
+	public Test210() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("Semantic errors:\n" + 
+				"\n" + 
+				"*** Errors: 9\n" + 
+				"\n" + 
+				"line 33, col 16 to line 33, col 19 of module test210\n" + 
+				"\n" + 
+				"Accessing subexpression labeled `laby' of ASSUME/PROVE clause within the scope of a declaration\n" + 
+				" from outside that declaration's scope.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 35, col 16 to line 35, col 16 of module test210\n" + 
+				"\n" + 
+				"Accessing ASSUME/PROVE clause within the scope of a declaration\n" + 
+				" from outside that declaration's scope.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 42, col 41 to line 42, col 55 of module test210\n" + 
+				"\n" + 
+				"Label not allowed within scope of declaration in nested ASSUME/PROVE.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 43, col 30 to line 43, col 44 of module test210\n" + 
+				"\n" + 
+				"Label not allowed within scope of declaration in nested ASSUME/PROVE.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 55, col 16 to line 55, col 16 of module test210\n" + 
+				"\n" + 
+				"Accessing non-existent subexpression of a SUFFICES\n" + 
+				"\n" + 
+				"\n" + 
+				"line 62, col 15 to line 62, col 18 of module test210\n" + 
+				"\n" + 
+				"Accessing subexpression labeled `laby' of ASSUME/PROVE clause within the scope of a declaration\n" + 
+				" from outside that declaration's scope.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 63, col 15 to line 63, col 18 of module test210\n" + 
+				"\n" + 
+				"Accessing subexpression labeled `labi' of ASSUME/PROVE clause within the scope of a declaration\n" + 
+				" from outside that declaration's scope.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 64, col 15 to line 64, col 18 of module test210\n" + 
+				"\n" + 
+				"Accessing subexpression labeled `labu' of ASSUME/PROVE clause within the scope of a declaration\n" + 
+				" from outside that declaration's scope.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 65, col 15 to line 65, col 15 of module test210\n" + 
+				"\n" + 
+				"Accessing ASSUME/PROVE clause within the scope of a declaration\n" + 
+				" from outside that declaration's scope.\n" + 
+				"\n" + 
+				"\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test212.java b/tlatools/test/tlc2/tool/suite/Test212.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a1cb6cec586378cf63e7653158785c48952867d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test212.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test212 extends SuiteETestCase {
+
+	public Test212() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("Semantic errors:\n" + 
+				"\n" + 
+				"*** Errors: 6\n" + 
+				"\n" + 
+				"line 27, col 1 to line 27, col 62 of module test212\n" + 
+				"\n" + 
+				"Error in instantiating module 'test212a':\n" + 
+				" A non-Leibniz operator substituted for 'Op2'.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 28, col 1 to line 28, col 62 of module test212\n" + 
+				"\n" + 
+				"Error in instantiating module 'test212a':\n" + 
+				" A non-Leibniz operator substituted for 'Op'.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 29, col 1 to line 29, col 62 of module test212\n" + 
+				"\n" + 
+				"Error in instantiating module 'test212a':\n" + 
+				" A non-Leibniz operator substituted for 'Op'.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 30, col 1 to line 30, col 64 of module test212\n" + 
+				"\n" + 
+				"Error in instantiating module 'test212a':\n" + 
+				" A non-Leibniz operator substituted for 'Op2'.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 31, col 1 to line 31, col 63 of module test212\n" + 
+				"\n" + 
+				"Error in instantiating module 'test212a':\n" + 
+				" A non-Leibniz operator substituted for 'Op2'.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 32, col 1 to line 32, col 63 of module test212\n" + 
+				"\n" + 
+				"Error in instantiating module 'test212a':\n" + 
+				" A non-Leibniz operator substituted for 'Op2'.\n" + 
+				"\n" + 
+				"\n" + 
+				"");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test213.java b/tlatools/test/tlc2/tool/suite/Test213.java
new file mode 100644
index 0000000000000000000000000000000000000000..e11f6b635b2df304e4a1ee36ab1c7ef35e49949a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test213.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test213 extends SuiteETestCase {
+
+	public Test213() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("Semantic errors:\n" + 
+				"\n" + 
+				"*** Errors: 5\n" + 
+				"\n" + 
+				"line 13, col 1 to line 13, col 52 of module test213\n" + 
+				"\n" + 
+				"Level error in instantiating module 'test213b':\n" + 
+				"The level of the expression or operator substituted for 'C' \n" + 
+				"must be at most 2.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 14, col 1 to line 14, col 52 of module test213\n" + 
+				"\n" + 
+				"Level error in instantiating module 'test213b':\n" + 
+				"The level of the expression or operator substituted for 'D' \n" + 
+				"must be at most 2.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 29, col 8 to line 29, col 15 of module test213\n" + 
+				"\n" + 
+				"Non-constant CASE for temporal goal.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 31, col 8 to line 31, col 15 of module test213\n" + 
+				"\n" + 
+				"Non-constant TAKE, WITNESS, or HAVE for temporal goal.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 33, col 8 to line 33, col 22 of module test213\n" + 
+				"\n" + 
+				"Non-constant TAKE, WITNESS, or HAVE for temporal goal.\n" + 
+				"\n" + 
+				"\n" + 
+				"");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test214.java b/tlatools/test/tlc2/tool/suite/Test214.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2cbd0887e04170b92772eea9a49f883fb17d65f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test214.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test214 extends SuiteETestCase {
+
+	public Test214() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("Semantic errors:\n" + 
+				"\n" + 
+				"*** Errors: 1\n" + 
+				"\n" + 
+				"line 10, col 11 to line 10, col 14 of module test214\n" + 
+				"\n" + 
+				"The only expression allowed as a fact in a HIDE is \n" + 
+				"the name of a theorem, assumption, or step.\n" + 
+				"\n" + 
+				"\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test215.java b/tlatools/test/tlc2/tool/suite/Test215.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b1d74e75cd73c5caec98c4cd791867275912ded
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test215.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test215 extends SuiteETestCase {
+
+	public Test215() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("Semantic errors:\n" + 
+				"\n" + 
+				"*** Errors: 8\n" + 
+				"\n" + 
+				"line 8, col 6 to line 8, col 14 of module test215\n" + 
+				"\n" + 
+				"Action used where only temporal formula or state predicate allowed.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 9, col 6 to line 9, col 14 of module test215\n" + 
+				"\n" + 
+				"Action used where only temporal formula or state predicate allowed.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 12, col 6 to line 12, col 16 of module test215\n" + 
+				"\n" + 
+				"Action used where only temporal formula or state predicate allowed.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 13, col 6 to line 13, col 16 of module test215\n" + 
+				"\n" + 
+				"Action used where only temporal formula or state predicate allowed.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 16, col 6 to line 16, col 11 of module test215\n" + 
+				"\n" + 
+				"<> followed by action not of form <<A>>_v.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 20, col 8 to line 20, col 13 of module test215\n" + 
+				"\n" + 
+				"[] followed by action not of form [A]_v.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 23, col 25 to line 23, col 26 of module test215\n" + 
+				"\n" + 
+				"Action-level bound of quantified temporal formula.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 26, col 26 to line 26, col 27 of module test215\n" + 
+				"\n" + 
+				"Action-level bound of quantified temporal formula.\n" + 
+				"\n" + 
+				"\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test216.java b/tlatools/test/tlc2/tool/suite/Test216.java
new file mode 100644
index 0000000000000000000000000000000000000000..ace958c23af804ff79b3a63b90963494b2f9d6c7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test216.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Test216 extends SuiteETestCase {
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "28", "7", "0"));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "21", "s", "7"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		assertUncovered("line 12, col 22 to line 12, col 32 of module test216: 0\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test217.java b/tlatools/test/tlc2/tool/suite/Test217.java
new file mode 100644
index 0000000000000000000000000000000000000000..4575c00f5737ab1d314bd5cee3a747204115b445
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test217.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test217 extends SuiteETestCase {
+
+	public Test217() {
+		super(ExitStatus.ERROR_SPEC_PARSE);
+	}
+	
+	@Test
+	public void testSpec() {
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertSubstring("Semantic errors:\n" + 
+				"\n" + 
+				"*** Errors: 2\n" + 
+				"\n" + 
+				"line 12, col 11 to line 12, col 19 of module test217\n" + 
+				"\n" + 
+				"Level error in applying operator I!Foo:\n" + 
+				"The level of argument 1 exceeds the maximum level allowed by the operator.\n" + 
+				"\n" + 
+				"\n" + 
+				"line 13, col 9 to line 13, col 19 of module test217\n" + 
+				"\n" + 
+				"Level error in applying operator I!Foo:\n" + 
+				"The level of argument 1 exceeds the maximum level allowed by the operator.\n" + 
+				"\n" + 
+				"\n");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test219.java b/tlatools/test/tlc2/tool/suite/Test219.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcf9f9bdb36edf7f20f85a3d0a296b34711715c0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test219.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test219 extends SuiteTestCase {
+	public Test219() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test22.java b/tlatools/test/tlc2/tool/suite/Test22.java
new file mode 100644
index 0000000000000000000000000000000000000000..9eb0e371b062c5e066af726c9e1db9ec77beb71b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test22.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test22 extends SuiteTestCase {
+	public Test22() {
+		super("1344", "64", "0", "64");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test220.java b/tlatools/test/tlc2/tool/suite/Test220.java
new file mode 100644
index 0000000000000000000000000000000000000000..259446f119cc80aed98fb104d02536a7d435b97f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test220.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+
+public class Test220 extends SuiteETestCase {
+    
+    /* (non-Javadoc)
+     * @see tlc2.tool.liveness.ModelCheckerTestCase#checkDeadLock()
+     */
+    protected boolean checkDeadLock() {
+        // Disable command-line parameter "-deadlock" to let configuration file take control.
+        return true;
+    }
+
+    @Test
+    public void testSpec() {
+        assertFalse(recorder.recorded(EC.TLC_DEADLOCK_REACHED));
+        assertTrue(recorder.recorded(EC.TLC_FINISHED));
+        assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "2", "0"));
+        assertFalse(recorder.recorded(EC.GENERAL));
+
+        assertZeroUncovered();
+    }
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test23.java b/tlatools/test/tlc2/tool/suite/Test23.java
new file mode 100644
index 0000000000000000000000000000000000000000..a97ec032c739baa3ee5d4ddb4813ec7e1dbcdd40
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test23.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test23 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test24.java b/tlatools/test/tlc2/tool/suite/Test24.java
new file mode 100644
index 0000000000000000000000000000000000000000..ead4a5db9668a8f5c2e13c1e92555a38bc7a6d07
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test24.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test24 extends SuiteTestCase {
+	public Test24() {
+		super("21", "2", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test25.java b/tlatools/test/tlc2/tool/suite/Test25.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5b290ea24df3afda2a54d6219c9548091c0328b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test25.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test25 extends SuiteTestCase {
+	public Test25() {
+		super("3", "1", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test26.java b/tlatools/test/tlc2/tool/suite/Test26.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe30483472ca481f4cdffc09aa07ee39fbc61252
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test26.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test26 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test27.java b/tlatools/test/tlc2/tool/suite/Test27.java
new file mode 100644
index 0000000000000000000000000000000000000000..8353f98f97a4c88921ed0a759d451b16c639b932
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test27.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test27 extends SuiteTestCase {
+	public Test27() {
+		super("1109824", "63504", "0", "16");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test28.java b/tlatools/test/tlc2/tool/suite/Test28.java
new file mode 100644
index 0000000000000000000000000000000000000000..05e3c4b2c6a3d37ce6124e9ad7e8329d0ac64352
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test28.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test28 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test29.java b/tlatools/test/tlc2/tool/suite/Test29.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c6153c36aaf091acc7f4d9930ee0c5a11552d90
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test29.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test29 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test3.java b/tlatools/test/tlc2/tool/suite/Test3.java
new file mode 100644
index 0000000000000000000000000000000000000000..65b5dd3a8bb1ffeb04df73b333e7b23f15998cf5
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test3.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test3 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test30.java b/tlatools/test/tlc2/tool/suite/Test30.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ebdf08a3be06b86b06a8761f88ec7cfc7fc48da
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test30.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test30 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test31.java b/tlatools/test/tlc2/tool/suite/Test31.java
new file mode 100644
index 0000000000000000000000000000000000000000..1650624c2f2ca17c393d3539ef0104eeaea8a66d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test31.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test31 extends SuiteTestCase {
+	public Test31() {
+		super("13", "3", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test32.java b/tlatools/test/tlc2/tool/suite/Test32.java
new file mode 100644
index 0000000000000000000000000000000000000000..94decb30f94b876e76acdbd948b3919973ab001d
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test32.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test32 extends SuiteTestCase {
+	public Test32() {
+		super("3", "1", "0", "1", "line 23, col 17 to line 23, col 21 of module test32: 0\n" + 
+				"line 23, col 26 to line 23, col 29 of module test32: 0\n" + 
+				"line 27, col 23 to line 27, col 29 of module test32: 0\n");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test33.java b/tlatools/test/tlc2/tool/suite/Test33.java
new file mode 100644
index 0000000000000000000000000000000000000000..126489f802d569f8c02e4ddc348b115502f90d42
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test33.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test33 extends SuiteTestCase {
+	public Test33() {
+		super("235298", "117649", "0", "117649");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test34.java b/tlatools/test/tlc2/tool/suite/Test34.java
new file mode 100644
index 0000000000000000000000000000000000000000..32d0702fc5ab908890a3fe614128f8fc197f5403
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test34.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test34 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test35.java b/tlatools/test/tlc2/tool/suite/Test35.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf9ef33f767fcbbb45d88bd0a5e9736dea6d8459
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test35.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test35 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test36.java b/tlatools/test/tlc2/tool/suite/Test36.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce6762f6ca2d313aee3b940c5d518c28f3cc9e7a
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test36.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test36 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test37.java b/tlatools/test/tlc2/tool/suite/Test37.java
new file mode 100644
index 0000000000000000000000000000000000000000..d94e6bdbc7c304a66fe2d66f09aad0f4a6907d38
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test37.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test37 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test38.java b/tlatools/test/tlc2/tool/suite/Test38.java
new file mode 100644
index 0000000000000000000000000000000000000000..63f083299e2e890675f9bf9c688fc6eec403ea3f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test38.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test38 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test39.java b/tlatools/test/tlc2/tool/suite/Test39.java
new file mode 100644
index 0000000000000000000000000000000000000000..f71ea0b574966446180bd32fb0c5e983b1e4747e
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test39.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test39 extends SuiteTestCase {
+	public Test39() {
+		super("6", "3", "0", "1");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test4.java b/tlatools/test/tlc2/tool/suite/Test4.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e42e72500bc020c470e158db88a4af2597b0768
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test4.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test4 extends SuiteTestCase {
+	public Test4() {
+		super("11", "1", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test40.java b/tlatools/test/tlc2/tool/suite/Test40.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ba4a84e94d9e78e7180f438fb105065d145d4ce
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test40.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test40 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test41.java b/tlatools/test/tlc2/tool/suite/Test41.java
new file mode 100644
index 0000000000000000000000000000000000000000..059d9a1ad4870891ea82b7599d1a1a75f826ad51
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test41.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test41 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test42.java b/tlatools/test/tlc2/tool/suite/Test42.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3b848de071e62f972365037331a47fbdbfbb2ae
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test42.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test42 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test43.java b/tlatools/test/tlc2/tool/suite/Test43.java
new file mode 100644
index 0000000000000000000000000000000000000000..78103b9eae1c42b65118bfda40b8d6219f8c05b0
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test43.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test43 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test44.java b/tlatools/test/tlc2/tool/suite/Test44.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fa8a4476348b56088e5e96bfa1c5c076fd5d727
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test44.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test44 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test45.java b/tlatools/test/tlc2/tool/suite/Test45.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ba45bfc4b44cf749ecc8fe2ccbb48db189af0c9
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test45.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test45 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test46.java b/tlatools/test/tlc2/tool/suite/Test46.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4c33c30e781103ba111330e3af6d09ebef302ab
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test46.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test46 extends SuiteTestCase {
+	public Test46() {
+		super("121", "6", "0", "1");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test47.java b/tlatools/test/tlc2/tool/suite/Test47.java
new file mode 100644
index 0000000000000000000000000000000000000000..ded35b6adfb01db8ca83e56ae0359dfc6cc1a664
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test47.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test47 extends SuiteTestCase {
+	public Test47() {
+		super("4", "3", "0", "1");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test48.java b/tlatools/test/tlc2/tool/suite/Test48.java
new file mode 100644
index 0000000000000000000000000000000000000000..8537c7f72de825c891a6cc22eb6cf34329bba845
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test48.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test48 extends SuiteTestCase {
+	public Test48() {
+		super("64", "1", "0", "1");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test49.java b/tlatools/test/tlc2/tool/suite/Test49.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f2d0fca27ef12db288308738d44b5a9eb5ebd5f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test49.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test49 extends SuiteTestCase {
+	public Test49() {
+		super("10", "1", "0", "1");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test5.java b/tlatools/test/tlc2/tool/suite/Test5.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff3c020840a57abf8220df632eadd9c28d4ddf84
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test5.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test5 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test50.java b/tlatools/test/tlc2/tool/suite/Test50.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a33c567067e582fbfb7fde89ce8e90d42b1e895
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test50.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test50 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test51.java b/tlatools/test/tlc2/tool/suite/Test51.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed9a0f0bb629140e1e1d720fc1e2ac0592fbf1a7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test51.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test51 extends SuiteTestCase {
+	public Test51() {
+		super("0", "0", "0", "0");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test52.java b/tlatools/test/tlc2/tool/suite/Test52.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f3d52317b4cfbd06a7cdaec4c0662de88b7b9cd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test52.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test52 extends SuiteTestCase {
+	public Test52() {
+		super("3", "2", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test53.java b/tlatools/test/tlc2/tool/suite/Test53.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9314bd6c320bca235ab77210730700644b69a84
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test53.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test53 extends SuiteTestCase {
+	public Test53() {
+		super("10", "1", "0", "1");
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/tool/suite/Test54.java b/tlatools/test/tlc2/tool/suite/Test54.java
new file mode 100644
index 0000000000000000000000000000000000000000..36600ea77819d0b8497af0f00691690813a5b447
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test54.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test54 extends SuiteTestCase {
+	public Test54() {
+		super("10", "1", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test55.java b/tlatools/test/tlc2/tool/suite/Test55.java
new file mode 100644
index 0000000000000000000000000000000000000000..a034f870693a844f213aab9320422b5528a01748
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test55.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test55 extends SuiteTestCase {
+	public Test55() {
+		super("3", "2", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test56.java b/tlatools/test/tlc2/tool/suite/Test56.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fdc56d9bc371f866c04516961ce7f62a78f5717
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test56.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test56 extends SuiteTestCase {
+	public Test56() {
+		super("9", "6", "0", "3");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test57.java b/tlatools/test/tlc2/tool/suite/Test57.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ee10dec3881f10844dbac36e338ab103d4ba3d8
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test57.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+
+public class Test57 extends SuiteTestCase {
+
+	public Test57() {
+		// Can pass any value to super because testSpec is overriden. 
+		super("-1", "-1", "-1", "-1");
+		setExitStatus(ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		// ModelChecker has finished and generated the expected amount of states
+		assertTrue(recorder.recorded(EC.TLC_MODE_MC));
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Invariant1", "x = 1\n"));
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test58.java b/tlatools/test/tlc2/tool/suite/Test58.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d2bba3c4d3a35b05fdae4002d51b841b2e4d714
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test58.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test58 extends SuiteTestCase {
+	public Test58() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test59.java b/tlatools/test/tlc2/tool/suite/Test59.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b2f1172a3edb6f73b73d19c90475a902d1fb80b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test59.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test59 extends SuiteTestCase {
+	public Test59() {
+		super("6", "5", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test6.java b/tlatools/test/tlc2/tool/suite/Test6.java
new file mode 100644
index 0000000000000000000000000000000000000000..6877ab55644e57e8283b2cc1cfbf83ad13bbebd9
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test6.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test6 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test60.java b/tlatools/test/tlc2/tool/suite/Test60.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc639b61e0202bdd2145898cb2a43f04b606e762
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test60.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test60 extends SuiteTestCase {
+	public Test60() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test61.java b/tlatools/test/tlc2/tool/suite/Test61.java
new file mode 100644
index 0000000000000000000000000000000000000000..a951f4cd770b9b64bc32495d6045e47fb2b0907b
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test61.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import org.junit.Ignore;
+
+@Ignore("known bug, it Handles JSpec but not ISpec")
+public class Test61 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test62.java b/tlatools/test/tlc2/tool/suite/Test62.java
new file mode 100644
index 0000000000000000000000000000000000000000..e057a74094cb68038107361d6d4c3d50276cc1ce
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test62.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test62 extends SuiteTestCase {
+	public Test62() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test63.java b/tlatools/test/tlc2/tool/suite/Test63.java
new file mode 100644
index 0000000000000000000000000000000000000000..37a9b0701095bf07c6bcdf9ed4529850772324ba
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test63.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test63 extends SuiteTestCase {
+	public Test63() {
+		super("696", "216", "0", "72");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test63a.java b/tlatools/test/tlc2/tool/suite/Test63a.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f02e3908ca68f7db16cfcd3a87511a68d816bfd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test63a.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test63a extends SuiteTestCase {
+	public Test63a() {
+		super("24", "12", "0", "12");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test64.java b/tlatools/test/tlc2/tool/suite/Test64.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5dbc4b3623a6c9a7f4291246f02ee747ef8065f
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test64.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test64 extends SuiteTestCase {
+	public Test64() {
+		super("3", "2", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test64a.java b/tlatools/test/tlc2/tool/suite/Test64a.java
new file mode 100644
index 0000000000000000000000000000000000000000..c86986761311805f3c5ce96d9831fef11e0f1760
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test64a.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test64a extends SuiteTestCase {
+	public Test64a() {
+		super("3", "2", "0", "1");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test65.java b/tlatools/test/tlc2/tool/suite/Test65.java
new file mode 100644
index 0000000000000000000000000000000000000000..daa3b3364288c8fccfffd07b1724d9f9aac00111
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test65.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test65 extends SuiteTestCase {
+	public Test65() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test65a.java b/tlatools/test/tlc2/tool/suite/Test65a.java
new file mode 100644
index 0000000000000000000000000000000000000000..65aa645d8193d31bcd4f3077502e35608ee420ea
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test65a.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test65a extends SuiteTestCase {
+	public Test65a() {
+		super("0", "0", "0", "0");
+	}
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test7.java b/tlatools/test/tlc2/tool/suite/Test7.java
new file mode 100644
index 0000000000000000000000000000000000000000..054ba5fb9e9baaa4d44345108ad4d7aa0e1a2bfd
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test7.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test7 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test8.java b/tlatools/test/tlc2/tool/suite/Test8.java
new file mode 100644
index 0000000000000000000000000000000000000000..f134ac13a0730df24a2bd4c069b93eac92dc49d7
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test8.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test8 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test9.java b/tlatools/test/tlc2/tool/suite/Test9.java
new file mode 100644
index 0000000000000000000000000000000000000000..320f7f76442674748f0e30d584e9c7d22dfbc505
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test9.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test9 extends SuiteTestCase {
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test99.java b/tlatools/test/tlc2/tool/suite/Test99.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c4a29f2a4ab10e808cbde2d6c017c2eb9a4d9e3
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test99.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test99 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/Test999.java b/tlatools/test/tlc2/tool/suite/Test999.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa282a7f681e137404bce82ce6fafdf4e650aa88
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/Test999.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+public class Test999 extends SuiteTestCase {
+
+}
diff --git a/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java b/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0941db03c9974c4aa9b0029da99c161821877bc
--- /dev/null
+++ b/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.tool.suite;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.output.EC.ExitStatus;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class TestInvalidInvariant extends ModelCheckerTestCase {
+
+	public TestInvalidInvariant() {
+		super("testinvalidinvariant", ExitStatus.FAILURE_SAFETY_EVAL);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_LEVEL, "Invariant"));
+		assertFalse(recorder.recorded(EC.GENERAL));
+		// See LevelNode.java line 590ff and tlc2.tool.Spec.processConfigInvariants().
+		assertFalse(recorder.recordedWithSubStringValue(EC.GENERAL,
+				"Note that a bug can cause TLC to incorrectly report this error."));
+	}
+}
diff --git a/tlatools/test/tlc2/util/BitVectorTest.java b/tlatools/test/tlc2/util/BitVectorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f996d8ba3e485689bde1e0cf194be63c2f0828e9
--- /dev/null
+++ b/tlatools/test/tlc2/util/BitVectorTest.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class BitVectorTest {
+
+	/**
+	 * Test method for {@link tlc2.util.BitVector#toString()}.
+	 */
+	@Test
+	public void testToString() {
+		final BitVector bitVector = new BitVector(8);
+		bitVector.set(0);
+		bitVector.set(1);
+		// unset
+		bitVector.set(3);
+		bitVector.set(4);
+		// unset
+		// unset
+		bitVector.set(7);
+		
+		assertEquals("[10011011]", bitVector.toString());
+	}
+
+	@Test
+	public void testToStringRange() {
+		final BitVector bitVector = new BitVector(8);
+		bitVector.set(0);
+		bitVector.set(1);
+		// unset
+		bitVector.set(3);
+		bitVector.set(4);
+		// unset
+		// unset
+		bitVector.set(7);
+		
+		assertEquals("[001]", bitVector.toString(4, 3));
+	}
+}
diff --git a/tlatools/test/tlc2/util/BufferedRandomAccessFileTest.java b/tlatools/test/tlc2/util/BufferedRandomAccessFileTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9221e6d54626cd6bf385a38f09fcb172d1792858
--- /dev/null
+++ b/tlatools/test/tlc2/util/BufferedRandomAccessFileTest.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+
+public class BufferedRandomAccessFileTest {
+
+	@Test
+	public void testWrite() throws IOException {
+		final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testWrite", ".bin");
+		tmpFile.deleteOnExit();
+		final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw");
+		for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+			raf.writeLong(i);
+		}
+		raf.close();
+	}
+	
+	@Test
+	public void testWriteSeek() throws IOException {
+		final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testWriteSeek", ".bin");
+		tmpFile.deleteOnExit();
+		final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw");
+		
+		raf.setLength(BufferedRandomAccessFile.BuffSz + 1L);
+		raf.seek(1);
+		
+		for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+			raf.writeLong(i);
+		}
+		raf.close();
+	}
+	
+	@Test
+	public void testWriteSeekNoLength() throws IOException {
+		final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testWriteSeekNoLength", ".bin");
+		tmpFile.deleteOnExit();
+		final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw");
+
+		// Do not set length, expect meaningful exception
+		raf.seek(1);
+		try {
+			for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+				raf.writeLong(i);
+			}
+		} catch (IOException expected) {
+			return;
+		} catch (Exception e) {
+			fail(e.getMessage());
+		} finally {
+			raf.close();
+		}
+	}
+	
+	@Test
+	public void testRead() throws IOException {
+		final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testRead", ".bin");
+		tmpFile.deleteOnExit();
+		final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw");
+		for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+			raf.writeLong(i);
+		}
+
+		raf.seek(0);
+		for (long i = 0l; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+			assertEquals(i, raf.readLong());
+		}
+		raf.close();
+	}
+	
+	@Test
+	public void testReadSeek() throws IOException {
+		final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testReadSeek", ".bin");
+		tmpFile.deleteOnExit();
+		final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw");
+		
+		raf.setLength(BufferedRandomAccessFile.BuffSz + 1L);
+		raf.seek(1);
+		
+		for (int i = 0; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+			assertEquals(0L, raf.readLong());
+		}
+		raf.close();
+	}
+	
+	@Test
+	public void testReadSeekNoLength() throws IOException {
+		final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testReadSeekNoLength", ".bin");
+		tmpFile.deleteOnExit();
+		final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw");
+
+		// Do not set length, expect meaningful exception
+		raf.seek(1);
+		try {
+			for (int i = 0; i < BufferedRandomAccessFile.BuffSz / 8; i++) {
+				raf.readLong();
+			}
+		} catch (IOException expected) {
+			return;
+		} catch (Exception e) {
+			fail(e.getMessage());
+		} finally {
+			raf.close();
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/util/ByteUtilsTest.java b/tlatools/test/tlc2/util/ByteUtilsTest.java
index c5509c98de5e0cf3dad6f0e415cdd831ede0acf4..f84ecdf5d6c6f630874daf8b5e22ff2c85ff7f9c 100644
--- a/tlatools/test/tlc2/util/ByteUtilsTest.java
+++ b/tlatools/test/tlc2/util/ByteUtilsTest.java
@@ -8,7 +8,9 @@ import java.io.IOException;
 import java.math.BigInteger;
 import java.util.Random;
 
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+
 import util.ToolIO;
 
 /**
@@ -16,7 +18,7 @@ import util.ToolIO;
  * @author Simon Zambrovski
  * @version $Id$
  */
-public class ByteUtilsTest extends TestCase
+public class ByteUtilsTest
 {
     public static final int ARRAYSIZE = 10000;
     public static final int BITS = 1000;
@@ -32,9 +34,9 @@ public class ByteUtilsTest extends TestCase
     long t1;
     long t2;
 
-    protected void setUp() throws Exception
+    @Before
+	public void setUp() throws Exception
     {
-        super.setUp();
         Arr = new BigInteger[ARRAYSIZE];
         Arr2 = new BigInteger[ARRAYSIZE];
         Arr3 = new BigInteger[ARRAYSIZE];
@@ -54,7 +56,8 @@ public class ByteUtilsTest extends TestCase
 
     }
 
-    public void test1()
+    @Test
+	public void test1()
     {
         t1 = System.currentTimeMillis();
         mainTestinttoByte();
@@ -62,7 +65,8 @@ public class ByteUtilsTest extends TestCase
         ToolIO.out.println("Testing IntToByteArray took " + (t2 - t1) + "ms");
     }
 
-    public void test2() throws FileNotFoundException, IOException
+    @Test
+	public void test2() throws FileNotFoundException, IOException
     {
         t1 = System.currentTimeMillis();
         mainTestWriteIntReadInt();
@@ -70,7 +74,8 @@ public class ByteUtilsTest extends TestCase
         ToolIO.out.println("Testing WriteInt, ReadInt took " + (t2 - t1) + "ms");
     }
 
-    public void test3()
+    @Test
+	public void test3()
     {
         t1 = System.currentTimeMillis();
         mainTestlongtoByte();
@@ -78,7 +83,8 @@ public class ByteUtilsTest extends TestCase
         ToolIO.out.println("Testing longToByteArray took " + (t2 - t1) + "ms");
     }
 
-    public void test4() throws FileNotFoundException, IOException
+    @Test
+	public void test4() throws FileNotFoundException, IOException
     {
         t1 = System.currentTimeMillis();
         mainTestWriteLongReadLong();
@@ -86,7 +92,8 @@ public class ByteUtilsTest extends TestCase
         ToolIO.out.println("Testing WriteLong, ReadLong took " + (t2 - t1) + "ms");
     }
 
-    public void test5() throws FileNotFoundException, IOException
+    @Test
+	public void test5() throws FileNotFoundException, IOException
     {
         t1 = System.currentTimeMillis();
         mainTestWriteReadSizeByteArray();
@@ -94,7 +101,8 @@ public class ByteUtilsTest extends TestCase
         ToolIO.out.println("Testing Write, Read took " + (t2 - t1) + "ms");
     }
 
-    public void test6() throws FileNotFoundException, IOException
+    @Test
+	public void test6() throws FileNotFoundException, IOException
     {
         t1 = System.currentTimeMillis();
         mainTestAppend();
diff --git a/tlatools/test/tlc2/util/CombinatoricsTest.java b/tlatools/test/tlc2/util/CombinatoricsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6696b19fd6d7e1e4a3b8e9970d1f76b70cf5b175
--- /dev/null
+++ b/tlatools/test/tlc2/util/CombinatoricsTest.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+
+import org.junit.Test;
+
+public class CombinatoricsTest {
+	@Test
+	public void testChoose() {
+		for (int n = 0; n < Combinatorics.MAXCHOOSENUM; n++) {
+			for (int k = 0; k < Combinatorics.MAXCHOOSENUM; k++) {
+				long choose = Combinatorics.choose(n, k);
+				long binomial = Combinatorics.binomial(n, k);
+				assertEquals(String.format("n=%s, k=%s", n, k), choose, binomial);
+			}
+		}
+	}
+	@Test
+	public void testChooseBigChoose() {
+		for (int n = 0; n < Combinatorics.MAXCHOOSENUM + 1; n++) {
+			for (int k = 0; k < Combinatorics.MAXCHOOSENUM + 1; k++) {
+				long choose = Combinatorics.choose(n, k);
+				long bigChoose = Combinatorics.bigChoose(n, k).longValueExact();
+				assertEquals(String.format("n=%s, k=%s", n, k), choose, bigChoose);
+			}
+		}
+	}
+	@Test
+	public void testSlowChooseBigChoose() {
+		for (int n = Combinatorics.MAXCHOOSENUM + 1; n < Combinatorics.MAXCHOOSENUM << 2; n++) {
+			for (int k = Combinatorics.MAXCHOOSENUM + 1; k < Combinatorics.MAXCHOOSENUM << 2; k++) {
+				BigInteger slowBigChoose = Combinatorics.slowBigChoose(n, k);
+				BigInteger bigChoose = Combinatorics.bigChoose(n, k);
+				assertEquals(String.format("n=%s, k=%s", n, k), slowBigChoose, bigChoose);
+			}
+		}
+	}
+	@Test
+	public void testBigChoose50c1() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(50, 1);
+		assertEquals(6, bigChoose.bitLength());
+		assertEquals(50L, bigChoose.longValueExact());
+	}
+	@Test
+	public void testBigChoose50c10() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(50, 10);
+		assertEquals(34, bigChoose.bitLength());
+		assertEquals(10272278170L, bigChoose.longValueExact());
+	}
+	@Test
+	public void testBigChoose50c20() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(50, 20);
+		assertEquals(46, bigChoose.bitLength());
+		assertEquals(47129212243960L, bigChoose.longValueExact());
+	}
+	@Test
+	public void testBigChoose50c30() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(50, 30);
+		assertEquals(46, bigChoose.bitLength());
+		assertEquals(47129212243960L, bigChoose.longValueExact());
+	}
+	@Test
+	public void testBigChoose400c1() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(400, 1);
+		assertEquals(9, bigChoose.bitLength());
+		assertEquals(400L, bigChoose.longValueExact());
+	}
+	@Test
+	public void testBigChoose400c50() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(400, 50);
+		assertEquals(214, bigChoose.bitLength());
+		assertEquals(
+				"17035900270730601418919867558071677342938596450600561760371485120",
+				bigChoose.toString());
+	}
+	@Test
+	public void testBigChoose400c100() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(400, 100);
+		assertEquals(321, bigChoose.bitLength());
+		assertEquals(
+				"2241854791554337561923210387201698554845411177476295990399942258896013007429693894018935107174320",
+				bigChoose.toString());
+	}
+	@Test
+	public void testBigChoose400c200() {
+		final BigInteger bigChoose = Combinatorics.bigChoose(400, 200);
+		assertEquals(396, bigChoose.bitLength());
+		assertEquals(
+				"102952500135414432972975880320401986757210925381077648234849059575923332372651958598336595518976492951564048597506774120",
+				bigChoose.toString());
+	}
+}
diff --git a/tlatools/test/tlc2/util/ContextTest.java b/tlatools/test/tlc2/util/ContextTest.java
index f916ed2f7313f25b3dd1b7c32d7598618ab1e28e..c85b502f6af20f15b1f8c87de7ed1245524edd32 100644
--- a/tlatools/test/tlc2/util/ContextTest.java
+++ b/tlatools/test/tlc2/util/ContextTest.java
@@ -26,8 +26,10 @@
 
 package tlc2.util;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
+import org.junit.Test;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
@@ -39,33 +41,37 @@ import tla2sany.semantic.SymbolNode;
 import tla2sany.xml.SymbolContext;
 import util.UniqueString;
 
-public class ContextTest extends TestCase {
+public class ContextTest {
 
+	@Test
 	public void testLookupEmpty() {
 		assertNull(Context.Empty.lookup(new DummySymbolNode()));
 		assertNull(Context.Empty.lookup(new DummySymbolNode(), true));
 		assertNull(Context.Empty.lookup(new DummySymbolNode(), false));
-		assertNull(Context.Empty.lookup(null));
+		assertNull(Context.Empty.lookup((SymbolNode)null));
 		assertNull(Context.Empty.lookup(null, true));
 		assertNull(Context.Empty.lookup(null, false));
 	}
 
+	@Test
 	public void testLookupBranch() {
 		// BranchCtx -> Empty
 		final Context ctx = Context.branch(Context.Empty);
 		assertNull(ctx.lookup(new DummySymbolNode()));
 		assertNull(ctx.lookup(new DummySymbolNode(), true));
 		assertNull(ctx.lookup(new DummySymbolNode(), false));
-		assertNull(ctx.lookup(null));
+		assertNull(ctx.lookup((SymbolNode)null));
 		assertNull(ctx.lookup(null, false));
 		assertNull(ctx.lookup(null, true));
 	}
 	
+	@Test
 	public void testLookupSymbolNodeNull() {
 		final Context ctx = Context.branch(Context.Empty);
-		assertNull(ctx.lookup(null));
+		assertNull(ctx.lookup((SymbolNode)null));
 	}
 	
+	@Test
 	public void testLookup() {
 		final DummySymbolNode name = new DummySymbolNode("ctx1");
 		final Object value = "value1";
@@ -79,6 +85,7 @@ public class ContextTest extends TestCase {
 		assertEquals(value, ctx3.lookup(name));
 	}
 	
+	@Test
 	public void testLookupCutOffFalse() {
 		final DummySymbolNode name = new DummySymbolNode("ctx1");
 		final Object value = "value1";
@@ -93,6 +100,7 @@ public class ContextTest extends TestCase {
 	}
 	
 	// Cutoff causes lookup to stop at branching context
+	@Test
 	public void testLookupCutOffTrue() {
 		final DummySymbolNode name = new DummySymbolNode("ctx1");
 		final Object value = "value1";
@@ -106,6 +114,7 @@ public class ContextTest extends TestCase {
 		assertNull(ctx3.lookup(name, true));
 	}
 	
+	@Test
 	public void testLookupWithAtBranching() {
 		final DummySymbolNode name = new DummySymbolNode("ctx1");
 		final Object value = "value1";
@@ -117,6 +126,7 @@ public class ContextTest extends TestCase {
 		assertEquals(value, branch.lookup(name));
 	}
 	
+	@Test
 	public void testLookupWithCutOffFalseAtBranching() {
 		final DummySymbolNode name = new DummySymbolNode("ctx1");
 		final Object value = "value1";
@@ -128,6 +138,7 @@ public class ContextTest extends TestCase {
 		assertEquals(value, branch.lookup(name, false));
 	}
 
+	@Test
 	public void testLookupWithCutOffTrueAtBranching() {
 		final DummySymbolNode name = new DummySymbolNode("ctx1");
 		final Object value = "value1";
@@ -142,6 +153,7 @@ public class ContextTest extends TestCase {
 	/**
 	 * Test method for {@link tlc2.util.Context#lookup(tla2sany.semantic.SymbolNode)}.
 	 */
+	@Test
 	public void testLookupSymbolNode() {
 		final DummySymbolNode name = new DummySymbolNode();
 		final Object value = new Object();
diff --git a/tlatools/test/tlc2/util/FP64Test.java b/tlatools/test/tlc2/util/FP64Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc31a31523abb5836f4e8aa7fd9b72c34d2b279c
--- /dev/null
+++ b/tlatools/test/tlc2/util/FP64Test.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Random;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class FP64Test {
+
+	@Before
+	public void setup() {
+		FP64.Init();
+	}
+	
+	@Test
+	public void testExtendLongInt() {
+		final Random random = new Random();
+		for (int i = 0; i < 1000; i++) {
+			final long fp = random.nextLong();
+			final int x = random.nextInt();
+			assertEquals(FP64.Extend(fp, x), FP64.ExtendLoop(fp, x));
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/util/GrowingLongVecTest.java b/tlatools/test/tlc2/util/GrowingLongVecTest.java
index 5d9d914ce22f531904c0189fa2da87fa8683702e..fb2f913401a430915efb09365e584e1907333939 100644
--- a/tlatools/test/tlc2/util/GrowingLongVecTest.java
+++ b/tlatools/test/tlc2/util/GrowingLongVecTest.java
@@ -26,6 +26,10 @@
 
 package tlc2.util;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
 
 public class GrowingLongVecTest extends LongVecTest {
 
@@ -34,6 +38,7 @@ public class GrowingLongVecTest extends LongVecTest {
 		return new LongVec(0);
 	}
 	
+	@Test
 	public void testGrowAndShrink() {
 		// Zero capacity, LongVec has to grow
 		final LongVec vec = new LongVec(0);
diff --git a/tlatools/test/tlc2/util/LongVecTest.java b/tlatools/test/tlc2/util/LongVecTest.java
index 2af02a8d0fb928fec0b8a2bf690527680bad503a..4b6903cf213e6b750a546a9a779c13ebf3209321 100644
--- a/tlatools/test/tlc2/util/LongVecTest.java
+++ b/tlatools/test/tlc2/util/LongVecTest.java
@@ -26,15 +26,19 @@
 
 package tlc2.util;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
-public class LongVecTest extends TestCase {
+import org.junit.Test;
+
+public class LongVecTest {
 
 	protected LongVec getLongVec() {
 		// default capacity
 		return new LongVec();
 	}
 	
+	@Test
 	public void testReadBeyondCapacity() {
 		final LongVec vec = getLongVec();
 		try {
@@ -45,6 +49,7 @@ public class LongVecTest extends TestCase {
 		fail("Read beyond capacity");
 	}
 
+	@Test
 	public void testAddAndReadBeyondCapacity() {
 		final LongVec vec = getLongVec();
 		vec.addElement(1L);
@@ -57,6 +62,7 @@ public class LongVecTest extends TestCase {
 		fail("Read beyond capacity");
 	}
 
+	@Test
 	public void testRemoveBeyondCapacity() {
 		final LongVec vec = new LongVec(10);
 		for (int i = -1; i <= 10; i++) {
@@ -69,6 +75,7 @@ public class LongVecTest extends TestCase {
 		}
 	}
 
+	@Test
 	public void testAddRemoveBeyondCapacity() {
 		final LongVec vec = new LongVec(10);
 		vec.addElement(1L);
@@ -81,6 +88,7 @@ public class LongVecTest extends TestCase {
 		fail("Read beyond capacity");
 	}
 	
+	@Test
 	public void testRemoveAndGet() {
 		final LongVec vec = getLongVec();
 		vec.addElement(1L);
@@ -103,6 +111,7 @@ public class LongVecTest extends TestCase {
 		fail("A new elements magically appeared in LongVec");
 	}
 	
+	@Test
 	public void testRemoveWrongOrder() {
 		final LongVec vec = getLongVec();
 		vec.addElement(1L);
@@ -120,6 +129,7 @@ public class LongVecTest extends TestCase {
 		fail("Removed non-existing element");
 	}
 
+	@Test
 	public void testGetNegative() {
 		final LongVec vec = getLongVec();
 		try {
@@ -130,6 +140,7 @@ public class LongVecTest extends TestCase {
 		fail("Read negative");
 	}
 
+	@Test
 	public void testRemoveNegative() {
 		final LongVec vec = getLongVec();
 		try {
diff --git a/tlatools/test/tlc2/util/MemIntQueueTest.java b/tlatools/test/tlc2/util/MemIntQueueTest.java
index cd645d2d93db3911471f102793666b9ce25f182f..e32fd2ff9b43d4ada16a0c134f1b2ed45c37c25d 100644
--- a/tlatools/test/tlc2/util/MemIntQueueTest.java
+++ b/tlatools/test/tlc2/util/MemIntQueueTest.java
@@ -26,12 +26,16 @@
 
 package tlc2.util;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
 import java.util.NoSuchElementException;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class MemIntQueueTest extends TestCase {
+public class MemIntQueueTest {
 
+	@Test
 	public void testDequeuePastLastElement() {
 		final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant");
 		queue.enqueueInt(1);
@@ -47,6 +51,7 @@ public class MemIntQueueTest extends TestCase {
 	
 	// Add 0 three times and make sure it's only returned this many times. 0
 	// is MemIntQueue's internal default for an empty slot.
+	@Test
 	public void testEnqueueZeros() {
 		final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant");
 		queue.enqueueInt(0);
@@ -65,6 +70,7 @@ public class MemIntQueueTest extends TestCase {
 		fail("Returned element where there should be none.");
 	}
 	
+	@Test
 	public void testEnqueueLong() {
 		final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant");
 		queue.enqueueLong(0);
@@ -80,6 +86,7 @@ public class MemIntQueueTest extends TestCase {
 		fail("Returned element where there should be none.");
 	}
 	
+	@Test
 	public void testEnqueueDequeueLong() {
 		final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant");
 		queue.enqueueLong(0);
@@ -93,4 +100,36 @@ public class MemIntQueueTest extends TestCase {
 		}
 		fail("Returned element where there should be none.");
 	}
+	
+	@Test
+	public void testGrow() {
+		final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant", 4);
+		queue.enqueueInt(0);
+		queue.enqueueInt(1);
+		queue.enqueueInt(2);
+		queue.enqueueInt(3);
+		assertEquals(4, queue.size());
+
+		queue.dequeueInt();
+		queue.dequeueInt();
+		queue.dequeueInt();
+		queue.dequeueInt();
+		assertEquals(0, queue.size());
+
+		queue.enqueueInt(4);
+		queue.enqueueInt(5);
+		queue.enqueueInt(6);
+		queue.enqueueInt(7);
+		assertEquals(4, queue.size());
+
+		queue.dequeueInt();
+
+		// This should make the queue grow internally.
+		queue.enqueueInt(8);
+		queue.enqueueInt(9);
+		for(int i = 5; i < 10; i++) {
+			assertEquals(i, queue.dequeueInt());
+		}
+		assertEquals(0, queue.size());
+	}
 }
diff --git a/tlatools/test/tlc2/util/MemIntStackTest.java b/tlatools/test/tlc2/util/MemIntStackTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..90ed6d70d64512ce1806f93347a137916d403c0e
--- /dev/null
+++ b/tlatools/test/tlc2/util/MemIntStackTest.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class MemIntStackTest {
+
+	@Test
+	public void testPeak() {
+		final MemIntStack memIntStack = new MemIntStack("", "");
+		
+		memIntStack.pushLong(4711L);
+		memIntStack.pushLong(2323L);
+		memIntStack.pushInt(1);
+		memIntStack.pushLong(77L);
+		
+		assertEquals(4711L, memIntStack.peakLong(0));
+		assertEquals(2323L, memIntStack.peakLong(2));
+		assertEquals(1, memIntStack.peakInt(4));
+		assertEquals(77L, memIntStack.peakLong(5));
+	}
+}
diff --git a/tlatools/test/tlc2/util/SynchronousDiskIntStackTest.java b/tlatools/test/tlc2/util/SynchronousDiskIntStackTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c52a98a268e0d8c7e1acc7754560eaa498e8e52
--- /dev/null
+++ b/tlatools/test/tlc2/util/SynchronousDiskIntStackTest.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+
+import org.junit.Test;
+
+public class SynchronousDiskIntStackTest {
+
+	@Test
+	public void testPushIntNoWrite() {
+		final String diskdir = System.getProperty("java.io.tmpdir") + File.separator + "SynchronousDiskIntStackTest_"
+				+ System.currentTimeMillis();
+
+		final int size = 8;
+		
+		final IntStack diskIntStack = new SynchronousDiskIntStack(diskdir, "SynchronousDiskIntStackTest", size);
+		
+		// Fill stack
+		for (int i = 0; i < size; i++) {
+			diskIntStack.pushInt(i);
+		}
+
+		// Check all elements still on stack
+		for (int i = size - 1; i >= 0; i--) {
+			assertEquals(i, diskIntStack.popInt());
+		}
+	}
+
+	@Test
+	public void testPushIntWrite() {
+		final String diskdir = System.getProperty("java.io.tmpdir") + File.separator + "SynchronousDiskIntStackTest_"
+				+ System.currentTimeMillis();
+		new File(diskdir).mkdirs();
+
+		final int size = 8;
+		
+		final IntStack diskIntStack = new SynchronousDiskIntStack(diskdir, "SynchronousDiskIntStackTest", size);
+		
+		// Fill stack trice
+		for (int i = 0; i < (size * 3); i++) {
+			diskIntStack.pushInt(i);
+		}
+
+		// Check all elements still on stack
+		for (int i = (3*size) - 1; i >= 0; i--) {
+			assertEquals(i, diskIntStack.popInt());
+		}
+		
+		assertEquals(0, diskIntStack.size());
+	}
+}
diff --git a/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java b/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java
index 390bc25ad3d2b207c89eb5022561ff2720635fc0..8c52812dfa3403bf7cbc19f1b52177b9f6e40630 100644
--- a/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java
+++ b/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java
@@ -18,7 +18,7 @@
  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINbucketStatistic IN THE SOFTWARE.
  *
  * Contributors:
  *   Markus Alexander Kuppe - initial API and implementation
@@ -26,163 +26,194 @@
 
 package tlc2.util.statistics;
 
-import tlc2.util.statistics.BucketStatistics;
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-public class BucketStatisticsTest extends TestCase {
-	
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BucketStatisticsTest {
+
+	@SuppressWarnings("rawtypes")
+	@Parameterized.Parameters
+	public static Collection<Class> bucketStatistics() {
+		return Arrays.asList(
+				new Class[] { ConcurrentBucketStatistics.class, BucketStatistics.class });
+	}
+
+	private final IBucketStatistics bucketStatistic;
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	public BucketStatisticsTest(Class bucketStatistic) throws InstantiationException, IllegalAccessException,
+			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+		this.bucketStatistic = (IBucketStatistics) bucketStatistic.getConstructor(String.class)
+				.newInstance("BucketStatisticsTest");
+	}
+
+	@Test
 	public void testInvalidArgument() {
 		try {
-			final IBucketStatistics gs = new BucketStatistics();
-			gs.addSample(-1);
+			bucketStatistic.addSample(-1);
 		} catch (IllegalArgumentException e) {
 			return;
 		}
 		fail();
 	}
-	
+
+	@Test
 	public void testMean() {
-		final IBucketStatistics gs = new BucketStatistics();
-		assertEquals(-1.0d, gs.getMean());
-
-		gs.addSample(0);
-		assertEquals(0.0d, gs.getMean());
-		
-		gs.addSample(1);
-		gs.addSample(2);
-		assertEquals(1.0d, gs.getMean());
-
-		gs.addSample(2);
-		gs.addSample(2);
-		assertEquals(1.4d, gs.getMean());
+		assertTrue(Double.compare(-1.0d, bucketStatistic.getMean()) == 0);
+
+		bucketStatistic.addSample(0);
+		assertTrue(Double.compare(0.0d, bucketStatistic.getMean()) == 0);
+
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		assertTrue(Double.compare(1.0d, bucketStatistic.getMean()) == 0);
+
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		assertTrue(Double.compare(1.4d, bucketStatistic.getMean()) == 0);
 	}
 
+	@Test
 	public void testMedian() {
-		final IBucketStatistics gs = new BucketStatistics();
-		assertEquals(-1, gs.getMedian());
-
-		gs.addSample(0);
-		assertEquals(0, gs.getMedian());
-		
-		gs.addSample(1);
-		gs.addSample(2);
-		assertEquals(1, gs.getMedian());
-
-		gs.addSample(2);
-		gs.addSample(2);
-		assertEquals(2, gs.getMedian());
+		assertEquals(-1, bucketStatistic.getMedian());
+
+		bucketStatistic.addSample(0);
+		assertEquals(0, bucketStatistic.getMedian());
+
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		assertEquals(1, bucketStatistic.getMedian());
+
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		assertEquals(2, bucketStatistic.getMedian());
 	}
 
+	@Test
 	public void testMin() {
-		final IBucketStatistics gs = new BucketStatistics();
-		assertEquals(-1, gs.getMin());
-
-		gs.addSample(0);
-		gs.addSample(0);
-		gs.addSample(0);
-		gs.addSample(1);
-		gs.addSample(1);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(2);
-		assertEquals(0, gs.getMin());
+		assertEquals(-1, bucketStatistic.getMin());
+
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		assertEquals(0, bucketStatistic.getMin());
 	}
-	
+
+	@Test
+	public void testMin2() {
+		assertEquals(-1, bucketStatistic.getMin());
+
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		assertEquals(1, bucketStatistic.getMin());
+	}
+
+	@Test
 	public void testMax() {
-		final IBucketStatistics gs = new BucketStatistics();
-		assertEquals(-1, gs.getMax());
-
-		gs.addSample(0);
-		gs.addSample(0);
-		gs.addSample(0);
-		gs.addSample(1);
-		gs.addSample(1);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(3);
-		assertEquals(3, gs.getMax());
+		assertEquals(-1, bucketStatistic.getMax());
+
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(3);
+		assertEquals(3, bucketStatistic.getMax());
 	}
 
+	@Test
 	public void testStandardDeviation() {
-		final IBucketStatistics gs = new BucketStatistics();
-		assertEquals(-1.0, gs.getStdDev());
-
-		gs.addSample(0);
-		gs.addSample(0);
-		gs.addSample(0);
-		gs.addSample(1);
-		gs.addSample(1);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(3);
-		assertEquals(1.005d, (Math.round(gs.getStdDev() * 10000d) / 10000d));
+		assertEquals(-1.0, bucketStatistic.getStdDev(), 0);
+
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(3);
+		assertTrue(Double.compare(1.005d, (Math.round(bucketStatistic.getStdDev() * 10000d) / 10000d)) == 0);
 	}
 
+	@Test
 	public void testGetPercentile() {
-		final IBucketStatistics gs = new BucketStatistics();
-		assertEquals(-1.0, gs.getPercentile(1));
-		
+		assertEquals(-1.0, bucketStatistic.getPercentile(1), 0);
+
 		try {
-			gs.addSample(1); // <- first element
-			gs.getPercentile(1.1);
-			gs.getPercentile(-0.1);
+			bucketStatistic.addSample(1); // <- first element
+			bucketStatistic.getPercentile(1.1);
+			bucketStatistic.getPercentile(-0.1);
 		} catch (Exception e) {
 			fail(e.getMessage());
 		}
-		
-		gs.addSample(1);
-		gs.addSample(1);
-		gs.addSample(2); // <- 0.5 percentile
-		gs.addSample(2);
-		gs.addSample(2);
-		gs.addSample(3);
-		assertEquals(2.0d, gs.getPercentile(0.5d));
-		assertEquals(2.0d, gs.getPercentile(0.75d));
-		assertEquals(3.0d, gs.getPercentile(0.999d));
+
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2); // <- 0.5 percentile
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(3);
+		assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.5d)) == 0);
+		assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.5d)) == 0);
+		assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.75d)) == 0);
+		assertTrue(Double.compare(3.0d, bucketStatistic.getPercentile(0.999d)) == 0);
 	}
+
 	// NaN test
+	@Test
 	public void testGetPercentileNaN() {
 		try {
-			final IBucketStatistics gs = new BucketStatistics();
-			gs.getPercentile(Double.NaN);
+			bucketStatistic.getPercentile(Double.NaN);
 		} catch (IllegalArgumentException e) {
 			return;
 		}
 		fail("Parameter not a number");
 	}
-	
+
+	@Test
 	public void testToString() {
 		try {
-			//just invoke to check for exceptions
-			final IBucketStatistics gs = new BucketStatistics();
-			gs.toString();
-			
-			gs.addSample(0);
-			gs.addSample(0);
-			gs.addSample(0);
-			gs.addSample(1);
-			gs.addSample(1);
-			gs.addSample(2);
-			gs.addSample(2);
-			gs.addSample(2);
-			gs.addSample(2);
-			gs.addSample(3);
-			gs.toString();
+			// just invoke to check for exceptions
+			bucketStatistic.toString();
+
+			bucketStatistic.addSample(0);
+			bucketStatistic.addSample(0);
+			bucketStatistic.addSample(0);
+			bucketStatistic.addSample(1);
+			bucketStatistic.addSample(1);
+			bucketStatistic.addSample(2);
+			bucketStatistic.addSample(2);
+			bucketStatistic.addSample(2);
+			bucketStatistic.addSample(2);
+			bucketStatistic.addSample(3);
+			bucketStatistic.toString();
 		} catch (Exception e) {
 			fail(e.getMessage());
 		}
 	}
-	
-	public void testMaximum() {
-		final IBucketStatistics gs = new BucketStatistics("test title", 8);
-		gs.addSample(16);
-		gs.addSample(16);
-		gs.addSample(16);
-		assertEquals(8, gs.getMedian());
-		assertEquals(3, gs.getObservations());
-	}
 }
\ No newline at end of file
diff --git a/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java b/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..377150c6cb0200b152b83bc4ef581a53c0e2732c
--- /dev/null
+++ b/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINbucketStatistic IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.util.statistics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FixedSizedBucketStatisticsTest {
+
+	@SuppressWarnings("rawtypes")
+	@Parameterized.Parameters
+	public static Collection<Class> bucketStatistics() {
+		return Arrays.asList(
+				new Class[] { FixedSizedConcurrentBucketStatistics.class, FixedSizedBucketStatistics.class });
+	}
+
+	private final IBucketStatistics bucketStatistic;
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	public FixedSizedBucketStatisticsTest(Class bucketStatistic) throws InstantiationException, IllegalAccessException,
+			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+		this.bucketStatistic = (IBucketStatistics) bucketStatistic.getConstructor(String.class, int.class)
+				.newInstance("FixedSizedBucketStatisticsTest", 8);
+	}
+	
+	@Test
+	public void testMin() {
+		assertEquals(-1, bucketStatistic.getMin());
+
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		assertEquals(0, bucketStatistic.getMin());
+	}
+
+	@Test
+	public void testMin2() {
+		assertEquals(-1, bucketStatistic.getMin());
+
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		assertEquals(1, bucketStatistic.getMin());
+	}
+
+	@Test
+	public void testMax() {
+		assertEquals(-1, bucketStatistic.getMax());
+
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(0);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(1);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(2);
+		bucketStatistic.addSample(3);
+		assertEquals(3, bucketStatistic.getMax());
+	}
+
+	@Test
+	public void testInvalidArgument() {
+		try {
+			bucketStatistic.addSample(-1);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail();
+	}
+
+	// NaN test
+	@Test
+	public void testGetPercentileNaN() {
+		try {
+			bucketStatistic.getPercentile(Double.NaN);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail("Parameter not a number");
+	}
+
+	@Test
+	public void testMaximum() {
+		bucketStatistic.addSample(16);
+		bucketStatistic.addSample(16);
+		bucketStatistic.addSample(16);
+		assertEquals(7, bucketStatistic.getMax());
+		assertEquals(7, bucketStatistic.getMedian());
+		assertEquals(3, bucketStatistic.getObservations());
+	}
+}
\ No newline at end of file
diff --git a/tlatools/test/tlc2/value/ValueInputOutputStreamTest.java b/tlatools/test/tlc2/value/ValueInputOutputStreamTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e35e6d2126505e0a7bcf90507371ec991f287545
--- /dev/null
+++ b/tlatools/test/tlc2/value/ValueInputOutputStreamTest.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import tlc2.TLCGlobals;
+
+public class ValueInputOutputStreamTest {
+
+	@Test
+	public void testWriteShort() throws IOException {
+		final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteShort", ".vos");
+		tempFile.deleteOnExit();
+		
+		final ValueOutputStream out = new ValueOutputStream(tempFile);
+		out.writeShort(Short.MAX_VALUE);
+		out.writeShort(Short.MIN_VALUE);
+		out.writeShort((short) 0);
+		out.close();
+		
+		final ValueInputStream in = new ValueInputStream(tempFile);
+		assertEquals(Short.MAX_VALUE, in.readShort());
+		assertEquals(Short.MIN_VALUE, in.readShort());
+		assertEquals((short) 0, in.readShort());
+		in.close();
+	}
+
+	@Test
+	public void testWriteInt() throws IOException {
+		final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteInt", ".vos");
+		tempFile.deleteOnExit();
+		
+		final ValueOutputStream out = new ValueOutputStream(tempFile);
+		out.writeInt(Integer.MAX_VALUE);
+		out.writeInt(Integer.MIN_VALUE);
+		out.writeInt(0);
+		out.close();
+		
+		final ValueInputStream in = new ValueInputStream(tempFile);
+		assertEquals(Integer.MAX_VALUE, in.readInt());
+		assertEquals(Integer.MIN_VALUE, in.readInt());
+		assertEquals(0, in.readInt());
+		in.close();
+	}
+
+	@Test
+	public void testWriteShortNat() throws IOException {
+		final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteShortNat", ".vos");
+		tempFile.deleteOnExit();
+		
+		TLCGlobals.useGZIP = true;
+		final ValueOutputStream out = new ValueOutputStream(tempFile);
+		out.writeShortNat(Short.MAX_VALUE);
+		out.writeShortNat((short) 0);
+		out.close();
+		
+		// 20 byte header
+		// 2 byte Short.MAX_VALUE
+		// 1 byte 0
+		assertEquals(20 + 2 + 1, tempFile.length());
+		
+		final ValueInputStream in = new ValueInputStream(tempFile);
+		assertEquals(Short.MAX_VALUE, in.readShortNat());
+		assertEquals(0, in.readShortNat());
+		in.close();
+	}
+
+	@Test
+	public void testWriteNat() throws IOException {
+		final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteNat", ".vos");
+		tempFile.deleteOnExit();
+		
+		TLCGlobals.useGZIP = true;
+		final ValueOutputStream out = new ValueOutputStream(tempFile);
+		out.writeNat(Integer.MAX_VALUE);
+		out.writeNat(0);
+		out.close();
+		
+		// 20 byte header
+		// 4 byte Integer.MAX_VALUE
+		// 2 byte 0
+		assertEquals(20 + 4 + 2, tempFile.length());
+		
+		final ValueInputStream in = new ValueInputStream(tempFile);
+		assertEquals(Integer.MAX_VALUE, in.readNat());
+		assertEquals(0, in.readNat());
+		in.close();
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/EnumerableValueTest.java b/tlatools/test/tlc2/value/impl/EnumerableValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..38584cb63f9743eed6aea9c95cd2c57fbcd40934
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/EnumerableValueTest.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 20178 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ *   Ian Morris Nieves - added support for fingerprint stack trace
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+
+import tlc2.value.IValue;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.EnumerableValue.SubsetEnumerator;
+
+public class EnumerableValueTest {
+
+	@Test
+	public void test() {
+		RandomEnumerableValues.setSeed(15041980);
+		
+		final Set<Integer> indices = new HashSet<>();
+		// For the first n \in Nat+ up to 10657 show that:
+		for (int n = 1; n < 10657; n++) {
+			final SubsetEnumerator enumerator = (SubsetEnumerator) new DummyValue(n).elements(n);
+
+			while (enumerator.hasNext()) {
+				final int index = enumerator.nextIndex();
+				assertTrue("Index %s out of bounds.", 0 <= index && index < n);
+				indices.add(index);
+			}
+			assertEquals("Missing indices.", n, indices.size());
+			indices.clear();
+		}
+	}
+
+	@SuppressWarnings("serial")
+	class DummyValue extends EnumerableValue {
+
+		private final int size;
+
+		public DummyValue(int size) {
+			this.size = size;
+		}
+
+		@Override
+		public int size() {
+			return size;
+		}
+		
+		@Override
+		public ValueEnumeration elements(final int k) {
+			return new EnumerableValue.SubsetEnumerator(k) {
+				@Override
+				public Value nextElement() {
+					return null;
+				}
+			};
+		}
+
+		@Override
+		public ValueEnumeration elements() {
+			return null;
+		}
+
+		@Override
+		public byte getKind() {
+			return 0;
+		}
+
+		@Override
+		public int compareTo(Object val) {
+			return 0;
+		}
+
+		@Override
+		public boolean member(Value elem) {
+			return false;
+		}
+
+		@Override
+		public Value takeExcept(ValueExcept ex) {
+			return null;
+		}
+
+		@Override
+		public Value takeExcept(ValueExcept[] exs) {
+			return null;
+		}
+
+		@Override
+		public boolean isNormalized() {
+			return false;
+		}
+
+		@Override
+		public Value normalize() {
+			return this;
+		}
+
+		@Override
+		public boolean isFinite() {
+			return false;
+		}
+
+		@Override
+		public boolean isDefined() {
+			return false;
+		}
+
+		@Override
+		public IValue deepCopy() {
+			return null;
+		}
+
+		@Override
+		public boolean assignable(Value val) {
+			return false;
+		}
+
+		@Override
+		public StringBuffer toString(StringBuffer sb, int offset, boolean swallow) {
+			return toString(sb, offset, swallow);
+		}
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/InitializeValueTest.java b/tlatools/test/tlc2/value/impl/InitializeValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0628e479e70a1bb83039d5ff820dacd8dada1a0f
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/InitializeValueTest.java
@@ -0,0 +1,283 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import tlc2.util.FP64;
+import util.InternTable;
+import util.UniqueString;
+
+public class InitializeValueTest {
+
+	@BeforeClass
+	public static void setup() {
+		FP64.Init();
+	}
+	
+	/**
+	 * Test method for {@link tlc2.value.impl.UnionValue#deepNormalize()}.
+	 */
+	@Test
+	public void union() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(42)}, false));
+		vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(23)}, false));
+		vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(4711)}, false));
+		vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(1)}, false));
+		
+		final UnionValue uv = new UnionValue(new SetEnumValue(vec, false));
+		assertFalse(uv.isNormalized());
+		assertFalse(uv.set.isNormalized());
+		
+		uv.initialize();
+		
+		assertTrue(uv.set.isNormalized());
+		assertTrue(uv.isNormalized());
+	}
+
+	/**
+	 * Test method for {@link tlc2.value.impl.SetCapValue#deepNormalize()}.
+	 */
+	@Test
+	public void setcap() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		
+		SetCapValue scv = new SetCapValue(new SetEnumValue(vec, false), new SetEnumValue(vec, false));
+		assertFalse(scv.isNormalized());
+		assertFalse(scv.set1.isNormalized());
+		assertFalse(scv.set2.isNormalized());
+		
+		scv.initialize();
+		
+		assertTrue(scv.set1.isNormalized());
+		assertTrue(scv.set2.isNormalized());
+		assertTrue(scv.isNormalized());
+	}
+
+	@Test
+	public void setcup() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		
+		SetCupValue scv = new SetCupValue(new SetEnumValue(vec, false), new SetEnumValue(vec, false));
+		assertFalse(scv.isNormalized());
+		assertFalse(scv.set1.isNormalized());
+		assertFalse(scv.set2.isNormalized());
+		
+		scv.initialize();
+		
+		assertTrue(scv.set1.isNormalized());
+		assertTrue(scv.set2.isNormalized());
+		assertTrue(scv.isNormalized());
+	}
+
+	@Test
+	public void setdiff() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		
+		SetDiffValue sdv = new SetDiffValue(new SetEnumValue(vec, false), new SetEnumValue(vec, false));
+		assertFalse(sdv.isNormalized());
+		assertFalse(sdv.set1.isNormalized());
+		assertFalse(sdv.set2.isNormalized());
+		
+		sdv.initialize();
+		
+		assertTrue(sdv.isNormalized());
+		assertTrue(sdv.set1.isNormalized());
+		assertTrue(sdv.set2.isNormalized());
+	}
+
+	@Test
+	public void subset() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		
+		SubsetValue sub = new SubsetValue(new SetEnumValue(vec, false));
+		assertFalse(sub.isNormalized());
+		assertFalse(sub.set.isNormalized());
+		
+		sub.initialize();
+		
+		assertTrue(sub.set.isNormalized());
+		assertTrue(sub.isNormalized());
+	}
+
+	@Test
+	public void record() {
+		final InternTable internTable = new InternTable(2);
+		final UniqueString a = internTable.put("a");
+		final UniqueString b = internTable.put("b");
+		
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		final Value aVal = new SetEnumValue(vec, false);
+		final Value bVal = new SetEnumValue(vec, false);
+		
+		final RecordValue rcdv = new RecordValue(new UniqueString[] {b, a}, new Value[] {bVal, aVal}, false);
+
+		assertFalse(rcdv.isNormalized());
+		for (Value v : rcdv.values) {
+			assertFalse(v.isNormalized());
+		}
+		
+		rcdv.initialize();
+		
+		for (Value v : rcdv.values) {
+			assertTrue(v.isNormalized());
+		}
+		assertTrue(rcdv.isNormalized());
+	}
+
+	@Test
+	public void fcnrecord() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		final Value aVal = new SetEnumValue(vec, false);
+		final Value bVal = new SetEnumValue(vec, false);
+
+		final FcnRcdValue rcdv = new FcnRcdValue(new Value[] { new StringValue("B"), new StringValue("A") },
+				new Value[] { bVal, aVal }, false);
+
+		assertFalse(rcdv.isNormalized());
+		for (Value v : rcdv.values) {
+			assertFalse(v.isNormalized());
+		}
+		
+		rcdv.initialize();
+		
+		for (Value v : rcdv.values) {
+			assertTrue(v.isNormalized());
+		}
+		assertTrue(rcdv.isNormalized());
+	}
+	
+	@Test
+	public void tuple() {
+		final ValueVec vec = new ValueVec();
+		vec.addElement(IntValue.gen(42));
+		vec.addElement(IntValue.gen(23));
+		vec.addElement(IntValue.gen(4711));
+		vec.addElement(IntValue.gen(1));
+		final Value aVal = new SetEnumValue(vec, false);
+		
+		final TupleValue tuple = new TupleValue(aVal);
+
+//		for (Value v : tuple.elems) {
+//			assertFalse(v.isNormalized());
+//		}
+//		assertFalse(tuple.isNormalized());
+
+		tuple.initialize();
+		
+		assertTrue(tuple.isNormalized());
+		for (Value v : tuple.elems) {
+			assertTrue(v.isNormalized());
+		}
+	}
+	
+	@Test
+	public void setOfTuple() {
+		final IntervalValue intVal = new IntervalValue(1, 2);
+		final SetOfTuplesValue inner = new SetOfTuplesValue(intVal, intVal);
+		final SetOfTuplesValue tuples = new SetOfTuplesValue(inner, inner);
+		
+//		for (Value v : tuples.sets) {
+//			assertFalse(v.isNormalized());
+//		}
+//		assertFalse(tuples.isNormalized());
+		
+		tuples.initialize();
+		
+		assertTrue(tuples.isNormalized());
+		for (Value v : tuples.sets) {
+			assertTrue(v.isNormalized());
+		}
+	}
+	
+	@Test
+	public void setOfRcds() {
+
+		final Value[] values = new Value[3];
+		values[0] = new SetEnumValue(getValue(7, "a"), true);
+		values[1] = new IntervalValue(1, 2);
+		values[2] = new IntervalValue(1, 4);
+
+		final SetOfRcdsValue setOfRcrds= new SetOfRcdsValue(getNames(3), values, true);
+
+//		for (Value v : setOfRcrds.values) {
+//			assertFalse(v.isNormalized());
+//		}
+//		assertFalse(setOfRcrds.isNormalized());
+
+		setOfRcrds.initialize();
+
+		assertTrue(setOfRcrds.isNormalized());
+		for (Value v : setOfRcrds.values) {
+			assertTrue(v.isNormalized());
+		}
+	}
+
+	private static final Value[] getValue(final int n, String str) {
+		final Value[] values = new Value[n];
+		for (int i = 0; i < n; i++) {
+			values[i] = new StringValue(str + i);
+		}
+		return values;
+	}
+
+	private static final UniqueString[] getNames(final int n) {
+		final UniqueString[] names = new UniqueString[n];
+		for (int i = 0; i < names.length; i++) {
+			names[i] = UniqueString.uniqueStringOf("N" + i);
+		}
+		return names;
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/IntervalValueTest.java b/tlatools/test/tlc2/value/impl/IntervalValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..759f3220b2a361bdc69f239a594c6690712496fc
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/IntervalValueTest.java
@@ -0,0 +1,114 @@
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import util.Assert.TLCRuntimeException;
+
+public class IntervalValueTest {
+
+	@Test
+	public void testElementAt() {
+		final IntervalValue iv = new IntervalValue(3, 11);
+		for (int i = 0; i < iv.size(); i++) {
+			assertEquals(IntValue.gen(i + 3), iv.elementAt(i));
+		}
+	}
+
+	@Test
+	public void testElementAtOutOfBoundsNegative() {
+		final IntervalValue iv = new IntervalValue(3, 11);
+		try {
+			iv.elementAt(-1);
+		} catch (TLCRuntimeException e) {
+			return;
+		}
+		fail();
+	}
+
+	@Test
+	public void testElementAtOutOfBoundsSize() {
+		final IntervalValue iv = new IntervalValue(3, 11);
+		try {
+			iv.elementAt(iv.size());
+		} catch (TLCRuntimeException e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void sizeOverflow() {
+		assertEquals(Integer.MAX_VALUE, new IntervalValue(1, Integer.MAX_VALUE).size());
+		assertEquals(Integer.MAX_VALUE, new IntervalValue(Integer.MIN_VALUE, -2).size());
+		
+		try {
+			assertEquals(0, new IntervalValue(-989_822_976, 1_157_660_672).size());
+		} catch (TLCRuntimeException e) {
+			assertTrue(e.getMessage().contains("Size of interval value exceeds the maximum representable size (32bits)"));
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void compareToOverflow1() {
+		final IntervalValue iv = new IntervalValue(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+		assertEquals(2, iv.size());
+		
+		final IntervalValue iv2 = new IntervalValue(Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2);
+		assertEquals(2, iv2.size());
+		
+		assertEquals(1, iv.compareTo(iv2));
+	}
+
+	@Test
+	public void testCompareExtremeIntervals() {
+		final IntervalValue x = new IntervalValue(Integer.MIN_VALUE, -2);
+		final IntervalValue y = new IntervalValue(1, Integer.MAX_VALUE);
+		assertEquals(x.size(), y.size());
+		assertTrue(x.compareTo(y) < 0);
+		assertTrue(y.compareTo(x) > 0);
+	}
+
+	@Test
+	public void testEmptyIntervalEquality() {
+		IntervalValue i1 = new IntervalValue(10, 1);
+		IntervalValue i2 = new IntervalValue(20, 15);
+		assertEquals(i1, i2);
+	}
+
+	@Test
+	public void testCompareEmptyIntervals() {
+		int cmp = new IntervalValue(10, 1).compareTo(new IntervalValue(20, 15));
+		assertEquals(0, cmp);
+	}
+
+	@Test
+	public void testSizeOfMaximumRepresentableInterval() {
+		final IntervalValue iv = new IntervalValue(Integer.MIN_VALUE, Integer.MAX_VALUE);
+		try {
+			iv.size();
+		} catch (TLCRuntimeException e) {
+			assertTrue(e.getMessage().contains("Size of interval value exceeds the maximum representable size (32bits)"));
+			return;
+		}
+		fail();
+	}
+
+	@Test
+	public void testExtremeIntervalSize() {
+		final IntervalValue iv1 = new IntervalValue(Integer.MAX_VALUE, Integer.MAX_VALUE);
+		assertEquals(1, iv1.size());
+		final IntervalValue iv2 = new IntervalValue(Integer.MIN_VALUE, Integer.MIN_VALUE);
+		assertEquals(1, iv2.size());
+		final IntervalValue iv3 = new IntervalValue(Integer.MAX_VALUE-10, Integer.MAX_VALUE);
+		assertEquals(11, iv3.size());
+		final IntervalValue iv4 = new IntervalValue(Integer.MIN_VALUE, Integer.MIN_VALUE+10);
+		assertEquals(11, iv4.size());
+	}
+
+}
diff --git a/tlatools/test/tlc2/value/impl/RecordValueTest.java b/tlatools/test/tlc2/value/impl/RecordValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..694acb43ad73a7b3bdfd3af480d11c1de4cced96
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/RecordValueTest.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import util.InternTable;
+import util.UniqueString;
+
+public class RecordValueTest {
+
+	/**
+	 * Test method for {@link tlc2.value.impl.RecordValue#deepCopy()}.
+	 * 
+	 * This test reveals a bug in the implementation of RecordValue#deepCopy()
+	 * when the original instance from which the deep copy has been created is
+	 * normalized afterwards and the normalization incorrectly ripples through
+	 * to the copy.
+	 */
+	@Test
+	public void testDeepCopy() {
+		final InternTable internTable = new InternTable(2);
+		final UniqueString a = internTable.put("a");
+		final UniqueString b = internTable.put("b");
+		
+		final Value aVal = new StringValue("aVal");
+		final Value bVal = new StringValue("bVal");
+		
+		// Create the source to create a deep copy of
+		final RecordValue orig = new RecordValue(new UniqueString[] {b, a}, new Value[] {bVal, aVal}, false);
+		
+		// Verify the mappings in RecordValue are correct
+		assertTrue(orig.names[0].equals(b));
+		assertTrue(orig.names[1].equals(a));
+		assertTrue(orig.values[0].equals(bVal));
+		assertTrue(orig.values[1].equals(aVal));
+		
+		// Make a deep copy of te origina record value
+		final RecordValue deepCopy = (RecordValue) orig.deepCopy();
+		
+		// Normalize the original record value and check its mappings have been
+		// re-organized
+		orig.deepNormalize();
+		assertTrue(orig.names[0].equals(a));
+		assertTrue(orig.names[1].equals(b));
+		assertTrue(orig.values[0].equals(aVal));
+		assertTrue(orig.values[1].equals(bVal));
+		
+		// Check that the mappings in the deep copy didn't change.
+		assertTrue(deepCopy.names[0].equals(b));
+		assertTrue(deepCopy.names[1].equals(a));
+		assertTrue(deepCopy.values[0].equals(bVal));
+		assertTrue(deepCopy.values[1].equals(aVal));
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/SetOfFcnsValueTest.java b/tlatools/test/tlc2/value/impl/SetOfFcnsValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..98d73b1e61fc4ca8675f3a59ce73044da28e3b2a
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/SetOfFcnsValueTest.java
@@ -0,0 +1,392 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+
+import org.junit.Test;
+
+import tlc2.util.FP64;
+import tlc2.value.impl.SetOfFcnsValue.SubsetEnumerator;
+import util.Assert.TLCRuntimeException;
+
+public class SetOfFcnsValueTest {
+
+	private static final Value[] getValue(String... strs) {
+		final Value[] values = new Value[strs.length];
+		for (int i = 0; i < strs.length; i++) {
+			values[i] = new StringValue(strs[i]);
+		}
+		return values;
+	}
+
+	@Test
+	public void testRangeSubsetValue() {
+		final Value[] values = new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") };
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(new SetEnumValue(values, true),
+				new SubsetValue(new SetEnumValue(
+						new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true)));
+
+		assertEquals(512, setOfFcnsValue.size());
+
+		final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(512);
+		final SetEnumValue emptyset = new SetEnumValue();
+		int i = 0;
+		assertEquals(new FcnRcdValue(values, new Value[] { emptyset, emptyset, emptyset }, true),
+				enumerator.elementAt(i++));
+		assertEquals(
+				new FcnRcdValue(values,
+						new Value[] { emptyset, emptyset,
+								new SetEnumValue(new Value[] { new StringValue("a") }, true) },
+						true),
+				enumerator.elementAt(i++));
+		assertEquals(
+				new FcnRcdValue(values,
+						new Value[] { emptyset, emptyset,
+								new SetEnumValue(new Value[] { new StringValue("b") }, true) },
+						true),
+				enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(values,
+				new Value[] { emptyset, emptyset,
+						new SetEnumValue(new Value[] { new StringValue("c") }, true) },
+				true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(values,
+				new Value[] { emptyset, emptyset,
+						new SetEnumValue(new Value[] { new StringValue("a"), new StringValue("b") }, true) },
+				true), enumerator.elementAt(i++));
+
+		// Last element
+		final SetEnumValue setEnumValue = new SetEnumValue(
+				new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true);
+		assertEquals(new FcnRcdValue(values, new Value[] { setEnumValue, setEnumValue, setEnumValue }, true),
+				enumerator.elementAt(511));
+	}
+	
+	@Test
+	public void testDomainEmpty() {
+		final SetEnumValue domain = new SetEnumValue();
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain,
+				new SetEnumValue(getValue("a", "b", "c"), true));
+		
+		// Non-subset behavior is our prototype.
+		assertEquals(1, setOfFcnsValue.size());
+		final ValueEnumeration elements = setOfFcnsValue.elements();
+		assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), elements.nextElement());
+		assertNull(elements.nextElement());
+		
+		// Subset behaves similar.		
+		final Enumerable subset = setOfFcnsValue.getRandomSubset(5);
+		final ValueEnumeration subsetElements = subset.elements();
+		assertEquals(1, subset.size());
+		assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), subsetElements.nextElement());
+		assertNull(subsetElements.nextElement());
+	}
+
+	@Test
+	public void testRangeEmpty() {
+		final IntervalValue domain = new IntervalValue(1, 2);
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, new SetEnumValue(new ValueVec(), true));
+		
+		// Non-subset behavior is our prototype.
+		assertEquals(0, setOfFcnsValue.size());
+		assertNull(setOfFcnsValue.elements().nextElement());
+		
+		// Subset behaves similar.		
+		final Enumerable subset = setOfFcnsValue.getRandomSubset(5);
+		assertEquals(0, subset.size());
+		assertNull(subset.elements().nextElement());
+		assertEquals(new SetEnumValue(), subset);
+	}
+	
+	@Test
+	public void testDomainAndRangeEmpty() {
+		final SetEnumValue domain = new SetEnumValue();
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, new SetEnumValue());
+		
+		// Non-subset behavior is our prototype.
+		assertEquals(1, setOfFcnsValue.size());
+		final ValueEnumeration elements = setOfFcnsValue.elements();
+		assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), elements.nextElement());
+		assertNull(elements.nextElement());
+		
+		// Subset behaves similar.		
+		final Enumerable subset = setOfFcnsValue.getRandomSubset(5);
+		final ValueEnumeration subsetElements = subset.elements();
+		assertEquals(1, subset.size());
+		assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), subsetElements.nextElement());
+		assertNull(subsetElements.nextElement());
+	}
+	
+	@Test
+	public void testRandomSubsetAndValueEnumerator() {
+		final Value[] domain = new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") };
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(new SetEnumValue(domain, true),
+				new SetEnumValue(getValue("a", "b", "c"), true));
+
+		assertEquals(27, setOfFcnsValue.size());
+
+		FP64.Init();
+		final Set<FcnRcdValue> enumeratorValues = new HashSet<>(27);
+		
+		final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(27);
+		for (int i = 0; i < setOfFcnsValue.size(); i++) {
+			FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i);
+			assertEquals(3, rcd.domain.length);
+			assertEquals(3, rcd.values.length);
+			enumeratorValues.add(rcd);
+		}
+
+		final Enumerable randomSubset = setOfFcnsValue.getRandomSubset(27);
+		final Set<FcnRcdValue> randomsubsetValues = new HashSet<>(27);
+		
+		final ValueEnumeration enumerator2 = randomSubset.elements();
+		FcnRcdValue rcd;
+		while ((rcd = (FcnRcdValue) enumerator2.nextElement()) != null) {
+			assertEquals(3, rcd.domain.length);
+			assertEquals(3, rcd.values.length);
+			randomsubsetValues.add(rcd);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfFcnsValue.member(rcd));
+		}
+
+		assertEquals(enumeratorValues.size(), randomsubsetValues.size());
+		assertEquals(enumeratorValues, randomsubsetValues);
+	}
+	
+	@Test
+	public void testDomainModelValue() {
+		final Value[] domain = new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") };
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(new SetEnumValue(domain, true),
+				new SetEnumValue(getValue("a", "b", "c"), true));
+
+		assertEquals(27, setOfFcnsValue.size());
+
+		FP64.Init();
+		final Set<FcnRcdValue> enumeratorValues = new HashSet<>(27);
+
+		final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(27);
+		for (int i = 0; i < setOfFcnsValue.size(); i++) {
+			FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i);
+			assertEquals(3, rcd.domain.length);
+			assertEquals(3, rcd.values.length);
+			enumeratorValues.add(rcd);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfFcnsValue.member(rcd));
+		}
+		assertEquals(27, enumeratorValues.size());
+		
+		int i = 0;
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b", "c"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c", "a"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c", "b"), true), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c", "c"), true), enumerator.elementAt(i++));
+	}
+
+	@Test
+	public void testDomainIntervalRangeSetEnumValueSize9() {
+		final IntervalValue domain = new IntervalValue(1, 2);
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain,
+				new SetEnumValue(getValue("a", "b", "c"), true));
+
+		assertEquals(9, setOfFcnsValue.size());
+
+		final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(9);
+		for (int i = 0; i < setOfFcnsValue.size(); i++) {
+			FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i);
+			assertEquals(2, rcd.domain.length);
+			assertEquals(2, rcd.values.length);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfFcnsValue.member(rcd));
+		}
+
+		int i = 0;
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c")), enumerator.elementAt(i++));
+	}
+
+	@Test
+	public void testDomainIntervalRangeSetEnumValueSize27() {
+		final IntervalValue domain = new IntervalValue(1, 3);
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain,
+				new SetEnumValue(getValue("a", "b", "c"), true));
+
+		assertEquals(27, setOfFcnsValue.size());
+
+		final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(27);
+		for (int i = 0; i < setOfFcnsValue.size(); i++) {
+			FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i);
+			assertEquals(3, rcd.domain.length);
+			assertEquals(3, rcd.values.length);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfFcnsValue.member(rcd));
+		}
+
+		int i = 0;
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "b", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "c", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "a", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "b", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("b", "c", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "a", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "b", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("c", "c", "c")), enumerator.elementAt(i++));
+	}
+
+	@Test
+	public void testDomainIntervalRangeSetEnumValueSize256() {
+		final IntervalValue domain = new IntervalValue(1, 4);
+		final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain,
+				new SetEnumValue(getValue("a", "b", "c", "d"), true));
+
+		assertEquals(256, setOfFcnsValue.size());
+
+		final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(256);
+		for (int i = 0; i < setOfFcnsValue.size(); i++) {
+			FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i);
+			assertEquals(4, rcd.domain.length);
+			assertEquals(4, rcd.values.length);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfFcnsValue.member(rcd));
+		}
+
+		int i = 0;
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "a")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "b")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "c")), enumerator.elementAt(i++));
+		assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "d")), enumerator.elementAt(i++));
+		// ... (to lazy to type them out)
+		assertEquals(new FcnRcdValue(domain, getValue("d", "d", "d", "b")), enumerator.elementAt(253));
+		assertEquals(new FcnRcdValue(domain, getValue("d", "d", "d", "c")), enumerator.elementAt(254));
+		assertEquals(new FcnRcdValue(domain, getValue("d", "d", "d", "d")), enumerator.elementAt(255));
+	}
+
+	@Test
+	public void testRandomSubsetFromReallyLarge() {
+		final List<SetOfFcnsValue> l = new ArrayList<>();
+
+		l.add(new SetOfFcnsValue(new IntervalValue(1, 11),
+				new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), true)));
+		l.add(new SetOfFcnsValue(new IntervalValue(1, 44), new IntervalValue(1, 20)));
+		l.add(new SetOfFcnsValue(new IntervalValue(1, 121), new IntervalValue(1, 19)));
+		l.add(new SetOfFcnsValue(new IntervalValue(1, 321), new IntervalValue(1, 29)));
+		
+		l.forEach(new Consumer<SetOfFcnsValue>() {
+			@Override
+			public void accept(final SetOfFcnsValue sofv) {
+				try {
+					sofv.size();
+				} catch (TLCRuntimeException tre) {
+					// OK, set is huge for size to reject it. Next get a tiny subset of it.
+
+					IntStream.of(0, 1, 2, 799, 1024, 8932, 16933/*, 109031*/).forEach(new IntConsumer() { // 109031 causes the test to take a little long to be included in the overall test suite.
+						@Override
+						public void accept(int kOutOfN) {
+							final Enumerable randomSubset = sofv.getRandomSubset(kOutOfN);
+							
+							// Check expected amount of elements.
+							assertEquals(kOutOfN, randomSubset.size());
+
+							// Check for no duplicates.
+							FP64.Init();
+							final Set<FcnRcdValue> set = new HashSet<>(kOutOfN);
+
+							final ValueEnumeration elements = randomSubset.elements();
+							FcnRcdValue rcd;
+							while ((rcd = (FcnRcdValue) elements.nextElement()) != null) {
+								// Check element is in the original SetOfFcnsValue.
+								assertTrue(sofv.member(rcd));
+								set.add(rcd);
+							}
+							assertEquals(kOutOfN, set.size());
+						}
+					});
+					return;
+				}
+				fail();
+			}
+		});
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/SetOfRcrdValueTest.java b/tlatools/test/tlc2/value/impl/SetOfRcrdValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b1200dfbd06d41b7ac0a93cc44f534e21e9c39e
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/SetOfRcrdValueTest.java
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import tlc2.util.FP64;
+import tlc2.value.impl.SetOfRcdsValue.SubsetEnumerator;
+import util.UniqueString;
+
+public class SetOfRcrdValueTest {
+
+	private static final int a = 97;
+
+	private static final Value[] getValue(final int n, String str) {
+		final Value[] values = new Value[n];
+		for (int i = 0; i < n; i++) {
+			values[i] = new StringValue(str + i);
+		}
+		return values;
+	}
+
+	private static final Value[] getValue(int n, String... strs) {
+		final Value[] values = new Value[strs.length];
+		for (int i = 0; i < values.length; i++) {
+			values[i] = new SetEnumValue(getValue(n, strs[i]), false);
+		}
+		return values;
+	}
+
+	private static final Value[] getValue(int n, UniqueString[] names) {
+		// a,b,c,d,e,...
+		return getValue(n, IntStream.range(a, a + names.length).mapToObj(ascii -> Character.toString((char) ascii))
+				.toArray(String[]::new));
+	}
+
+	private static final UniqueString[] getNames(final int n) {
+		final UniqueString[] names = new UniqueString[n];
+		for (int i = 0; i < names.length; i++) {
+			names[i] = UniqueString.uniqueStringOf("N" + i);
+		}
+		return names;
+	}
+
+	@BeforeClass
+	public static void setup() {
+		FP64.Init();
+	}
+
+	@Test
+	public void testSimple() {
+		final UniqueString[] names = getNames(3);
+		
+		final Value[] values = new Value[3];
+		values[0] = new SetEnumValue(getValue(7, "a"), true);
+		values[1] = new IntervalValue(1, 2);
+		values[2] = new IntervalValue(1, 4);
+		
+		final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, values, true);
+
+		checkElements(names, values, setOfRcrdValue, (SubsetEnumerator) setOfRcrdValue.elements(setOfRcrdValue.size()));
+	}
+
+	private static void checkElements(final UniqueString[] names, final Value[] values, final SetOfRcdsValue set,
+			final SubsetEnumerator rcds) {
+		for (int i = 0; i < set.size(); i++) {
+			// Check names are stable.
+			final RecordValue rcd = rcds.elementAt(i);
+			assertArrayEquals(names, rcd.names);
+			// Check values are from correct range.
+			final Value[] rcdValues = rcd.values;
+			for (int j = 0; j < rcdValues.length; j++) {
+				assertTrue(values[j].member(rcdValues[j]));
+			}
+		}
+	}
+	
+	@Test
+	public void testRangeSubsetValue() {
+		final UniqueString[] names = getNames(4);
+		final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(2, names), true);
+
+		final Set<Value> actual = new HashSet<>(setOfRcrdValue.elements(setOfRcrdValue.size()).all());
+		assertEquals(setOfRcrdValue.size(), actual.size());
+
+		final Set<Value> expected = new HashSet<>(setOfRcrdValue.elements().all());
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	public void testRandomSubset() {
+		final UniqueString[] names = getNames(4);
+		final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(3, names), true);
+
+		final int size = 27;
+		final Enumerable randomSubset = setOfRcrdValue.getRandomSubset(size);
+		final Set<RecordValue> randomsubsetValues = new HashSet<>(size);
+
+		final ValueEnumeration enumerator = randomSubset.elements();
+		RecordValue rcd;
+		while ((rcd = (RecordValue) enumerator.nextElement()) != null) {
+			assertEquals(names.length, rcd.names.length);
+			assertEquals(names.length, rcd.values.length);
+			randomsubsetValues.add(rcd);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfRcrdValue.member(rcd));
+		}
+
+		assertEquals(size, randomsubsetValues.size());
+	}
+
+	@Test
+	public void testRandomSubsetVaryingParameters() {
+		for (int n = 1; n < 7; n++) {
+			final UniqueString[] names = getNames(n);
+			for (int m = 1; m < 5; m++) {
+				final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(m, names), true);
+				final Set<RecordValue> randomsubsetValues = new HashSet<>();
+				for (int kOutOfN = 0; kOutOfN < setOfRcrdValue.size(); kOutOfN++) {
+					final Enumerable randomSubset = setOfRcrdValue.getRandomSubset(kOutOfN);
+
+					final ValueEnumeration enumerator = randomSubset.elements();
+					RecordValue rcd;
+					while ((rcd = (RecordValue) enumerator.nextElement()) != null) {
+						assertEquals(names.length, rcd.names.length);
+						assertEquals(names.length, rcd.values.length);
+						randomsubsetValues.add(rcd);
+						// Check element is in the original SetOfFcnsValue.
+						assertTrue(setOfRcrdValue.member(rcd));
+					}
+
+					assertEquals(kOutOfN, randomsubsetValues.size());
+					randomsubsetValues.clear();
+				}
+			}
+		}
+	}
+
+	@Test
+	public void testRandomSubsetAstronomically() {
+		//	RandomSubset(10000, [a1 : {k : k \in 1..50}, a2 : {k : k \in 1..50}, a3 : {k : k \in 1..50},
+		//                       a4 : {k : k \in 1..50}, a5 : {k : k \in 1..50}, a6 : {k : k \in 1..50},
+		//                       a7 : {k : k \in 1..50}, a8 : {k : k \in 1..50}, a9 : {k : k \in 1..50},
+		//                      a10 : {k : k \in 1..50}])
+		final UniqueString[] names = getNames(10);
+		final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(50, names), true);
+
+		final int k = 10000;
+		final Enumerable randomSubset = setOfRcrdValue.getRandomSubset(k);
+		final Set<RecordValue> randomsubsetValues = new HashSet<>(k);
+
+		final ValueEnumeration enumerator = randomSubset.elements();
+		RecordValue rcd;
+		while ((rcd = (RecordValue) enumerator.nextElement()) != null) {
+			assertEquals(names.length, rcd.names.length);
+			assertEquals(names.length, rcd.values.length);
+			randomsubsetValues.add(rcd);
+			// Check element is in the original SetOfFcnsValue.
+			assertTrue(setOfRcrdValue.member(rcd));
+		}
+
+		assertEquals(k, randomsubsetValues.size());
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/SetOfTuplesValueTest.java b/tlatools/test/tlc2/value/impl/SetOfTuplesValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab56fd81fc6b92bc80db91da0d344944af77c970
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/SetOfTuplesValueTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import tlc2.TLCGlobals;
+import tlc2.value.impl.IntervalValue;
+import tlc2.value.impl.SetOfTuplesValue;
+
+public class SetOfTuplesValueTest {
+	
+	@Test
+	public void testToStringLazy() {
+		// Force toString representation to be lazy.
+		TLCGlobals.enumBound = 1;
+		
+		final IntervalValue intVal = new IntervalValue(1, 2);
+		final SetOfTuplesValue inner = new SetOfTuplesValue(intVal, intVal);
+		final SetOfTuplesValue outter = new SetOfTuplesValue(inner, inner);
+		assertTrue(outter.toString().contains("\\X"));
+		assertEquals("((1..2 \\X 1..2) \\X (1..2 \\X 1..2))", outter.toString());
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/SubsetEnumeratorTest.java b/tlatools/test/tlc2/value/impl/SubsetEnumeratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..187ef6256c4d6bdfb26a6b508b0106ad1a45f2b2
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/SubsetEnumeratorTest.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ *   Ian Morris Nieves - added support for fingerprint stack trace
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.stream.DoubleStream;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import tlc2.util.FP64;
+
+@RunWith(Parameterized.class)
+public class SubsetEnumeratorTest {
+
+	private static final Value[] getValue(String... strs) {
+		final List<Value> values = new ArrayList<>(strs.length);
+		for (int i = 0; i < strs.length; i++) {
+			values.add(new StringValue(strs[i]));
+		}
+		return values.toArray(new Value[values.size()]);
+	}
+
+	@Parameters
+	public static List<Enumerable> getEnumerable() {
+		final List<Enumerable> params = new ArrayList<Enumerable>();
+		
+		// IntervalValue
+		params.add(new IntervalValue(1, 10));
+		
+		// SetEnumValue
+		final ValueVec vec = new ValueVec();
+		final String input = "ABCDEFGHIJ";
+		input.chars().forEach(new IntConsumer() {
+			@Override
+			public void accept(final int value) {
+				vec.addElement(ModelValue.make(String.valueOf(value)));
+			}
+		});
+		params.add(new SetEnumValue(vec, false));
+		
+		// SetOfTuplesValue
+		params.add(new SetOfTuplesValue(new IntervalValue(1, 5), new IntervalValue(1, 5)));
+		params.add(new SetOfTuplesValue(new SetEnumValue(), new SetEnumValue())); // empty
+		
+		// UnionValue
+		params.add(new UnionValue(
+				new SetEnumValue(new Value[] { new IntervalValue(1, 5), new IntervalValue(5, 11) }, true)));
+		params.add(new UnionValue(new SetEnumValue())); // empty
+		
+		// SetOfFcnsValue
+		params.add(new SetOfFcnsValue(new IntervalValue(2, 5),
+				new SetEnumValue(new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true)));
+		params.add(new SetOfFcnsValue(new IntervalValue(3, 5), new SetEnumValue())); // empty range
+
+		// SetOfFcnsValue with SubsetValue as range.
+		params.add(new SetOfFcnsValue(
+				new SetEnumValue(new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") },
+						true),
+				new SubsetValue(new SetEnumValue(
+						new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true))));
+
+		// SetOfFcnsValue
+		final SetEnumValue domain = new SetEnumValue(getValue("A1", "A2", "A3"), true);
+		final SetEnumValue range = new SetEnumValue(getValue("v1", "v2", "v3"), true);
+		params.add(new SetOfFcnsValue(domain, range));
+
+		// SubsetValue
+		params.add(new SubsetValue(new SetEnumValue(
+				new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true)));
+		params.add(new SubsetValue(new SetEnumValue())); // empty
+		
+		// Adding values to Set<Value> requires fingerprinting.
+		FP64.Init();
+
+		return params;
+	}
+	
+	private final Enumerable enumerable;
+
+	public SubsetEnumeratorTest(final Enumerable enumerable) {
+		this.enumerable = enumerable;
+	}
+
+	@Test
+	public void testElementsInt() {
+		// for various fractions...
+		DoubleStream.of(0, .1, .2, .3, .4, .55, .625, .775, .8, .9, 1).forEach(new DoubleConsumer() {
+			@Override
+			public void accept(double fraction) {
+				final int k = (int) Math.ceil(enumerable.size() * fraction);
+				final List<Value> values = enumerable.elements(k).all();
+				
+				// Expected size.
+				Assert.assertEquals(String.format("Failed for fraction: %s", fraction), k, values.size());
+
+				// Unique values.
+				Assert.assertEquals(String.format("Failed for fraction: %s", fraction), values.size(),
+						new HashSet<Value>(values).size());
+
+				// Each value is actually a member of enumerable.
+				for (Value v : values) {
+					Assert.assertTrue(String.format("Failed for fraction: %s", fraction), enumerable.member(v));
+				}
+			}
+		});
+	}
+	
+	@Test
+	public void testGetRandomSubset() {
+		DoubleStream.of(0, .1, .2, .3, .4, .55, .625, .775, .8, .9, 1).forEach(new DoubleConsumer() {
+			@Override
+			public void accept(final double fraction) {
+				final int k = (int) Math.ceil(enumerable.size() * fraction);
+				
+				final Enumerable enumValue = enumerable.getRandomSubset(k);
+				
+				// Expected size.
+				assertEquals(String.format("Failed for fraction: %s", fraction), k, enumValue.size());
+
+				final Set<Value> values = new HashSet<>(enumValue.size());
+				
+				// Each value is actually a member of enumerable.
+				ValueEnumeration elements = enumValue.elements();
+				Value v = null;
+				while ((v = elements.nextElement()) != null) {
+					Assert.assertTrue(String.format("Failed for fraction: %s", fraction), enumerable.member(v));
+					values.add(v);
+				}
+
+				// Unique values.
+				Assert.assertEquals(String.format("Failed for fraction: %s", fraction), enumValue.size(),
+						new HashSet<Value>(values).size());
+				
+			}
+		});
+	}
+}
diff --git a/tlatools/test/tlc2/value/impl/SubsetValueTest.java b/tlatools/test/tlc2/value/impl/SubsetValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6270858465f43a5dd32ee7ac2d4f1c5ff672e0ed
--- /dev/null
+++ b/tlatools/test/tlc2/value/impl/SubsetValueTest.java
@@ -0,0 +1,552 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved.
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package tlc2.value.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import tlc2.util.FP64;
+import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.SubsetValue.CoinTossingSubsetEnumerator;
+import tlc2.value.impl.SubsetValue.KElementEnumerator;
+import tlc2.value.impl.SubsetValue.SubsetEnumerator;
+import tlc2.value.impl.SubsetValue.Unrank;
+import util.Assert;
+
+public class SubsetValueTest {
+
+	private static final Value[] getValue(String... strs) {
+		final List<Value> values = new ArrayList<>(strs.length);
+		for (int i = 0; i < strs.length; i++) {
+			values.add(new StringValue(strs[i]));
+		}
+		Collections.shuffle(values);
+		return values.toArray(new Value[values.size()]);
+	}
+
+	@BeforeClass
+	public static void setup() {
+		// Make test repeatable by setting random seed always to same value. 
+		RandomEnumerableValues.setSeed(15041980L);
+		// Needed to insert elements into java.util.Set (because of hashcode) later to
+		// detect duplicates.
+		FP64.Init();
+	}
+
+	private void doTest(final int expectedSize, final EnumerableValue innerSet) {
+		doTest(expectedSize, innerSet, expectedSize);
+	}
+
+	private void doTest(final int expectedSize, final EnumerableValue innerSet,
+			int expectedElements) {
+		final SubsetValue subsetValue = new SubsetValue(innerSet);
+		assertEquals(expectedSize, subsetValue.size());
+
+		final Set<Value> s = new TreeSet<>(new Comparator<Value>() {
+			@Override
+			public int compare(Value o1, Value o2) {
+				// o1.normalize();
+				// ((SetEnumValue) o1).elems.sort(true);
+				//
+				// o2.normalize();
+				// ((SetEnumValue) o2).elems.sort(true);
+
+				return o1.compareTo(o2);
+			}
+		});
+		
+		final ValueEnumeration elements = subsetValue.elements(expectedElements);
+		assertTrue(elements instanceof SubsetEnumerator);
+		
+		SetEnumValue next = null;
+		while ((next = (SetEnumValue) elements.nextElement()) != null) {
+			final int size = next.elems.size();
+			assertTrue(0 <= size && size <= innerSet.size());
+			s.add(next);
+		}
+		assertEquals(expectedElements, s.size());
+	}
+
+	@Test
+	public void testRandomSubsetE7F1() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g"), true);
+		doTest(1 << innerSet.size(), innerSet);
+	}
+
+	@Test
+	public void testRandomSubsetE7F05() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g"), true);
+		doTest(1 << innerSet.size(), innerSet, 64);
+	}
+
+	@Test
+	public void testRandomSubsetE6F1() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f"), true);
+		doTest(1 << innerSet.size(), innerSet);
+	}
+
+	@Test
+	public void testRandomSubsetE5F01() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		doTest(1 << innerSet.size(), innerSet, 4);
+	}
+
+	@Test
+	public void testRandomSubsetE5F025() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		doTest(1 << innerSet.size(), innerSet, 8);
+	}
+
+	@Test
+	public void testRandomSubsetE5F05() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		doTest(1 << innerSet.size(), innerSet, 16);
+	}
+
+	@Test
+	public void testRandomSubsetE5F075() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		doTest(1 << innerSet.size(), innerSet, 24);
+	}
+
+	@Test
+	public void testRandomSubsetE5F1() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		doTest(1 << innerSet.size(), innerSet);
+	}
+
+	@Test
+	public void testRandomSubsetE32F1ENeg6() {
+		final IntervalValue innerSet = new IntervalValue(1, 32);
+		final SubsetValue subsetValue = new SubsetValue(innerSet);
+
+		ValueEnumeration elements = subsetValue.elements(2342);
+		assertTrue(elements instanceof CoinTossingSubsetEnumerator);
+
+		final Set<Value> s = new HashSet<>();
+		SetEnumValue next = null;
+		while ((next = (SetEnumValue) elements.nextElement()) != null) {
+			final int size = next.elems.size();
+			assertTrue(0 <= size && size <= innerSet.size());
+			s.add(next);
+		}
+
+		CoinTossingSubsetEnumerator tossingEnumerator = (CoinTossingSubsetEnumerator) elements;
+		assertTrue(tossingEnumerator.getNumOfPicks() - 100 <= s.size() && s.size() <= tossingEnumerator.getNumOfPicks());
+	}
+
+	@Test
+	public void testRandomSubsetE17F1ENeg3() {
+		final IntervalValue innerSet = new IntervalValue(1, 17);
+		final SubsetValue subsetValue = new SubsetValue(innerSet);
+
+		final ValueEnumeration elements = subsetValue.elements(4223);
+		assertTrue(elements instanceof SubsetEnumerator);
+
+		final Set<Value> s = new HashSet<>();
+		SetEnumValue next = null;
+		while ((next = (SetEnumValue) elements.nextElement()) != null) {
+			final int size = next.elems.size();
+			assertTrue(0 <= size && size <= innerSet.size());
+			s.add(next);
+		}
+
+		final SubsetEnumerator enumerator = (SubsetEnumerator) elements;
+		assertEquals(enumerator.k, s.size());
+	}
+
+	@Test
+	public void testRandomSubsetSubset16() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b"), true);
+		final SubsetValue innerSubset = new SubsetValue(innerSet);
+		final SubsetValue subsetValue = new SubsetValue(innerSubset);
+
+		final int expectedSize = 1 << innerSubset.size();
+		assertEquals(expectedSize, subsetValue.size());
+
+		// No duplicates
+		final Set<Value> s = new HashSet<>(expectedSize);
+		final ValueEnumeration elements = subsetValue.elements(expectedSize);
+		Value next = null;
+		while ((next = elements.nextElement()) != null) {
+			s.add(next);
+		}
+		assertEquals(expectedSize, s.size());
+	}
+
+	@Test
+	public void testRandomSubsetSubset256() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c"), true);
+		final SubsetValue innerSubset = new SubsetValue(innerSet);
+		final SubsetValue subsetValue = new SubsetValue(innerSubset);
+
+		final int expectedSize = 1 << innerSubset.size();
+		assertEquals(expectedSize, subsetValue.size());
+
+		// No duplicates
+		final Set<Value> s = new HashSet<>(expectedSize);
+		final ValueEnumeration elements = subsetValue.elements(expectedSize);
+		Value next = null;
+		while ((next = elements.nextElement()) != null) {
+			s.add(next);
+		}
+		assertEquals(expectedSize, s.size());
+	}
+
+	@Test
+	public void testRandomSubsetSubset65536() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true);
+		final SubsetValue innerSubset = new SubsetValue(innerSet);
+		final SubsetValue subsetValue = new SubsetValue(innerSubset);
+
+		final int expectedSize = 1 << innerSubset.size();
+		assertEquals(expectedSize, subsetValue.size());
+
+		// No duplicates
+		final Set<Value> s = new HashSet<>(expectedSize);
+		final ValueEnumeration elements = subsetValue.elements(expectedSize);
+		Value next = null;
+		while ((next = elements.nextElement()) != null) {
+			s.add(next);
+		}
+		assertEquals(expectedSize, s.size());
+	}
+
+	@Test
+	public void testRandomSubsetSubsetNoOverflow() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		final SubsetValue innerSubset = new SubsetValue(innerSet);
+		final SubsetValue subsetValue = new SubsetValue(innerSubset);
+
+		try {
+			subsetValue.size();
+		} catch (Assert.TLCRuntimeException e) {
+			final Set<Value> s = new HashSet<>();
+
+			final ValueEnumeration elements = subsetValue.elements(2148);
+			assertTrue(elements instanceof CoinTossingSubsetEnumerator);
+			Value next = null;
+			while ((next = elements.nextElement()) != null) {
+				s.add(next);
+			}
+			// No duplicates
+			assertEquals(2148, s.size());
+		}
+	}
+	
+	@Test
+	public void testKSubsetEnumerator() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		
+		assertEquals(1, subset.numberOfKElements(0));
+		assertEquals(4, subset.numberOfKElements(1));
+		assertEquals(6, subset.numberOfKElements(2));
+		assertEquals(4, subset.numberOfKElements(3));
+		assertEquals(1, subset.numberOfKElements(4));
+
+		ValueEnumeration enumerator = subset.kElements(0);
+		assertEquals(new SetEnumValue(), enumerator.nextElement());
+		assertNull(enumerator.nextElement());
+		
+		// Need to sort KElementEnumerator to be able to predict the order in which
+		// elements get returned.
+		enumerator = ((KElementEnumerator) subset.kElements(1)).sort();
+		assertEquals(new SetEnumValue(getValue("a"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("b"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("c"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("d"), false), enumerator.nextElement());
+		assertNull(enumerator.nextElement());
+		
+		enumerator = ((KElementEnumerator) subset.kElements(2)).sort();
+		assertEquals(new SetEnumValue(getValue("a", "b"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("a", "c"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("b", "c"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("a", "d"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("b", "d"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("c", "d"), false), enumerator.nextElement());
+		assertNull(enumerator.nextElement());
+
+		enumerator = ((KElementEnumerator) subset.kElements(3)).sort();
+		assertEquals(new SetEnumValue(getValue("a", "b", "c"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("a", "b", "d"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("a", "c", "d"), false), enumerator.nextElement());
+		assertEquals(new SetEnumValue(getValue("b", "c", "d"), false), enumerator.nextElement());
+		assertNull(enumerator.nextElement());
+		
+		enumerator = ((KElementEnumerator) subset.kElements(4)).sort();
+		assertEquals(new SetEnumValue(getValue("a", "b", "c", "d"), false), enumerator.nextElement());
+		assertNull(enumerator.nextElement());
+	}
+	
+	@Test
+	public void testKSubsetEnumeratorNegative() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		try {
+			subset.kElements(-1);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testKSubsetEnumeratorGTCapacity() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		try {
+			subset.kElements(innerSet.size() + 1);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testNumKSubset() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+
+		assertEquals(1, subset.numberOfKElements(0));
+		assertEquals(5, subset.numberOfKElements(1));
+		assertEquals(10, subset.numberOfKElements(2));
+		assertEquals(10, subset.numberOfKElements(3));
+		assertEquals(5, subset.numberOfKElements(4));
+		assertEquals(1, subset.numberOfKElements(5));
+	}
+
+	@Test
+	public void testNumKSubset2() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g", "h"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+
+		int sum = 0;
+		for (int i = 0; i <= innerSet.size(); i++) {
+			sum += subset.numberOfKElements(i);
+		}
+		assertEquals(1 << innerSet.size(), sum);
+	}
+	
+	@Test
+	public void testNumKSubsetNeg() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+
+		try {
+			subset.numberOfKElements(-1);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testNumKSubsetKGTN() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+
+		try {
+			subset.numberOfKElements(innerSet.size() + 1);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail();
+	}
+	
+	@Test
+	public void testNumKSubsetUpTo62() {
+		for (int i = 1; i < 62; i++) {
+			final SubsetValue subset = new SubsetValue(new IntervalValue(1, i));
+			long sum = 0L;
+			for (int j = 0; j <= i; j++) {
+				sum += subset.numberOfKElements(j);
+			}
+			assertEquals(1L << i, sum);
+		}
+	}
+	
+	@Test
+	public void testNumKSubsetPreventsOverflow() {
+		final IntervalValue innerSet = new IntervalValue(1, 63);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		for (int i = 0; i <= innerSet.size(); i++) {
+			try {
+				subset.numberOfKElements(i);
+			} catch (IllegalArgumentException e) {
+				continue;
+			}
+			fail();
+		}
+	}
+	
+	@Test
+	public void testUnrankKSubsets() {
+		final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		
+		final int sizeS = innerSet.size();
+		for (int k = 0; k < sizeS; k++) {
+			final Unrank unranker = subset.getUnrank(k);
+			// for each k-subset...
+			final long numElementsInKSubset = subset.numberOfKElements(k);
+			final Set<Value> kSubset = new HashSet<>();
+			for (int i = 0; i < numElementsInKSubset; i++) {
+				final Value kElementAt = unranker.subsetAt(i);
+				// check k-Subset is indeed k-Subset
+				assertEquals(k, kElementAt.size());
+				kSubset.add(kElementAt);
+			}
+			// check for no duplicates.
+			assertEquals(numElementsInKSubset, kSubset.size());
+		}
+	}
+
+	@Test
+	public void testUnrank16viaRank() {
+		final IntervalValue innerSet = new IntervalValue(1, 16);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		
+		int size = innerSet.size();
+		
+		final long sizeS = 1L << size; // 2^innerSet.size()
+		final Set<Value> unranked = new HashSet<>((int)sizeS);
+		
+		for (int k = 0; k <= size; k++) {
+			final Unrank unranker = subset.getUnrank(k);
+			// for each k-subset...
+			final long numElementsInKSubset = subset.numberOfKElements(k);
+			for (int i = 0; i < numElementsInKSubset; i++) {
+				final Value kElementAt = unranker.subsetAt(i);
+				assertEquals(k, kElementAt.size());
+				unranked.add(kElementAt);
+			}
+		}
+		assertEquals(sizeS, unranked.size());
+	}
+
+	@Test
+	public void testRandomSetOfSubsets() {
+		final IntervalValue innerSet = new IntervalValue(1, 22);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		
+		final int maxLength = 10;
+		final int k = 23131;
+		final SetEnumValue setOfSubsets = (SetEnumValue) subset.getRandomSetOfSubsets(k, maxLength);
+		assertEquals(k, setOfSubsets.size());
+		
+		final ValueEnumeration elements = setOfSubsets.elements();
+		Value val = null;
+		while ((val = elements.nextElement()) != null) {
+			assertTrue(val.size() <= maxLength);
+		}
+		setOfSubsets.normalize();
+		assertEquals(k, setOfSubsets.size());
+	}
+
+	@Test
+	public void testRandomSetOfSubsets300() {
+		final IntervalValue innerSet = new IntervalValue(1, 300);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		
+		final int maxLength = 9;
+		final int k = 23071;
+		final SetEnumValue setOfSubsets = (SetEnumValue) subset.getRandomSetOfSubsets(k, maxLength);
+		assertEquals(k, setOfSubsets.size());
+		
+		final ValueEnumeration elements = setOfSubsets.elements();
+		Value val = null;
+		while ((val = elements.nextElement()) != null) {
+			assertTrue(val.size() <= maxLength);
+		}
+		setOfSubsets.normalize();
+		assertEquals(k, setOfSubsets.size());
+	}
+
+	@Test
+	public void testRandomSetOfSubsets400() {
+		final IntervalValue innerSet = new IntervalValue(1, 400);
+		final SubsetValue subset = new SubsetValue(innerSet);
+		
+		final int maxLength = 9;
+		final int k = 23077;
+		final SetEnumValue setOfSubsets = (SetEnumValue) subset.getRandomSetOfSubsets(k, maxLength);
+		assertEquals(k, setOfSubsets.size());
+		
+		final ValueEnumeration elements = setOfSubsets.elements();
+		Value val = null;
+		while ((val = elements.nextElement()) != null) {
+			assertTrue(val.size() <= maxLength);
+		}
+		setOfSubsets.normalize();
+		assertEquals(k, setOfSubsets.size());
+	}
+	
+	@Test
+	public void testSubsetNeedsNormalization() {
+		final IntervalValue inner = new IntervalValue(1, 5);
+		final SubsetValue subset = new SubsetValue(inner);
+
+		final ValueVec vec = new ValueVec(subset.size());
+		for (int i = 0; i <= inner.size(); i++) {
+			List<Value> kElements = subset.kElements(i).all();
+			kElements.forEach(e -> vec.addElement(e));
+		}
+        final Value unnormalized = new SetEnumValue(vec, false);
+        
+        final Value normalized = subset.toSetEnum().normalize();
+        
+        assertEquals(normalized, unnormalized);
+	}
+	
+	@Test
+	public void testSubsetNeedsNormalization2() {
+		final IntervalValue inner = new IntervalValue(1, 6);
+		final SubsetValue subset = new SubsetValue(inner);
+
+		final ValueVec vec = new ValueVec(subset.size());
+		final ValueEnumeration bElements = subset.elementsNormalized();
+		bElements.forEach(e -> vec.addElement(e));
+        final Value unnormalized = new SetEnumValue(vec, true);
+        
+        final Value normalized = subset.toSetEnum().normalize();
+        
+        assertEquals(normalized, unnormalized);
+	}
+}
diff --git a/tlatools/test/util/ExecutionStatisticsCollectorTest.java b/tlatools/test/util/ExecutionStatisticsCollectorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c991bb67183820c6266be2b1605bd0287f57b13
--- /dev/null
+++ b/tlatools/test/util/ExecutionStatisticsCollectorTest.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.junit.Test;
+
+public class ExecutionStatisticsCollectorTest {
+
+	// Opt-out
+
+	@Test
+	public void testOptOutNoFile() throws IOException {
+		final String tmpdir = System.getProperty("java.io.tmpdir");
+		final File tempFile = new File(tmpdir + File.separator + "esc" + System.currentTimeMillis() + ".txt");
+		tempFile.deleteOnExit();
+		
+		final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath());
+		
+		final String identifierA = esc.getIdentifier();
+		assertNotNull(identifierA);
+		assertNotNull(esc.getIdentifier());
+		assertEquals(identifierA, esc.getIdentifier());
+	}
+
+	@Test
+	public void testOptOutUnreadableFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		tempFile.setReadable(false);
+		tempFile.deleteOnExit();
+		
+		final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath());
+		
+		assertNull(esc.getIdentifier());
+	}
+
+	@Test
+	public void testOptOutEmptyFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		tempFile.deleteOnExit();
+		
+		final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath());
+		
+		assertNull(esc.getIdentifier());
+	}
+
+	@Test
+	public void testOptOutNoESCFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		Files.write(tempFile.toPath(), ("   " + ExecutionStatisticsCollector.Selection.NO_ESC.toString() + "   ").getBytes());
+		tempFile.deleteOnExit();
+		
+		final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath());
+		
+		assertNull(esc.getIdentifier());
+	}
+
+	@Test
+	public void testOptOutRandomIdFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		Files.write(tempFile.toPath(), ("   " + ExecutionStatisticsCollector.Selection.RANDOM_IDENTIFIER.toString() + "   ").getBytes());
+		tempFile.deleteOnExit();
+		
+		final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath());
+		
+		final String identifierA = esc.getIdentifier();
+		assertNotNull(identifierA);
+		final String identifierB = esc.getIdentifier();
+		assertNotNull(identifierB);
+		assertNotEquals(identifierA, identifierB);
+	}
+
+	@Test
+	public void testOptOutUserDefinedIdFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		Files.write(tempFile.toPath(), "   123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ   ".getBytes());
+		tempFile.deleteOnExit();
+		
+		final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath());
+		
+		assertEquals("123456789ABCDEFGHIJKLMNOPQRSTUVW", esc.getIdentifier());
+	}
+	
+	// Opt-In
+
+	@Test
+	public void testNoFile() {
+		assertNull(new ExecutionStatisticsCollector("/path/does/not/exist").getIdentifier());
+	}
+
+	@Test
+	public void testUnreadableFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		tempFile.setReadable(false);
+		tempFile.deleteOnExit();
+		
+		assertNull(new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier());
+	}
+
+	@Test
+	public void testEmptyFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		tempFile.deleteOnExit();
+		
+		assertNull(new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier());
+	}
+
+	@Test
+	public void testNoESCFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		Files.write(tempFile.toPath(), ("   " + ExecutionStatisticsCollector.Selection.NO_ESC.toString() + "   ").getBytes());
+		tempFile.deleteOnExit();
+		
+		assertNull(new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier());
+	}
+
+	@Test
+	public void testRandomIdFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		Files.write(tempFile.toPath(), ("   " + ExecutionStatisticsCollector.Selection.RANDOM_IDENTIFIER.toString() + "   ").getBytes());
+		tempFile.deleteOnExit();
+		
+		ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(tempFile.getAbsolutePath());
+		
+		final String identifierA = esc.getIdentifier();
+		assertNotNull(identifierA);
+		final String identifierB = esc.getIdentifier();
+		assertNotNull(identifierB);
+		assertNotEquals(identifierA, identifierB);
+	}
+
+	@Test
+	public void testUserDefinedIdFile() throws IOException {
+		File tempFile = File.createTempFile("esc", "txt");
+		Files.write(tempFile.toPath(), "   123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ   ".getBytes());
+		tempFile.deleteOnExit();
+		
+		assertEquals("123456789ABCDEFGHIJKLMNOPQRSTUVW", new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier());
+	}
+}
diff --git a/tlatools/test/util/IsolatedTestCaseRunner.java b/tlatools/test/util/IsolatedTestCaseRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..05c44d4a1e4e85006c5a00146da232f12157fefe
--- /dev/null
+++ b/tlatools/test/util/IsolatedTestCaseRunner.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.JUnit4;
+import org.junit.runners.model.InitializationError;
+
+public class IsolatedTestCaseRunner extends Runner {
+
+	private final JUnit4 delegate;
+
+	public IsolatedTestCaseRunner(final Class<?> testFileClass)
+			throws InitializationError, ClassNotFoundException, InstantiationException, IllegalAccessException,
+			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+	
+		// Since IsolatedTestCaseRunner runs several isolated tests in a single VM, it
+		// is good practice to clean resources before each new test.
+		System.gc();
+		
+		ClassLoader classLoader = IsolatedTestCaseRunner.class.getClassLoader();
+		if (classLoader instanceof URLClassLoader) {
+			// When run within e.g. Eclipse, the classloader might not be of instance
+			// URLClassLoader. In this case, just use the provided class loader which won't
+			// isolate tests. A set of tests can thus only be run in a single VM from ant
+			// or maven which pass a URLClassLoader.
+			classLoader = new IsolatedTestCaseClassLoader((URLClassLoader) classLoader);
+		}
+		delegate = new JUnit4(classLoader.loadClass(testFileClass.getName()));
+	}
+
+	@Override
+	public Description getDescription() {
+		return delegate.getDescription();
+	}
+
+	@Override
+	public void run(RunNotifier notifier) {
+		delegate.run(notifier);
+	}
+	
+	private class IsolatedTestCaseClassLoader extends URLClassLoader {
+
+		private final Map<String, Class<?>> cache = new HashMap<>();
+		private final Set<String> packages = new HashSet<>();
+		
+		public IsolatedTestCaseClassLoader(URLClassLoader classLoader) {
+			super(classLoader.getURLs());
+			
+			// All of TLC's java packages. 
+			packages.add("tla2sany");
+			packages.add("pcal");
+			packages.add("util");
+			packages.add("tla2tex");
+			packages.add("tlc2");
+		}
+
+		@Override
+		public Class<?> loadClass(String name) throws ClassNotFoundException {
+			if (cache.containsKey(name)) {
+				// Cache to not load classes twice for a single test case which results in a
+				// LinkageError.
+				return cache.get(name);
+			}
+			for (final String pkg : packages) {
+				if (name.startsWith(pkg)) {
+					final Class<?> findClass = findClass(name);
+					cache.put(name, findClass);
+					return findClass;
+				}
+			}
+			return super.loadClass(name);
+		}
+	}
+}
diff --git a/tlatools/test/util/SimpleFilenameToStreamTest.java b/tlatools/test/util/SimpleFilenameToStreamTest.java
index 3082129fc822977b8ea928694efa82b0785b3164..18322e3bfaec3c95c7695d3a874f6c185c516d83 100644
--- a/tlatools/test/util/SimpleFilenameToStreamTest.java
+++ b/tlatools/test/util/SimpleFilenameToStreamTest.java
@@ -1,14 +1,18 @@
 package util;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class SimpleFilenameToStreamTest extends TestCase {
+public class SimpleFilenameToStreamTest {
 
 	/**
 	 * Try to load a standard module
 	 */
+	@Test
 	public void testResolveStandardModule() {
 		final SimpleFilenameToStream sfts = new SimpleFilenameToStream();
 		final File file = sfts.resolve("TLC.tla", true);
@@ -16,4 +20,22 @@ public class SimpleFilenameToStreamTest extends TestCase {
 		assertNotNull(file);
 		assertTrue(file.getAbsolutePath() + " does not exist!", file.exists());
 	}
+	
+	/**
+	 * Test whether the fix for #424 still works
+	 */
+	@Test
+	public void testWindowsTLAFileCreation() {
+		if (System.getProperty("os.name").toLowerCase().indexOf("win") > -1) {
+			final String driveLetter = "X:";
+			final String parentDirectory = driveLetter + "\\Develop\\myspecs\\DecentSpec\\";
+			final String child = parentDirectory + "Fromage.tla";
+			final FilenameToStream.TLAFile file = new FilenameToStream.TLAFile(parentDirectory, child, null);
+			final int driveLetterCount = file.getAbsolutePath().split(driveLetter).length - 1;
+			
+			assertTrue("There should be 1 drive letter in the child's absolute path, but there are " + driveLetterCount,
+					   (1 == driveLetterCount));
+		}
+	}
+	
 }
diff --git a/tlatools/test/util/StringHelperTest.java b/tlatools/test/util/StringHelperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5dc5ff71dc2ab365d573fa674f78b30cb44c135
--- /dev/null
+++ b/tlatools/test/util/StringHelperTest.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+
+package util;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import junit.framework.TestCase;
+import util.StringHelper;
+
+public class StringHelperTest extends TestCase {
+
+	public void testGetWords() {
+		String[] words = StringHelper.getWords("Abc def ghi.");
+		assertArrayEquals(new String[]{"Abc", "def", "ghi."}, words);
+
+		words = StringHelper.getWords("Abc  def    ghi.      ");
+		assertArrayEquals(new String[]{"Abc", "def", "ghi."}, words);
+	}
+	public void testGetWordsLeadingSpace() {
+		String[] words = StringHelper.getWords("     Abc def ghi.");
+		assertArrayEquals(new String[]{"Abc", "def", "ghi."}, words);
+	}
+//TODO What is the intended behavior here?
+//	public void testGetWordsEmpty() {
+//		String[] words = StringHelper.getWords("");
+//		assertArrayEquals(new String[0], words);
+//	}
+	public void testGetWordsNull() {
+		try {
+			StringHelper.getWords(null);
+		} catch (NullPointerException e) {
+			return;
+		}
+		fail("null should not be accepted!");
+	}
+	
+	public void testCopyString0() {
+		String copyString = StringHelper.copyString("abc", 0);
+		assertEquals("", copyString);
+	}
+	public void testCopyStringPos1() {
+		String copyString = StringHelper.copyString("abc", 1);
+		assertEquals("abc", copyString);
+	}
+	public void testCopyStringPos5() {
+		String copyString = StringHelper.copyString("abc", 5);
+		assertEquals("abcabcabcabcabc", copyString);
+	}
+	public void testCopyStringNeg1() {
+		String copyString = StringHelper.copyString("abc", -1);
+		assertEquals("", copyString);
+	}
+	public void testCopyStringNeg5() {
+		String copyString = StringHelper.copyString("abc", -5);
+		assertEquals("", copyString);
+	}
+//TODO What is the intended behavior of passing a null string?
+//	public void testCopyStringNullPos() {
+//		try {
+//			StringHelper.copyString(null, 1);
+//		} catch (NullPointerException e) {
+//			return;
+//		}
+//		fail("null should not be accepted!");
+//	}
+//TODO What is the intended behavior of passing a null string?
+//	public void testCopyStringNullNeg() {
+//		try {
+//			StringHelper.copyString(null, -1);
+//		} catch (NullPointerException e) {
+//			return;
+//		}
+//		fail("null should not be accepted!");
+//	}
+	
+	public void testOnlySpaces() {
+		assertTrue(StringHelper.onlySpaces("        "));
+		assertTrue(StringHelper.onlySpaces(" "));
+		assertFalse(StringHelper.onlySpaces("  a  "));
+		assertFalse(StringHelper.onlySpaces(" a"));
+		assertFalse(StringHelper.onlySpaces("a "));
+		assertFalse(StringHelper.onlySpaces("a"));
+	}
+	public void testOnlySpacesNull() {
+		try {
+			StringHelper.onlySpaces(null);
+		} catch (NullPointerException e) {
+			return;
+		}
+		fail("null should not be accepted!");
+	}
+//TODO What is the intended behavior for the empty string?
+//	public void testOnlySpacesEmptyString() {
+//		assertFalse(StringHelper.onlySpaces(""));
+//	}
+	
+	public void testTrimFront() {
+		assertEquals("", StringHelper.trimFront("    "));
+		assertEquals("a", StringHelper.trimFront(" a"));
+		assertEquals("a ", StringHelper.trimFront(" a "));
+		assertEquals("aaa", StringHelper.trimFront(" aaa"));
+		assertEquals("aa", StringHelper.trimFront("  aa"));
+		assertEquals("a ", StringHelper.trimFront("   a "));
+		assertEquals("aa  a ", StringHelper.trimFront("  aa  a "));
+	}
+	public void testTrimFrontNull() {
+		try {
+			StringHelper.trimFront(null);
+		} catch (NullPointerException e) {
+			return;
+		}
+		fail("null should not be accepted!");
+	}
+	public void testTrimFrontWhitespaces() {
+		assertEquals("Abc def ghi.", StringHelper.trimFront("Abc def ghi."));
+	}
+
+	public void testTrimEnd() {
+		assertEquals("", StringHelper.trimEnd("    "));
+		assertEquals("a", StringHelper.trimEnd("a "));
+		assertEquals(" a", StringHelper.trimEnd(" a "));
+		assertEquals("aaa", StringHelper.trimEnd("aaa   "));
+		assertEquals("aa", StringHelper.trimEnd("aa  "));
+		assertEquals(" a", StringHelper.trimEnd(" a  "));
+		assertEquals(" a aa", StringHelper.trimEnd(" a aa "));
+	}
+	public void testTrimEndNull() {
+		try {
+			StringHelper.trimEnd(null);
+		} catch (NullPointerException e) {
+			return;
+		}
+		fail("null should not be accepted!");
+	}
+	public void testTrimEndWhitespaces() {
+		assertEquals("Abc def ghi.", StringHelper.trimEnd("Abc def ghi."));
+	}
+	
+	public void testLeadingSpace() {
+		assertEquals(1, StringHelper.leadingSpaces(" "));
+		assertEquals(2, StringHelper.leadingSpaces("  "));
+		assertEquals(1, StringHelper.leadingSpaces(" a "));
+		assertEquals(2, StringHelper.leadingSpaces("  a  a  "));
+		assertEquals(0, StringHelper.leadingSpaces("a"));
+	}
+	public void testLeadingSpacesNull() {
+		try {
+			StringHelper.leadingSpaces(null);
+		} catch (NullPointerException e) {
+			return;
+		}
+		fail("null should not be accepted!");
+	}
+	
+	public void testIsIdentifier() {
+		assertFalse(StringHelper.isIdentifier("123"));
+		assertFalse(StringHelper.isIdentifier(" 123"));
+		assertFalse(StringHelper.isIdentifier("123 "));
+		assertFalse(StringHelper.isIdentifier(" 123 "));
+		assertTrue(StringHelper.isIdentifier("1A1"));
+		assertTrue(StringHelper.isIdentifier("_1"));
+		assertTrue(StringHelper.isIdentifier("1_"));
+		assertTrue(StringHelper.isIdentifier("_"));
+	}
+	public void testIsIdentifierNull() {
+		try {
+			StringHelper.isIdentifier(null);
+		} catch (NullPointerException e) {
+			return;
+		}
+		fail("null should not be accepted!");
+	}
+//TODO What is the intended behavior for the empty string?
+//	public void testIsIdentifierMetaChars() {
+//		assertTrue(StringHelper.isIdentifier("\\"));
+//	}
+//TODO What is the intended behavior for the empty string?
+//	public void testIsIdentifierEmptyString() {
+//		assertTrue(StringHelper.isIdentifier(""));
+//	}
+}
diff --git a/tlatools/test/util/TLCRuntimeTest.java b/tlatools/test/util/TLCRuntimeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4853b42a8d12cd5f6e6f65ccc78b7c32bd157167
--- /dev/null
+++ b/tlatools/test/util/TLCRuntimeTest.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class TLCRuntimeTest {
+
+	@Test
+	public void testIsThroughputOptimized() {
+		// customBuild.xml executes the tests with UseParallelGC set. This should thus
+		// pass. It will pass without UseParallelGC set on a pre Java 9 VM where
+		// parallel GC used to be the default.
+		assertTrue(TLCRuntime.getInstance().isThroughputOptimizedGC());
+	}
+}
diff --git a/tlatools/test/util/TestPrintStream.java b/tlatools/test/util/TestPrintStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad12a39b826eba7f167eed3e091692ab88490f98
--- /dev/null
+++ b/tlatools/test/util/TestPrintStream.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Research. All rights reserved. 
+ *
+ * The MIT License (MIT)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy 
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. 
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contributors:
+ *   Markus Alexander Kuppe - initial API and implementation
+ ******************************************************************************/
+package util;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestPrintStream extends PrintStream {
+
+	private final StringBuffer buf = new StringBuffer();
+	private final List<String> strings = new ArrayList<String>();
+	
+	public TestPrintStream() {
+        super(new PipedOutputStream());
+	}
+
+	/* (non-Javadoc)
+	 * @see java.io.PrintStream#println(java.lang.String)
+	 */
+	public void println(String x) {
+		strings.add(x);
+		buf.append(x + "\n");
+		System.out.println(x);
+		super.println(x);
+	}
+	
+	public void assertEmpty() {
+		assertTrue(this.strings.isEmpty());
+	}
+	
+	public void assertContains(final String seq) {
+		assertTrue(buf.toString().contains(seq));
+	}
+	
+	public void assertSubstring(String substring) {
+		for (String string : strings) {
+			if (string.contains(substring)) {
+				return;
+			}
+		}
+		fail("Substring not found");
+	}
+	
+	public void assertNoSubstring(String substring) {
+		for (String string : strings) {
+			if (string.contains(substring)) {
+				fail("Substring not found");
+			}
+		}
+	}
+}
\ No newline at end of file